{"id":17639,"library":"express-slonik","title":"Slonik Transaction Middleware for Express.js","description":"express-slonik provides an Express.js middleware for managing PostgreSQL transactions using the Slonik client library. It simplifies the integration of database transactions into Express route handlers, ensuring that all database operations within a request either commit successfully or roll back entirely. The current stable version is 3.2.0, released in October 2023. The project maintains a frequent release cadence, primarily to align with new major versions of its peer dependency, Slonik, adding support for recent Slonik versions shortly after their release. Its key differentiator is its zero-dependency approach (beyond Express and Slonik itself) and its focus on robust transaction management within the Express middleware pattern, including support for isolation levels and explicit transaction control across multiple handlers.","status":"active","version":"3.2.0","language":"javascript","source_language":"en","source_url":"https://github.com/AndrewJo/express-slonik","tags":["javascript","nodejs","typescript","postgresql","postgres","express","expressjs","middleware"],"install":[{"cmd":"npm install express-slonik","lang":"bash","label":"npm"},{"cmd":"yarn add express-slonik","lang":"bash","label":"yarn"},{"cmd":"pnpm add express-slonik","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core database client for PostgreSQL transactions. express-slonik wraps Slonik's transaction capabilities.","package":"slonik","optional":false},{"reason":"The middleware is built for the Express.js web framework.","package":"express","optional":false}],"imports":[{"note":"The primary export is a default export, not a named one.","wrong":"import { createMiddleware } from 'express-slonik';","symbol":"createMiddleware","correct":"import createMiddleware from 'express-slonik';"},{"note":"Type import for `req.transaction` context, useful for TypeScript users.","symbol":"TransactionContext","correct":"import { type TransactionContext } from 'express-slonik';"},{"note":"When using CommonJS `require`, access the default export via `.default`.","wrong":"const createMiddleware = require('express-slonik');","symbol":"createMiddleware (CJS)","correct":"const createMiddleware = require('express-slonik').default;"}],"quickstart":{"code":"import express from 'express';\nimport { createPool, sql, NotFoundError } from 'slonik';\nimport createMiddleware from 'express-slonik';\nimport { z } from 'zod';\nimport { Server } from 'http';\n\nconst userSchema = z.object({\n  id: z.number().int(),\n  name: z.string(),\n  email: z.string().email(),\n});\n\nexport const createExpressApp = ({ app, pool }) => {\n  const transaction = createMiddleware(pool);\n\n  app.get(\n    '/user/:id',\n\n    // Starts the transaction.\n    transaction.begin(),\n\n    async (req, res, next) => {\n      try {\n        const userId = parseInt(req.params.id, 10);\n        if (isNaN(userId)) {\n          return res.status(400).json({ message: 'Invalid user ID' });\n        }\n\n        const user = await req.transaction.one(\n          sql.type(userSchema)`SELECT * FROM users WHERE users.id = ${userId}`\n        );\n\n        res.json(user);\n      } catch (error) {\n        if (error instanceof NotFoundError) {\n          res.status(404).json({\n            name: error.name,\n            message: `User with given id (${req.params.id}) not found.`,\n          });\n          return;\n        }\n\n        next(error);\n      }\n    },\n\n    // Optional: Explicitly commits the transaction. If omitted, it auto-commits on response send or rolls back on error.\n    transaction.end()\n  );\n\n  return app;\n};\n\n/**\n * Gracefully attempt to shut down the server.\n */\nasync function shutdownHandler(server: Server, pool) {\n  return async function () {\n    server.close();\n    await pool.end(); // Ensure the Slonik pool is also terminated\n  };\n}\n\n(async function () {\n  const app = express();\n  // Ensure DATABASE_URL is set in your environment variables, e.g., 'postgres://user:pass@host:port/database'\n  const pool = await createPool(process.env.DATABASE_URL ?? 'postgres://user:password@localhost:5432/mydb');\n  const server = createExpressApp({ app, pool }).listen(8080, () => {\n    console.log('Server listening on http://localhost:8080');\n  });\n\n  process\n    .on('SIGTERM', shutdownHandler(server, pool))\n    .on('SIGINT', shutdownHandler(server, pool));\n})();","lang":"typescript","description":"This example demonstrates basic usage, setting up an Express application with Slonik, initializing express-slonik middleware, and handling a GET request within a database transaction."},"warnings":[{"fix":"Ensure your project's `slonik` dependency is at version `33.x.x` or higher when upgrading to `express-slonik` v3.x.x. Specifically, peer dependency `slonik` should be `>=33.0.0 <38.0.0` for `express-slonik@3.2.0`.","message":"Version 3.0.0 introduced a breaking change by upgrading to Slonik v33. This change is not backward compatible with older Slonik versions due to API differences in Slonik itself.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Upgrade your `slonik` dependency to version `30.x.x` or higher before upgrading to `express-slonik` v2.x.x.","message":"Version 2.0.0 dropped support for Slonik versions 29 and below, requiring Slonik v30+.","severity":"breaking","affected_versions":">=2.0.0 <3.0.0"},{"fix":"Ensure `express-slonik` middleware is placed appropriately in your Express middleware chain so that `transaction.begin()` runs before route handlers that need `req.transaction`, and no subsequent middleware overwrites `req.transaction`.","message":"Transaction middleware relies on `req.transaction` being available. If other middleware modifies `req` in a conflicting way, it might cause issues.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"For explicit control, always include `transaction.end()` after your route logic. Implement robust error handling (`try-catch`) to manage transaction outcomes precisely, especially for non-critical errors that shouldn't roll back the entire transaction.","message":"Failing to explicitly call `transaction.end()` or having unhandled errors within a transactional route handler will automatically commit/rollback the transaction. While this is often desired, it can lead to unexpected behavior if manual control is intended.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Ensure `transaction.begin()` is used before the route handler that accesses `req.transaction`. Verify `slonik` is installed and meets the `express-slonik` peer dependency requirements (e.g., `npm ls slonik` or `yarn why slonik`).","cause":"`req.transaction` is undefined, typically because `transaction.begin()` middleware was not run or `slonik` peer dependency is missing/incorrect.","error":"TypeError: Cannot read properties of undefined (reading 'one')"},{"fix":"Upgrade your `slonik` dependency to a compatible version, e.g., `npm install slonik@^37` or `yarn add slonik@^37`.","cause":"The installed `slonik` version does not satisfy the peer dependency range specified by `express-slonik`.","error":"Error: Peer dependency \"slonik\" is not met. Expected \">=33.0.0 <38.0.0\" but found \"^32.0.0\"."},{"fix":"Either change your file to use CommonJS `require()` syntax (`const createMiddleware = require('express-slonik').default;`) or configure your project to use ES Modules by adding `\"type\": \"module\"` to your `package.json` and using `.mjs` file extensions if necessary.","cause":"Attempting to use ES module `import` syntax in a CommonJS (`.js` without `\"type\": \"module\"` in `package.json`) Node.js environment.","error":"SyntaxError: Cannot use import statement outside a module"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}