Express Integration
The expressContext middleware attaches request context to all queries made within Express route handlers. Uses AsyncLocalStorage for zero-overhead propagation.
Setup
typescript
import express from 'express';
import { createMonitor, expressContext } from '@periodic/arsenic';
const app = express();
const monitor = createMonitor({ /* config */ });
// MUST be before routes
app.use(expressContext(monitor, {
attachUser: (req) => req.user?.id, // optional
}));
// All queries in handlers below will have request context
app.get('/api/users', async (req, res) => {
const users = await User.find({ active: true });
res.json(users);
});
app.listen(3000);Add middleware BEFORE routes
expressContext must be added before any route handlers. Adding it after routes means those routes' queries will have no request context.With user attribution
typescript
// If using JWT auth
app.use(expressContext(monitor, {
attachUser: (req) => {
// Return any string — user ID, email, etc.
return req.user?.id || req.headers['x-user-id'] as string;
},
}));
// Event output will include:
// { "request": { "userId": "user_abc123", ... } }With multiple routers
typescript
import express from 'express';
import { Router } from 'express';
const app = express();
const apiRouter = Router();
// Attach context to app — covers all routers
app.use(expressContext(monitor));
app.use(express.json());
// Mount routers normally
app.use('/api', apiRouter);
apiRouter.get('/users', async (req, res) => {
// Request context is available here
const users = await User.find();
res.json(users);
});Event context field
json
{
"request": {
"id": "req_8f29a3b1c",
"method": "GET",
"route": "/api/users/:id",
"userId": "user_abc123"
}
}Queries made outside of Express handlers (e.g. background jobs, startup scripts) will have no
request field. This is expected behavior — Arsenic still monitors these queries and emits events, just without HTTP context.