Express Integration
Capture errors and request context from Express.js applications.
Installation
The Express integration is included in the main package:
npm install @statly/observeSetup
import express from 'express';
import { init } from '@statly/observe';
import { requestHandler, expressErrorHandler } from '@statly/observe/express';
// Initialize SDK first
init({
dsn: 'https://[email protected]/your-org',
environment: 'production',
});
const app = express();
// Add request handler FIRST (before routes)
app.use(requestHandler());
// Your routes
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/error', (req, res) => {
throw new Error('Test error');
});
// Add error handler LAST (after routes)
app.use(expressErrorHandler());
app.listen(3000);Request Handler
The requestHandler() middleware:
- Records request start time for duration calculation
- Adds HTTP request breadcrumb
- Sets transaction name from route
- Captures user from
req.user(if using Passport.js or similar)
app.use(requestHandler());The handler adds context to req.statlyContext:
{
transactionName: 'GET /api/users/:id',
startTime: 1705320000000
}Error Handler
The expressErrorHandler() captures unhandled errors:
app.use(expressErrorHandler());Options
app.use(expressErrorHandler({
shouldHandleError: (error) => {
// Only capture 5xx errors
return error.status >= 500;
},
}));What's Captured
For each error, the handler captures:
Request Context:
- HTTP method and URL
- Query parameters
- Headers (sanitized)
- Request body (sanitized)
- Client IP address
Automatic Sanitization:
Headers filtered:
authorizationcookiex-api-keyx-auth-token
Body fields filtered:
passwordsecrettokenapiKey,api_keycredit_cardssn
Manual Capture
You can also capture errors manually:
import { captureException } from '@statly/observe';
app.post('/api/users', async (req, res, next) => {
try {
const user = await createUser(req.body);
res.json(user);
} catch (error) {
captureException(error, {
route: '/api/users',
method: 'POST',
userId: req.body.email,
});
next(error);
}
});With Async Handlers
For async route handlers, wrap with try/catch:
app.get('/api/data', async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (error) {
next(error); // Error handler will capture it
}
});Or use a wrapper:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/api/data', asyncHandler(async (req, res) => {
const data = await fetchData();
res.json(data);
}));Full Example
import express from 'express';
import { init, setUser, addBreadcrumb } from '@statly/observe';
import { requestHandler, expressErrorHandler } from '@statly/observe/express';
init({
dsn: process.env.STATLY_DSN,
environment: process.env.NODE_ENV,
});
const app = express();
app.use(express.json());
app.use(requestHandler());
// Set user after authentication
app.use((req, res, next) => {
if (req.user) {
setUser({
id: req.user.id,
email: req.user.email,
});
}
next();
});
// Add custom breadcrumbs
app.post('/api/orders', async (req, res, next) => {
try {
addBreadcrumb({
category: 'order',
message: 'Creating order',
data: { items: req.body.items.length },
});
const order = await createOrder(req.body);
res.json(order);
} catch (error) {
next(error);
}
});
app.use(expressErrorHandler({
shouldHandleError: (error) => error.status >= 500,
}));
app.listen(3000);