๐๐ฒ๐๐ ๐ฃ๐ฟ๐ฎ๐ฐ๐๐ถ๐ฐ๐ฒ๐ ๐ถ๐ป Restful ๐๐ฃ๐ ๐๐ฒ๐๐ถ๐ด๐ป
What is REST? โ It stands for Representational State Transfer.
REST API transfers a representation of a resource (data) that resides in the server (in the Database) to the client. The representation can be in the form of JSON, XML, or even in HTML.
The data that is transferred by REST API is organized according to a design. So, we can say REST is a data architecture.
Also, it always produces predictable and consistent outputs. REST API consists of six constraints.
- What are the six constraints of REST API?
- Client โ Server Architecture โ This rule ensures the separation of concerns. Client manages the UI concerns while the Server manages the data persistence concerns. In return, we get a highly portable system where once REST API can manage different clients.
- Statelessness โ No client data can be stored on the server between requests. If the client state is relevant for the requests, it must be sent along with the requests. If server needs to save the client sessions, it should be saved in a DB for a given time of period.
- Cacheability โ All responses should be marked as cacheable or non-cacheable. If response can be changed constantly, we should not cache them. Cacheability matters for the performance of the REST API.
- Layered System โ Client cannot know / should not care whether it directly connected to the original server or intermediary along the way. It means REST allows you to have a layered system architecture and the request can be sent through different layers. This helps with security and scalability (CDN, authorization server).
- Code on demand โ REST API can transfer executable JavaScript files and compiled components to the client when needed.
- Uniform Interface โ As the name implies, there should be an interface for the resources which are exposed to the API clients. A resource in the server should have only one logical URI to retrieve or manipulate the resource.
There are four sub rules defined for this constraint.
They are:
1) Uniform Interface should have a unique resource identifier.
2) Uniform Interface Resource manipulation through representations. Once a client has a representation of a resource, it should be able to modify or delete it.
3) Uniform Interface must issue self-descriptive messages for sending and receiving representational data (content-type).
4) Hypermedia as the engine of application state. Once a client has access to a REST service, it should be able to discover all available resources and methods through the hyperlinks provided (Ex: curl https://api.github.com).
What is RESTful API? โ REST API can be derived using many protocols (Ex: HTTP, FTP). In practical an API which follows REST constraints + HTTP (use of HTTP methods) is called RESTful API.
So, if you want to implement a service,
- based on client-server architecture
- and want to serve different clients through HTTP protocol
- and want to use the REST constraints described above
you can select RESTful architecture for your service.
The standard method of communication between the systems is through APIs. Therefore, itโs crucial to properly build REST APIs to avoid issues in the future. A well-defined API should be ๐ฒ๐ฎ๐๐ ๐๐ผ ๐๐ผ๐ฟ๐ธ ๐๐ถ๐๐ต, ๐ฐ๐ผ๐ป๐ฐ๐ถ๐๐ฒ, ๐ฎ๐ป๐ฑ ๐ต๐ฎ๐ฟ๐ฑ ๐๐ผ ๐บ๐ถ๐๐๐๐ฒ.
What is API Design?
API design refers to the process of developing application programming interfaces (APIs) that expose data and application functionality for use by developers and users.
The Importance of Knowing Use Cases:
When you understand how your software will be used you can design it better. The biggest mistake in API design is to make decisions based on how your system works, rather than what your consumers need to support. In order to design around use cases, youโll need to talk to the consumers, or at least include those who know them better.
Software is rarely built entirely by engineers. There are stakeholders throughout the organization. And while many engineers can be very product-minded, they donโt always have the visibility of the full picture. If your organization has a product group, thatโs often where the voice of the customer is most heard. Involve anyone who understands how an API will be used in discussions as you design the API. When you involve others in API design, you build something better.
API Design Best Practices:
Armed with an understanding of your use cases, youโre ready to begin your API design. Each project is different, so best practices may not always fit your situation.
In general, an effective API design will have the following characteristics:
Easy to read and work with
Hard to misuse
Complete and concise
Here are some general recommendations:
๐ญ. ๐จ๐๐ฒ ๐ป๐ผ๐๐ป๐ ๐ถ๐ป๐๐๐ฒ๐ฎ๐ฑ ๐ผ๐ณ ๐๐ฒ๐ฟ๐ฏ๐
Verbs should not be used in endpoint paths. Instead, the pathname should contain the nouns that identify the object that the endpoint that we are accessing or altering belongs to.
E.g., instead of using http://api.movieservice.com/๐๐๐๐ฐ๐๐Movies to fetch all clients, use http://api.movieservice.com/movies.
๐ฎ. ๐จ๐๐ฒ ๐ฝ๐น๐๐ฟ๐ฎ๐น ๐ฟ๐ฒ๐๐ผ๐๐ฟ๐ฐ๐ฒ ๐ป๐ผ๐๐ป๐
Try to use the plural form for resource nouns, because this fits all types of endpoints.
E.g., instead of using /movie/:๐๐/, use /movies/:๐๐/
๐ฏ. ๐๐ฒ ๐ฐ๐ผ๐ป๐๐ถ๐๐๐ฒ๐ป๐
When we say to be consistent, this means to be predictable. When we have one endpoint defined, others should behave in the same way. So, use the same case for resources, the same auth methods for all endpoints, use the same headers, use the same status codes, etc.
๐ฐ. ๐๐ฒ๐ฒ๐ฝ ๐ถ๐ ๐๐ถ๐บ๐ฝ๐น๐ฒ
We should make naming all endpoints to be resource-oriented, as they are. If we want to define an API for movies, we would define it as:
http://api.movieservice.com/v1/movies/
http://api.movieservice.com/v1/movies/๐ท๐ธ๐บ
So, the first API gets all movies and the second one gets a specific movie.
๐ฑ. ๐จ๐๐ฒ ๐ฝ๐ฟ๐ผ๐ฝ๐ฒ๐ฟ ๐๐๐ฎ๐๐๐ ๐ฐ๐ผ๐ฑ๐ฒ๐
This one is super important. There are many HTTP status codes, but we usually use just some of them. Donโt use too many, but use the same status codes for the same outcomes across the API, e.g.,
- 200 for general success
- 201 for successful creation
- 202 (Accepted)
- 400 for bad requests
- 401 for unauthorized requests
- 403 for missing permissions
- 404 for missing resources
- 5xx for internal errors
202 (Accepted) for backend calls, which do not immediately can return a result for the desired request. This usually applies for long running processes, i.e. shrinking a video file or executing a BPMN process inside a workflow engine.
๐ฒ. ๐๐ผ๐ปโ๐ ๐ฟ๐ฒ๐๐๐ฟ๐ป ๐ฝ๐น๐ฎ๐ถ๐ป ๐๐ฒ๐ ๐
REST APIs should accept JSON for request payload and also respond with JSON because it is a standard for transferring data. Yet, it is not enough just to return a body with JSON-formatted string, we need to specify a Content-Type header too to be application/json. The only exception is if weโre trying to send and receive files between the client and server.
############################################################################
# Movie Apis Definitions #
############################################################################
# Code completion support is available so start typing for available options.
swagger: '2.0'
# This is your document metadata
info:
version: "1.0.1"
title: The movie api
# Describe your paths here
paths:
# This is a path endpoint. Change it.
/movies:
# This is a HTTP operation
get:
# Describe this verb here. Note: you can use markdown
description: Returns all movies
operationId: getMovies
# Expected responses for this operation:
responses:
# Response code
200:
description: Successful response
# A schema describing your response object.
# Use JSON Schema format
schema:
title: ArrayOfMovies
type: array
items:
$ref: '#/definitions/movie'
default:
description: Error
schema:
$ref: 'https://zalando.github.io/problem/schema.yaml#/Problem'
post:
description: Add a new movie
operationId: addMovie
parameters:
- name: movie
in: body
description: The new movie
required: true
schema:
$ref: '#/definitions/movie'
responses:
'201':
description: The new movie
schema:
$ref: '#/definitions/movie'
default:
description: Error
schema:
$ref: 'https://zalando.github.io/problem/schema.yaml#/Problem'
/movies/{id}:
parameters:
- name: id
in: path
description: ID of the movie
required: true
type: integer
format: int64
get:
description: Returns a single movie
operationId: getMovieById
responses:
200:
description: Successful response
schema:
$ref: '#/definitions/movie'
default:
description: Error
schema:
$ref: 'https://zalando.github.io/problem/schema.yaml#/Problem'
put:
description: Update an existing movie
operationId: updateMovieById
parameters:
- name: movie
in: body
description: The movie
required: true
schema:
$ref: '#/definitions/movie'
responses:
'200':
description: The new movie
schema:
$ref: '#/definitions/movie'
default:
description: Error
schema:
$ref: 'https://zalando.github.io/problem/schema.yaml#/Problem'
delete:
description: Delete a movie
operationId: deleteMovieById
responses:
'204':
description: Movie deleted
default:
description: Error
schema:
$ref: 'https://zalando.github.io/problem/schema.yaml#/Problem'
definitions:
movie:
type: object
required:
- id
- title
properties:
id:
type: integer
format: int64
title:
type: string
ratings:
type: object
properties:
criticsScore:
type: integer
minimum: 0
maximum: 100
audienceScore:
type: integer
minimum: 0
maximum: 100
criticsConsensus:
type: string
abridgedDirectors:
type: array
items:
type: string
abridgedCast:
type: array
items:
$ref: '#/definitions/cast'
posters:
$ref: '#/definitions/posters'
cast:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
characters:
type: array
items:
type: string
posters:
properties:
thumbnail:
type: string
format: uri
profile:
type: string
format: uri
detailed:
type: string
format: uri
original:
type: string
format: uri
7.Attribute Names in camelCase:
If you are using JSON it makes sense to follow JavaScriptโs naming conventions especially if the consumer applications are likely to be written in javascript too. snake_case is easy to read but try to avoid it.
8. Cache data to improve performance:
The good thing about caching is that users can get data faster. We can add caching to return data from the local memory cache instead of querying the database to get the data every time we want to retrieve some data that users request.
9. ๐๐ผ ๐ฝ๐ฟ๐ผ๐ฝ๐ฒ๐ฟ ๐ฒ๐ฟ๐ฟ๐ผ๐ฟ ๐ต๐ฎ๐ป๐ฑ๐น๐ถ๐ป๐ด
Here we want to eliminate any confusion when an error occurs, so we need to handle errors properly and return a response code that indicates what error happened (from 400 to 5xx errors). Along with a status code we need to return some details in the response body.
10. ๐๐ฎ๐๐ฒ ๐ด๐ผ๐ผ๐ฑ ๐๐ฒ๐ฐ๐๐ฟ๐ถ๐๐ ๐ฝ๐ฟ๐ฎ๐ฐ๐๐ถ๐ฐ๐ฒ๐
We want that all communication between a client and a server is protected, which means that we need to use SSL/TLS all the time, with no exceptions. Also, allow auth via API keys, which should be passed using a custom HTTP header, with an expiration day.
- Use OAuth2 to secure your API.
- Use an auto-expiring Bearer token for authentication (
Authorisation: Bearer f0ca4227-64c4-44e1-89e6-b27c62ac2eb6
). - Require HTTPS.
- Consider using JSON Web Tokens.
- Enforce use of the Content-Type and Accept-Type headers even if you use JSON as default for both requests and responses.
e.g. Content-Type: application/json Accept-Type: application/json
- Responses contain header:
X-Content-Type-Options: nosniff
- Responses contain header:
X-Frame-Options: deny
11. ๐จ๐๐ฒ ๐ฝ๐ฎ๐ด๐ถ๐ป๐ฎ๐๐ถ๐ผ๐ป
Use pagination if our API needs to return a lot of data, as this will make our API future-proof. Use page and page_size is recommended here.
E.g., http://api.movieservice.com/v1/movies?๐๐๐๐=๐ท0&๐๐๐๐_๐๐๐ฃ๐=๐ธ0
๐ญ2. ๐ฉ๐ฒ๐ฟ๐๐ถ๐ผ๐ป๐ถ๐ป๐ด
It is very important to version APIs from the first version, as there could be different users for our APIs. This will allow our users not to be affected by changes that we can do in the future. API versions can be passed through HTTP headers or query/path params.
E.g., http://api.movieservice.com/v1/movies/๐บ56789
And donโt forget to ๐ฑ๐ผ๐ฐ๐๐บ๐ฒ๐ป๐ ๐๐ฃ๐, because API will be only good as its documentation. The docs should show examples of complete request/response cycles. Here we can use the OpenAPI definition as a source of truth.
Swagger is the most widely used standard for specifying and documenting REST Services. The real power of the Swagger standard comes from the ecosystem of powerful tools that surrounds it.
13.APIs also comply to the HATEOAS standard because it makes life easy for the consumers of your API
In 2008, Leonard Richardson proposed the following maturity model for web APIs:
- Level 0: Define one URI, and all operations are POST requests to this URI.
- Level 1: Create separate URIs for individual resources.
- Level 2: Use HTTP methods to define operations on resources.
- Level 3: Use hypermedia (HATEOAS, described below).
Level 3 corresponds to a truly RESTful API according to Fieldingโs definition. In practice, many published web APIs fall somewhere around level 2.
If you want to start developing APIs, check Swagger and OpenAPI specifications, Postman, or Stoplight.
14. Implement and monitor an uncached /health endpoint
, e.g.GET /api/health
{
"healthy": true,
"dependencies":
{ "name": "moviesapi",
"healthy": true
}
}
15. Idempotent operations
An operation is idempotent if it can be called multiple times without producing additional side-effects after the first call. Idempotency can be a useful resiliency strategy, because it allows an upstream service to safely invoke an operation multiple times.
The HTTP specification states that GET, PUT, and DELETE methods must be idempotent. POST methods are not guaranteed to be idempotent. If a POST method creates a new resource, there is generally no guarantee that this operation is idempotent. The specification defines idempotent this way:
A request method is considered โidempotentโ if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. (RFC 7231)
Itโs important to understand the difference between PUT and POST semantics when creating a new entity. In both cases, the client sends a representation of an entity in the request body. But the meaning of the URI is different.
- For a POST method, the URI represents a parent resource of the new entity, such as a collection. For example, to create a new movie, the URI might be
/api/movies
. The server creates the entity and assigns it a new URI, such as/api/movies/6812360
. This URI is returned in the Location header of the response. Each time the client sends a request, the server will create a new entity with a new URI. - For a PUT method, the URI identifies the entity. If there already exists an entity with that URI, the server updates the existing entity with the version in the request.
16.For multi-lingual APIs, use the Accept-Language header for locale setting (Accept-Language: nl, en-gb;q=0.8, en;q=0.7
)
17. All endpoints return the Date header
Date โ Date and time the response was returned (in RFC1123 format) (Date: Sun, 06 Nov 1994 08:49:37 GMT
)
18.Implement strong caching (by client, transport, proxy, etc.) through the cache-control
response-header. As a minimum have public GET-endpoints return the following response headers:
- Cache-Control โ The maximum number of seconds (ttl) a response can be cached. (
Cache-Control: public, 360
orCache-Control: no-store
) - Strong caching minimizes the number of requests a server receives
- Consider weak caching through the
ETag
response-header - ETag โ Use a SHA1 hash for the version of a resource. Make sure to include the media type in the hash value, because that makes a different representation. (
ETag: "2dbc2fd2358e1ea1b7a6bc08ea647b9a337ac92d"
). The client needs to send a If-None-Match header for this mechanism to work. - Weak caching minimizes the work a server needs to do (but not the number of requests it receives)
- Enable header-based caching on all proxies and clients (e.g. NGINX, Apache, APIM) to increase speed and robustness
- No privacy or security compromising data in URLโs
19. For APIโs that need content / payload encrypytion
- Implement content encryption on the furthest endpoints (in the REST-server, not the proxies or APIM)
- When content signing is used, this is done after the content is (optionally) encrypted.
- Use the X-Signing-Algorithm header to communicate the type of content signing (
X-Signing-Algorithm: RS256
) - Use the X-SHA256-Checksum header to communicate the SHA256 hash value of the content (
X-SHA256-Checksum: e1d58ba0a1810d6dca5f086e6e36a9d81a8d4bb00378bdab30bdb205e7473f87
) - Use the X-Encryption-Algorithm header to communicate the type of content encryption (
X-Encryption-Algorithm: A128CBC-HS256
)
20 A good API (as any other interface) is
- Consistent (avoid surprises by being predictable)
- Cohesive (only lists endpoints with functional dependency)
- Complete (has all necessary endpoints for its purpose)
- Minimal (no more endpoints than necessary to be complete, no featurism)
- Encapsulating (hiding implementation details)
- Self explaining
- Documented (if self explanation is not sufficient)
21 API Security : Api Security is becoming a well known and heavily used in most of the enterprise software systems. Every time you implement a digital transformation strategy within your organization, APIs have become the cornerstone of that strategy. There are many vendors who have solid solutions to implement this pattern. Here are some of the vendors.
Other Interesting Articles :
Many ways to learn GCP for Free with Google Cloud over the holidays
Linux Commands for Cloud Learning
Happy Readingโฆ..