js

Building Smarter API Gateways: From Express Proxies to Kong and Beyond

Learn how to build scalable, secure API gateways using Express.js, Consul, and Kong to manage microservice communication effectively.

Building Smarter API Gateways: From Express Proxies to Kong and Beyond

I’ve been thinking about how modern applications talk to each other. It’s not just one big program anymore. We have many small services, each doing a specific job. How do we manage all these conversations? How do we keep things secure, fast, and reliable when dozens of services need to connect? This question led me down a path of building gateways—the traffic directors of the software world.

Let’s talk about API gateways. Think of a gateway as a receptionist for a large office building. Instead of letting visitors wander around looking for the right department, the receptionist directs them. An API gateway does this for software. It sits between your users and your collection of backend services. It routes requests, checks credentials, and manages traffic flow.

Why would you need one? If you have more than a couple of services, things get messy fast. Each service might have a different address. You’d need security and logging in every single one. A gateway centralizes this. It provides one front door. All your rules about who can do what, and how often, live in one place.

I started with a simple custom gateway using Express.js. It’s a great way to understand the core concepts. You create a server that listens for requests. Then, based on the request path, you forward it to the correct backend service. Here’s a basic structure.

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// Route requests for '/users' to the user service
app.use('/users', createProxyMiddleware({
    target: 'http://localhost:3001',
    changeOrigin: true,
}));

// Route requests for '/orders' to the order service
app.use('/orders', createProxyMiddleware({
    target: 'http://localhost:3002',
    changeOrigin: true,
}));

app.listen(3000, () => {
    console.log('Gateway running on port 3000');
});

This works, but it’s static. The addresses are hardcoded. What happens when the user service moves or a new instance starts up? You’d have to stop your gateway and update the code. That’s not practical for a system that needs to be always available.

This is where service discovery comes in. Instead of hardcoding addresses, your gateway needs to ask a directory: “Where is the user service right now?” A tool like Consul acts as this dynamic phone book. Services register themselves when they start. The gateway queries Consul to find them.

Setting up Consul is straightforward. You run it as a separate process. Services send a heartbeat to say, “I’m alive.” The gateway can then get a list of all healthy instances. But how do you make this automatic? You need a client in your gateway that talks to Consul.

const Consul = require('consul');

class ServiceDiscovery {
    constructor() {
        this.consul = new Consul({ host: 'localhost', port: 8500 });
    }

    async findService(serviceName) {
        try {
            // Ask Consul for healthy instances of a service
            const instances = await this.consul.health.service({
                service: serviceName,
                passing: true
            });
            // Pick one (simple round-robin logic)
            if (instances.length > 0) {
                const instance = instances[0];
                return `http://${instance.Service.Address}:${instance.Service.Port}`;
            }
            throw new Error(`Service ${serviceName} not found`);
        } catch (error) {
            console.error('Discovery failed:', error);
            throw error;
        }
    }
}

Now, your gateway can use this findService method to get a live address before forwarding a request. But what if the service you pick is slow or failing? You don’t want to send all traffic to a broken instance. This is where patterns like circuit breakers and retries become critical.

A circuit breaker is like a fuse in your house. If a service fails too many times, the “circuit” trips. The gateway stops sending requests to it for a while, giving it time to recover. This prevents a cascade of failures. After a timeout, it tries again to see if the service is back.

Have you considered what happens during a sudden traffic spike? Your gateway needs to protect your services from being overwhelmed. This is where rate limiting shines. You can define rules like “a single IP can only make 100 requests per minute to the login endpoint.” It’s a simple but powerful defense.

While building a custom gateway teaches you a lot, maintaining it for production is a big job. You need to handle updates, scaling, and complex security policies. This is why many teams use a dedicated gateway like Kong. Kong is built for this. It runs as a separate layer, often in its own containers. You configure it with declarative files instead of writing lots of code.

