Link Search Menu Expand Document

3. A Simple HTTP API

Up to now, Autobarn has only produced HTML for people using a browser. This module adds a first, deliberately simple HTTP API that returns the same data as JSON for other programs to use. It’s built with ASP.NET Core minimal APIs and runs alongside the MVC controllers in the same web application.

What’s in this module

Autobarn.Website/
├── Api/
│   └── AutobarnApiEndpointRouteBuilderExtensions.cs   (new)
└── Program.cs                                          (changed)

Everything else is the same as Module 02.

Changes from Module 02

File Change
Autobarn.Website/Api/AutobarnApiEndpointRouteBuilderExtensions.cs Added. Defines four GET endpoints
Autobarn.Website/Program.cs Modified. Maps the API under /api

Step 1: Define the API endpoints as an extension method

File: Autobarn.Website/Api/AutobarnApiEndpointRouteBuilderExtensions.cs (new)

using Autobarn.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;

namespace Autobarn.Website.Api;

public static class AutobarnApiRouteBuilderExtensions {

	public static IEndpointRouteBuilder MapAutobarnApi(this IEndpointRouteBuilder endpoints, string pattern) {

		endpoints.MapGet(pattern + "/vehicles",
			async (AutobarnDbContext db) => await db.Vehicles.ToListAsync());

		endpoints.MapGet(pattern + "/vehicles/{registration}",
			async (AutobarnDbContext db, string registration) => await db.Vehicles.FindAsync(registration));

		endpoints.MapGet(pattern + "/makes",
			async (AutobarnDbContext db) => await db.Makes.ToListAsync());

		endpoints.MapGet(pattern + "/makes/{code}/models",
			async (AutobarnDbContext db, string code) => await db.Models.Where(m => m.MakeCode == code).ToListAsync());

		return endpoints;
	}
}

How it works:

  • Minimal APIs. Each MapGet call links a route template to a lambda. ASP.NET Core fills in the lambda’s parameters automatically. AutobarnDbContext comes from dependency injection, and string registration and string code come from the {registration} and {code} route segments.
  • Automatic JSON serialisation. Whatever the lambda returns is written to the response as JSON using System.Text.Json.
  • Extension method on IEndpointRouteBuilder. Putting the API in its own MapAutobarnApi() method keeps Program.cs short and the API code in one place. It’s the same pattern the framework uses for MapControllerRoute and MapStaticAssets.
  • pattern parameter. The caller chooses the URL prefix, so the API isn’t tied to /api.
Method URL Returns
GET /api/vehicles Every vehicle in the database
GET /api/vehicles/{registration} One vehicle
GET /api/makes Every make
GET /api/makes/{code}/models Every model for one make

Step 2: Map the API in Program.cs

File: Autobarn.Website/Program.cs

using Autobarn.Data;
using Autobarn.Website.Api;                  // new
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
app.MapStaticAssets();

app.MapAutobarnApi("/api");                  // new

app.MapControllerRoute(
	name: "default",
	pattern: "{controller=Home}/{action=Index}/{id?}")
	.WithStaticAssets();

Why: The API and the MVC site share one process, one DbContext registration and one in-memory database. A vehicle added through the website’s Advertise form shows up straight away in /api/vehicles.

What’s wrong with this API?

The API is intentionally naive so that later modules have something to fix. Some things to point out to attendees:

  1. It returns entities directly. EF Core entities are serialised as they are, so the JSON shape is tied to the database schema. Navigation properties appear too. Vehicle.Model is null because it wasn’t loaded with Include, and VehicleMake.Models is an empty list. (Module 05 adds dedicated resource types.)
  2. No pagination. /api/vehicles returns all ~5,000 vehicles in one response. (Module 05 adds index and count paging.)
  3. No 404s. /api/vehicles/NOPE doesn’t return 404 Not Found, and /api/makes/nope/models returns an empty list, which looks the same as a real make with no models. (Module 04 adds typed results.)
  4. No documentation. A client developer can only find out what endpoints exist by reading the source. (Module 04 adds OpenAPI and Scalar.)
  5. No links between resources. A client has to know it should build /api/makes/{code}/models itself. (Module 05 adds hypermedia.)
  6. Inconsistent route parameters. The make is {code} in one place, and there’s no endpoint for a single make or model.

Trying it out

dotnet run --project Autobarn.Website
curl http://localhost:5000/api/makes
curl http://localhost:5000/api/makes/nissan/models

What’s next

Module 04 makes the API self-documenting with OpenAPI and adds an interactive API reference with Scalar.