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:
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:
dotnet new tool-manifestdotnet tool install NSwag.ConsoleCoredotnet 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:
dotnet add package Newtonsoft.JsonHotChocolate.* 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.
// 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.csbuilder.Services.AddHttpClient<TodoService>();
builder .AddGraphQL() .AddTypes();
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 }}
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.
// DataLoaders/TodoByIdDataLoader.cspublic 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
- Need to batch REST calls? See DataLoader.
- Need to fetch from a database instead? See Fetching from Databases.
- Need to understand resolvers? See Resolvers.