10
Apr
It is challenging to design a RESTful API which follows the REST principles and attains the maximum level in Richardson maturity model for rest. Most of the enterprise APIs do not follow any of the REST standards or they implements the minimal principles and yet they are named REST API. Such a poorly designed API is an insult to be named as REST API. As a developer, please do not design such an API.
It is important to understand the approach of designing an API on a contract first or spec driven development. This means that, API specification is designed first before you start to code. An API is developed for consumption and it will be consumed only if the implementation adheres to the contract and the API with proper documentation. One of the easiest way to kill an API is to define an API in the code without contract and documentation.
RAML makes it easier for a developer to design and document an API. If you follow the best practices, the API itself serves as a document for the consumer. It is practically English, if done properly. RAML specification promotes standardization and reusability by making the API modular. You can also use and force design patterns.
Use the following best practices to design your API when using RAML
I cannot emphasize enough on the importance of contract first approach which saves tons of development time by knowing almost all the variables before starting to code. This process is useful for both the API developer and the consumer. The following process is API lifecycle when doing spec driven development.
APIs should be designed for a long term use. It is easier if you understand your targeted API consumer. So, When you design an API, think long-term at least 2 – 3 years. It is important to build an API which is standard scalable and backward compatible.
When you break things, or when you break backward compatibility the API consumer has to fix the issue instead of building features and none of the developers like it and they are less likely to use the API.
If you think versioning is a solution, think again. Versioning is a necessary evil yet it is best to avoid it. Utilizing pattern design and code reuse helps to ensure that your API remains uniform across the full interface.
The best designed API has only one version.
Do not design to have one huge API file with everything in it. Use the RAML structuring to separate the common types and modularize. This creates reuseable file fragments which can be used later. In RAML you can define traits, examples, schemas in a separate file and include it in the main API file. This makes the fragments reusable in a different API across your organization when needed.
For example, Traits can be used to define common method properties such as query-parameters and responses. Separate the traits from the base RAML file.
Common data types, traits, resource types, schemas, examples, must be externalized as API Fragments in Exchange to promote reusability in API Design.
Another huge advantage of tools like RAML or Swagger is that they allow you to mock your API. This means that you can not only build your API in a visual interface and take advantage of the very same best practices we utilize in development, but you can also share a mock version of your API with potential clients.
Using MuleSoft’s API Designer you can easily turn on a mocking service that gives you a URL that can be shared with other developers. This allows your clients to “test out” your API by making real calls as if they would from their application.
By utilizing the example responses defined in the RAML file developers can quickly identify issues and inconsistencies, helping you eliminate the majority of design related issues before development even starts.
By passing along tools like the API Notebook, developers can interact with your Mock API through JavaScript without having to code any calls themselves, and also having the ability to send you the specific use case back giving you true examples of what your developers are trying to accomplish.
Always provide mock data in your API. All the request and response should contain proper examples which represents the real business data. Separate the mock data to an external file.
Naming the resources should follow the general REST API guidelines.
Use nouns in lowercase to represent a resource. For resources with multiple words, use lowercase for all the words or use ‘-’ (dash) in between the words and make it readable.
example
/books -> collection
/books/123 -> unique resource under collection
Use URI parameters to get an instance resource and query parameter to filter the collection resources. When dealing with collection resources, implement filter and pagination by default. Define a default limit and offset. Detailed difference and when to use URI parameter and Query parameter is explained in this post.
title: GitHub API
version: v3
baseUri: https://api.github.com/{version}
/users:
get:
description: Get a list of users
queryParameters:
page:
description: Specify the page that you want to retrieve
type: integer
required: true
example: 1
per_page:
description: Specify the amount of items that will be retrieved per page
type: integer
minimum: 10
maximum: 200
default: 30
example: 50
Types are similar to Java classes. Types borrow additional features from JSON Schema, XSD, and more expressive object oriented languages. Where ever possible, use the RAML inheritance to resuse schemas and common files. Also, know when not to to inherit schemas.
title: API with Types
types:
User:
type: object
properties:
firstname: string
lastname: string
age: number
/users/{id}:
get:
responses:
200:
body:
application/json:
type: User
Each resource should have at least one HTTP verb associated with it to represent the action that can be performed on that particular resource. And each method should return accurate HTTP code for resource-method pair. Add description to each method.
Use the correct HTTP Codes for each response
1xx: Informational
2xx: Success
3xx: Redirect
4xx: Client error
5xx: Server error
Use the correct verbs to define the correct action.
GET: For obtaining data. Idempotent
PUT (Idempotent): To update data. It will update the entire instance. Idempotent. Can be used for both creating and updating. Commonly used for updating (full updates). Always include the whole payload in the request. It’s all or nothing. PUT is not meant to be used for partial updates
PATCH (Idempotent): To update data. It will update partial data of an instance.
POST (Not idempotent): To store data.
DELETE (Idempotent): To delete an instance.
These are some of the best practices I follow when designing a REST API and it is not limited to the things mentioned in this post.
I hope this article helped you. If you have any queries, please feel free to comment or contact me here for any help.