← Back to Article List         
Ocelot API Gateway

Ocelot API Gateway

Published on 26 Sep 2026     7 min read Web API
Web API

Ocelot is an open-source API Gateway library for .NET. It receives client requests and forwards them to the appropriate backend API.

For example, your application might have separate Product, Order, and Payment APIs. Ocelot gives clients one common entry point for accessing them.

1. What is an API Gateway?

An API Gateway is an application that sits between clients and backend APIs.

A client sends its request to the gateway. The gateway decides which backend API should receive it, forwards the request, and returns the response.

Example:

Client calls the gateway Gateway forwards to
/products Product API
/orders Order API
/payments Payment API

Ocelot is one implementation of this gateway pattern for ASP.NET Core.

It runs inside an ASP.NET Core application and uses configuration to define routing rules. ocelot.readthedocs.io

2. Why do we need it?

Suppose your backend applications run at these addresses:

Application Address
Product API http://localhost:5001
Order API http://localhost:5002
Payment API http://localhost:5003

Without a gateway, the client must know these separate addresses.

With Ocelot, the client uses one gateway address:

http://localhost:5000

For example:

GET http://localhost:5000/products
GET http://localhost:5000/orders

The gateway handles the internal destination.

This helps when:

  • You have multiple backend services.
  • Backend addresses change.
  • You want a consistent public API entry point.
  • You want shared gateway policies.

A gateway is optional. A small application with one API may not need this extra component.

3. Important terms: Upstream and Downstream

Ocelot configuration uses these two terms:

Term Meaning Example
Upstream Request arriving at the gateway from the client /products
Downstream Request forwarded from the gateway to a backend API /api/products

For example:

Client request:
GET http://localhost:5000/products

Ocelot forwards it internally to:

GET http://localhost:5001/api/products

This is server-side forwarding, not a browser redirect. The client continues communicating with the gateway.

4. Full simple example

We will create three separate applications:

Project Purpose Port
ProductApi Returns products 5001
OrderApi Returns orders 5002
ApiGateway Runs Ocelot 5000

This example targets .NET 8, using Ocelot 23.4.3 for a fixed, compatible demonstration. It is not presented as the latest package version. The Ocelot 23.4 documentation includes .NET 8 support. ocelot.readthedocs.io

HTTP is used below for local testing. Use HTTPS for public production traffic.

Step 1: Create the projects

Run these commands from the same parent folder:

dotnet new web -n ProductApi -f net8.0
dotnet new web -n OrderApi -f net8.0
dotnet new web -n ApiGateway -f net8.0

Install Ocelot only in the gateway project:

dotnet add ApiGateway/ApiGateway.csproj package Ocelot --version 23.4.3

Step 2: Product API

Replace ProductApi/Program.cs:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/api/products", () =>
{
    var products = new[]
    {
        new { Id = 1, Name = "Laptop", Price = 55000 },
        new { Id = 2, Name = "Mouse", Price = 800 }
    };

    return Results.Ok(products);
});

app.Run();

Step 3: Order API

Replace OrderApi/Program.cs:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/api/orders", () =>
{
    var orders = new[]
    {
        new { Id = 101, ProductId = 1, Quantity = 1 },
        new { Id = 102, ProductId = 2, Quantity = 2 }
    };

    return Results.Ok(orders);
});

app.Run();

The backend APIs can also use controllers. Ocelot communicates over HTTP and does not require Minimal APIs.

Step 4: Configure the gateway

Create ocelot.json in the ApiGateway project root, alongside Program.cs:

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/products",
      "UpstreamHttpMethod": [ "GET" ],

      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/orders",
      "UpstreamHttpMethod": [ "GET" ],

      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5002
        }
      ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000"
  }
}

Step 5: Gateway Program.cs

Replace ApiGateway/Program.cs:

using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

// Load routing configuration.
builder.Configuration.AddJsonFile(
    "ocelot.json",
    optional: false,
    reloadOnChange: true);

// Register Ocelot services.
builder.Services.AddOcelot(builder.Configuration);

var app = builder.Build();

// Configure Ocelot's request pipeline.
await app.UseOcelot();

await app.RunAsync();

The gateway does not need a products or orders controller. Ocelot performs the forwarding.

Step 6: Run all three applications

Open three terminals in the parent folder.

Terminal 1:

