D3 Edge
SDKs

Vercel

Routing Middleware for any framework on Vercel. Install, configure, verify.

The Vercel SDK is Routing Middleware: a root middleware.ts that runs in the request path, before the cache, for any framework on Vercel. It extracts request metadata, asks the decision service for a decision, and either lets the request continue or returns a 403. No proxy hop, no DNS change.

Install

npm install @d3-inc/d3-edge-vercel-middleware

Create middleware.ts at the root of your project:

import { createD3Middleware, config } from '@d3-inc/d3-edge-vercel-middleware';

export default createD3Middleware();
export { config };

Set two environment variables in your Vercel project. The key belongs in the secret store, not in code:

VariableValue
POLICY_WORKER_URLhttps://edge-api.d3.com/v1/decision
POLICY_ADAPTER_KEYThe key from API keys (created here)

Deploy, then verify it worked.

Already have a middleware.ts? Composing createD3Middleware() with an existing middleware function isn't supported. Tell us what your existing middleware does and we'll help you sequence it.

Testing locally? Leave POLICY_WORKER_URL unset in local dev. The middleware becomes a passthrough and marks every response with x-d3-edge: disabled, so you can confirm it loads without making a single network call. Set the variable only in the deployed environment.

Configuration

Every option can be set as an argument to createD3Middleware() or as an environment variable; arguments win.

OptionEnv varTypeDefaultWhat it does
policyWorkerUrlPOLICY_WORKER_URLstring (URL)unsetDecision endpoint. Unset means passthrough: the middleware does nothing.
adapterKeyPOLICY_ADAPTER_KEYstringunsetYour org's key, sent as the bearer on every decision call. Required for anything to be logged.
timeoutMsPOLICY_TIMEOUT_MSnumber (ms)150Hard budget for the decision call.
failModeFAIL_MODE"open" | "closed"openopen: pass traffic when the call fails. closed: block instead.

The exported config matcher excludes Vercel's own build assets (_next/static, _next/image, favicon.ico). Files you serve from public/ are not excluded by default. If you have large uploads or heavy static routes, extend the matcher to skip them; non-GET requests have their body read to compute a content digest for signature verification.

Fail-open is the default on purpose. The worst case D3 Edge adds to your site is the timeout budget, never an outage. Only set FAIL_MODE=closed if serving unclassified traffic is worse for you than serving a 403.

Response shape

Every response that passed through the middleware carries an x-d3-edge header: disabled, pass, block, fail-open, or fail-closed. A blocked request gets a 403 with a JSON body:

{ "blocked": true, "ruleId": "…", "tier": "…", "identity": "…" }

ruleId is absent when the block came from your org's default action rather than a specific rule. Don't assume it's always present.

Troubleshooting

Nothing shows up in the log. Check that POLICY_WORKER_URL is set in the deployed environment; unset is silent passthrough by design. Then check your Vercel logs. The middleware logs every decision failure, prefixed policy call rejected: with the HTTP status and reason.

401 with unknown-key, expired, or revoked. The key is wrong, past its expiry, or was revoked in the dashboard. Create a fresh one and update the secret.

503 with not-configured. Our side, not yours: key verification isn't ready for your org yet. If it persists more than a few minutes, contact us.

Middleware doesn't run at all. It must be middleware.ts at the project root with a default export. If you replaced the exported config with your own matcher, confirm it covers the routes you're testing.

On this page