Different Problem, One Solution : API Gateways

grenishrai

Grenish rai

Posted on July 10, 2024

Different Problem, One Solution : API Gateways

Greetings fellow developers, both experienced and newcomers.

Have you ever found yourself dealing with various APIs featuring different endpoints, leading to confusion about which one the frontend should access? Perhaps you even accidentally exposed all endpoint responses to the frontend.

There exists a solution to this issue: the API Gateway.

What is API Gateway?

API Gateway is a service that helps manage and secure APIs (Application Programming Interfaces). It acts as a front door for the APIs, routing requests to the appropriate backend services, handling API versioning, security, and access control, and also providing features like rate limiting and caching. In simpler terms, API Gateway facilitates the communication and interaction between different software applications.

API Gateway diagram from Medium

Lets say you have a mobile e-commerce app with various features like user authentication, product search, and ordering. To manage the APIs for this app, you can use Amazon API Gateway. With API Gateway, you can create different API endpoints for user authentication, product search, and order placement.

For instance, you can set up an API endpoint /auth for user authentication that routes incoming requests to a Lambda function for verifying user credentials. Another endpoint /products can handle product search requests by connecting to a separate backend service that retrieves product information from a database.

Additionally, let's implement API versioning by creating different versions of the APIs to support backward compatibility. The API Gateway allows you to easily manage and switch between versions without disrupting the app's functionalities.

Moreover, you can enhance security by setting up API key authentication or integrating with IAM roles to control access to specific APIs. With features like rate limiting, you can prevent abuse of the APIs by restricting the number of requests a user can make within a certain time period.

By utilizing API Gateway in this scenario, you can effectively manage and secure the communication between your mobile e-commerce app and the backend services, ensuring a seamless and protected experience for your users.

Some most used API Gateway examples include AWS API Gateway, Azure API Management, Google Cloud Endpoints, and Kong.

Work In Progress

After understanding What, let's move on to How.

To begin with, create an empty repository

mkdir api-gateway

Next, initialize npm

npm init

Once npm is initialized, proceed to install necessary packages for the application

npm i express nodemon morgan express-rate-limit axios
Enter fullscreen mode Exit fullscreen mode

Create an index.js file in the repository, and your final result should resemble this.

server folder structure

Ensure making slight alterations within package.json file to initiate nodemon. Optionally, you may set "type": "module" for proceeding with ES6, although our approach will be in CommonJs.

Starting with necessary imports,

const express = require('express');
const axios = require('axios');
const rateLimit = require('express-rate-limit');
const morgan = require('morgan');
Enter fullscreen mode Exit fullscreen mode

initializing the app

const app = express();
const PORT = 3000;
Enter fullscreen mode Exit fullscreen mode

NOTE: Although CORS is not utilized in this example, it is essential to implement it.

Let's set the middleware for Morgan

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);

app.use(morgan('combined'));
Enter fullscreen mode Exit fullscreen mode

And for error handlers middleware

const handleError = (err, req, res, next) => {
  console.error(err);
  res.status(500).send('Internal Server Error');
};
app.use(handleError);
Enter fullscreen mode Exit fullscreen mode

In this instance, we will utilize the dummy APIs provided by JSONPlaceHolder and ReqRes.

  • JSONPlaceHolder

    • /posts - Fetch a collection of posts.
    • /comments - Access a set of comments.
    • /albums - Get a listing of albums.
    • /photos - Obtain a series of photos.
    • /todos - Get a rundown of todos.
    • /users - Retrieve a register of users.
  • ReqRes

    • /login - Used for requesting login access.
app.use('/api', async (req, res, next) => { ... }
Enter fullscreen mode Exit fullscreen mode

inside we'll implement try and catch method

try{ ... } catch (error) { ... }
Enter fullscreen mode Exit fullscreen mode

inside try lets start the main work

let response;
const path = req.path;
Enter fullscreen mode Exit fullscreen mode
if (
    path.startsWith("/posts") ||
    path.startsWith("/comments") ||
    path.startsWith("/albums") ||
    path.startsWith("/photos") ||
    path.startsWith("/todos") ||
    path.startsWith("/users")
) {
    // Route to JSONPlaceholder
    response = await axios.get(`https://jsonplaceholder.typicode.com${path}`);
} else if (path.startsWith("/reqres-users")) {
    // Route to ReqRes
    response = await axios.get(`https://reqres.in/api${path}`);
} else if (path.startsWith("/reqres-login")) {
    // Handle ReqRes login
    response = await axios.post("https://reqres.in/api/login", {
      email: "eve.holt@reqres.in",
      password: "cityslicka",
  });
} else {
    res.status(404).send("Endpoint not found");
    return;
}

res.json(response.data);
Enter fullscreen mode Exit fullscreen mode

And that is it. We're done implementing our first API Gateway.

The overall code should look like this.

const express = require("express");
const axios = require("axios");
const rateLimit = require("express-rate-limit");
const morgan = require("morgan");

const app = express();
const PORT = 3000;


const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
});
app.use(limiter);


app.use(morgan("combined"));

// Error Handling Middleware
const handleError = (err, req, res, next) => {
  console.error(err);
  res.status(500).send("Internal Server Error");
};
app.use(handleError);

// Unified API endpoint
app.use("/api", async (req, res, next) => {
  try {
    let response;
    const path = req.path;

    if (
      path.startsWith("/posts") ||
      path.startsWith("/comments") ||
      path.startsWith("/albums") ||
      path.startsWith("/photos") ||
      path.startsWith("/todos") ||
      path.startsWith("/users")
    ) {
      // Route to JSONPlaceholder
      response = await axios.get(`https://jsonplaceholder.typicode.com${path}`);
    } else if (path.startsWith("/reqres-users")) {
      // Route to ReqRes
      response = await axios.get(`https://reqres.in/api${path}`);
    } else if (path.startsWith("/reqres-login")) {
      // Handle ReqRes login
      response = await axios.post("https://reqres.in/api/login", {
        email: "eve.holt@reqres.in",
        password: "cityslicka",
      });
    } else {
      res.status(404).send("Endpoint not found");
      return;
    }

    res.json(response.data);
  } catch (error) {
    next(error);
  }
});

app.listen(PORT, () => {
  console.log(`API Gateway running on port ${PORT}`);
});

Enter fullscreen mode Exit fullscreen mode

Test and Results

Its time to test our API Endpoint, I'm using Insomnia, but you can use any of your choiceSure, let's go ahead and send a request to the API endpoint to see if everything is working as expected.

user enpoint

post endpoint

todos endpoint

endpoint logs

Keep Hacking!

💖 💪 🙅 🚩
grenishrai
Grenish rai

Posted on July 10, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related