Computer scienceBackendFlaskApplication Architecture

Designing REST API

9 minutes read

Implementing an API to communicate with the frontend side of an application or another backend service from the outside can be a challenging task for developers. Especially when you need it to respect some request logic, and be convenient to use in conjunction with other services and user interfaces. This is almost always the case.

Ideally, you would want it to be understandable by fellow developers.

REST API helps alleviate some of this stress.

The principle

Imagine a software development world that knows no standards, and every developer implements APIs like they think will be most convenient for them in the future. You would definitely have a hard (and long) time understanding the request/response structure, endpoints building principle, and so on.

That's where REST (short for REpresentational State Transfer) comes in. It is an architecture style that will help you implement a fast, simple, scalable, extendable interface. The important note to remember is that REST is not a strict set of rules like programming language syntax. Instead, it provides a series of recommendations for API authors, ensuring that their product is easy to use and supports various data access scenarios and frameworks/languages.

REST and HTTP

Most applications' interfaces use HTTP (HyperText Transfer Protocol) as a data transfer protocol. And since REST is built upon HTTP and uses all its tools, we'll briefly describe them in this chapter. When we talk about what one or the other piece of information is, we will use a particular abstraction — resource. Through that resource, we can describe every entity of our application: users, products, shops, or whatever your API is about.

You also will need a way to access our newly defined resources. For that, we use the Uniform Resource Identifier or URI — the unique name of the resource used in the construction of the Uniform Resource Locator — URL. For example, If you requested information about a user, your request according to REST will look like this:

GET /users/1 where 1 is the ID of some exemplary user.

The second important part of HTTP and hence REST APIs are verbs. Verbs are used to determine what action we need to perform on resources. Usually, developers use GET to retrieve information about some resource, POST to create a resource, PUT to change, and DELETE to remove a resource

Within the previous example, you have the following possibilities:

Method URI to request Expected result Comment
GET /users/1 Got data about the user with id 1 Only need to pass the resource id
DELETE /users/2 User with id 2 is removed
PUT /users/3 To change some data for user 3 The API should expect some new data to change the resource
POST /users to create a new user we would send You will need to provide some data in the request body for creating a new user

You can find code examples for all of them in the examples section below.

Constraints of REST

RESTful interfaces are usually presented with a number of restrictions, one should be:

  1. Uniform Interface
    • Identification of resources. The interface must uniquely identify requested resources.
    • Manipulation of resources. Through representations. Clients should have enough information from resource representation to be able to change or delete the resource.
    • Self-descriptive messages. Each resource representation should carry enough information so the client can decide how to process responses.
    • Hypermedia as the engine of the application state. Clients can receive additional information about related resources they need to complete a task. This is achieved by sending links to related resources.
  2. Client-server separation. Client-server design should consider the separation of responsibilities between the client (interface-related concerns) and server (business logic and data storage concerns). A client should only know an identifier of the requested resource and that should be enough to get the required data.
  3. Stateless interface. In REST each request is completed independently of all previous requests and of an order of requests. A client is responsible for storing session information that may be needed for future requests.
  4. Cacheability. Web service should support caching, meaning storing some responses with rare modification cases. REST service control caching by defining responses as cacheable or non-cacheable.
  5. Layered system architecture. The layered system allows the whole application architecture to be composed of multiple layers with different areas of responsibility(for example, security, business logic, and so on). The client can only see the layer they interact with.
  6. Code on demand (optional). REST also CAN extend the functionality of the client by downloading scripts from the server.

Response data

The REST APIs also have some generally accepted recommendations for responses, let's list them here.

The server response contains a status code, body, and headers

  • A status code is a three-digit number by which a client may determine further action.

For example prompt error message or redirect

These are common status codes:

status code standard status message status meaning
200 OK Generic success response, returns by default with jsonified response but no harm in specifying directly
201 Created POST method success response
400 Bad Request Incorrect request that the server cannot process
404 Resource not found Well, that's pretty self-descriptive
  • The body contains resource representation

