# RESTful API Development Best Practices In general, an effective API design will have the following characteristics: - **Easy to read and work with**: A well-designed API will be easy to work with, and its resources and associated operations can quickly be memorized by developers who work with it constantly. - **Hard to misuse**: Implementing and integrating with an API with good design will be a straightforward process, and writing incorrect code will be a less likely outcome. It has informative feedback and doesn't enforce strict guidelines on the API's end consumer. - **Complete and concise**: Finally, a complete API will make it possible for developers to make full-fledged applications against the data you expose. Completeness happens over time usually, and most API designers and developers incrementally build on top of existing APIs. It is an ideal which every engineer or company with an API must strive towards. ## Versioning APIs should be versioned near the root of the URL path, immediately after '/api'. For example, `/api/v1/students`. Versions should start at 1, i.e. `/api/v1`. If an API URL does not contain a version number, it is considered **anonymous**. When it is anonymous, it should be understood that it always refers to the latest version. Anonymous API versions should not be considered stable, because they change for each release. ## Casing Casing should be consistent throughout. Use **kebab-case** for URLs and **camelCase** for Query Parameters and JSON request/response. ## Metadata / Headers Here is a link to a list of [Standard Metadata/Headers](/publicapi/standard-metadata-headers) related to HS1 Services. ## URLs ### Nouns describe URLs better The base URL should be neat, elegant, and simple so that developers using your product can easily use them in their web applications. A long and difficult-to-read base URL is not just bad to look at but can also be prone to mistakes when trying to recode it. Nouns should always be trusted. There's no hard fast rule but resource nouns are preferred to be singular or plural. Keeping these nouns self-explanatory helps developers understand the kind of resource described from the URL, which can eventually enable them to become self-sufficient while working with your API. ### URL Depth | DOs / DON'Ts | URL Depth Example | | --- | --- | | ✅ DO | `/api/v1/students` | | ✅ DO | `/api/v1/students/{studentId}/classes` | | ❌ DON'T | `/api/v1/students/{studentId}/classes/{classId}` | > **Note**: If your API has URLs deeper than `resource/identifier/resource`, consider revisiting the granularity of your API design. **Consistency**: Ensure consistent naming conventions and structures. Use HTTP response status codes to represent operation outcomes. ### URL Path & Methods | DOs / DON'Ts | Method | Action | URL Example | | --- | --- | --- | --- | | ✅ DO | GET | Read a collection of students. | `/api/v1/students` | | ✅ DO | POST | Create a new student. | `/api/v1/students` | | ❌ DON'T | POST | Create a new student. | `/api/v1/students/create` | ## Query Parameters ### General Rules - **Validate Everything**: Never trust client input. Validate parameter names, data types, allowed values, and ranges. - **Prevent Injection**: Be especially wary of parameters that might be used directly in database queries (e.g., SQL injection in raw filter strings). Use ORMs or parameterized queries. - **Limit Complexity**: Restrict the complexity of filter expressions and the number of sort fields to prevent denial-of-service (DoS) attacks via resource exhaustion. - **Authorization**: Ensure that filtering and sorting do not inadvertently expose data the user/client isn't authorized to see. Apply authorization rules after filtering if the filter itself might reveal existence, or before if the filter criteria are sensitive. GET requests with query parameters for filtering, sorting, and pagination should always be **idempotent**. Multiple identical requests should yield the same result (assuming no underlying data changes). ### Filtering & Sorting **Data Types**: Clearly define and validate the expected data type for each filterable field (string, number, boolean, date/datetime) **Dates/Datetimes**: Standardize on ISO 8601 format (`YYYY-MM-DDTHH:mm:ssZ`). **Default Sort Order**: Define a sensible default sort order if the client doesn't specify one (e.g., by creation date descending, or by name ascending). Document this default. #### Example Query Parameters with Filtering (single filter) **Simple Equality**: Use the attribute name directly ``` GET /patients?someProperty=active ``` **Multiple Values for a Single Field (OR logic)**: Use comma-separated values ``` GET /patients?someProperty=active&anotherProperty=123 GET /patients?someProperty=active&someProperty=pending ``` #### Example Query Parameters with Sorting **Parameter Naming**: Use a clear parameter like `sortBy` or `sort`. ``` GET /products?sortBy=name (ascending by default) GET /products?sortBy=category,createdAt (multiple sort fields, comma-separated) ``` ## Pagination API pagination is a technique used to divide up a large set of resources into more manageable chunks. Some recommended strategies to follow when implementing pagination: ### Parameter Naming `page`, `pageSize`, `totalPages`, `totalItems` ### Page Size - Implement a **default page size** (e.g., 20 items) if the client doesn't specify one. - Implement a **reasonable maximum page size** (e.g., 100 items) to prevent clients from requesting excessive amounts of data in a single request, which can lead to performance degradation or DoS. Document these limits. ### Handle Edge Cases Handling requests beyond the available range, etc., gracefully with appropriate error messages and status codes. ### Security Considerations Ensure that pagination parameters are validated. #### Example Query Parameters with Pagination (Page-based) ``` /patients?page=2&pageSize=10 ``` #### Example Response with page-based pagination ```json { ..., "pagination": { "page": 2, "pageSize": 10, "totalPages": 15, "totalItems": 143 } } ``` ## Collections and Resources ### Understanding Resources and Collections Resources are fundamental to the concept of REST. A resource is an object that's important enough to be referenced in itself. A resource has data, relationships to other resources, and methods that operate against it to allow for accessing and manipulating the associated information. A group of resources is called a **collection**. The contents of collections and resources depend on your organizational and consumer requirements. If, for example, you believe the market will benefit from obtaining basic information about your product's user base, then you could expose this as a collection or resource. A Uniform Resource Locator (URL) identifies the online location of a resource. This URL points to the location where your API's resources exist. The base URL is the consistent part of this location. - `/users`: a collection of users - `/users/{userid}`: a resource with information about a specific user ### Describe resource functionality with HTTP methods All resources have a set of methods that can be operated against them to work with the data being exposed by the API. RESTful APIs comprise majorly of HTTP methods which have well defined and unique actions against any resource. Here's a list of commonly used HTTP methods that define the CRUD (Create, Read, Update, Delete) operations for any resource or collection in a RESTful API. | Method | Description | Idempotent | | --- | --- | --- | | GET | Used to retrieve a representation of a resource. | ✅ | | HEAD | Used to return information about a resource | ✅ | | POST | Used to create new resources and sub-resources | ❌ | | PUT | Used to update existing resources | ✅ | | PATCH | Used to update existing resources | ❌ | | DELETE | Used to delete existing resources | ✅ | > **Note**: If you want to know the difference between PUT and PATCH, check out [this feed on StackOverflow](https://stackoverflow.com/questions/28459418/rest-api-put-vs-patch-with-real-life-examples). Keeping verbs out of your URLs is also a good idea. The operations GET, PUT, POST and DELETE are already used to operate on your resources described by the URL, so having verbs instead of nouns in the URL can make working with your resources confusing. ## Error Handling Errors happen, but it is important to handle them correctly and return a helpful response (along with the correct Status Code). ### Standard Error Response Format ```json { "timestamp": "2024-10-29T10:11:12.1234Z", "errors": [ { "code": "ErrorCode123", "message": "Service XYZ is down.", "detail": "Relative source code details, if applicable/wanted" }, { "code": "ErrorCode987", "message": "Your amazing DB is down", "detail": "Relative source code details, if applicable/wanted" } ] } ``` ### Standard Status Codes when handling errors | Status Code | Meaning | Use Case | | --- | --- | --- | | 400 | Bad Request | Client sent an invalid request, such as lacking the required request body and/or parameters. | | 401 | Unauthorized | Client failed to authenticate with the server. | | 403 | Forbidden | Client authenticated but does not have permission to call the API. | | 404 | Not Found | The requested resource does not exist. | | 412 | Precondition Failed | One or more conditions in the request header fields evaluated to false | | 500 | Internal Server Error | This is a generic "catch-all" response due to unforeseen server issues, as typically you would use the appropriate Status Code response and not this. |