Hookdeck Vercel Middleware
The Hookdeck Vercel Middleware adds the ability to authenticate, delay, filter, queue, throttle, and retry asynchronous HTTP requests (e.g., webhooks) made to a Vercel application. The use cases for this include managing webhooks from API providers such as Stripe, Shopify, and Twilio, or when building asynchronous APIs.
Get Started
Before you begin:
Install the Hookdeck Vercel package:
npm i @hookdeck/vercel
Once installed, package a hookdeck.config.js
file is created at the root of your project. Also, the command hookdeck-vercel deploy
is appended to the prebuild
script of your package.json
.
Ensure the match
key in hookdeck.config.js
matches the route you want the middleware to intercept:
const { RetryStrategy, DestinationRateLimitPeriod } = require('@hookdeck/sdk/api');
const hookdeckConfig = {
match: {
'/api/webhooks': {
retry: {
strategy: RetryStrategy.Linear,
count: 5,
interval: 1 * 60 * 1000,
},
rate: {
limit: 10,
period: DestinationRateLimitPeriod.Second,
},
},
},
};
module.exports = hookdeckConfig;
This example configures a retry strategy with a linear back-off and a delivery rate limit of 10 requests per second.
ℹ️ The Hookdeck Vercel Middleware uses VERCEL_BRANCH_URL
by default. If you are not deploying your application via source control, set hookdeckConfig.vercel_url
to a different URL such as process.env.VERCEL_URL
.
Update middleware.ts
(or middleware.js
) to add the Hookdeck Vercel Middleware and ensure config.matcher
has the same (or broader) value as you have in hookdeck.config.js
(e.g., /api/webhooks
):
import { withHookdeck } from '@hookdeck/vercel';
import hookdeckConfig from './hookdeck.config';
import { NextResponse } from 'next/server';
export const config = {
matcher: '/api/webhooks',
};
function middleware(request: Request) {
NextResponse.next();
}
export default withHookdeck(hookdeckConfig, middleware);
If you don't already have a route, create one. For example, app/api/webhooks/route.ts
for the /api/webhooks
endpoint in a Next.js app using the app router:
export async function POST() {
const data = { received: true };
return Response.json(data);
}
Deploy the application to Vercel.
Once the deployment has succeeded, make a request to your middleware endpoint:
curl --location 'http://your.vercel.app/api/webhooks' \
--header 'Content-Type: application/json' \
--data '{
"test": "value"
}'
Check the Vercel logs to see that the middleware is processing the events:
Check the Hookdeck logs to see that the requests are being handled and the events are being processed and delivered:
Configuration
Environment Variables
Environment variables used by the middleware. Get the values from the Hookdeck project secrets.
You can also set these values programmatically within hookdeck.config.js
.
middleware.ts
The Vercel Edge Middleware must be updated as follows:
- Update the exported
config.matcher
to match the route for the Hookdeck Vercel Middleware - Import and use
withHookdeck
middleware wrapper
import { withHookdeck } from '@hookdeck/vercel';
import hookdeckConfig from './hookdeck.config';
export const config = {
matcher: 'path/to/match',
};
function middleware(request: Request) {
}
export default withHookdeck(hookdeckConfig, middleware);
hookdeck.config.js
The minimum configuration is the following, with the match
containing an object with a matching path (path/to/match
) as the key. This value should be the same as the value exported via config.matcher
in middleware.ts
.
const hookdeckConfig = {
match: {
'path/to/match': {},
},
};
module.exports = hookdeckConfig;
IMPORTANT: Ensure config.matcher
in your middleware
includes the routes specified in the hookdeck.config.js
match
fields. Only routes that match both expressions will trigger the Hookdeck functionality.
Full configuration options:
api_key
: The Hookdeck project API Key used to manage the connections within your project. This config value will override the HOOKDECK_API_KEY
environment variable. Get the value from the Hookdeck project secrets.signing_secret
: Used to check the signature of the inbound HTTP request when it is received from Hookdeck. This config value will override the HOOKDECK_SIGNING_SECRET
environment variable. See webhook signature verification. Get the value from the Hookdeck project secrets.vercel_url
: The Vercel URL that receives the requests. If not specified, the url stored in env var VERCEL_BRANCH_URL
will be used.match
: a key-value map of paths for routes and the configuration for each of those routes.
[path]
- the matching string or regex that will be compared or tested against the pathname of the URL that triggered the middleware. If there is more than one match, then the request is sent to every matching configuration.
retry
: use the values specified in the Retry documentation to configure Hookdeck's retry strategy.delay
: the number of milliseconds to hold the event when it arrives at Hookdeck.filters
: specify different filters to exclude some events from forwarding. Use the syntax specified in the Filter documentation.rate
: set the delivery rate to be used for the outcoming traffic. Check the syntax in the rate_limit_period
key in the Destination documentation.verification
: the inbound (source) verification mechanism to use. Check all possible values and syntax in the Source documentation.custom_response
: the custom response to send back the webhook origin. Check the syntax in the Source documentation.
Here's an example with all the configuration options:
const {
RetryStrategy,
DestinationRateLimitPeriod,
SourceCustomResponseContentType,
} = require('@hookdeck/sdk/api');
const hookdeckConfig = {
match: {
'/api/webhooks': {
retry: {
strategy: RetryStrategy.Linear,
count: 5,
interval: 1 * 60 * 1000,
},
delay: 1 * 60 * 1000,
filters: [
{
headers: {
'x-my-header': 'my-value',
},
body: {},
query: {},
path: {},
},
],
rate: {
limit: 10,
period: DestinationRateLimitPeriod.Minute,
},
verification: {
type: 'API_KEY',
configs: {
header_key: 'x-my-api-key',
api_key: 'this-is-my-token',
},
},
custom_response: {
content_type: SourceCustomResponseContentType.Json,
body: '{"message": "Vercel handled the webhook using Hookdeck"}',
},
},
},
};
module.exports = hookdeckConfig;
This includes request delay, retry, and a rate of delivery:
const { RetryStrategy, DestinationRateLimitPeriod } = require('@hookdeck/sdk/api');
const hookdeckConfig = {
vercel_url: 'https://my-vercel-project-eta-five-30.vercel.app',
match: {
'/api/webhook': {
name: 'my-webhook-source-name',
retry: {
strategy: RetryStrategy.Linear,
count: 5,
interval: 15000,
},
delay: 30000,
rate: {
limit: 100,
period: DestinationRateLimitPeriod.Minute,
},
},
},
};
module.exports = hookdeckConfig;
Considerations and Limitations
Removing the Middleware and Going Directly to Hookdeck
The Hookdeck Vercel middleware adds a hop to every process request, so if milliseconds are a factor in processing requests, you may want to use Hookdeck directly and not use the middleware.
With the Hookdeck Vercel Middleware:
- Request to Vercel URL
- Forward request to Hookdeck
- Request back to Vercel URL (which the middleware passes through)
Without the Hookdeck Vercel Middleware:
- Request to Hookdeck Source URL
- Request to Vercel URL
You can remove the middleware by uninstalling the package and removing any configuration and directly using the Hookdeck Source URL where you previously used the Vercel URL, for example, as your Stripe or Shopify webhook URL.
Parallel Matching
If you have multiple entries in the config file with the same match
path, be aware that the middleware will send the request via fetch
call once per match and will try to do that in parallel. This heavy use is not a common case, but please check Edge Middleware Limitations if you are in this scenario.
How the Hookdeck Vercel Middleware Works
The @hookdeck/vercel
package is supported in the Vercel Edge Middleware and executes before a request is processed on your site. This way, the request can be forwarded to Hookdeck and then received again by your specified endpoint, but with all the extra features you may need from Hookdeck, such as queuing, filters, and retry strategies.
This Hookdeck Vercel Middleware package is used in two stages:
Deploy/Build
During deployment, a `prebuild`` hook checks a configuration file and dynamically creates and configures a connection in Hookdeck:
For example, the following hookdeck.config.js
:
const hookdeckConfig = {
vercel_url: 'https://hookdeck-vercel-example.vercel.app/',
match: {
'/api/webhooks': {},
},
};
module.exports = hookdeckConfig;
Results in something like the following connection being created in Hookdeck:
Runtime
The package also in runtime by sending to Hookdeck the requests that match your configuration:
When your Edge Middleware is triggered (because your middleware config matches), the withHookdeck
wrapper acts as follows:
Development
Build
npm run build
Release
Bump the version according to semver.
Commit the changes.
Push the changes to GitHub.
Release to NPM
npm run release
Release on GitHub.