Design predictable API routes by naming resources in the URL and letting HTTP methods carry the action.
Why it matters
Almost every app you will build talks to a server. A mobile app loads a user's feed, a web page saves a form, a dashboard pulls numbers. The server side of that conversation is an API (application programming interface): a set of URLs that accept requests and send back data. If the API is designed well, front-end and mobile developers can guess how it works before they read the docs. If it is designed badly, every screen needs special cases.
Most web APIs today follow a style called REST (representational state transfer). REST is not a library or a standard you install. It is a set of conventions built on top of HTTP, the same protocol your browser uses to load pages.
The core idea: resources and verbs
REST splits every request into two parts:
- The resource: a noun, identified by a URL path.
/courses,/courses/42,/courses/42/lessons. - The verb: the HTTP method, which says what to do with that resource.
| Method | Meaning | Typical path | Changes data? |
|---|---|---|---|
GET | Read | /courses, /courses/42 | No |
POST | Create a new item in a collection | /courses | Yes |
PUT | Replace an item completely | /courses/42 | Yes |
PATCH | Update some fields of an item | /courses/42 | Yes |
DELETE | Remove an item | /courses/42 | Yes |
Two properties are worth remembering:
- Safe methods do not change data.
GETis safe, so browsers, caches and crawlers may call it freely. Never let aGETdelete or update anything. - Idempotent methods give the same result if you repeat them.
GET,PUTandDELETEare idempotent: deleting item 42 twice leaves it just as deleted.POSTis not: sending "create order" twice can create two orders. That is why payment APIs often ask for an idempotency key.
Worked example: designing routes for a resource
Say you are building the API for a course platform, and the first resource is a course. Start with the nouns, then attach verbs:
GET /courses list courses (maybe ?level=beginner&page=2)
POST /courses create a course (data in the request body)
GET /courses/:id read one course
PATCH /courses/:id change some fields of one course
DELETE /courses/:id delete one course
GET /courses/:id/lessons list the lessons that belong to one course
A few habits make this predictable:
- Plural nouns for collections.
/courses, not/courseor/getCourses. The verb is already in the method, so do not repeat it in the path. - IDs in the path, filters in the query string.
/courses/42names one thing./courses?level=beginnernarrows a list. Sorting and paging (?sort=title&page=2) also go in the query string. - Nest only one level for ownership.
/courses/42/lessonsis clear./schools/1/courses/42/lessons/7/commentsis painful; once a lesson has its own ID,/lessons/7/commentsworks. - Lowercase, hyphenated paths.
/learning-paths, not/LearningPaths.
A full request and response look like this when you run curl -i against a local server:
GET /courses/42 HTTP/1.1
Host: localhost:3000
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 42, "title": "Back-End Web APIs", "level": "intermediate" }
The request line holds the method and path. Headers carry metadata such as Accept (what format the client wants) and Content-Type (what format the body is in). The body holds the data. The response starts with a status code (200 OK here), which the next lesson covers in detail.
Actions that are not CRUD
Some operations do not fit create, read, update and delete neatly: "publish a course", "reset a password", "send a test email". Two common answers:
- Model it as a field change:
PATCH /courses/42with{ "status": "published" }. - Model it as creating a new resource:
POST /password-resetswith{ "email": "..." }. The "reset request" becomes a thing you create.
Both are fine. What you should avoid is a path full of verbs such as POST /publishCourse?id=42, because each one becomes a special case for every client.
Common mistakes
- Using GET for changes. A link like
/courses/42/deletecan be triggered by a browser prefetch, a crawler or an image tag on another site. - Verbs in paths.
/createUser,/getAllUsers. Let the method carry the verb. - Inconsistent shapes. If
GET /coursesreturns{ "items": [...] }, do not makeGET /lessonsreturn a bare array. Pick one shape and keep it. - Leaking database details. The URL is a public contract. Exposing table names or internal columns makes it hard to change your database later.
- Changing a route that clients already use. Once a mobile app ships, old versions keep calling the old URL for months. Add new fields and routes; remove old ones slowly, or version the API (
/v2/...).
Quick recap
- REST is a convention: resources are nouns in the URL, methods are verbs.
GETreads and must never change data.POSTcreates,PUTreplaces,PATCHupdates part,DELETEremoves.- Use plural collections, IDs in the path, filters and paging in the query string, and shallow nesting.
- Your routes are a contract with every client. Keep them consistent and change them carefully.
Head to the Do tab and test yourself on route design before moving on to status codes.