@device-router/middleware-express
v0.4.0
Published
Express middleware for DeviceRouter — device classification and rendering hints per request
Maintainers
Readme
@device-router/middleware-express
Express middleware for DeviceRouter. Adds device classification and rendering hints to every request.
Installation
pnpm add @device-router/middleware-express @device-router/storage cookie-parserFor automatic probe injection:
pnpm add @device-router/probeQuick start
import express from 'express';
import cookieParser from 'cookie-parser';
import { createDeviceRouter } from '@device-router/middleware-express';
import { MemoryStorageAdapter } from '@device-router/storage';
const app = express();
const { middleware, probeEndpoint } = createDeviceRouter({
storage: new MemoryStorageAdapter(),
});
app.use(express.json());
app.use(cookieParser());
app.post('/device-router/probe', probeEndpoint);
app.use(middleware);
app.get('/', (req, res) => {
const profile = req.deviceProfile;
if (profile?.hints.preferServerRendering) {
return res.send(renderSSR());
}
if (profile?.hints.deferHeavyComponents) {
return res.send(renderLite());
}
res.send(renderFull());
});
app.listen(3000);How it works
- Probe endpoint receives device signals from the browser and stores a classified profile
- Middleware reads the session cookie, loads the profile from storage, and attaches it to
req.deviceProfile - Your route handlers use
req.deviceProfile.hintsandreq.deviceProfile.tiersto adapt responses
Probe auto-injection
Automatically inject the probe <script> into HTML responses before </head>:
const { middleware, probeEndpoint, injectionMiddleware } = createDeviceRouter({
storage: new MemoryStorageAdapter(),
injectProbe: true,
probeNonce: 'my-csp-nonce', // optional, for Content-Security-Policy
});
app.use(injectionMiddleware); // before routes
app.post('/device-router/probe', probeEndpoint);
app.use(middleware);Streaming responses: Injection intercepts
res.send()and requires the body to be a string. If you stream HTML viares.write(), the injection is silently skipped. Add the probe<script>tag to your HTML shell manually instead.
Custom thresholds
Override default tier classification boundaries:
const { middleware, probeEndpoint } = createDeviceRouter({
storage,
thresholds: {
cpu: { lowUpperBound: 4, midUpperBound: 8 },
memory: { midUpperBound: 8 },
},
});Options
| Option | Type | Default | Description |
| --------------------- | -------------------------------------- | ----------------- | --------------------------------------------- |
| storage | StorageAdapter | (required) | Storage backend for profiles |
| cookieName | string | 'dr_session' | Session cookie name |
| cookiePath | string | '/' | Cookie path |
| cookieSecure | boolean | false | Set Secure flag on the session cookie |
| ttl | number | 86400 (24h) | Profile TTL in seconds |
| rejectBots | boolean | true | Reject bot/crawler probe submissions |
| probePath | string | — | Custom probe endpoint path |
| thresholds | TierThresholds | Built-in defaults | Custom tier thresholds (validated at startup) |
| injectProbe | boolean | false | Auto-inject probe into HTML |
| probeNonce | string \| ((req: Request) => string) | — | CSP nonce for injected script |
| fallbackProfile | FallbackProfile | — | Fallback profile for first requests |
| classifyFromHeaders | boolean | false | Classify from UA/Client Hints |
| onEvent | OnEventCallback | — | Observability callback for logging/metrics |
Observability
Pass an onEvent callback to receive events for classification, storage, bot rejection, and errors:
const { middleware, probeEndpoint } = createDeviceRouter({
storage,
onEvent: (event) => {
console.log(`[device-router] ${event.type}`, event);
},
});See the Observability guide for details.
Exports
createDeviceRouter(options)— All-in-one setup returning{ middleware, probeEndpoint, injectionMiddleware? }createMiddleware(options)— Standalone middlewarecreateProbeEndpoint(options)— Standalone probe endpoint handlercreateInjectionMiddleware(options)— Standalone probe injection middleware
Prerequisites
cookie-parser— required for session cookie handling (req.cookies)
Compatibility
- Express 4.x and 5.x
- Node.js >= 20
License
MIT
