Frameworks & ORMs
Framework-specific setup for Express, Fastify, Prisma, and Sequelize — plus manual tracking for custom events.
The http.Server patch captures every inbound request automatically for any Node.js framework. Framework-specific patches add enrichment — route patterns, params, parsed body, and authenticated user.
Express
Express is auto-detected when installed — no extra setup needed. The Express patch captures:
- Route pattern —
req.route.path(e.g./api/orders/:id) - Params —
req.params(e.g.{ id: 'order-123' }) - Body —
req.body(parsed byexpress.json()or similar) - User —
req.user(set by auth middleware like Passport, express-jwt, etc.)
const { instrument } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/orders/:id/ship', async (req, res) => {
// Route pattern, params, body, and req.user all captured automatically
res.json({ shipped: true });
});Fastify
Fastify requires an explicit call because the Fastify instance is user-created:
const { instrument, instrumentFastify } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
const fastify = require('fastify')();
// Pass your Fastify instance to enable route-level enrichment
instrumentFastify(fastify);
fastify.get('/api/orders/:id', async (request) => {
return { id: request.params.id, status: 'shipped' };
});
fastify.listen({ port: 3000 });instrumentFastify() adds Fastify-specific enrichment:
- Route pattern —
request.routeOptions.url(e.g./api/orders/:id) instead of the raw URL - Params —
request.params(e.g.{ id: 'order-123' }) - Body —
request.body(parsed by Fastify) - User —
request.user(set by auth plugins like@fastify/jwt)
Without instrumentFastify(), requests are still tracked — but with raw paths instead of route patterns, and without params or user info.
Fastify with auth
If you use @fastify/jwt or similar auth plugins that set request.user, the user is captured automatically:
const { instrument, instrumentFastify } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
const fastify = require('fastify')();
await fastify.register(require('@fastify/jwt'), { secret: 'your-secret' });
instrumentFastify(fastify);
fastify.addHook('onRequest', async (request) => {
await request.jwtVerify();
});
fastify.get('/api/orders/:id', async (request) => {
// request.user is captured automatically
return { id: request.params.id };
});The user is picked up from both onRequest hooks (early auth) and preHandler hooks (late auth). Sensitive fields like password, token, and secret are automatically stripped.
Prisma
Prisma requires an explicit call because the client is instantiated in your code:
const { instrument, instrumentPrisma } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// Pass your Prisma client to enable ORM-level tracking
instrumentPrisma(prisma);This tracks every create, update, upsert, delete, findUnique, findFirst, and findMany call with the model name and operation type.
Sequelize
Sequelize requires an explicit call because the instance is user-created:
const { instrument, instrumentSequelize } = require('@stdiolabs/provenance-sdk/auto');
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
});
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL);
// Pass your Sequelize instance to enable ORM-level tracking
instrumentSequelize(sequelize);This hooks into Sequelize's global lifecycle hooks and tracks:
afterCreate/afterBulkCreate→CREATEafterUpdate/afterBulkUpdate/afterUpsert→UPDATEafterDestroy/afterBulkDestroy→DELETEafterFind→READ
Model names and resource IDs are extracted automatically from the instance. The resolver's models map works the same as with Prisma:
instrument({
apiKey: process.env.PROVENANCE_API_KEY,
origin: 'order-service',
models: {
Order: { resourceType: 'PURCHASE_ORDER' }, // override convention
AuditLog: null, // exclude from tracking
},
});Manual tracking
Inside an instrumented request, you can manually add interactions to the current buffer:
const { track, getContext } = require('@stdiolabs/provenance-sdk/auto');
app.post('/api/orders/:id/process', async (req, res) => {
// Your business logic...
const result = await processOrder(req.params.id);
// Manually track a domain event
track({
resourceType: 'ORDER',
resourceId: req.params.id,
action: 'PROCESS',
interaction: { result: result.status, processor: 'stripe' },
});
// Read the current UOW ID
const ctx = getContext();
console.log('UOW:', ctx.uowId);
res.json(result);
});Manual interactions are batched and flushed with the auto-tracked ones — same UOW, same flush.