javascript
41 lines · 7 steps
A per-request transaction middleware in Express
An Express middleware that opens a database transaction per request and commits or rolls it back based on the response outcome.
Explained by
highlit
1const { pool } = require('./db');
2
3function withTransaction() {
4 return async (req, res, next) => {
5 const client = await pool.connect();
6 req.db = client;
7
8 let settled = false;
9 const finalize = async (commit) => {
10 if (settled) return;
11 settled = true;
12 try {
13 await client.query(commit ? 'COMMIT' : 'ROLLBACK');
14 } catch (err) {
15 console.error('transaction finalize failed', err);
16 } finally {
17 client.release();
18 }
19 };
20
21 try {
22 await client.query('BEGIN');
23 } catch (err) {
24 client.release();
25 return next(err);
26 }
27
28 res.on('finish', () => {
29 const success = res.statusCode < 400;
30 finalize(success);
31 });
32
33 res.on('close', () => {
34 if (!res.writableEnded) finalize(false);
35 });
36
37 next();
38 };
39}
40
41module.exports = { withTransaction };
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Tying transaction commit/rollback to the response lifecycle keeps request handlers free of manual cleanup.
- 2A one-shot guard flag prevents double-finalizing when multiple response events fire.
- 3Always release pooled connections in a finally block so a failed query never leaks a client.
Related explainers
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
typescript
import { Injectable, Scope, Inject, NotFoundException } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; import { DataSource } from 'typeorm';
Per-tenant database connections in NestJS
multi-tenancy
connection-pooling
dependency-injection
Advanced
8 steps
javascript
import { useEffect, useRef, useState } from 'react'; export function useDelayedFlag(active, delay = 300) { const [visible, setVisible] = useState(false);
Delaying a loading spinner with a React hook
custom-hooks
debouncing
cleanup
Intermediate
8 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-per-request-transaction-middleware-in-express-explained-javascript-8cba/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.