Prompt
How do I expose legacy and modern APIs through the same gateway?
Latest observation
To expose legacy and modern APIs through the same gateway, the usual pattern is to make the gateway act as a single front door while it routes, transforms, and secures requests differently based on the API version, client, or path.
Common approach
1. Put both APIs behind one gateway
Use a gateway like Kong, Apigee, AWS API Gateway, Azure API Management, NGINX, Envoy, etc.
The gateway handles:
- authentication/authorization
- routing
- rate limiting
- logging/monitoring
- request/response transformation
- versioning
2. Separate legacy and modern backends
For example:
- Legacy service:
legacy-system.internal - Modern service:
new-platform.internal
The gateway exposes:
api.example.com/v1/...→ legacy backendapi.example.com/v2/...→ modern backend
Or by host:
legacy.api.example.comapi.example.com
3. Normalize the external API contract
Try to present a consistent public API even if the backends differ internally.
The gateway can:
- translate field names
- map URLs and methods
- convert payload formats
- adapt status codes
- enrich or strip data
This is often called the anti-corruption layer pattern.
Example routing patterns
Path-based versioning
GET /v1/customers -> legacy service
GET /v2/customers -> modern service
Header-based routing
Accept: application/vnd.company.v1+json
Accept: application/vnd.company.v2+json
Host-based routing
legacy-api.example.com -> legacy
api.example.com -> modern
If you need both APIs to look the same externally
Use the gateway to adapt requests and responses:
Example
Legacy returns:
{ "cust_id": 123, "cust_nm": "Alice" }
Modern expects/returns:
{ "id": 123, "name": "Alice" }
The gateway can transform:
- inbound
id→cust_id - outbound
cust_nm→name
If transformation gets too complex, consider a backend-for-frontend (BFF) or facade service behind the gateway.
Recommended architecture
Option A: Simple gateway routing
Best when:
- legacy and modern APIs are fairly independent
- versioning is mostly path/header based
- little transformation is needed
Option B: Gateway + adapter layer
Best when:
- legacy APIs are inconsistent or hard to change
- modern clients need a clean contract
- payload transformations are significant
Architecture:
Client -> Gateway -> Adapter/API Facade -> Legacy/Modern services
Option C: Dual-run migration
Best when:
- you’re gradually migrating from legacy to modern
- you need to compare outputs or roll out safely
You can:
- route a percentage of traffic to modern
- mirror requests to modern
- keep legacy as fallback
Key design tips
- Keep external contracts stable
- Version explicitly
- Avoid leaking legacy quirks to clients
- Centralize auth and observability at the gateway
- Use contract tests for both versions
- Plan deprecation for legacy endpoints
- Document differences clearly
Practical example
/api/v1/orders -> legacy order system
/api/v2/orders -> new order microservice
Gateway responsibilities:
- authenticate user
- route
/v1to legacy - route
/v2to modern - translate response shape if needed
- log and monitor both
Rule of thumb
If the gateway only needs to route, use a normal API gateway.
If it must translate business semantics, add a dedicated adapter/facade layer behind it.
If you want, I can sketch:
- a reference architecture diagram,
- an example using Kong / NGINX / Apigee / AWS API Gateway, or
- a migration plan from legacy to modern APIs.