☰
Current Page
Main Menu
Home
Home
Editing RESTful API Excellence - Best Practices for Design, Security, and Performance
Edit
Preview
H1
H2
H3
default
Set your preferred keybinding
default
vim
emacs
markdown
Set this page's format to
AsciiDoc
Creole
Markdown
MediaWiki
Org-mode
Plain Text
RDoc
Textile
Rendering unavailable for
BibTeX
Pod
reStructuredText
Help 1
Help 1
Help 1
Help 2
Help 3
Help 4
Help 5
Help 6
Help 7
Help 8
Autosaved text is available. Click the button to restore it.
Restore Text
https://medium.com/@sylvain.tiset/restful-api-excellence-best-practices-for-design-security-and-performance-fa24c1360ab9 # RESTful API Excellence: Best Practices for Design, Security, and Performance | by Sylvain Tiset | Medium  [Sylvain Tiset](https://medium.com/@sylvain.tiset) A REST API (Representational State Transfer Application Programming Interface) is a set of rules that allows different software applications to communicate with each other over the web. It provides a standardized way to access and manipulate web resources, typically using HTTP methods. In this article, generalities and best practices will be reviewed. API Best Practices (Generated by Microsoft Bing AI) 0\. RESTful API key characteristics -----------------------------------  API representation by [Rapid\_API](https://x.com/Rapid_API/status/1543252234703872000/photo/1) In order to well understand best practices of REST APIs, let’s have a quick look at its key concepts: * **Resource-based architecture**: In a REST API, resources are the key entities that the API manages. Each resource is identified by a unique URL (Uniform Resource Locator), often referred to as an endpoint. * **Stateless communication**: REST APIs are stateless, meaning each request from a client contains all the information needed for the server to fulfill that request. The server does not store any client context between requests. * **Client-server separation**: RESTful APIs keep clients and servers separate. The client makes requests to the server while the server processes client requests and sends back responses. * **Standardized methods**: RESTful APIs use standard HTTP methods to perform operations on resources (GET, POST, PUT, DELETE and PATCH). * **Representation**: Resources can be represented in different formats, most commonly in JSON (JavaScript Object Notation) or XML. The client specifies the desired format using the `Accept` header in the HTTP request, and the server responds accordingly. > Choosing REST vs SOAP or GraphQL > > **Choose REST**: For simplicity, ease of use, broad support, and when a lightweight and flexible approach are needed. > **Choose SOAP**: For strict enterprise requirements, standardized messaging, and built-in security. > **Choose GraphQL**: For highly flexible querying needs and complex data interactions, where precise control over the data fetched is needed. 1\. Methods standards --------------------- RESTful APIs use standard HTTP methods to perform operations on resources: * **GET**: Retrieve a resource or a collection of resources.  GET by [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxeyUDJ5FNv42Djv1IMKD2wSZCktqhhKpF) * **POST**: Create a new resource.  POST by [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxeyUDJ5FNv42Djv1IMKD2wSZCktqhhKpF) * **PUT**: Update an existing resource.  PUT by [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxeyUDJ5FNv42Djv1IMKD2wSZCktqhhKpF) * **DELETE**: Remove a resource.  DELETE by [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxeyUDJ5FNv42Djv1IMKD2wSZCktqhhKpF) * **PATCH**: Partially update a resource.  PATCH by [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxeyUDJ5FNv42Djv1IMKD2wSZCktqhhKpF) The first piece of advice is to use these HTTP methods among the standard described here. Don’t use POST to do an update nor retrieving a collection of ressources. If possible and depending on the use case, prefer not using PATCH as it can conflict with PUT actions. 2\. Idempotency --------------- Idempotency implies that a certain operation can be applied many times, without changing the result. It offers many benefits for a REST API: * **Error Recovery and Fault Tolerance**: Ensures consistent system state despite multiple requests due to network issues or retries, enhancing fault tolerance. * **Consistent State and Data Integrity**: Maintains data accuracy and prevents unintended side effects by ensuring repeated operations yield the same outcome. * **Safe Retry Mechanisms**: Allows confident and straightforward retries without risking inconsistencies, simplifying error handling. * **Concurrency and Parallelism**: Supports safe concurrent processing by preventing conflicts and race conditions in distributed environments. * **Caching Optimization**: Enables effective caching since repeated idempotent requests produce the same result, improving performance and reducing backend load. * **Simplified Client Code**: Reduces the need for complex retry and state management logic, leading to cleaner, more maintainable client code. * **Scalability and Load Balancing**: Facilitates horizontal scaling by ensuring consistent data across multiple servers, aiding in effective load balancing. * **Auditability and Logging**: Enhances traceability and monitoring by producing consistent results for requests, aiding debugging and compliance. * **Cross-System Integration**: Improves reliability and predictability in integrations with other systems, reducing synchronization issues. GET method is naturally safe and idempotent. DELETE and PUT methods should be designed to be idempotent. For POST methods, idempotence should be implemented based on business requirements. > **Idempotent** operations produce the same outcome no matter how many times they are repeated. **Safe** methods do not alter the system state. While all safe methods are idempotent, not all idempotent methods are safe.  Idempotency vs Safe frm [RestfulApi.net](https://restfulapi.net/idempotent-rest-apis/) Basically, there are 3 ways to implement idempotency: * **Using a unique business constraint:** when a field is unique, the server can know if the request has already been proceed or not ``` POST /users { "name": "John Doe", "email": "john@doe.com" // email is unique in the database } ``` * **Using Entity Tags (ETags)**: ETags are generated by the server based on the current resource representation. If the same request is sent twice, the ETags won’t match and the second request will return a 4XX error. Note that it can only work on already created data (so not for POST methods). ``` HTTP/1.1 200 Ok ETag: "b125ecb02a9136f8cfc0c2c5b4433c4b" { "name": "John Doe", "email": "john@doe.com" } PUT /users/123 If-Match: "b125ecb02a9136f8cfc0c2c5b4433c4b" { "name": "John Smith" } ``` * **Using a separate idempotency key**: The client generates a key and adds it to the request using a custom header. The server can persist the idempotency key and reject any further requests using the same key. ``` POST /users Idempotency-Key: 2006ef6e-267b-72cd-b874-dcf1e861c78a { "name": "John Doe", "email": "john@doe.com" } ``` 3\. HTTP Status Code -------------------- The response codes for HTTP are divided into five categories: * Informational (100–199) * Success (200–299)  Success HTTP status from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxbhAChoobdaXddDvvLTVbHCbZS186omNx) * Redirection (300–399)  Redirection HTTP status from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxbhAChoobdaXddDvvLTVbHCbZS186omNx) * Client Error (400–499)  Client error HTTP status from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxbhAChoobdaXddDvvLTVbHCbZS186omNx) * Server Error (500–599)  Server error HTTP status from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxbhAChoobdaXddDvvLTVbHCbZS186omNx) Here make sure to chose the right status code when returning a server response. > **401 vs 403** > 401 Unauthorized: This means the user isn’t not authorized to access a resource. It usually returns when the user isn’t authenticated. > > 403 Forbidden: This means the user is authenticated, but it’s not allowed to access a resource. 4\. Semantics ------------- Here are some tips and standards to follow: * Understand singleton, collection and sub-collection resources ``` /customers // is a collection resource /customers/{id} // is a singleton resource /customers/{id}/accounts // is a sub-collection resource ``` * Use lowercase letters ``` ❌ GET /v1/Cards/123 ✅ GET /v1/cards/123 ``` * Use resource names (nouns) instead of verbs for consistency ``` ❌ GET /v1/querycarts/123 ✅ GET /v1/cards/123 ❌ POST /v1/users/login ✅ POST /v1/authorise ``` * Use plurial nouns when possible for consistency ``` ❌ GET /v1/cart/123 ✅ GET /v1/carts/123 ❌ GET /v1/order/{id}{item} ✅ GET /v1/orders/{id}{items} ``` * Use forward slash (/) to indicate hierarchical relationships. However, don’t use it at the end of URI’s path ``` ❌ GET /v1/store/items/ ✅ GET /v1/store/items ``` * Use hyphens (-), not underscores (\_) to improve readability ``` ❌ GET /v1/store/itemmanagement ❌ GET /v1/store/item_management ✅ GET /v1/store/item-management ``` * Do not use file extensions. They don’t add any value to the URI ``` ❌ GET /v1/store/items.xml ✅ GET /v1/store/items ``` * Use path parameters to identify or retrieve a resource. Prefer query parameters to sort data (filtering and paginating) ``` /v1/books/{id} // is a path parameter /v1/books?category=fiction // is a query parameter ❌ GET /v1/carts/123?item=321 ✅ GET /v1/carts/123/items/321 ❌ GET /v1/items ✅ GET /v1/items?sortby=time ✅ GET /v1/items?filter=color:red ``` 5\. Versioning and Documenting ------------------------------ API versioning helps to easily manage changes and updates to an API while still maintaining compatibility with previous versions of the APIs for clients. There are three ways to version the API: * URL versioning ``` GET /v1/users ``` * Query parameter versioning ``` GET /users?v1 ``` * Header versioning ``` GET /users Header: version: v1 ``` To enhance the adoption and usability of your APIs, it’s essential to develop and maintain thorough API documentation. This documentation should detail available endpoints, request and response formats, authentication methods, and more. OpenAPI, previously known as Swagger, is a popular specification used for documenting REST APIs.  OpenAPI example from [neoteroi.dev](https://www.neoteroi.dev/blacksheep/openapi/) 6\. Performance --------------- In order to improve performance of a REST API, there are several ways to follow: * **Sorting**: allows clients to request data in a specific order. This way, the sorting calculation is done on server side, generally quicker than on client side. ``` ✅ GET /v1/items?sortby=time ``` * **Filtering**: enables clients to retrieve only the data that meets certain conditions, reducing the amount of data transferred and processed. ``` ✅ GET /v1/items?filter=color:red ``` * **Pagination**: controls the amount of data returned in a single API response, breaking down large sets of data into manageable chunks. ``` ✅ GET /v1/items?page=2&limit=10 ```  Pagination from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxBihEmWuZWj60rXYpXrEFwQtHJuq8X6Jy) * **Batch Operations**: Do several operations in one API call. Benefits are fewer calls to the server, faster overall as less communications with the server and less server load as the server does one big task instead of many small ones. ``` POST /product-import [ { "name": "Cup of Tea", "price": "15.50 USD" }, { "name": "Spoon", "price": "5.60 USD" }, ... ] ``` * **Caching**: Implement caching mechanisms to store frequently accessed data, reducing server load. This can greatly enhance response times during data exchanges between the client and server, as well as decrease network traffic. Use HTTP headers like Cache-Control and ETag to control how caching works.  Caching from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxBihEmWuZWj60rXYpXrEFwQtHJuq8X6Jy) * **Compression**: Use compression techniques like gzip or brotli to minimize the size of data transferred between the client and server. This will enhance response times, particularly in bandwidth-constrained environments and within the specific architectural constraints of your application. Use the Content-Encoding HTTP header to tell which compression method is used.  Compression from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxBihEmWuZWj60rXYpXrEFwQtHJuq8X6Jy) * **Asynchronous Logging**: This method sends logs to a lock-free buffer and returns immediately, avoiding disk operations on every call. Logs are then periodically flushed to disk, which greatly reduces I/O overhead.  Asynchronous Logging from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxBihEmWuZWj60rXYpXrEFwQtHJuq8X6Jy) * **Connection Pool**: Use a pool of open connections to handle database interactions, minimizing the overhead of repeatedly opening and closing connections. The pool efficiently manages the connection lifecycle to optimize resource usage.  Connection Pool from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxBihEmWuZWj60rXYpXrEFwQtHJuq8X6Jy) Another piece of advice is to add logs about API requests and responses, and information on errors and warnings. Last but not least, monitor and analyse API performance: * **Response time**: How long it takes to answer a request * **Latency**: General delay in the system * **Throughput**: How many requests the API can handle in a time frame * **Uptime and Downtime**: How long the API stays working / not working * **Number of requests per day** * **Most used route** These metrics along with a functional analysis will help the team to focus on the next improvements. 7\. Testing ----------- Testing APIs is crucial for ensuring its functionality, security, performance, and overall quality. It helps in delivering a reliable and efficient service while facilitating smooth integration and maintaining high code quality. Here are the various types of tests that can be conducted for a REST API: * **Smoke Testing**: Performed after development to verify that the APIs are functioning and no critical issues exist.  Smoke Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Functional Testing**: Creates test cases based on functional requirements and compares actual results with expected outcomes.  Functional Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Integration Testing**: Combines multiple API calls to conduct end-to-end tests, assessing intra-service communications and data exchanges.  Integration Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Regression Testing**: Ensures that recent changes, such as bug fixes or new features, do not negatively impact existing functionality.  Regression Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Load Testing**: Assesses application performance by simulating varying loads to determine its capacity.  Load Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Stress Testing**: Deliberately imposes extreme loads on the APIs to test their ability to handle high traffic conditions.  Stress Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Security Testing**: Evaluates the APIs against potential security threats to identify vulnerabilities. This might require a security expert.  Security Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **UI Testing**: Checks the interactions between the user interface and APIs to ensure that data is displayed correctly.  UI Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) * **Fuzz Testing**: Injects invalid or unexpected data into the API to identify and expose potential vulnerabilities.  Fuxx Testing from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxgajF5r3doUGRIRc--2RzNJ9YH5OlDMdl) 8\. Security ------------ Making API safe and controlling who can use it are one of the most important thing to make a good REST API. Let’s have a look at the main ways to do it. * **Input Validations**: Ensure thorough validation and sanitization of user inputs on the server side, and encode API responses to prevent the execution of malicious code. Adhering to these practices will safeguard your REST APIs against vulnerabilities such as SQL injection and cross-site scripting (XSS).  Input Validation from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) ``` // Sanitize example String sanitizedUsername = HtmlUtils.htmlEscape(userDto.getUsername()); ``` * **Idempotency**: As already mention in section 2, idempotency is a key point to improve security as it maintains data accuracy and prevents unintended side effects. * **Rate Limiting**: Implement rate limiting to manage the number of requests a client can make within a given timeframe. This helps prevent abuse and ensures fair access to your REST API resources.  Rate limiting from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) * **Log API requests**: Logging requests will help tracking users and IP adresses to detect a malicious attack. * **Use CORS (Cross-Origin Resource Sharing)**: a security feature implemented in web browsers that allows or restricts web applications running at one origin (domain) from making requests to a different origin. To allow cross-origin requests, the server hosting the resource must include specific HTTP headers in its responses. ``` public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("https://okwebsite.com") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("Content-Type", "Authorization"); } ``` * **Set Up an IP white list**: It restricts API access to known, trusted IP addresses, reducing the risk of unauthorized access from unknown or malicious sources.  Allowlist from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) * **Use RBAC (Role-Based Access Control)**: RBAC enables control of what different users can do. It protects the database resources from being accessed by unauthorized users. To implement it, create roles like “admin” or “user”, give users a role, and check roles when users access a route API.  Authorization from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) * **Use HTTPS**: HTTPS secures data during transmission between users and the API, preventing unauthorized access or tampering. Always use TLS (Transport Layer Security) to encrypt data transmitted and protect API traffic.  HTTPS from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) * **Use OAuth2**: OAuth2 is a protocol for sharing user authorization across systems. It grants third parties limited access to user resources without exposing credentials by issuing access tokens following user authentication.  OAuth2 flow from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=Ugkx6FILeyXf2pvwsLyv01t5YmZOwXG00MZ0) * **Use JWT (JSON Web Token)**: JWTs are a secure method for transmitting information between systems and verifying whether a user has access to your API. It works with a header, payload (user info) and the signature to make sure the token is real. To use JWT, make a token for each user, put it in the API request, and check the token when the request is received by the server. Token generated by JWT has an expiration date, preventing replay attacks by ensuring requests are only valid for a short period.  JWT structure from [research.securitum.com](https://research.securitum.com/jwt-json-web-token-security/) * **Hides secret information on error**: Prevents an attacker from finding a potential vulnerability.  Error Handling from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) * **Check OWSAP security risks**: OWSAP enables developers to stay updated on the last security threats.  OWSAP from [ByteByteGo](https://www.youtube.com/channel/UCZgt6AzoyjslHTC9dz0UoTw/community?lb=UgkxL_jaa0-W3iXwD1rFIxG0_vYYuCeq0gzS) Hoping with this article gave you the best practices to design and implement good RESTful APIs. If you liked this article, feel free to clap it, comment it or share it. To stay updated with my content, you can follow me on Medium.
Uploading file...
Edit message:
Cancel