This is the 10th day of my participation in Gwen Challenge. For details, see Gwen Challenge.

A serious misconception is that REST apis must be CRUD-based, and that there is no connection between the two, just a way of styling the API. This article also introduces 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 generally follows the following pattern:

  • GET /articles– Lists/paginates/filters article lists
  • POST /articles– Create a new article
  • GET /articles/{articleId}– Obtain article details
  • PATCH /articles/{articleId}Update article details
  • DELETE /articles/{articleId}– Deletes the article with the specified ID

REST: Beyond CRUD

Before discussing API styles beyond 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, CRUD is not mentioned 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 restrict what may be a resource, but the term “resource” is used in a general sense for anything that may be identified by a URI. – the RFC 3986

REST is about standardizing the way clients and servers communicate. Taking advantage of the capabilities provided by the HTTP protocol, these limitations are free to focus on API design:

  1. Unified interface: Requests from different clients are the same, whether the client is a browser, mobile device, or any other device
  2. Client-server separation: The client and the server operate independently and interact with each other 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. Layered systems: 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 principle of HTTP, allowing caching servers, reverse proxies, and access security layering — 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 clients or proxies to cache data outside of 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 resources. CRUD is a style of API that we can apply to REST, but it is not a requirement for a REST-based API.

This gives you more freedom in designing the API so that you can provide resources with a CRUD-based style when appropriate, and mix functional resources when CRUDS are not needed.

You don’t have to limit yourself to this style when designing your API, but you can explore some other extension styles that can help you design great API styles that meet 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 submit, approve, reject, and publish operations. Given the limited number of methods available in HTTP, how do you add them to the API?

One option is to use the status field modified through PATCH requests. While this is an option, it seems clearer to use functional endpoints as follows:

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

In this design, functional endpoints are provided for the article instances in the collection, which are used by the PUT operation (meaning update only), and this design has the following benefits:

  1. There is better fine-grained access management at the API level, where each particular operation is a unique URL and can be assigned different role-based (RBAC) access. Therefore, decoupling access from the code into the deployment model gives you the flexibility to further restrict endpoint access without changing the code to enforce those restrictions

  2. The workflow supported by the API is more explicit, eliminating the need to look at PATCH endpoints to see valid state values that can be updated, as well as state machine rules that allow transitions to states and times. Each endpoint declares this independently and more intuitively

  3. Finally, hypermedia links can be utilized more efficiently in response loads by providing unique urls for additional process endpoints. For example, the author might see the following API response:

    Hypermedia links: apis are not just data stores over HTTP, but state machines over HTTP. It still has data, but it can also provide “the next available action” in a self-describing way.

    Href: "/articles/99/submit"},}Copy the code

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

{name: "API serf ", status: "draft", _links: [{rel: "approve", href: "/articles/99/approve"}, {rel: "decline", href: "/articles/99/decline" }, ], }Copy the code

The client 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 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 that go beyond some or all of the resources they provide.

Functional resources for searching and computing

What if the capabilities that need to be provided to consumers do not require a collection of resources? What if you only need to evaluate one value and return it? Does this break the RESTFUL approach? No, as long as we follow HTTP rules and choose the right action verb as needed, 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 it as a functional resource, rather than a collection of resources that follow a plural noun format (e.g., project, account, customer).

Transaction resources for complex, long-running workflows

In the era of SOAP and Web services, WS-Transaction is a specification intended to encapsulate multiple Web service invocations into a single distributed Transaction, which increases the complexity of service design.

With REST-based API design, you can also encounter complex API design that spans 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. Here’s a look at the booking process of a hotel management system:

  1. Start by submitting a request to create a new reservation resource instance that contains details of the room to be booked, such as:POST /reservations
  2. This resource instance is then used to get or modify booking details, including arrival and departure dates, number of reservations, additional requests, and so on. This endpoint could bePATCH /reservations/hotel66It will be used as often as needed until the booking completes the customer’s requirements
  3. For flexibility and audit purposes, changes can be captured as a separate nested resource each time a change is made, allowing the history of scheduled changes to be extracted, for exampleGET /reservations/hotel66/modifications
  4. If necessary, set an expiration date for the reservation, such as 1 day after creation. You will receive one if you try to update after expiration or pay a reservation fee400 Bad Request409 Conflict
  5. When ready, the customer can make payment for the reservation order, such as POST/reservations/hotel66/payments
  6. The result of a successful payment will mean that the reservation is complete and the relevant information needs to be obtained, for exampleGET /bookings/book66
  7. After the booking is successful, the processing status of the order needs to be updated, for examplePATCH /bookings/book66, if additional fees or partial or full refunds are required.

This is a reasonable design for modeling long-running transactions or workflow processes, maintaining audit trails for resource changes, and supporting transaction expiration times. While staying within the constraints of reST-based API design, avoid adding complexity to the API consumer.

Singleton resource: There can be only one

Singleton resources are useful when the API does not require a collection of resources. A singleton resource can represent a virtual resource that interacts directly with an existing resource instance, such as GET /me instead of GET/Users /66.

The API can provide nested singleton resources, such as PUT/Accounts /66/ Preferences, when there is only one relationship between parent and child resources.

Singleton resources may already exist without having to create them, or they may need to be created through a POST endpoint. While singleton resources may not provide the full CRUD style of all operations as their collector-based counterparts do, they should still insist on correctly selecting HTTP action verbs that are best suited for the necessary operations.

Batch and batch resource operations

Bulk or Batch processing is required for resources that require a large amount of data to be imported at a time to avoid 100 to 10,000 individual POST operations. Although the terms are sometimes used interchangeably, it is felt that a distinction should be made:

  • bulkThe batch operation processes each committed record, allowing for processing failures, but the rest of the import is a success, meaning that out of 1000 records, 76 records may fail and the remaining 924 records are imported successfully. The client needs to obtain the records that failed to be imported in order to modify and commit the records that failed.
  • batchBatch operations process all committed records in a single pass or fail transaction, meaning that if 76 records out of 1000 fail, none of the records will actually be stored. The customer needs to revise 76 nonconforming records, and then submit the modified records again.

conclusion

When it comes to REST-based apis, many people confuse using resources based on the CRUD pattern with REST. REST is a set of constraints (specifications) to be more efficient in collaboration in front and back end development. For API design, I recommend taking some time to design and choose the best pattern.