A serious misconception is that REST APIs have to be CRUD-based, and that there is no connection between the two, it’s just a way of styling the API. This article also covers several implementation rules for REST-based APIs.

Introduction of CRUD

A CRUD-based API is an API that provides a collection of resources containing instances, following the Create, Read, Update, and Delete lifecycle patterns. The CRUD pattern is useful when you have a set of resource instances that represent content or state, and it usually follows the following pattern:

  • GET /articles— List/page/filter the article list
  • POST /articlesCreate a new article
  • GET /articles/{articleId}— Get details of the article
  • PATCH /articles/{articleId}— Update the article details
  • DELETE /articles/{articleId}Deletes the article with the specified ID

REST: Beyond CRUD

Before discussing API styles other than CRUD, there is a serious misconception about REST that needs to be cleared up:

CRUD is not a requirement for a “RESTful” API.

In fact, there is no mention of CRUD in Fielding’s dissertation.

When talking about REST-based APIs, many people confuse the use of CRUD-style resources with the REST style. However, these are HTTP abstract resource concepts that are not directly related to database design:

This specification does not limit what may be a resource; instead, the term “resource” is used in the general sense of any content that may be identified by a URI. –
RFC 3986

REST is a way to regulate communication between clients and servers, taking advantage of the capabilities provided by the HTTP protocol, and these limitations are free to focus on API design:

  1. Uniform interface: Requests from different clients are the same, whether the client is a browser, a mobile device, or any other device
  2. Client-server separation: The client and the server operate independently, and the interaction between them takes place only in the form of requests and responses
  3. Stateless: The server does not store any information about the user using the API, so the client must provide all the information necessary to process the request on each request
  4. Hierarchy system: The client does not know how many layers there are between the client and the actual server responding to the request, which is a key tenet of HTTP, allowing caching servers, reverse proxies, and access security layers — all of which are transparent to the client sending the request
  5. Cacheable: The server response must contain information about whether the data is cacheable, allowing the client or proxy to cache the data outside the API server
  6. On-demand code (optional) : The ability to extend client functionality by sending executable code from the server to the client on request.

Similarly, REST does not need to have CRUD-style based resources. CRUD is a style of API that we can apply to RESTful, but it is not a requirement for a RESTful API.

This gives you more freedom in designing APIs to provide resources with a CRUD-based style when appropriate, while mixing functional resources when CRUD is not needed.

When designing an API, you don’t have to limit yourself to this style. You can explore a number of other extension styles that can help you design a great API style that meets your current and future development needs.

Extend CRUD with endpoints

A common situation in API design is the need to go beyond the typical CRUD interaction model; for example, some APIs may need to support operations such as submit, approve, reject, and publish. Since methods are limited in HTTP, how do you add them to the API?

One option is to use the status field modified by the PATCH request. Although this is an option, I think it would be clearer to use the functional endpoint, as follows:

  • PUT /articles/{articleId}/submit
  • PUT /articles/{articleId}/approve
  • PUT /articles/{articleId}/decline
  • PUT /articles/{articleId}/publish

In this design, a functional endpoint is provided for article instances in the collection, which is used through a PUT operation (meaning only updates). This design has the following benefits:

  1. There is better fine-grained access management in the API management, where each specific operation is a unique URL that can be assigned to different role-based (RBAC) access. Therefore, decoupling access rights from the code to the deployment model gives you the flexibility to further restrict endpoint access without changing the code to enforce these restrictions
  2. The workflows supported by the API are more explicit and don’t have to be looked atPATCHEndpoints to see the valid state values that can be updated, as well as the state machine rules that allow transitions to states and times. This is declared independently and more intuitively by each endpoint
  3. Finally, hypermedia links can be used more effectively in response loads by providing unique URLs for additional process endpoints. For example, the author might see the following API response:

    Hypermedia links: An API is not just a data store over HTTP, it becomes a state machine over HTTP. It still has data, but it can also provide “next available action” in a self-describing way.

{name: "sermons API ", status: "draft", _links: [{rel: "submit", href: "/articles/99/submit"}],}

Once a draft of the article is submitted, you may see the following when editing:

{name: "API Serve ", status:" Draft ", _links: [{rel: "approve", href: "/articles/99/approve"}, {rel: "decline", href: "/articles/99/decline" }, ], }

