If you’re a developer working on an app (i.e., everyone) you’re probably either using or building some basic CRUD endpoints: think creating a new user, updating profile information, etc. If this is your first time doing so, there are some non-intuitive best practices to follow around URL naming, which HTTP methods to use, and how to handle versioning. So we’ve put together this collection of tips that will (hopefully) help make your first few endpoints easy to use, performant, and secure.

URL design

You’ve probably used endpoints ranging from /users to /users/edit to /v1/users/edit/update. But it turns out there are some standards worth following.

Use nouns and centralize operations

Endpoints should bear the name of a noun and ideally not contain verbs. Think of your business concept (i.e., a user) and build one endpoint for them, with separate HTTP methods taking place of the things you’d want to do with that user data.

If you were to use a verb-based approach, the /createuser endpoint would be particular to users but can only create a user. Other actions with this approach will include /edituser for editing user details and /deleteuser to delete a user. This means you have multiple endpoints for a single entity when you could have just one. Instead of using a verb, you can provide a /users endpoint and support different actions with HTTP action verbs.

For instance, for the /users endpoint, you can use GET /users to fetch users and POST /users to create a user. You have more simplicity here because you get to provide only one endpoint with different HTTP actions. This of course only works with basic CRUD operations that map 1:1 with HTTP methods; for anything more complex you will need to couch that logic in the URL or your headers / body.

While these two implementations serve the same purpose, the use of a noun (POST) with HTTP action verbs is better because you don't have to manage different URLs for a single resource.

Additionally, nouns should be in their plural form unless they represent a single resource. Unnecessarily long nouns should also be avoided because they make things look messy and unorganized. For example, you should use /subscribers instead of /api-subscribers.

Use lowercase in URLs

Case sensitivity in URLs causes ambiguity: it’s best to stick with lowercase (i.e., /user instead of /User) when declaring your API endpoints.

Use resource identifiers in paths

Adding resource identifiers (i.e., resource IDs) in a URL can get tricky and cause security risks like Insecure Direct Object Reference (IDOR). In an IDOR, an attack is carried out by manipulating the raw resource ID of a database entity revealed in the URL.

This security issue can be eliminated by not including the actual resource ID from the server in the path. Instead, you should use a Universally Unique Identifier (UUID) generated from the server, represented as an alphanumeric.

When using resource IDs in URLs, you should use them as a path parameter like /orders/{orderId} instead of using them as a query parameter like /orders?id={orderId}. As a general guideline for REST APIs, path parameters should be used for resource identification while query parameters should be used for resource sorting and filtering.

Avoid complex structures

In API design, good URL implementation focuses on making sure the URL paths are as simple as possible even if resources are nested. You should avoid more than two levels of nesting in the URL paths.

In two-level nesting more than two nested resources cannot be used. For example, posts/:postId/comments/:commentId is nested in two levels and is more readable compared to customers/:customerId/projects/:projectId/orders/:orderId/lines/:lineId which is more than two levels.

API versioning

API versioning is a controversial part of API design because there are different ways to version APIs and different teams have different ways of doing things. In the end, it's all about the users of your API. How do you version your API to ensure that your users know how to use it correctly? Below are two ways to version your API and its limitations:

URI path

URI path versioning means specifying the API version in the path. This might seem harmless, but it can become complicated for users. For example, api/v1/products can either indicate version one for the products resource or version one for all the API endpoints. Most of the time, the versions are prefixed for all endpoints and the user may be uncertain how to proceed.

This approach can work if your API doesn't change frequently, but if it does, you should avoid it so that you avoid frequent breaks because of changes on the user’s end.

Headers

API versioning with headers requires passing the version in the request object. This method is more crude compared to URI path versioning. However, it can still pose the same problem as that of the URI path: the user may not be sure if the version is for an endpoint or the entire API.

The two approaches mentioned above have one thing in common: the user is unsure of the versioned resources. To avoid this ambiguity, it’s important to make a disclaimer in your documentation or any format you find appropriate to make sure the users of your API services are well-informed.

HTTP verbs

HTTP verbs are relatively straightforward when it comes to API versioning. Always make sure to use the appropriate HTTP verb for the operation specified.

For example, the GET method should only be used to read or fetch resources from the server. It should not be used to create or update a resource on the server. GET is considered the operational method for the R (read) in CRUD.

The POST method is used to implement the C (create) operation in CRUD. It should be used to create a new resource.

Please note that this method can often be confused with the PUT method. Remember that the action of a PUT method is to update a resource and not to create a new one.

The PUT method updates a resource on a server. A PUT request to the /users/20 can be used to update the profile of the user with an ID 20.

A PATCH request works like a PUT request but with only one difference. The PATCH request is used to update a resource with partial data, like updating a user's email address, whereas the PUT method is used to update the entire resource.

And finally, the DELETE method removes resources from the server.

When using HTTP verbs, always make sure to use the appropriate method for their respective operations to avoid ambiguity in implementation. You can use POST to create a new resource and not have to update an existing one. Likewise, you can use PUT to update the entire resource and not a field in the resource.