This is more of a reference than a how to guide. It goes through the steps of setting up a new ASP.NET Core API with a database, but there's not much information on why you would do things a certain way.
dotnet new web -o MyApp
If you want controller's you can add them in the endpoints section.
Models
directory with a model class for your resource.For example, if you were creating a Todo
list application, you might name your model file Todo.cs
and it would look like this:
// /Models/Todo.cs
namespace MyApp.Models;
public class Todo
{
public int Id { get; set; }
public string Title { get; set; }
public bool Completed { get; set; }
public DateTime CreatedAt { get; set; }
}
Models
directory to manage your database
connection.using Microsoft.EntityFrameworkCore;
namespace MyApp.Models;
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Todo>()
.Property(e => e.CreatedAt)
.HasDefaultValueSql("now()");
}
}
Choose either Postgres or MySQL.
Npgsql.EntityFrameworkCore.PostgreSQL
package:dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
Program.cs
file:var connectionString = "Host=localhost;Database=yourDBName;Username=yourUsername;Password=yourPassword";
services.AddDbContext<DatabaseContext>(
opt =>
{
opt.UseNpgsql(connectionString);
if (builder.Environment.IsDevelopment())
{
opt
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
);
Optionally, you could also add the Diagnostics
package to get some nice error pages when things go wrong. I haven't seen this work yet, but the docs suggest using it so 🤷♀️
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
if (builder.Environment.IsDevelopment())
{
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
}
This isn't really document well anywhere and seems to go against how the .NET community likes to store configuration values. However, here's my argument for using normal environment variables:
dotnet add package DotNetEnv
.env
file in the root of your project and add your environment
variables:DATABASE_CONNECTION_STRING="your database connection string"
DotNetEnv.Env.Load();
var connectionString = Environment.GetEnvironmentVariable("DATABASE_CONNECTION_STRING");
dotnet-ef
tool installed on your machine:dotnet tool install --global dotnet-ef
Microsoft.EntityFrameworkCore.Design
package:dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet ef migrations add InitialCreate
dotnet ef database update
Once you have your database setup, you can start creating CRUD endpionts. This is the fun part!
app.MapGet("/api/todoitems", async (DatabaseContext db) =>
await db.Todos.ToListAsync());
app.MapGet("/api/todoitems/complete", async (DatabaseContext db) =>
await db.Todos.Where(t => t.Completed).ToListAsync());
app.MapGet("/api/todoitems/{id}", async (int id, DatabaseContext db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/api/todoitems", async (Todo todo, DatabaseContext db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/api/todoitems/{todo.Id}", todo);
});
app.MapPut("/api/todoitems/{id}", async (int id, Todo inputTodo, DatabaseContext db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/api/todoitems/{id}", async (int id, DatabaseContext db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.Ok(todo);
}
return Results.NotFound();
});
You can test the API using a tool like Postman, but I suggest adding swagger.
ASP.NET APIs provide built-in support for generating information about endpoints via the Microsoft.AspNetCore.OpenApi
package.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/openapi?view=aspnetcore-7.0
dotnet add package Microsoft.AspNetCore.OpenApi
dotnet add package Swashbuckle.AspNetCore
using Microsoft.AspNetCore.OpenApi;
// ...
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
Now you can test your app and see the swagger UI at https://localhost:5053/swagger/
This react app will be deployed as part of your .NET app in production. They will exist on the same server, with the .NET app serving the react app as its frontend.
yarn vite create my-app-frontend
// https://vitejs.dev/config/
export default defineConfig({
server: {
proxy: {
"/api": "http://localhost:5053",
},
},
plugins: [react()],
})
Make GET, POST, PUT, and DELETE requests to the API to CRUD your resource. Example:
async function createTodo(name) {
const result = await fetch("/api/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name }),
}).then((res) => res.json())
}
yarn build
dist
folder from your react app into your .NET project and rename
dist
to wwwroot
app.UseDefaultFiles();
app.UseStaticFiles();
app.MapFallbackToFile("index.html");
The dotnet app should now serve up your frontend app when you make a request that isn't handled by the API.
Find an issue with this page? Fix it on GitHub