A Kong configuration might define a route and attach plugins to it. A plugin could be for rate-limiting, authentication, or logging. The power is in this plugin system. You get battle-tested features without building them yourself. Here’s a glimpse of what a Kong config looks like in YAML.

_format_version: "2.1"
services:
  - name: user-service
    url: http://user-service.default.svc.cluster.local
    routes:
      - name: user-route
        paths:
          - /users
    plugins:
      - name: rate-limiting
        config:
          minute: 20
          policy: local

You define a service and its upstream address. You define a route that matches certain paths. Then you attach plugins to that route. Kong handles the rest. It integrates with Consul too, so it can also do dynamic service discovery.

So, when do you use a custom Express gateway versus Kong? Start with Express if you have simple needs or want full control over the logic. It’s also excellent for learning. Choose Kong when you need enterprise features, high performance under load, and a team to manage the infrastructure. Kong takes the operational burden off your application code.

Monitoring is non-negotiable. You must know how your gateway is performing. How many requests are failing? What’s the average response time? Tools like Prometheus collect metrics. You can expose a /metrics endpoint from your gateway. Then, a dashboard tool like Grafana can visualize the data. This lets you spot problems before users do.

Let’s not forget security. The gateway is your first line of defense. It should validate API keys or JSON Web Tokens (JWT) before a request even reaches your business logic. It should also strip out any sensitive headers from the response that might leak internal information. Centralizing this logic is much safer than hoping every service team implements it correctly.

Building a robust gateway system connects several pieces: the routing logic, the service directory, the safety features like circuit breakers, and the observability tools. It might seem complex, but each piece solves a clear problem. Start simple. Get a basic proxy working. Add service discovery. Then introduce resilience patterns. Finally, consider moving to a dedicated tool like Kong as your needs grow.

What’s the biggest challenge you’ve faced when connecting multiple services? Was it finding them, securing them, or keeping them fast? I find that starting with a clear map of all service interactions helps. Draw it out. Identify the critical paths. Then build your gateway strategy around those.

I hope this walk through API gateways gives you a foundation to build upon. The goal is to make your network of services feel like a single, coherent application to your clients. It’s a challenging but rewarding piece of modern architecture. If this exploration helped you, please share it with a colleague who might be facing similar design decisions. I’d also love to hear your thoughts or questions in the comments below—what’s your experience with managing microservice communication?


As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!


📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!


Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Keywords: api gateway,express js,service discovery,kong microservices,api security



Similar Posts
Blog Image
Complete Guide to Integrating Next.js with Prisma ORM for Type-Safe Database Operations

Learn how to integrate Next.js with Prisma ORM for type-safe, scalable web apps. Complete guide with setup, best practices, and real-world examples.

Blog Image
Complete Guide to Next.js Prisma Integration: Build Type-Safe Full-Stack Apps in 2024

Learn how to integrate Next.js with Prisma for powerful full-stack development. Build type-safe, scalable applications with seamless database operations.

Blog Image
Build Event-Driven Microservices: Complete NestJS, NATS, MongoDB Guide with Production Examples

Learn to build scalable event-driven microservices with NestJS, NATS, and MongoDB. Complete guide covering architecture, implementation, and deployment best practices.

Blog Image
Build Event-Driven Microservices with NestJS, RabbitMQ, and Redis: Complete Performance Guide

Learn to build scalable event-driven microservices with NestJS, RabbitMQ & Redis. Master async messaging, caching strategies, and distributed transactions. Complete tutorial with production deployment tips.

Blog Image
Complete Guide to Integrating Svelte with Supabase: Build Real-Time Web Applications Fast

Learn how to integrate Svelte with Supabase to build fast, real-time web apps with authentication and database management. Complete guide for modern developers.

Blog Image
Master Next.js 13+ App Router: Complete Server-Side Rendering Guide with React Server Components

Master Next.js 13+ App Router and React Server Components for SEO-friendly SSR apps. Learn data fetching, caching, and performance optimization strategies.