NodeJS

Create a Proxy Server For APIs

Creating a Proxy Server in Typescript Today, we will create a proxy server in the Nodejs Typescript version. We will have an...

Written by Luci · 3 min read >

Creating a Proxy Server in Typescript

Today, we will create a proxy server in the Nodejs Typescript version. We will have an endpoint that will be redirected to some external route and return a response from their end.

What is a Proxy?

You might be asking what is a Proxy. and why we even need that. So basically any route to your URL will get served by some other URL on your behalf of you. For example, let’s say we have a route called https://api.any-orm.com/lead/create but you do not want your end user to see the request instead it should be navigated from POST https://your-url.com/orm/lead without letting the user feel like leaving the website.

This example is for a specific route but you can also proxy any route like https://api.any-orm.com/lead/* to your backend https://your-backend.com/orm/lead/*

Get started

Now we have an idea about what proxy is so we can start its implementation in the project.

Let us focus on the proxy now. We will be proxying 5 methods including GET, POST, PUT, PATCH, DELETE pointing to the endpoint /proxy

Whenever anyone visits the website http://localhost:8000/api/proxy then all the requests will get redirected to https://reqres.in/api

STEP-I — Create routes

We have made all requests after the * route pointing to our proxyRequest function that will take requests and responses. We will be writing the implementation for proxyRequest sometimes in the article.

import { Router } from "express";
import proxyRequest from "./request";
const router = Router();

router.get('/*', async (req, res) => await proxyRequest(req, res));
router.post('/*', async (req, res) => await proxyRequest(req, res));
router.put('/*', async (req, res) => await proxyRequest(req, res));
router.patch('/*', async (req, res) => await proxyRequest(req, res));
router.delete('/*', async (req, res) => await proxyRequest(req, res));

export default router

STEP-II — Copy required headers

A part of proxying means the website should behave similarly to the actual website which also means all the required headers should also be accepted at that time only and get copied over once we have the request.

import { IncomingHttpHeaders } from "http";
import { Request, Response } from "express";
import fetch, { Headers } from "node-fetch";

async function copyRequiredHeaders(incomingHeaders: IncomingHttpHeaders): Promise<Headers> {
  const localHeaders = new Headers();

  // Preserve the Accept header
  if (incomingHeaders["accept"]) {
    localHeaders.append("accept", incomingHeaders["accept"]);
  }

  // Preserve If-Match
  if (incomingHeaders["if-match"]) {
    localHeaders.append("if-match", incomingHeaders["if-match"]);
  }

  // Preserve Content-Type
  if (incomingHeaders["content-type"]) {
    localHeaders.append("content-type", incomingHeaders["content-type"]);
  }

  return localHeaders;
}

For simplicity same, I am coping only three primary headers accpet, if-match, content-type but you can also create more sections if required.

Now, headers are copied we can move to creating a request and managing its response.

STEP-III — Creating a request and managing the response

First, we will create a URL by replacing our path with the actual path and domain then we will copy the required headers from req.headers and then if you want to send any custom data or any credentials you can send it otherwise we will fetch the request.

node-fetch is the equivalent of fetch API from a browser that means in the second stage of response we need to detect what response type we would like to generate. In our case, we want the same response as the user wanted so we will be using response.headers to determine that using buffer method of response. Later if you again want to send some more data to the client then you can set in the response and return the value.

export default async function proxyRequest(req: Request, res: Response): Promise<void> {
  const url = req.path.replace("/proxy", ``);
  const domain = 'https://reqres.in/api'
  const newUrl = `${domain}${url}`;
  const headers = await copyRequiredHeaders(req.headers);
  const authData = {
    token: 'attached custom token', // amy data you required
    date: new Date().toUTCString()
  }

  const response = await fetch(newUrl, {
    method: req.method,
    headers: {
      ...headers,
      Authorization: authData.token,
      "x-version": "2016-07-11",
      "x-date": authData.date,
    },
    body: req.method !== "GET" ? JSON.stringify(req.body) : undefined,
  });

  const responseType = response.headers.get("content-type") ?? "application/json";
  const bodyData = await response.buffer();
  res
    .status(response.status)
    .type(responseType)
    .set({
      "request-id": response.headers.get("request-id"),
      "client-request-id": response.headers.get("client-request-id"),
    })
    .send(bodyData);
}

Lets Test

To test this after running the app, we can go to http://localhost:8000/proxy/users that will be pointed to the actual route https://reqres.in/api/users and it will return the corresponding data.

Conclusion

This will be it, now every request will be served from reqres.in/api and you can too customize it.

There are a few important things to notice you should only proxy open APIs, routes, or protected REST APIs for end users to avoid any gateway or cors errors. Also, you can customize the authentication mechanism to some extent only, for example, if the routes are protected with oAuth then you will have to manage its tokens on your end and process them for the next request which can become a pain to manage.

I hope you have learned something new today and will use it in the future for similar use cases. 

Written by Luci
I am a multidisciplinary designer and developer with a main focus on Digital Design and Branding, located in Cluj Napoca, Romania. Profile

Most common Node.js interview questions

Luci in NodeJS
  ·   1 min read

Install Node.js and NPM on Window

Luci in NodeJS
  ·   3 min read
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x