Express / Connect
Any Node web server - page yourself the moment a 5xx leaks.
One-line Express/Connect error middleware. Goes after all your routes, catches 5xx responses + thrown errors, and fires a Chirp notification with the route, status code, and the first stack frame. Never blocks a request: the Chirp transport is fire-and-forget so a Chirp outage cannot worsen an incident.
Use this when you want fast pager-style alerts for production errors without standing up Sentry / Datadog. Pair with the rate limiter (`rateLimit: "5/min"`) to avoid getting buried under cascading-failure spam - Chirp deduplicates identical (route, status, error.message) tuples within the window.
Prerequisites
- Node 18+ with Express 4+ or Connect.
- A Chirp API key set in the environment.
Setup
- 1
Install the SDK
Single dep. The Express middleware lives at
chirp/express.shellnpm install chirp - 2
Set CHIRP_API_KEY in production env
However your platform handles env vars: Railway/Render/Heroku config var, Kubernetes secret, .env file (don't commit). The middleware reads
process.env.CHIRP_API_KEYon startup and caches it. - 3
Mount the middleware after your routes
Order matters: Express error middleware must come AFTER all routes and the request body parser. Mount it last.
server.tsimport express from 'express'; import { chirpErrorNotifier } from 'chirp/express'; const app = express(); // Body parser, routes, etc. app.use(express.json()); app.use('/api', apiRoutes); // Error notifier - last middleware in the chain. app.use(chirpErrorNotifier({ service: 'api', env: process.env.NODE_ENV ?? 'production', rateLimit: '10/min', // dedupe + rate-limit window ignore: [400, 401, 404], // don't ping on these (default: [400-499]) })); app.listen(3000); - 4
(Optional) Wrap async handlers explicitly
Express 4 doesn't auto-catch async errors. Either upgrade to Express 5, or wrap async handlers with
chirp/express'sasyncHandlerso thrown errors hit the notifier instead of crashing the process.typescriptimport { asyncHandler } from 'chirp/express'; app.get('/users/:id', asyncHandler(async (req, res) => { const user = await db.users.findById(req.params.id); res.json(user); }));
What you’ll see
Notification: "API · 500" - title is the HTTP method + route (`POST /users`), body is the error class + first line of the stack. Severity-tinted (5xx red, 4xx muted). Tap to deep-link to your error tracker if you've configured `errorTrackerBaseUrl`. Bursts of identical errors collapse into one notification per `rateLimit` window.
Troubleshooting
- Errors don't fire pings.
- Most common: middleware mounted before routes. Express only routes errors to the *next* error middleware, so it must come AFTER everything. Verify by adding
app.get('/boom', () => { throw new Error('test') })and hitting it. - Async route errors crash the process instead of pinging.
- Express 4 doesn't auto-catch async throws. Either wrap with
asyncHandleror upgrade to Express 5. Crashing is the default behavior; the notifier never sees the error. - Getting paged for 4xx errors.
- Default
ignoreis[400-499]. If you've overriddenignore, restore the default or be explicit:ignore: [400, 401, 403, 404, 422].