DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Implementing Secure API Gateways for Microservices Architecture
  • Contract-First Integration: Building Scalable Systems With Flyway, OpenAPI, and Kafka
  • Building a Zero-Cost Approval Workflow With AWS Lambda Durable Functions
  • Programmatic Brand Extraction: Pulling Logos, Colors, and Assets from Any URL

Trending

  • End-to-End Event Streaming With Kafka, Spring Boot and AWS SQS/SNS (Production-Ready Code Guide)
  • 11 Agentic Testing Tools to Know in 2026
  • Programmatic Brand Extraction: Pulling Logos, Colors, and Assets from Any URL
  • Setting Up a Data Catalog With Azure Purview and Collibra: What Three Attempts Taught Me
  1. DZone
  2. Data Engineering
  3. Databases
  4. Hide Your API Keys With an API Proxy Server

Hide Your API Keys With an API Proxy Server

The main goal of this article/tutorial is to demonstrate how to conceal public API keys by building an API proxy server using NodeJS.

By 
Pantelis Theodosiou user avatar
Pantelis Theodosiou
·
Aug. 11, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.5K Views

Join the DZone community and get the full member experience.

Join For Free

In this article/tutorial, we are going to create an API Proxy Server using NodeJS, and the main reason behind this is to show you how to hide public API keys and not expose them in the public like I was doing in the past. All of us have created an application once just to get to know a new library or a framework, and the best way to achieve this is to use an open API (like TMDB).

Most of these open APIs require adding the API key in the request URL or in the headers before making the request. Either way, the API key can be stolen and used by people that are not actually own this key. As a result, you may end up with a suspended account, or your application not work as you've expected it to.

Remember this: The API keys belong to the server side of your application.

Without further talking, let's create this API Proxy server. For the sake of this article/tutorial, we are going to use Weather API.

Requirements

Before start coding, we need to have the followings:

  • API key
    Open the OpenWeatherMapAPI website, make a free account, go to My API keys and create one.
  • Basic knowledge of JavaScript and NodeJS
  • Coffee

Dependencies and Scripts

Create the application's project folder

Shell
 
cd ~/Documents/tutorials
mkdir api-proxy-server && cd api-proxy-server
npm init -y


Install required NPM packages

Shell
 
npm install -S express cors dotenv axios
npm install -D nodemon # for faster development


Prepare scripts inside package.json file

JSON
 
{
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon --config nodemon.json"
  }
}


Let's take a look at the project's folders and files just to have a clearer image of the structure.

 
server/
├── src
│   ├── utils
│   │   └── env.js
│   └── routes
│       └── index.js
├── package.json
├── nodemon.json
├── index.js
└── .env
3 directories, 6 files


Create an index.js and a nodemon.json file at the root of the project.

JSON
 
// nodemon.json
{
  "verbose": true,
  "ignore": ["node_modules/"],
  "watch": ["./**/*"]
}


Before we start editing the entry (index.js) file, we need to create two other useful files. The first one is the .env file where all of our environment variables live. It's going to be a bit reusable in order to be able to use it with other public APIs except OpenWeatherMap API.

 
API_PORT = 1337
API_BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
API_KEY = "1d084c18063128c282ee3b41e91b6740" # not actually api key; use a valid one


And the other file is the src/utils/env.js which is responsible to read and export all the environment variables.

JavaScript
 
require("dotenv").config();

module.exports = {
  port: Number(process.env.API_PORT) || 3000,
  baseURL: String(process.env.API_BASE_URL) || "",
  apiKey: String(process.env.API_KEY) || "",
};


Now it's time to edit the main file and add some logic to this proxy server application.

JavaScript
 
// imports
const express = require("express");
const cors = require("cors");
const env = require("./src/utils/env");

// Application
const app = express();

// Middlewares
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());

// Routes
app.use("/api", require("./src/routes")); // Every request that starts with /api will be handled by this handler

// This route will handle all the requests that are not handled by any other route handler
app.all("*", (request, response, next) => {
  return response.status(404).json({ message: "Endpoint not found!" });
});

// Bootstrap server
app.listen(env.port, () => {
  console.log(`Server is up and running at http://localhost:${env.port}`);
});


Things here are very simple; it's a basic ExpressJs server application. On the top, we've got the required imports. Then we have some needed middleware, such as the JSON middleware, which is responsible to parse incoming requests with JSON payloads and is based on body-parser. Next, we have the routes where here we manage the endpoints and their handlers. And lastly, we've got the bootstrapping of the server.

Let's take a look at the handler of the /api route.

JavaScript
 
// server/src/routes/index.js
const router = require("express").Router();
const { default: axios } = require("axios");
const env = require("../utils/env");

// [GET] Current weather data
router.get("/", async (request, response, next) => {
  try {
    const query = request.query || {}; 
    const params = {
      appid: env.apiKey, // required field
      ...query,
    };
    const { data } = await axios.get(env.baseURL, { params });

    return response.status(200).json({
      message: "Current weather data fetched!",
      details: { ...data },
    });
  } catch (error) {
    const {
      response: { data },
    } = error;
    const statusCode = Number(data.cod) || 400;
    return response
      .status(statusCode)
      .json({ message: "Bad Request", details: { ...data } });
  }
});

module.exports = router;


The GET request handler function is taking as inputs the query fields that we've provided when we call it.

For example, if we didn't have this API Proxy server, our request would look like this https://api.openweathermap.org/data/2.5/weather?q=Thessaloniki,Greece&appid={API key}, but now that we've got it, our request looks like this http://localhost/api?q=Thessaloniki,Greece. There is no need to add the required field appid because the proxy server adds it by itself as a request parameter. We spared not showing the API key while we make the request on the OpenWeatherMap API.

Summary

The main concept is to hide your public API keys behind an API Proxy Server in order to prevent users to steal your keys. To achieve this, it is to create endpoints that wrap the endpoints of the actual open API providing the API key inside the proxy server as an environment variable in every request that needs this key. The obvious con of this approach is that this is a time-consuming process and, in some cases, is not even needed.

You can get the source code of this API Proxy server here.

What's Next

I was thinking to add some features to this proxy server. One of these is to add caching functionality in order to serve the data that was recently requested faster. The other one is to add a rate limiter in order to prevent users from overusing the API and crashing the server.

Drop a comment if you've thought of another solution to hide your API keys or if you have any tips or tricks to tell me about.

API

Published at DZone with permission of Pantelis Theodosiou. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Implementing Secure API Gateways for Microservices Architecture
  • Contract-First Integration: Building Scalable Systems With Flyway, OpenAPI, and Kafka
  • Building a Zero-Cost Approval Workflow With AWS Lambda Durable Functions
  • Programmatic Brand Extraction: Pulling Logos, Colors, and Assets from Any URL

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook