In the last two chapters we talked about Go in the abstract. Now let us look at the thing you will actually build. The whole course points at one project: a Task API, a small web service for managing a to-do list over HTTP. This chapter is a map of that destination, so when we start writing code you already know where each piece fits.
No code here yet. Just the shape of what is coming.
A Task API is a backend service that stores tasks and lets other programs create, read, update, and delete them over the network. There is no website or screen attached to it. It speaks JSON (a plain-text format for sending data) over HTTP (the protocol the web runs on), so anything that can make an HTTP request can use it: a web app, a mobile app, a command-line tool, or you, poking at it with curl.
A single task is simple. It has a title, maybe a longer description, and a flag for whether it is done. That is the entire domain. We deliberately keep it small so the interesting part is never "what does a task mean" but "how do you build a service properly in Go."
A to-do app is the classic beginner project, and for backend work that reputation is earned. Here is what makes it a good teacher.
The domain gets out of your way. You already understand tasks, so no mental energy goes into the problem itself. All of it goes into the code.
It exercises the full set of REST operations. REST is the common style for HTTP APIs, where you act on resources (here, tasks) using a small set of HTTP verbs. A task naturally needs all of them: create one, list them, fetch one, change one, remove one. By the time the API is finished you will have used every verb you are likely to need in real work.
And it grows in exactly the directions a real service does. We start with tasks held in memory, then add validation, then tests, then a real database, then the production trimmings. Each step has an obvious reason to exist, so nothing feels like busywork. The same arc, from "it works on my laptop" to "it is ready to ship," is the arc of most backend projects you will build for real.
Here are the five endpoints the finished API exposes. Each pairs an HTTP verb with a path, and each returns a specific success status code. (A status code is the three-digit number HTTP uses to say how a request went. 2xx means success; 200 OK, 201 Created, and 204 No Content are the three we use here.)
| Method & path | Purpose | Success status |
|---|---|---|
POST /tasks | Create a new task | 201 Created |
GET /tasks | List all tasks | 200 OK |
GET /tasks/{id} | Get one task by its ID | 200 OK |
PUT /tasks/{id} | Update an existing task | 200 OK |
DELETE /tasks/{id} | Delete a task | 204 No Content |
A few things worth noticing now, because they come back later.
The verb carries the intent. GET reads, POST creates, PUT replaces, DELETE removes. The path names what you are acting on; the verb says what to do with it. That is the core idea of REST.
The {id} in three of the paths is a placeholder for a real task's ID, like GET /tasks/42. The same path with a different verb does a different thing, which is why routing requests to the right handler gets its own chapter later.
The status codes are not interchangeable. Create returns 201 (something new exists), a successful read or update returns 200 (here is the result), and delete returns 204 (it worked, and there is nothing to send back). Choosing the right one is part of building an API that other developers can trust, so we will be deliberate about it.
Every task the API stores and returns looks like this:
json{"id": 1,"title": "Write the first HTTP handler","description": "Return a JSON response from net/http","done": false,"createdAt": "2026-06-24T09:30:00Z"}
The fields:
true or false, whether the task is finished. New tasks start false.Notice that the server, not the client, owns id and createdAt. When you create a task you do not send those; the server fills them in and hands the complete task back. That keeps IDs unique and timestamps honest, and it is a small design decision you will see pay off when we write the create endpoint.
To make the JSON concrete, here is what creating a task looks like from both sides.
You send a POST to /tasks with just the fields you control:
json{"title": "Write the first HTTP handler","description": "Return a JSON response from net/http"}
The server stores it and replies with 201 Created and the full task, now including the id, the done flag defaulted to false, and the createdAt it stamped on:
json{"id": 1,"title": "Write the first HTTP handler","description": "Return a JSON response from net/http","done": false,"createdAt": "2026-06-24T09:30:00Z"}
That round trip, JSON in and JSON out, is the heartbeat of the whole service. Every endpoint is a variation on it.
When a request arrives, it does not hit one giant block of code. It passes through a few clear layers, each with one job. Keeping these separate is what makes the service easy to test and easy to change later.

Reading left to right:
curl, a browser, an app. It sends an HTTP request and waits for a response.That last layer is the one to watch. We begin with an in-memory store: tasks held in a Go map that lives only while the program runs. Restart the server and they are gone. It is the simplest possible storage, which lets us focus on HTTP and JSON without a database in the way. Later we replace it with PostgreSQL, a real relational database, so tasks survive restarts.
Here is the part that makes the layering worth the effort: the service and HTTP layers will not change when we swap the store. They talk to storage through a single shared definition of what a store must do, so the in-memory version and the Postgres version are interchangeable. We will lean on that seam more than once, and it is one of the most useful ideas in the whole course.
We do not build all of this at once. The course adds one capability at a time, in the order a real project tends to grow.

You may have noticed we write tests before adding the database. That ordering is deliberate. It is easier to learn testing against the simple in-memory version, and once the tests exist they act as a safety net for the bigger change that follows. Throughout, we build the service the way it is done in production, not as a toy that happens to run.
Enough planning. In the next chapter we install Go, set up your editor, and run your first program, so the toolchain is ready before we write any of the API.