Enterprise API Gateway

Part 1: Core Gateway Implementation

software active Part 1 of 4
Technologies:
GoEnvoygRPCKubernetesPrometheus

In this first part, we’ll build the foundation of our API gateway with basic routing and proxy functionality. We’ll use Envoy as our data plane and implement a control plane in Go.

Architecture Overview

The core gateway consists of:

  • Control Plane: Go service that manages configuration
  • Data Plane: Envoy proxy handling requests
  • Configuration API: gRPC interface for dynamic updates

Setting Up the Project

Let’s start by setting up our Go module and basic project structure:

mkdir api-gateway
cd api-gateway
go mod init github.com/yourusername/api-gateway

Control Plane Implementation

The control plane is responsible for managing Envoy’s configuration and providing service discovery:

// cmd/control-plane/main.go
package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    "github.com/envoyproxy/go-control-plane/pkg/server/v3"
    "github.com/yourusername/api-gateway/internal/controlplane"
)

func main() {
    ctx := context.Background()
    
    // Create the control plane server
    cp := controlplane.NewServer()
    
    // Start the xDS server
    grpcServer := grpc.NewServer()
    server.RegisterServer(grpcServer, cp, cp.log)
    
    listener, err := net.Listen("tcp", ":18000")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }
    
    log.Println("Control plane listening on :18000")
    if err := grpcServer.Serve(listener); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

Service Discovery

We implement a simple service discovery mechanism that watches for changes in upstream services:

// internal/controlplane/discovery.go
package controlplane

import (
    "context"
    "sync"
    "time"
    
    core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
)

type ServiceDiscovery struct {
    mu       sync.RWMutex
    services map[string]*Service
    watchers []chan struct{}
}

type Service struct {
    Name      string
    Endpoints []Endpoint
}

type Endpoint struct {
    Address string
    Port    uint32
    Health  HealthStatus
}

func (sd *ServiceDiscovery) RegisterService(service *Service) {
    sd.mu.Lock()
    defer sd.mu.Unlock()
    
    sd.services[service.Name] = service
    sd.notifyWatchers()
}

func (sd *ServiceDiscovery) GetEndpoints(serviceName string) []*endpoint.LocalityLbEndpoints {
    sd.mu.RLock()
    defer sd.mu.RUnlock()
    
    service, exists := sd.services[serviceName]
    if !exists {
        return nil
    }
    
    var endpoints []*endpoint.LbEndpoint
    for _, ep := range service.Endpoints {
        endpoints = append(endpoints, &endpoint.LbEndpoint{
            HostIdentifier: &endpoint.LbEndpoint_Endpoint{
                Endpoint: &endpoint.Endpoint{
                    Address: &core.Address{
                        Address: &core.Address_SocketAddress{
                            SocketAddress: &core.SocketAddress{
                                Protocol: core.SocketAddress_TCP,
                                Address:  ep.Address,
                                PortSpecifier: &core.SocketAddress_PortValue{
                                    PortValue: ep.Port,
                                },
                            },
                        },
                    },
                },
            },
        })
    }
    
    return []*endpoint.LocalityLbEndpoints{
        {
            LbEndpoints: endpoints,
        },
    }
}

Envoy Configuration

We generate Envoy configuration dynamically based on discovered services:

// internal/controlplane/config.go
package controlplane

import (
    route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    "github.com/envoyproxy/go-control-plane/pkg/wellknown"
)

func (cp *Server) generateRouteConfiguration() *route.RouteConfiguration {
    return &route.RouteConfiguration{
        Name: "local_route",
        VirtualHosts: []*route.VirtualHost{
            {
                Name:    "local_service",
                Domains: []string{"*"},
                Routes: []*route.Route{
                    {
                        Match: &route.RouteMatch{
                            PathSpecifier: &route.RouteMatch_Prefix{
                                Prefix: "/api/v1/users",
                            },
                        },
                        Action: &route.Route_Route{
                            Route: &route.RouteAction{
                                ClusterSpecifier: &route.RouteAction_Cluster{
                                    Cluster: "user-service",
                                },
                            },
                        },
                    },
                    {
                        Match: &route.RouteMatch{
                            PathSpecifier: &route.RouteMatch_Prefix{
                                Prefix: "/api/v1/products",
                            },
                        },
                        Action: &route.Route_Route{
                            Route: &route.RouteAction{
                                ClusterSpecifier: &route.RouteAction_Cluster{
                                    Cluster: "product-service",
                                },
                            },
                        },
                    },
                },
            },
        },
    }
}

Testing the Core Gateway

We can test our basic gateway functionality:

# Start the control plane
go run cmd/control-plane/main.go

# Start Envoy with our configuration
envoy -c envoy.yaml -l debug

# Test routing
curl -H "Host: api.example.com" http://localhost:8080/api/v1/users

Next Steps

In the next part, we’ll add authentication and authorization capabilities to secure our API gateway, including:

  • JWT token validation
  • OAuth2 integration
  • Role-based access control
  • API key management

The core gateway provides the foundation for all these advanced features.