This is documentation for v16, which is currently in preview.
See the latest stable version instead.

Fetching from REST

GraphQL requires knowledge of the types it returns at build time. When wrapping a REST API, the most reliable approach is to generate a typed .NET client from an OpenAPI specification and inject it into your resolvers.

Generating a Client from OpenAPI

If your REST endpoint exposes an OpenAPI specification (Swagger), you can generate a fully typed .NET client for it.

Step 1: Get the OpenAPI Specification

Download the swagger.json from your REST endpoint:

Bash
curl -o swagger.json http://localhost:5000/swagger/v1/swagger.json

Step 2: Generate the Client

Use the NSwag CLI tool to generate a C# client:

Bash
dotnet new tool-manifest
dotnet tool install NSwag.ConsoleCore
dotnet nswag swagger2csclient /input:swagger.json /classname:TodoService /namespace:TodoReader /output:TodoService.cs

This generates a TodoService.cs file with a typed client for your REST API. The generated client requires Newtonsoft.Json:

Bash
dotnet add package Newtonsoft.Json
Warning
All HotChocolate.* packages need to have the same version.

Exposing the REST API

Register the generated client in your DI container and inject it into your resolvers.

C#
// Types/TodoQueries.cs
[QueryType]
public static partial class TodoQueries
{
public static async Task<ICollection<TodoItem>> GetTodosAsync(
TodoService service,
CancellationToken ct)
=> await service.GetAllAsync(ct);
public static async Task<TodoItem> GetTodoByIdAsync(
long id,
TodoService service,
CancellationToken ct)
=> await service.GetByIdAsync(id, ct);
}
C#
// Program.cs
builder.Services.AddHttpClient<TodoService>();
builder
.AddGraphQL()
.AddTypes();

You can now open Nitro on your GraphQL server at /graphql and query your REST data:

GraphQL
{
todoById(id: 1) {
id
isComplete
name
}
todos {
id
isComplete
name
}
}

Using DataLoaders with REST

When multiple GraphQL fields resolve data from the same REST endpoint, use a DataLoader to batch and deduplicate calls. This prevents sending redundant HTTP requests for the same resource.

C#
// DataLoaders/TodoByIdDataLoader.cs
public class TodoByIdDataLoader : BatchDataLoader<long, TodoItem>
{
private readonly TodoService _service;
public TodoByIdDataLoader(
TodoService service,
IBatchScheduler batchScheduler,
DataLoaderOptions? options = null)
: base(batchScheduler, options)
{
_service = service;
}
protected override async Task<IReadOnlyDictionary<long, TodoItem>> LoadBatchAsync(
IReadOnlyList<long> keys,
CancellationToken ct)
{
var todos = await _service.GetByIdsAsync(keys, ct);
return todos.ToDictionary(t => t.Id);
}
}

Next Steps

Last updated on April 23, 2026 by Rafael Staib