Web APIs
A web API is a program that answers requests from other programs. A client sends an HTTP request: a method such as GET (read) or POST (create) and a path such as /todos. The API answers with a status code and data, usually JSON.
Here is a whole web API in nine lines of Program.cs, written with ASP.NET Core minimal APIs. dotnet new web makes the project; its Web SDK brings in ASP.NET Core. Line 9 declares Todo, a record with an Id and a Title.
Build the app
Line 1 builds the app with ready-made defaults: settings, logging and Kestrel, the web server built into ASP.NET Core. Line 2 makes todos, an empty List<Todo>. It lives in the server's memory, so every request shares it.
MapGet and MapPost add endpoints. An endpoint pairs an HTTP method and a route pattern with a handler: a lambda that runs when a request matches. Nothing runs yet. The routing table now holds GET /todos and POST /todos.
Line 8, app.Run(), starts Kestrel and waits for requests until the app is stopped. With no address configured, Kestrel listens on http://localhost:5000. Start it with dotnet run --no-launch-profile, which skips the port saved in launchSettings.json.
Handle requests
The client sends POST /todos with a JSON body. Kestrel turns the bytes into an HTTP request, then routing compares its method and path with every endpoint. POST /todos matches, so the handler on lines 4 to 7 is picked.
The handler asks for a Todo t, so ASP.NET Core reads the body as JSON and builds a Todo with System.Text.Json. This is parameter binding. Names match whatever their case, so "title" fills Title. A bad body gets 400 Bad Request.
Now the handler runs. todos.Add(t) puts the new to-do in the list, so Count goes from 0 to 1. Then Results.Created makes a 201 Created result whose Location header points at the new item: /todos/1.
ASP.NET Core writes the result as an HTTP response: status 201 Created, the Location header, and the to-do turned back into JSON. Kestrel sends it to the client. The list stays in memory, ready for the next request.
Next the client sends GET /todos, with no body: GET only reads. Routing matches the first endpoint, and its handler on line 3 simply returns the list. Each request is handled on its own; only todos remembers anything.
The handler returned a List<Todo>, so ASP.NET Core writes it as JSON with web defaults: names become camelCase, so Id is written as id. The client gets 200 OK, a JSON Content-Type and the body [{"id":1,"title":"Buy milk"}].
Not every request finds a handler. DELETE /todos matches a path but no method, so routing answers 405 Method Not Allowed. GET /nope matches nothing at all, so the answer is 404 Not Found. No handler runs for either.
What happens
// step 1/12 · web-api
client → request GET /todos HTTP/1.1
method GET reads · POST creates
path /todos
server → response a status code (200 OK) + data (JSON)
The HTTP messages in the figure show only the headers that matter here. Real responses also carry Date, Server and Content-Length or Transfer-Encoding. Kestrel can handle many requests at the same time on different threads, and List<T> is not safe for that. Real code uses a database, a lock or a thread-safe collection such as ConcurrentDictionary.