dotnet run --project ProductApi --no-launch-profile --urls http://localhost:5001

Terminal 2:

dotnet run --project OrderApi --no-launch-profile --urls http://localhost:5002

Terminal 3:

dotnet run --project ApiGateway --no-launch-profile --urls http://localhost:5000

All three applications must be running.

Step 7: Test through the gateway

Get products:

GET http://localhost:5000/products

Response:

[
  {
    "id": 1,
    "name": "Laptop",
    "price": 55000
  },
  {
    "id": 2,
    "name": "Mouse",
    "price": 800
  }
]

Get orders:

GET http://localhost:5000/orders

Response:

[
  {
    "id": 101,
    "productId": 1,
    "quantity": 1
  },
  {
    "id": 102,
    "productId": 2,
    "quantity": 2
  }
]

5. Explanation of ocelot.json

Consider the product route:

Setting Value Purpose
UpstreamPathTemplate /products Path the client calls
UpstreamHttpMethod ["GET"] HTTP methods this route matches
DownstreamPathTemplate /api/products Path on the backend API
DownstreamScheme http Protocol used to reach the backend
Host localhost Backend host name or IP address
Port 5001 Backend listening port
BaseUrl http://localhost:5000 Gateway’s externally visible base URL

Ocelot constructs the downstream address using the scheme, host, port, and path. Ocelot Gateway 23.4 documentation

BaseUrl does not configure the listening port. In this example, --urls sets that port. BaseUrl describes the external gateway address for Ocelot features that need it. ocelot.readthedocs.io

6. What happens during a request?

For:

GET http://localhost:5000/products
  1. The gateway receives the request.
  2. Ocelot matches /products and the GET method.
  3. It selects the configured Product API destination.
  4. It sends an HTTP request to http://localhost:5001/api/products.
  5. Product API executes its handler.
  6. Product API returns its JSON response.
  7. Ocelot forwards the response to the client.

The product business logic stays inside Product API.

7. How do we pass an ID?

Add a separate route for individual products:

{
  "UpstreamPathTemplate": "/products/{id}",
  "UpstreamHttpMethod": [ "GET" ],
  "DownstreamPathTemplate": "/api/products/{id}",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    }
  ]
}

Now:

GET http://localhost:5000/products/10

is forwarded to:

GET http://localhost:5001/api/products/10

The backend must also implement /api/products/{id}. A gateway route does not create the backend endpoint.

8. Ocelot and load balancing

Routing chooses the service. Load balancing chooses an instance of that service.

Suppose Product API has two running instances:

Instance Address
Product API instance 1 localhost:5001
Product API instance 2 localhost:5003

Replace the product route with:

{
  "UpstreamPathTemplate": "/products",
  "UpstreamHttpMethod": [ "GET" ],

  "DownstreamPathTemplate": "/api/products",
  "DownstreamScheme": "http",

  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    },
    {
      "Host": "localhost",
      "Port": 5003
    }
  ],

  "LoadBalancerOptions": {
    "Type": "RoundRobin"
  }
}

Run another Product API instance:

dotnet run --project ProductApi --no-launch-profile --urls http://localhost:5003

RoundRobin cycles requests between the configured instances. Ocelot also supports options such as LeastConnection. ocelot.readthedocs.io

Adding two addresses does not automatically provide health monitoring or guarantee failover. Those concerns need an appropriate discovery, health, and resilience setup.

9. Advantages

  • One client-facing entry point: Clients use a common gateway address.
  • Centralized routing: Backend addresses are maintained in gateway configuration.
  • Flexible public paths: Public URLs can differ from backend URLs.
  • Load balancing: Requests can be distributed across service instances.
  • Independent services: Backend APIs remain separate applications.

10. Limitations and key points

  • Ocelot is a library hosted in your own ASP.NET Core application.
  • Installing it alone does not create routing rules; configure ocelot.json.
  • AddOcelot() registers services; UseOcelot() configures the gateway pipeline.
  • The sample enables routing only; it does not automatically secure your APIs.
  • Backend services should enforce their required security and business rules.
  • A gateway adds an extra network hop and another application to operate.
  • For high availability, run multiple gateway instances behind a suitable load balancer.
  • In separate containers, localhost refers to the current container. Use backend service names or reachable addresses.
  • Include ocelot.json in deployment output.
  • Check Ocelot package compatibility with your target .NET version before upgrading.
  •