Clients can know what actions are currently available to the client based on the user’s permissions (for example, editors may be able to approve, reject, and publish, while authors may only be able to submit articles in draft mode) by whether there is an endpoint link to the “next available action.”

This is a very useful option for API designs that require standard create, read, update, and delete operations beyond some or all of the resources they provide.

Functional resources for search and computation

What if the capabilities that need to be provided to consumers don’t require a resource set? What if you only need to evaluate one value and return it? Does this break the REST-based approach? No, as long as we follow the HTTP rules and choose the correct action verb as needed, which is usually POST or GET.

Some common examples might include:

  • GET /search? q=devpoint
  • GET /verification-code? code=202106
  • POST /sales-tax

Usually, the naming pattern for functional resources is verb-noun form, sometimes just verb form. This makes it easy for developers to understand that it is a functional resource, rather than a collection of resources (such as projects, accounts, customers) that follow a plural noun format.

Transactional resources for complex, long-running workflows

In the SOAP and Web services era, WS-Transaction was a specification intended to encapsulate multiple Web service calls into a single distributed Transaction, which would increase the complexity of service design.

With REST-based API designs, you can also encounter complex API designs that span multiple HTTP requests. Here is a solution that is easy to implement and flexible.

Resources can be designed to represent not only business objects, but also long-running transactions or complex workflows. These resource collections represent long-running transactions or workflows that need to be provided to API consumers. Let’s look at the booking process of a hotel management system:

  1. First submit a request to create a new reservation resource instance with room details to be reserved, such as:POST /reservations
  2. You then use this resource instance to get or modify the booking details, including the arrival and departure dates, the number of reservations, additional requests, and so on. This endpoint could bePATCH /reservations/hotel66, will be used as often as needed until the booking fulfills the customer’s requirements
  3. For increased flexibility and auditing purposes, each time a change is made, the change can be fetched as a single nested resource, allowing the history of the scheduled change to be extracted, for exampleGET /reservations/hotel66/modifications
  4. If necessary, you can set an expiration date for the reservation, such as 1 day after it is created. If you try to update or pay for a reservation after the expiration date, you will receive one400 Bad Request409 Conflict
  5. When ready, the customer can make a payment for the reservation order, such as a POST/reservations/hotel66/payments
  6. The result of a successful payment will mean that the reservation has been completed and you need to obtain the appropriate information, for exampleGET /bookings/book66
  7. After the reservation is successful, you need to update the processing status of the order, for examplePATCH /bookings/book66, if additional fees are required or partial or full refund is required.

This is a reasonable design to model a long-running transaction or workflow process, keep an audit track of changes to resources, and support transaction expiration times. At the same time, stay within the constraints of the REST-based API design and avoid adding complexity to API users.

Singleton resources: There can only be one

Singleton resources are useful when the API does not require resource collections. A singleton resource can represent a virtual resource that is used to interact directly with an existing resource instance, such as GET /me instead of GET /users/12345.

An API can provide nested singleton resources, such as PUT/Accounts /66/ Preferences, when only one relationship exists between a parent resource and a child resource.

The singleton resources may already exist without having to create them, or they may need to be created through the POST endpoint. Although singleton resources may not be able to provide the full range of CRUD-style operations as their collect-based counterparts, they should still insist on correctly selecting the HTTP action verbs that best fit the necessary operations.

Batch and batch resource operations

Bulk or batch processing is required for resources that need to import large amounts of data in one batch to avoid 100 to 10,000 separate POST operations. Although the terms are sometimes used interchangeably, it is felt that a distinction should be made:

  • bulkBatch operations that process each committed record allow processing to fail, but the rest of the import to succeed, meaning that out of 1000 records, 76 records may fail and the remaining 924 records may be imported successfully. The client needs to get the record information that was not successfully imported in order to modify the record and then submit the record that was not successfully imported.
  • batchBatch operations process all committed records in a single pass or fail transaction, meaning that if 76 records fail out of 1000, then none of the records are actually stored. The customer needs to revise 76 nonconformance records and resubmit the revised records.

conclusion

When talking about REST-based APIs, many people confuse REST with resources that leverage CRUD schemas. REST is a set of constraints (specifications) to be more efficient in collaboration in front-end development. As for API design, I suggest that we still need to spend some time on design and choose the best mode.