# API Design

# Introduction

Imagine the Twitter API. You can use it to post, get, and even delete tweets. The Twitter API is an example of an external API. It's provided by a third-party service, Twitter, that allows us to interact with their service programmatically.

We need a way to interact with the Twitter API. How we do that is where API design comes in.

In this post, we'll focus on the basics of API design.

# CRUD

When designing an API, we often talk about CRUD operations. CRUD stands for Create, Read, Update, and Delete. These are the basic operations that we can perform on a resource.

Often, it'll be helpful to think about the operations as functions, e.g., createTweet, getTweet, updateTweet, deleteTweet.

However, in REST APIs, the HTTP methods dictate the operation. `POST` is used to create a resource, `GET` is used to read a resource, `PUT` is used to update a resource, and `DELETE` is used to delete a resource.

# Resource

A resource is an object or representation of something with data associated with it. For example, a user is a resource, and a user's name, email, and address are data associated with that resource.

Referring back to the Twitter example, a Tweet is a resource. A Tweet has data associated with it, like the tweet's content, the user who posted the tweet, and the timestamp when the tweet was posted.

# Why API Design Matters

When designing an API, we need to consider how it will be used and evolve over time. We can't simply change it whenever we want. Once an API is released and used, we need to be careful about making changes. We need to maintain backward compatibility so existing clients can continue using the API without any issues.

# Example of introducing a new feature

Let's say we have `createTweet(userId, content)` API for creating a tweet.

In REST, it'd look something like [`https://api.twitter.com/tweets`](https://api.twitter.com/tweets) with a `POST` request.

We want to add a feature where users can reply to a tweet. We can't simply add another field like `parentId` to the `createTweet` API. It wouldn't be backward compatible. We could make the field optional to maintain backward compatibility, but that's not a good design. It's not a good design because it's not clear that the `parentId` field is for replying to a tweet.

But, you can imagine a different scenario where introducing an optional field is a good design not to break existing clients.

Instead, we could create a new API like `createReply(userId, content, parentId)`.

In REST, it'd look something like [`https://api.twitter.com/replies`](https://api.twitter.com/replies) with a `POST` request.

# Versioning

If we have to introduce breaking changes, we can version our API. We can have different versions of our API running simultaneously. This way, existing clients can continue to use the old version, and new clients can use the new version.

For example, the Twitter API might look like [`https://api.twitter.com/v1/tweets`](https://api.twitter.com/v1/tweets) for version 1 and [`https://api.twitter.com/v2/tweets`](https://api.twitter.com/v2/tweets) for version 2. Existing clients can continue to use `v1`, and new clients can use `v2`.

# What to pass in the request

Let's take a look at what a Tweet may look like:

```javascript
{
  id: string
  userId: string
  content: string
  createdAt: Date
  likes: int
}
```

If the user wants to create a tweet, they'd send a `POST` request to [`https://api.twitter.com/tweets`](https://api.twitter.com/tweets) with the following payload:

```javascript
{
  userId: "123",
  content: "Hello, world!"
}
```

They would only need to pass the `userId` and `content` fields. The `id`, `createdAt`, and `likes` fields would be generated by the server.

It's important to understand that not everything in a resource needs to be passed in the request. Some fields should be generated by the server.

# Get a specific resource

What if we wanted to get a specific tweet?

We could make a `GET` request to [`https://api.twitter.com/tweets/:id`](https://api.twitter.com/tweets/:id), where `:id` is the ID of the tweet. It would look something like [`https://api.twitter.com/tweets/123`](https://api.twitter.com/tweets/123).

To the same endpoint, we could make a `DELETE` request to delete the tweet.

# Get a list of resources by a related resource

What if we wanted a specific user's list of tweets?

We could make a `GET` request to [`https://api.twitter.com/users/:userId/tweets`](https://api.twitter.com/users/:userId/tweets), where `:userId` is the ID of the user. It would look something like [`https://api.twitter.com/users/123/tweets`](https://api.twitter.com/users/123/tweets).

## Pagination

What happens if the user has thousands of tweets?

It would be inefficient to return all the tweets at once. Instead, we can use pagination. We can limit the number of tweets returned per page and provide a way to get to the next page of tweets.

This is where query parameters like `limit` and `offset` come in. We could make a `GET` request to [`https://api.twitter.com/users/123/tweets?limit=10&offset=0`](https://api.twitter.com/users/123/tweets?limit=10&offset=0) to get the first 10 tweets, and [`https://api.twitter.com/users/123/tweets?limit=10&offset=10`](https://api.twitter.com/users/123/tweets?limit=10&offset=10) to get the next 10 tweets.

`limit` specifies how many tweets to return, and `offset` specifies where to start fetching the tweets.

Internally, we may order the tweets by `createdAt` in descending order to get the latest tweets first.

# Idempotence

`GET` and `PUT` requests are supposed to be idempotent, meaning they should not have any side effects.

`POST` and `DELETE` requests are not idempotent. They can have side effects. For example, making the same `POST` request multiple times will create multiple resources.

# Conclusion

The most important points to remember when designing an API are:

* Keep your API backward compatible.
    
* Make sure your API is idempotent where it should be.
    
* Endpoints should be intuitive and easy to understand.
    
* Use query parameters for filtering, sorting, and pagination.
    
* Use nouns for resources and HTTP methods for operations.
    
* If you have to introduce breaking changes, version your API.
