inaka / rest_guidelines

REST API Design Guidelines
Apache License 2.0
11 stars 1 forks source link
guidelines rest-api rest-guidelines

REST API Design Guidelines

Introduction

What you will find in this repository is a list of guidelines we follow at Inaka when designing our REST APIs. These guidelines are not taxative, some exceptions can be found here or there, but we do base our tools (like Jayme, Dayron and SumoREST) on them. That's why, if you're going to use one of those tools, it would be much simpler to have a server that complies to the following guides.

Contact Us

And you can check all of our open-source projects at inaka.github.io

Conventions & Rules

Maintain existing style

When editing a system written by someone else, stick to the style in which it was designed. If a project has an overall style, stick to that when adding, removing or updating API endpoints as well.

Examples
Good
Bad
Ugly
Reasoning

It's better to maintain a system that just looks ugly to you than to have a system that looks half ugly to you, half ugly to somebody else.


Use snake_case with JSON

When the endpoint returns or receive a JSON, the keys should always be in snake_case.

Examples
Good
{
    "caption": "string",
    "comment_count": 0,
    "created_at": "2017-03-07T18:57:44.622Z",
    "id": "string",
    "like_count": 0,
    "media_type": "image",
    "owner": "string"
}
Bad
{
    "caption": "string",
    "commentCount": 0,
    "createdAt": "2017-03-07T18:57:44.622Z",
    "id": "string",
    "likeCount": 0,
    "mediaType": "image",
    "owner": "string"  
}
Reasoning

Even though in some programming languages is common to name variables or object instances with camelCase names, we consider the snake_case is the most readable and understandable way to name the key fields on a JSON object.


Don't use response envelopes by default

When the endpoint returns, for example, an array, it should be returned as a plain array, not a dictionary with the array as a value.

Examples
Good
[
    {
        "like_count": 0,
        "id": "string",
        "media_type": "comment",
        "owner": "string"
    },
    {
        "like_count": 0,
        "id": "string",
        "media_type": "comment",
        "owner": "string"
    },
    {
        "like_count": 0,
        "id": "string",
        "media_type": "comment",
        "owner": "string"
    }
]
Bad
{ "items" :
    [
        {
            "like_count": 0,
            "id": "string",
            "media_type": "comment",
            "owner": "string"
        },
        {
            "like_count": 0,
            "id": "string",
            "media_type": "comment",
            "owner": "string"
        },
        {
            "like_count": 0,
            "id": "string",
            "media_type": "comment",
            "owner": "string"
        }
    ]
}
Reasoning

If you're calling a GET on /entities , you're asking for a list of entities, not an object with the entities inside one of its properties. So don't envelope things that should be retrieved directly.


Use all HTTP verbs (Not everything is a GET)

Use all the HTTP verbs. If you have to Create something use POST. If you want to Read something use GET. If you want to Update something use PATCH or PUT. If you want to Delete something use DELETE. Always remember C.R.U.D. (Create, Read, Update, Delete) when setting the verb for your endpoint.

Examples
Good

Creating a comment on a media item:

Bad

Creating a comment on a media item:

Updating the comment content on a media item:

Reasoning

Using the right HTTP Verb on each endpoint will make clearer what the endpoint is made for and what we should expect it to do, receive and return.


Use Nouns for endpoints names (URLs)

Your endpoint names should be always nouns, not verbs.

Examples
Good

Getting a user

Creating a user

Deleting a user

Bad

Getting a post

Creating a post

Deleting a post

Your endpoints should reflect what they do based on the HTTP Verb plus the endpoint name (noun). Therefore it makes sense that doing a GET to /entities retrieves a list of entities. On the contrary, doing a GET to /get/entity looks redundant and it's not self descriptive. Let's try with another example: POST /update_entity/:id. That, semantically speaking, should be interpreted as creating/adding an entity to the update_entity set. If your intention was to update the entity with id :id, you should've used PUT or PATCH /entities/:id instead.


Plural nouns or singular nouns?

If your endpoint manages a list or a group of entities instead of just one then it should be named with a plural noun. If it always manages a single entity then it should be named with a singular noun.

Examples
Good

Getting comments from a post

Getting the likes count of a post

Deleting all the comments from a post

Bad

Getting a post

Updating a post

Deleting a comment from a post

Reasoning

Your endpoint name should be clear about the fact that it affects a single object or a group/list of objects. If you do a POST to /posts , you'll find the object you created by doing a GET to /posts/:id.


Query String VS. Request Body. Where to send the object?

There are four places where you can add paremeters in your HTTP request: query string, path, body and headers. Query Strings are only valid on GET requests and every parameter they include is or should be used as a filter on the data returned. The only scenario where a parameter goes in the path is to identify a resource. Every other paremeter (in POST, PUT, PATCH, etc) should go in the request body. Any parameter that doesn't match any of the previous rules should go in headers (authentication, API versioning, etc).

Examples
Good

Getting posts for an specific user

Rating a post

Geting places nearby the user

Bad

Creating a place

Updating a place

Geting places from Buenos Aires

Reasoning

Is a good semantic practice to use query strings only when you're using GET and use the request body to send all the data needed by the endpoint when using POST, PUT, PATCH, etc.