How to securely call an authenticated API from your front end
Tags: javascript, security, tutorial, node
Published: 2021-02-05 14:21:13

---
title: How to securely call an authenticated API from your front end
published: true
date: 2021-02-05 14:20:00 UTC
tags: javascript,security,tutorial,node
canonical_url: https://simonplend.com/how-to-securely-call-an-authenticated-api-from-your-front-end/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/8nqv3jwe8wyiwuaqnnxd.png
---

_Credit: Key icon by Gregor Cresnar from the Noun Project_

Your front end needs to access data from an API which requires an API key. If you put this API key in your client side JavaScript, you know that anyone viewing your website could view this API key (with a little help from their browser's Developer Tools). This doesn't seem secure at all, but what can you do instead?

Only applications running on a server - i.e. a back end - should have access to secrets like an API key. This means that requests with an API key can only be made from the server side. The thing is, you want your _front end_ to be able to request and use data from the API in a secure way.

One way of achieving this is to create a "proxy" server. Instead of directly calling the API, your client side JavaScript will make requests to the proxy server. The proxy server can add an API key to every request and forward it on to the API. This keeps the API key secure and away from your front end.

In this article I'll show you how to use the [Fastify](https://www.fastify.io/) framework and the [fastify-http-proxy](https://github.com/fastify/fastify-http-proxy) plugin to create a proxy server.

The full code for this article is [on GitHub](https://github.com/simonplend/example-nodejs-api-proxy-server).

> _This article uses ECMAScript (ES) module syntax. ES modules are well supported in v12.20.0 / v14.13.0 (and higher) releases of Node.js. [Learn more about ES module support in Node.js](https://simonplend.com/what-you-need-to-know-about-es-modules-in-node-js/)._

## Jump links

- [Request flow with a proxy server](#request-flow-with-a-proxy-server)
- [Create a server with Fastify](#create-a-server-with-fastify)
- [Add and configure the fastify-http-proxy plugin](#add-and-configure-the-fastifyhttpproxy-plugin)
- [Add an API key to proxied requests](#add-an-api-key-to-proxied-requests)
  - [HTTP request header](#http-request-header)
  - [URL query string](#url-query-string)
- [Conclusion](#conclusion)

## Request flow with a proxy server

Let's assume that we have some client side JavaScript running on a web page - it could be a React application, or "vanilla" JavaScript (no framework or library). This client side JavaScript needs to retrieve data from an API which requires an API key to be sent in the request.

As we don't want our client side JavaScript to contain the API key for security reasons, we're going to create a proxy server in Node.js which can receive a request from the client side JavaScript (made with [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), or a request library like [Axios](https://github.com/axios/axios)). This proxy server will add the required API key to the request and forward it on to the API server.

The request flow from the client (JavaScript running on a web page in a user's browser) through to the API server will look like this:

```
Request from client side JavaScript to our proxy server
 ↓
Proxy server receives request, adds the API key, forwards request to API server
 ↓
API server receives request, sends response back to proxy server
```

When the proxy server receives a response from the API server it will send it back to the client. At no point will the API key be exposed to the client side JavaScript.

Instead of making requests to `https://some-api.com/some/path` from our client side JavaScript, we'll now make requests to our proxy server: `https://my-proxy.com/some/path`. Neat, right?

## Create a server with Fastify

We're going to use the [Fastify](https://www.fastify.io/) framework and the [fastify-http-proxy](https://github.com/fastify/fastify-http-proxy) plugin to create our proxy server in Node.js.

First let's install the dependencies which our proxy server application will require:

```sh
npm install fastify fastify-http-proxy
```

We're now going to create and configure a Fastify server instance:

```javascript
// src/server.js

import createFastifyServer from "fastify";

/**
 * Create a Fastify server instance with logging enabled.
 * Fastify uses the library `pino` for logging.
 *
 * @see https://www.fastify.io/docs/latest/Logging/
 * @see https://github.com/pinojs/pino/
 */
const fastify = createFastifyServer({
	logger: true,
});

try {
	/**
	 * Make use of top-level `await` i.e. outside of an `async` function.
	 *
	 * @see https://nodejs.org/docs/latest-v14.x/api/esm.html#esm_top_level_await
	 */
	await fastify.listen(3000);
} catch (error) {
	fastify.log.error(error);
	process.exit(1);
}
```

If we run this code (`node src/server.js`), we'll have an HTTP server listening on port `3000`.

Our server doesn't provide any endpoints which you can make a request to, so making a request to `http://localhost:3000/` will result in a 404 error response. We need to add and configure the `fastify-http-proxy` plugin in order for our server to be able to handle requests.

## Add and configure the fastify-http-proxy plugin

We want to configure our Fastify server to proxy requests which it receives to `https://some-api.com`. In order to do this, let's `import` the [fastify-http-proxy](https://github.com/fastify/fastify-http-proxy) plugin and configure it:

```javascript
// src/server.js

import fastifyHttpProxy from "fastify-http-proxy";

/**
 * Register and configure the `fastify-http-proxy` plugin.
 *
 * This plugin supports all the options of `fastify-reply-from`,
 * as well as a few additional options e.g. `upstream`.
 *
 * @see https://github.com/fastify/fastify-http-proxy#options
 * @see https://github.com/fastify/fastify-reply-from
 */
fastify.register(fastifyHttpProxy, {
	upstream: "https://some-api.com",
	undici: true,
});
```

Our Fastify server is now configured as a proxy server. It will forward all requests it receives to `https://some-api.com` (our "upstream" server).

Our proxy server will use the HTTP client library [undici](https://github.com/nodejs/undici) to make requests to the upstream server. The `undici` library is a dependency of [fastify-reply-from](https://github.com/fastify/fastify-reply-from), which `fastify-http-proxy` is using under the hood. `undici` is [much faster](https://github.com/nodejs/undici#benchmarks) than the native [HTTP client provided by Node.js](https://nodejs.org/api/http.html).

Now that we have our proxy server set up, we need to configure it to add an API key to proxied requests.

## Add an API key to proxied requests

There are several different ways that APIs can implement authentication. One of the most common methods is for the client to pass an API key in the request. Typically APIs require the API key to be sent in a request header e.g. `X-Api-Key: abc123`. Some APIs might require the API key in a query string parameter e.g. `?apiKey=abc123`.

[fastify-http-proxy](https://github.com/fastify/fastify-http-proxy) accepts a `replyOptions` object which it passes through to [fastify-reply-from](https://github.com/fastify/fastify-reply-from#replyfromsource-opts). These options give us full control to modify requests and responses as they pass through our proxy server.

Let's take a look at how we can modify requests and add an API key before our proxy server forwards it on to the API server (our "upstream").

### HTTP request header

In order to add an API key to the HTTP request headers, we're going to set a [replyOptions.rewriteRequestHeaders](https://github.com/fastify/fastify-reply-from#rewriterequestheadersoriginalreq-headers) function. We will access our API key from [an environment variable](https://12factor.net/config) and set it as the value of an `X-Api-Key` request header. This code builds on our initial configuration for the `fastify-http-proxy` plugin:

```javascript
// src/server.js

const CONFIG = {
	apiKey: process.env.API_KEY,
};

fastify.register(fastifyHttpProxy, {
	upstream: "https://some-api.com",
	undici: true,
	replyOptions: {
		rewriteRequestHeaders: (originalRequest, headers) => {
			return {
				/**
				 * Preserve the existing request headers.
				 */
				...headers,
				/**
				 * Add the header which the API we're proxying requests
				 * to requires to authenticate the request.
				 */
				'X-Api-Key': CONFIG.apiKey,
			};
		},
	},
});
```

With a little extra configuration our server is now adding an API key to every request that it is proxying.

While `X-Api-Key` is a commonly used request header name, the API you are making requests to might require a different header e.g. `Authorization: Bearer <TOKEN>`. The `replyOptions.rewriteRequestHeaders` option allows us to add any request headers that we need.

[View the full code for an example proxy server which authenticates using an API key request header](https://github.com/simonplend/example-nodejs-api-proxy-server/blob/main/src/server.js)

### URL query string

I do not recommend that you design your own APIs to accept an API key via a URL query string. An API key is a "secret", just like a password is. When you put an API key in a URL it is much easier to accidentally leak it than if you send it via a request header e.g. by an accidental copy and paste, or by logging it in server request logs.

Unfortunately, some APIs do require you to send an API key in the URL query string. If this is the only way for you to authenticate with the API you are making requests to, you can use the [replyOptions.queryString](https://github.com/fastify/fastify-reply-from#querystring) option provided by `fastify-http-proxy`.

## Conclusion

In this article we've learnt how we can use Fastify and the `fastify-http-proxy` plugin to proxy requests and add an API key to them. This allows us to keep our API key secure and away from our client side JavaScript.

While `fastify-http-proxy` is very powerful and allows us to set up a proxy with minimal configuration, there are cases in which you might want to take a different approach e.g.

- You need to proxy complex API requests
- You want to create your own abstraction over another API
- You have an existing back end Node.js server application

In these cases you might want to consider creating your own API endpoints which then make requests to an upstream API. The [node-fetch](https://www.npmjs.com/package/node-fetch) library is a popular choice for making requests from Node.js. However, if you are interested in the features offered by [undici](https://npmjs.com/package/undici), I recommend keeping an eye on the [undici-fetch](https://github.com/Ethan-Arrowood/undici-fetch) library. It is being developed as a WHATWG Fetch implementation based on `undici`.

# Tired of wasting time reading Node.js blog posts which don't actually help you improve your projects?

[Sign up to my weekly-ish newsletter](https://simonplend.com/newsletter/) and I'll let you know when I publish a new blog post which helps solve real developer problems ✨