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
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • The Permission Was Valid. The Action Was Not Authorized.
  • Why AI Agents Need a Shared Enterprise Ontology
  • Adding Graph Retrieval to a RAG Pipeline With Python
  • Why 200 OK Is the Most Dangerous Lie in Enterprise Integration

Trending

  • Part 4: Multi-Agent Orchestration — Pairing Goose With Quarkus Flow State Machines
  • MQTT, Kafka, NATS, or Zenoh? Choosing Edge-to-Cloud Messaging by Traffic Pattern
  • When a Coding Agent Fixes the Test Instead of the Bug
  • Member Spotlight: Shai Almong

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  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
·
Aug. 11, 22 · Tutorial
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
5.8K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

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

  • The Permission Was Valid. The Action Was Not Authorized.
  • Why AI Agents Need a Shared Enterprise Ontology
  • Adding Graph Retrieval to a RAG Pipeline With Python
  • Why 200 OK Is the Most Dangerous Lie in Enterprise Integration

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • 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