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 13, 2026 by Michael Staib