I recently created a tiny proxy server to edit responses of a public API on the fly, and I was impressed by how easy it is to build and deploy such things on Vercel.
In my case, the goal was to allow all origins to fetch the Pinboard API by adding an "Access-Control-Allow-Origin": "*"
header to the API response, but there are plenty of other cases where a proxy server may come in handy.
So, here's how you can create and deploy a tiny but flexible Node.js proxy server on Vercel in 10 minutes.
TL;DR
We'll create a Node.js serverless function that uses http-proxy-middleware
to proxy any /api
request to (e.g.) https://example.org
. Within the serverless function code, we can intercept requests/responses and manipulate them on the fly.
The serverless function will be deployed on Vercel.
Project setup
Create a project directory, cd
into it, and initialize an npm project:
mkdir my-proxy && cd my-proxy
npm init
mkdir my-proxy && cd my-proxy
npm init
Install vercel
as a dependency:
npm install -D vercel
npm install -D vercel
Update the start
script of your package.json
to "start": "vercel dev"
in order to run your serverless function locally:
{
"name": "my-proxy",
"version": "1.0.0",
"scripts": {
+ "start": "vercel dev"
},
{
"name": "my-proxy",
"version": "1.0.0",
"scripts": {
+ "start": "vercel dev"
},
Create an api
directory and an index.js
file inside of it.
mkdir api && touch api/index.js
mkdir api && touch api/index.js
Vercel serverless functions use a file-system-based convention. So the api/index.js
file you just created will automatically handle all requests of the /api
endpoint:
// In Vercel, any file inside the "api" directory is exposed on an "/api" endpoint.
// For an API route to work, you need to export a function as default (a.k.a request handler),
// which then receives the following parameters:
// - req: The request object.
// - res: The response object.
// See https://vercel.com/docs/serverless-functions/supported-languages#node.js for details.
export default async function handler(req, res) {
res.status(200).send(`Hello world!`);
}
// In Vercel, any file inside the "api" directory is exposed on an "/api" endpoint.
// For an API route to work, you need to export a function as default (a.k.a request handler),
// which then receives the following parameters:
// - req: The request object.
// - res: The response object.
// See https://vercel.com/docs/serverless-functions/supported-languages#node.js for details.
export default async function handler(req, res) {
res.status(200).send(`Hello world!`);
}
By default, on Vercel, the /api/index.js
file would strictly match only requests made to the /api
endpoint, ignoring sub-paths like /api/hello
.
To make /api/index.js
handle the entire path, we can configure a Vercel rewrite to redirect all /api/*
calls to the /api/index.js
file (by specifying the rewrite rule in a vercel.json
file at the root of the project):
touch vercel.json
touch vercel.json
{
"rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]
}
{
"rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]
}
You should now be able to deploy your code to Vercel (of course, we haven't added any "real" logic in api/index.js
, so it won't do anything for now).
My go-to approach on these occasions is to create a GitHub repo and connect it to Vercel to automatically deploy the project on each commit. But you can also follow the automated setup by running npm start
.
Proxy logic setup
Install http-proxy-middleware
, an easy-to-use proxy middleware compatible with Vercel:
npm i http-proxy-middleware
npm i http-proxy-middleware
In /api/index.js
, use http-proxy-middleware
to create a new proxy and expose it on the route handler:
// Create a proxy to redirect requests of the "/api/*" path to "https://example.org".
//
// Examples:
// GET /api/hello → GET https://example.org/hello
// POST /api/test?color=red → POST https://example.org/test?color=red
//
// Additionally, the proxy will:
// - Add an "x-added" header
// - Remove the "x-removed" header
// From the proxied response.
//
// You can/should update the proxy to suit your needs.
// See https://github.com/chimurai/http-proxy-middleware for more details.
const { createProxyMiddleware } = require("http-proxy-middleware");
const apiProxy = createProxyMiddleware({
target: "https://example.org",
changeOrigin: true,
pathRewrite: {
"^/api": "", // strip "/api" from the URL
},
onProxyRes(proxyRes) {
proxyRes.headers["x-added"] = "foobar"; // add new header to response
delete proxyRes.headers["x-removed"]; // remove header from response
},
});
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
return apiProxy(req, res);
}
// Create a proxy to redirect requests of the "/api/*" path to "https://example.org".
//
// Examples:
// GET /api/hello → GET https://example.org/hello
// POST /api/test?color=red → POST https://example.org/test?color=red
//
// Additionally, the proxy will:
// - Add an "x-added" header
// - Remove the "x-removed" header
// From the proxied response.
//
// You can/should update the proxy to suit your needs.
// See https://github.com/chimurai/http-proxy-middleware for more details.
const { createProxyMiddleware } = require("http-proxy-middleware");
const apiProxy = createProxyMiddleware({
target: "https://example.org",
changeOrigin: true,
pathRewrite: {
"^/api": "", // strip "/api" from the URL
},
onProxyRes(proxyRes) {
proxyRes.headers["x-added"] = "foobar"; // add new header to response
delete proxyRes.headers["x-removed"]; // remove header from response
},
});
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
return apiProxy(req, res);
}
Et voilà!
By deploying your code (or running it locally with npm start
) any call made to the /api
endpoint will be proxied to https://example.org
.
Check the documentation of the http-proxy-middleware
library (and of the node-http-proxy
library, used under-the-hood) to learn how you can manipulate the proxied request & response.