Guides.NET
ASP.NET Core GraphQL with Hot Chocolate and EF Core
Create a .NET GraphQL API with Hot Chocolate, EF Core, PostgreSQL, Serilog, health checks, xUnit, and Testcontainers using Better Fullstack.
Use this stack when a .NET backend should expose GraphQL instead of REST, with EF Core and PostgreSQL ready to back the resolvers.
npm create better-fullstack@latest my-graphql-api -- \ --ecosystem dotnet \ --dotnet-web-framework aspnet-minimal \ --dotnet-orm ef-core \ --dotnet-auth none \ --dotnet-api graphql-hotchocolate \ --dotnet-testing xunit testcontainers-dotnet \ --dotnet-job-queue none \ --dotnet-realtime none \ --dotnet-observability serilog health-checks \ --dotnet-validation none \ --dotnet-caching none \ --dotnet-deploy docker \ --dotnet-libraries none \ --database postgresThis matches the .NET GraphQL template preset, with every option written out.
What this creates
- An ASP.NET Core application with
HotChocolate.AspNetCore, registered throughAddGraphQLServer()and mapped withMapGraphQL(). - A
Querytype with onehellofield. - An EF Core
AppDbContextwith aTodosset, using the Npgsql provider. - Serilog console logging and ASP.NET Core health checks at
/health. - An xUnit test project that references
Testcontainers, plus aDockerfile.
Generated shape
my-graphql-api/
├── my_graphql_api.csproj
├── my_graphql_api.http
├── Program.cs
├── appsettings.json
├── appsettings.Development.json
├── Dockerfile
└── my_graphql_api.Tests/
├── my_graphql_api.Tests.csproj
└── ApiTests.csProgram.cs holds service registration, the TodoItem model, AppDbContext, and the Query class.
Prerequisites
- .NET SDK 10 or newer.
- PostgreSQL for the data context. The
hellofield works without a database. - Docker if you write tests that start containers through Testcontainers.
Run and verify
cd my-graphql-api
dotnet restore
dotnet runSend a query to the URL ASP.NET Core prints at startup:
curl -X POST http://localhost:5000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ hello }"}'Hot Chocolate exposes the Hello() method as the hello field. Run the generated test project by path, since no solution file is generated:
dotnet test my_graphql_api.Tests/my_graphql_api.Tests.csprojConnect the schema to EF Core
The generated schema does not read from the database yet. AppDbContext is registered, but Query only returns a string:
builder.Services.AddGraphQLServer().AddQueryType<Query>();
public sealed class Query
{
public string Hello() => "Hello from my-graphql-api";
}Hot Chocolate injects services into resolver parameters marked with [Service], so a first data field can take the context directly:
public sealed class Query
{
public string Hello() => "Hello from my-graphql-api";
public IQueryable<TodoItem> GetTodos([Service] AppDbContext db) => db.Todos.OrderBy(todo => todo.Id);
}Nothing creates the Todos table at startup. Microsoft.EntityFrameworkCore.Design is already referenced, so generate and apply a migration before querying it:
dotnet tool install --global dotnet-ef
dotnet ef migrations add InitialCreate
dotnet ef database updateTests
The test project boots the application with WebApplicationFactory<Program> and asserts that / returns 200. It references the core Testcontainers package but contains no container test. For a PostgreSQL container, add the Testcontainers.PostgreSql module to the test project and point ConnectionStrings:DefaultConnection at the container in a custom factory.
When to choose it
Choose Hot Chocolate when clients need to pick fields and combine data in one request. Choose --dotnet-api minimal-api when a handful of REST endpoints is enough; the EF Core guide covers that path.
Compatibility notes
--dotnet-api graphql-hotchocolatereplaces the Minimal API todo endpoints, so/api/todosis not mapped.--dotnet-orm ef-corechooses Npgsql for--database postgresand the SQLite provider forsqlite.--dotnet-observabilityaccepts several values. This command combines Serilog with health checks;AddHealthChecks()has no database check.
Deployment notes
The Dockerfile publishes with the .NET 10 SDK and runs on the ASP.NET 10 runtime image, listening on port 8080 through ASPNETCORE_URLS. Set ConnectionStrings__DefaultConnection and apply migrations before routing traffic.
Troubleshooting
| Symptom | Check |
|---|---|
The .http file's /api/todos request returns 404 | The generated .http file still lists /api/todos; GraphQL requests go to /graphql. |
relation "Todos" does not exist | Create and apply an EF Core migration. |
| A new resolver field is missing | Public methods named GetX become field x; rebuild and restart the app. |
| No containers start during tests | The generated test does not use Testcontainers yet. |
localhost:5000 returns 403 on macOS | AirPlay Receiver also listens on port 5000. Start the app with another port, for example PORT=5080 dotnet run. |
| The generated README mentions TypeScript and npm | The .NET scaffold currently writes the TypeScript README. Use the commands in this guide. |
Next steps
- Open the Stack Builder.
- Read the .NET ecosystem docs.
- Compare with ASP.NET Core Minimal API with Dapper.