website/src/docs/hotchocolate/v16/resolvers-and-data/fetching-from-rest.md
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.
If your REST endpoint exposes an OpenAPI specification (Swagger), you can generate a fully typed .NET client for it.
Download the swagger.json from your REST endpoint:
curl -o swagger.json http://localhost:5000/swagger/v1/swagger.json
Use the NSwag CLI tool to generate a C# client:
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:
Register the generated client in your DI container and inject it into your resolvers.
<ExampleTabs> <Implementation>// 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);
}
// Program.cs
builder.Services.AddHttpClient<TodoService>();
builder
.AddGraphQL()
.AddTypes();
// Types/TodoQueries.cs
public class TodoQueries
{
public async Task<ICollection<TodoItem>> GetTodosAsync(
TodoService service,
CancellationToken ct)
=> await service.GetAllAsync(ct);
public async Task<TodoItem> GetTodoByIdAsync(
long id,
TodoService service,
CancellationToken ct)
=> await service.GetByIdAsync(id, ct);
}
// Types/TodoQueriesType.cs
public class TodoQueriesType : ObjectType<TodoQueries>
{
protected override void Configure(IObjectTypeDescriptor<TodoQueries> descriptor)
{
descriptor
.Field(f => f.GetTodoByIdAsync(default, default!, default))
.Type<TodoType>();
descriptor
.Field(f => f.GetTodosAsync(default!, default))
.Type<ListType<TodoType>>();
}
}
// Program.cs
builder.Services.AddHttpClient<TodoService>();
builder
.AddGraphQL()
.AddQueryType<TodoQueriesType>();
You can now open Nitro on your GraphQL server at /graphql and query your REST data:
{
todoById(id: 1) {
id
isComplete
name
}
todos {
id
isComplete
name
}
}
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.
// 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);
}
}