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
- The gateway receives the request.
- Ocelot matches
/productsand theGETmethod. - It selects the configured Product API destination.
- It sends an HTTP request to
http://localhost:5001/api/products. - Product API executes its handler.
- Product API returns its JSON response.
- 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,
localhostrefers to the current container. Use backend service names or reachable addresses. - Include
ocelot.jsonin deployment output. - Check Ocelot package compatibility with your target .NET version before upgrading.