The client gets a response in a certain format (JSON is more popular nowadays but XML is also used) whose name is also passed in the Content-Type header. In case of an error, response data would be a descriptive readable message that should point to the cause of the error.

  • And headers contain metadata about the server, encoding, date, content type, etc.

Here are some examples of previously designated recommendations:

from flask import Flask, jsonify, request
import json

app = Flask(__name__)


# retrieving resource data with GET method and providing the id of the resource
@app.route("/users/<user_id>", methods=["GET"])
def get_user(user_id: int):
    user_data = ... # query user table by user_id instead of '...'
    if not user_data:
        # If the user is not found, return the applicable status code with a descriptive message
        return jsonify({"error": "no such user"}), 404

    # 200 with data is sent otherwise
    return jsonify('your_real_user_data_dict'), 200


# using POST method for resource creation, assuming there is some data in the passed form  
@app.route("/users", methods=["POST"])
def create_user():
    # get user data from some form 
    user_data = json.loads(request.data)
    # your business logic, you probably want to save the user data to a database...

    # resource successfully created, correct code for this case is 201
    return jsonify('your_real_user_data_dict'), 201

# using PUT method for update of the resource by passed id
@app.route("/users/<user_id>", methods=["PUT"])
def update_user(user_id: int):
    # your business logic here

    # returning code for a successful update operation
    return jsonify('your_real_user_data_dict'), 200


# using DELETE method and providing the id of the resource to delete 
@app.route("/users/<user_id>", methods=["DELETE"])
def delete_user(user_id: int):
    # your business logic
    return jsonify('your_real_user_data_dict') # return an id of the whole deleted object

REST API best practices

Although there are a number of restrictions that are required for an API to be called RESTful,

There also are some possibilities for making more sense of REST features (namings, response messages, etc.) Let's name a couple of the most crucial recommended practices:

  • Use nouns in URI

REST API has a set of verbs also called methods mentioned in the previous paragraph that describe what we are going to do with resource. No need to append verbs in the URI path too. Use nouns instead. It should be clear what action requesting an endpoint should produce based on the HTTP method and URI with one or multiple nouns.

  • Use the plural form of the resource noun

When we look at two use cases:

GET /article/1 GET /article

and

GET /articles/1 GET /articles

We can say that using plural is uniform for all types of endpoints as we request all articles (not a single article) when requesting an endpoint with no id.

And frontend developers will say thank you for 1 endpoint line less in their config files.

  • Return correct and consistent response codes

There is a more or less consistent bad practice in returning code 200 in absolutely every case. When one looks at the body of that 200 response, one can see status code 404, 500, or any other status that requires debugging and proper error message on the user interface level. That is not the way. ONLY return 200 with a successful GET, PUT, etc. request. There is a status 201 (CREATED) with a successful POST request, and so on. Future you will thank you for this.

Here is great documentation on HTTP codes that lists all statuses and tells about their purpose

  • Another thing about responses: use informative error messages

For the sake of future debugging and user experience, return descriptive error messages. Which field is missing or which check is failed.

  • API versioning

API versioning is the practice that helps manage changes in an interface. It helps users of our API notice what has changed and keep using the same versions of the API for the time needed to adapt to the changes. Here is a link to a short description of versioning strategies.

  • Swagger

It has become more common to use the Swagger or OpenApi interface to describe endpoints, expected request body, and possible responses. Unfortunately, Flask does not generate OpenApi out of the box, but it can be connected via one of the existing python modules: Connexion, flask-swagger, etc. You can find more info and documentation on Swagger or OpenAPI on the official webpage

Conclusion

In this topic, you learned about the base that is behind the REST architecture concept. Also, how exactly it is going to make your API more readable for both developers and browsers.

You also saw what REST looks like on a real-life application and which practices help make the most of it, and get the properly built API architecture and URL routing.

5 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo