How to correctly design REST APIs
Tags: webdev, api, backend
Published: 2022-08-18 17:45:00

REST stands for "Representational State Transfer". This is an architectural style. A web API conforming to this style is a REST API.

REST stands on **6 guiding principles**, important among them are:
- Client-server: separate backend and frontend.
- Stateless: each http request contains enough data (e.g. auth, session, user data) to understand it without knowing what the previous request was.
- Cacheable
  - GET: always 
  - POST: using http headers `expire`, `cache-control`, `etag`, `last-modified`
  - PUT, DELETE: never


# 🧠 Concepts 
![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79n67b6tyw7nss50ow6z.png)

The following are some important, concise, and simplified REST concepts a good backend dev must be familiarized with.

### Resource
REST APIs are modeled as a resource hierarchy - where each node is either a collection, or a single resource. A single resource has some state, or sub-resources. 

Example of a stateful resource is the following, where a user resource has data/states:
```js
POST /users/:id

{
  id: 4e7d418a70f,
  name: 'muhammad',
  country: 'earth',
  verified: true
}
```

Example of a resource with sub-resources is the following, where a user has multiple projects, each of which are a resource.

```js
GET /users/:id/projects
content-type: application/json

[
  {
    id: 'postman cli',
    desc: 'postman implemented in command-line'
    url: 'github.com/midnqp/postman-cli'
  },
  {
    id: 'typescript',
    desc: 'typescript is a superset of javascript',
    url: 'github.com/microsoft/typescript'
  }
]
```

### Idempotency
API Idempotency means "a client can make a request multiple times, returning same response - without having any side-effect".

Making the following requests multiple times produces the same result, and has no further side-effect - thus are "idempotent" APIs:
- `GET /users/:id` - just retrieving same user data
- `PUT /users` - just updating same json data
- `DELETE /users/:id` - just deleteing the same user

A non-idempotent API:
- `POST /users/:id` - because a new user will be created everytime this request is made

# 🎨 Designs


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/luuhhldx2mxb4u14wm8r.jpg)

Some constraints in REST API naming ensures a design of scalable API endpoints:

- Use *plural* nouns, e.g. users, orders, categories.
- Use *hyphen*, not underscores. Use *lowercase*, never camel-case, e.g. `GET /food-categories`, not `GET /foodCategories`.
- *Never* use CRUD function names, e.g. `GET /users/list` or `POST /users/create`.


Some perfect API endpoints:
- `GET /users` - get a collection/list of users.
- `GET /users/:id` - get a single user.
- `POST /users/:id` - create a single user.
- `PUT /users/:id` - update a single user.
- `DELETE /users/:id` - delete a single user.

For sub-resources:
- `GET /users/:id/projects` - get projects of given user
- `DELETE /users/:id/projects/:id` - remove a project of given user

Adding some features:
- `GET /users?country=earth&verified=true` - search users by `country` and `verified`
- `GET /users?$fields=name,country` - list users, but return only 2 data attributes: `name` and `country`
- `GET /users?$sort=name&$order=asc` - list users and sort by `name` in ascending order
- `GET /users/$page=1&$limit=10` - list users and paginate with `page` and `limit`


# 🚫 Errors
![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpwe00cap7q5v0pbb7h5.jpg)

Most Google APIs use resource-oriented API design. Instead of defining different `NOT_FOUND` errors, the server uses one standard `NOT_FOUND` status code, and tells the client which specific resource was not found.

The smaller error space has advantages:
- reduces the complexity of documentation
- better mapping
- reduces client logic complexity

### Model

```ts
type Error = {
  code: number
  message: string
  details: any[]
}
```

- `Error.code`: simple code, easily handled by client
- `Error.message`: Developer-facing human-readable error
- `Error.details`: Additional error information for client, such as retry info, help link, etc.

### Code

- Individual APIs __must__ avoid defining additional error codes.
- Developers must use canonical error codes.
- Standard error codes for Google APIs:
  - `200 OK`, not an error; returned on success.
  - `500 UNKNOWN`, internal server error; unexpected and insufficient info
  - `400 INVALID_ARGUMENT`, invalid/problematic user data
  - `404 NOT_FOUND`, something doesn't exist globally, for everyone
  - `409 ALREADY_EXISTS`, something already exists
  - `403 PERMISSION_DENIED`, access denied for a user; relevant people have access
  - `401 UNAUTHENTICATED`, no bearer token; no valid auth creds
  - `429 RESOURCE_EXHAUSTED`, too many requests; rate-limit exceeded
  - `400 FAILED_PRECONDITION`, the operation was rejected; business logic unmet
  - `400 UNAVAILABLE`, the operation was rejected; user should retry

### Message

Error messages should help users understand & resolve API errors easily.

- Do not assume the user to an expert user of the API.
- Do not assume user knows anything about service implementation.
- Should be constructed such that technical users can respond, and correct it.
- Keep the message brief. If needed, provide a link to more info, questions, feedback. Otherwise, use "details" field.