This blog demonstrates how to create an HTTP proxy in Next.js using serverless functions. HTTP proxies can be used to add an extra client-server transport layer, providing several benefits, including securing authorization tokens as http-only cookies, filtering requests, and enhancing protection and security. The blog provides a simple step-by-step guide on setting up an HTTP proxy by creating a catch-all route in the 'api' directory of next.js and utilizing the 'http-proxy' package. The proxy adds a middleware layer to all API calls, separates auth routes for handling cookies, and includes existing auth tokens in the headers of other APIs.
With next.js introducing API routes and serverless functions, a lot of ideas become possible for front-end developers.
Some of the benefits are
You can read more about this concepts in next.js documentation.
A way of using them is by writing HTTP proxies to add proxies as an extra client-server transport layer.
There are so many interesting things that come with using proxies, for example, you can save an authorization token as an http-only cookie and pass every API through the HTTP proxy to add the token to the header. This way accessing tokens is a lot harder for malicious users (not impossible of course). Other useful things you can do are: "hiding the IP address", "filtering requests", "protection and security", "caching" and ...
So how to create an HTTP proxy with next.js serverless functions? It's so simple, First you need two packages, cookies and http-proxy.
We need to a catch all route in the api
directory. for that create a pages/api/proxy/[...path].js
and paste this code into it.
import * as HttpProxy from "http-proxy";
import url from "url";
import Intervals from "@constants/Intervals";
import { TOKEN_COOKIE_KEY } from "@constants/cookies";
import {
externalApiBaseURL,
headerTokenKey,
proxyBasePath,
proxyBasePathRegExp,
} from "@config";
import * as Cookies from "cookies";
const proxy = HttpProxy.createProxyServer();
const config = {
api: {
bodyParser: false,
},
};
export default (req, res) => {
return new Promise((resolve, reject) => {
const pathname = url.parse(req.url).pathname;
const isAuth = pathname === proxyBasePath + "/auth/login";
const cookies = new Cookies(req, res);
const token = cookies.get(TOKEN_COOKIE_KEY);
req.url = req.url.replace(proxyBasePathRegExp, "");
req.headers.cookie = "";
if (token) req.headers[headerTokenKey] = token;
if (isAuth) proxy.once("proxyRes", interceptLoginResponse);
proxy.once("error", reject);
proxy.web(req, res, {
target: externalApiBaseURL,
autoRewrite: false,
selfHandleResponse: isAuth,
});
function interceptLoginResponse(proxyRes, req, res) {
let apiResponseBody = "";
proxyRes.on("data", (chunk) => {
apiResponseBody += chunk;
});
proxyRes.on("end", () => {
try {
const response = JSON.parse(apiResponseBody);
const token = response?.result?.token;
const cookies = new Cookies(req, res);
cookies.set(TOKEN_COOKIE_KEY, token, {
maxAge: Intervals.Month * 1000,
httpOnly: true,
sameSite: "lax",
});
delete response?.result?.token;
res.status(200).json(response);
resolve(response);
} catch (err) {
reject(err);
}
});
}
});
};
The code speaks for itself, but in a simple word, what we are doing here is