Most modern Web applications expose apis that clients can use to interact with the application. A well-designed Web API should be designed to support:

  • Platform independence. No matter how the API is implemented internally, any client should be able to call the API. This requires the use of standard protocols and a mechanism for clients and Web services to agree on the format of the data to be exchanged.

  • Service evolution. The Web API should be able to evolve and add functionality independently of the client application. As the API evolves, existing client applications should continue to run without modification. All functionality should be discoverable so that client applications can make full use of it.

This article introduced you to the issues to consider when designing a Web API.

Introduction of REST

In 2000, Roy Fielding proposed representational state transition (REST) as an architectural approach to designing Web services. REST is an architectural style for building hypermedia-based distributed systems. REST is independent of any underlying protocol and is not necessarily bound to HTTP. However, most common REST implementations use HTTP as an application protocol, and this guide focuses on designing REST apis for HTTP.

REST’s main advantage over HTTP is that it uses open standards and does not bind apis or client application implementations to any particular implementation. For example, REST Web services can be written in ASP.NET, and client applications can use any language or toolset that can generate HTTP requests and parse HTTP responses.

Here are some key design principles for RESTful apis using HTTP:

  • REST apis are built around

    resources

    The design,

    resources

    Is any type of object, data, or service that a client can access.

  • Resources are

    identifier

    Is the URI that uniquely identifies the resource. Specific customer orders, for example, the URI of the possible is: https://www.abc.com/orders/1

  • Clients exchange resources

    said

    Interact with the service. Many Web apis use JSON as an interchange format. For example, a GET request to the URIs listed above might return this response body (JSON) :

    {” orderId “: 1,” orderValue “: 99.90,” productId “: 1,” quantity “: 1}

  • REST apis use a uniform interface, which helps decouple client and service implementations. For REST apis built over HTTP, the unified interface includes performing operations on resources using standard HTTP verbs. The most common operations are GET, POST, PUT, PATCH, and DELETE.

  • REST apis use a stateless request model. HTTP requests should be stand-alone and can occur in any order, so it is not feasible to retain transient information between requests. The only place where information is stored is the resource itself, and each request should be an atomic operation. This constraint makes Web services highly scalable because there is no need to preserve any kinship between the client and a particular server. Any server can handle any request from any client. That said, other factors may limit scalability. For example, many Web services write to back-end data stores, which can be difficult to scale.

  • REST apis are driven by hypermedia links contained in the presentation. For example, the following shows a JSON representation of the order. It contains links to get or update the customer associated with the order.

{
“orderID”:3,
“productID”:2,
“quantity”:4,
“OrderValue” : 16.60,
“links”: [
{“rel”:”product”,”href”:”https://adventure-works.com/customers/3″, “action”:”GET” },
{“rel”:”product”,”href”:”https://adventure-works.com/customers/3″, “action”:”PUT” }
]
}

In 2008, Leonard Richardson proposed the following maturity model for Web apis:

  • Level 0: A URI is defined and all operations are POST requests to that URI.

  • Level 1: Create a separate URI for a single resource.

  • Level 2: Use HTTP methods to define operations on resources.

  • Level 3: Use hypermedia (HATEOAS, described below).

According to Fielding’s definition, level 3 corresponds to a true RESTful API. In fact, many published Web apis are around level 2.

Organize apis around resources

Focus on business entities exposed by Web apis. For example, in an e-commerce system, the primary entities might be customers and orders. An order can be created by sending an HTTP POST request containing the order information. The HTTP response indicates whether the order was placed successfully. If possible, resource URIs should be based on nouns (resources) rather than verbs (operations on resources).

https://www.abc.com/orders // Goodhttps://www.abc.com/create-order // AvoidCopy the code

Resources need not be based on a single physical data item. For example, an order resource might be implemented internally as multiple tables in a relational database, but presented to the customer as a single entity. Avoid creating apis that reflect only the internal structure of the database. The purpose of REST is to model entities and the operations that applications can perform on those entities. Customers should not touch internal implementations.

Entities are typically grouped into collections (orders, customers). A collection is a resource that is independent of the items in the collection and should have its own URI. For example, the following URIs might represent a collection of orders:

https://www.abc.com/orders

Send an HTTP GET request to the collection URI to retrieve the list of items in the collection. Each item in the collection has its own unique URI. An HTTP GET request to an item URI returns detailed information about the item.

Use consistent naming conventions in URIs. In general, it helps to use plural nouns for URIs that refer to collections. It is a good practice to organize the URIs of collections and projects into a hierarchy. For example, /customers is the path to the customers set/Customers /5, with ID equal to 5. This approach helps keep the Web API intuitive. In addition, many Web API frameworks can route requests based on parameterized URI paths, so you can define a route /customers/{id} for path.

Also consider the relationships between different types of resources and how you expose those relationships. For example, /customers/5/ Orders might represent all of customer 5’s orders. You can also go in the other direction and use urIs like urIs to represent the association/Orders /99/ Customer from order to customer. However, extending the model too far may be difficult to implement. A better solution is to provide navigable links to associated resources in the body of the HTTP response message.

In a more complex system, provides the URI enables clients to browse multiple levels of the relationship (for example) may be very attractive/customers / 1 / orders / 99 / products. However, if the relationship between resources changes in the future, this level of complexity can be difficult to maintain and difficult to maintain flexibility. Instead, try to keep the URI relatively simple. Once an application references a resource, it should be possible to use that reference to find items associated with that resource. You can replace the previous query with URI, /customers/1/ Orders to find all orders from Customer 1, and /orders/99/products to find the products in this order.

Another factor is that all Web requests impose a load on the Web server. The more requests, the heavier the load. Therefore, try to avoid “chat” Web apis that expose a lot of small resources. Such an API might require the client application to send multiple requests to find all the data it needs. Instead, you might want to de-normalize the data and combine related information into larger resources that can be retrieved on a single request. However, you need to balance this approach with the overhead of getting data that the client doesn’t need. Retrieving large objects can add latency to requests and incur additional bandwidth costs.

Avoid introducing dependencies between the Web API and the underlying data source. For example, if your data is stored in a relational database, the Web API does not need to display every table as a collection of resources. In fact, it may be a bad design. Instead, you can think of a Web API as an abstraction of a database. If necessary, introduce a mapping layer between the database and the Web API. In this way, client applications do not remain isolated from changes to the underlying database schema.

Finally, it may not be possible to map every operation performed by the Web API to a specific resource. You can handle this class through HTTP requests that invoke the function

The resource
/ add?
Operand1 = 99&operand2 = 1

Define operations based on HTTP methods

The HTTP protocol defines a number of ways to assign semantics to requests. The common HTTP methods used by most RESTful Web apis are:

  • GET retrieves the representation of the resource at the specified URI. The body of the response message contains details of the requested resource.

  • POST creates a new resource at the specified URI. The body of the request message provides details of the new resource. Note that POST can also be used to trigger actions that do not actually create resources.

  • PUT creates or replaces a resource at a specified URI. The body of the request message specifies the resource to be created or updated.

  • PATCH performs partial updates of resources. The request body specifies a set of changes to be applied to the resource.

  • DELETE Deletes the resource at the specified URI.

The effect of a particular request should depend on whether the resource is a collection or a single project. The following table summarizes the common conventions adopted by most RESTful implementations using e-business examples. Not all of these requests can be fulfilled – it depends on the particular scenario.

resource Boot self-inspection get put delete
/ customer Build new customers Retrieve all customers Batch update customers

Delete all

The customer

/ Client / 1 error Retrieve customer 1’s details Update customer 1 details (if present)

Remove the guest

Units 1

/ customers / 1 / orders Create a new order for customer 1 Retrieve all orders for customer 1 Batch update customer 1’s order Delete all orders for customer 1

The differences between POST, PUT, and PATCH can be confusing.

  • POST requests that the resource be created. The server assigns a URI to the new resource and returns the URI to the client. In the REST model, you often apply POST requests to collections. New resources will be added to the collection. POST requests can also be used to submit data to process existing resources without creating any new resources.

  • PUT requests to create a resource

    or

    Update existing resources. The client specifies the URI of the resource. The request body contains a complete representation of the resource. If a resource with this URI already exists, it is replaced. Otherwise, a new resource is created if the server supports it. PUT requests are most often applied to a single project (such as a specific customer) rather than a collection of resources. The server may support updates, but not creation via PUT. Support for creation through PUT depends on whether a client can meaningfully assign a URI to a resource before it exists. If not, the resource is created using POST and updated using PUT or PATCH.

  • PATCH requests are executed for existing resources

    Part of the update

    . The client specifies the URI of the resource. The request body specifies a group

    To change the

    To be applied to resources. This can be more efficient than using PUT, because the client only sends changes, not a full representation of the resource. Technically, PATCH can also create new resources (by specifying a set of updates for the “empty” resources) if the server supports it.

PUT requests must be idempotent. If the client submits the same PUT request multiple times, the result should always be the same (the same resource will be modified with the same value). POST and PATCH requests are not guaranteed to be idempotent.

HTTP semantics compliant

This section describes some typical considerations for designing AN API that conforms to the HTTP specification. However, it does not cover all possible details or scenarios. If in doubt, consult the HTTP specification.

The media type

As mentioned earlier, the client and server exchange representations of resources. For example, in a POST request, the request body contains a representation of the resource to be created. In a GET request, the response body contains the representation of the fetched resource.

In THE HTTP protocol, by using

The media type

The Content-Type header in the request or response specifies the format of the presentation. Here is an example POST request with JSON data:

POSThttps://adventure-works.com/orders HTTP / 1.1

Content-Type: application/json; charset=utf-8

Content-Length: 57

{” Id “: 1,” Name “:” the Gizmo “, “Category” : “Widgets”, “Price” : 1.99}

If the server does not support the media type, return HTTP status code 415 (unsupported media type).

A client request can contain an Accept header that contains a list of media types that the client will Accept from the server in the response message. Such as:

HTTP / 1.1 GEThttps://adventure-works.com/orders/2 Accept: application/json

If the server cannot match any of the media types listed, the server should return the HTTP status code 406 (unacceptable).

The GET method

Successful GET methods typically return an HTTP status code of 200 (OK). If the resource is not found, the method should return 404 (not found).

POST method

If the POST method creates a new resource, it returns the HTTP status code 201 (created). The URI of the new resource is included in the Location header of the response. The response body contains the representation of the resource.

If the method does some processing but does not create a new resource, it can return the HTTP status code 200 and include the result of the operation in the response body. Alternatively, if there is no result to return, the method can return the HTTP status code 204 (no content) with no response body.

If the client puts invalid data into the request, the server should return the HTTP status code 400 (error request). The response body can contain additional information about the error, or it can contain URI links that provide more details.


PUT method

If the PUT method creates a new resource, like the POST method, it returns the HTTP status code 201 (created). If the method updates an existing resource, it returns 200 (ok) or 204 (no content). In some cases, it may not be possible to update existing resources. In this case, consider returning the HTTP status code 409 (conflict).


Consider implementing a batch HTTP PUT operation, which can batch update multiple resources in a collection. The PUT request should specify the URI of the collection, and the request body should specify the details of the resource to be modified. This approach can help reduce chat situations and improve performance.

The Delete method

If the deletion was successful, the Web server should respond with HTTP status code 204 indicating that the process was successfully handled, but the response body contains no additional information. If the resource does not exist, the Web server can return HTTP 404 (not found).

Asynchronous operations

Sometimes POST, PUT, PATCH, or DELETE operations may require some processing to complete. Waiting to complete before sending the response to the client can result in unacceptable delays. If so, consider making the operation asynchronous. Return HTTP status code 202 (Accepted) to indicate that the request was accepted for processing but not completed.

You should expose an endpoint that returns the status of asynchronous requests so that clients can monitor the status by polling the status endpoint. Include the URI of the state endpoint in the Location header of the 202 response. Such as:

HTTP / 1.1 202 Accepted

Location: /api/status/12345

If the client sends a GET request to this endpoint, the response should contain the current status of the request. Optionally, it can also include an estimated completion time or a link to cancel the action.

HTTP / 1.1 200 OK

Content-Type: application/json

{

“status”:”In progress”, “link”: { “rel”:”cancel”, “method”:”delete”, “href”:”/api/status/12345″ } }

If an asynchronous operation creates a new resource, the status endpoint should return a status code 303 when the operation is complete. In the 303 ring

Contains a Location header that provides the URI of the new resource:

HTTP / 1.1 303 See Other

Location: /api/orders/12345

Filter and paging data

Exposing a collection of resources through a single URI can result in an application getting a lot of data when only a fraction of the information is needed. For example, suppose a client application needs to find all orders that cost more than a certain value. It might go from

/ orders

Instead, the API can allow filters to be passed in the query string of the URI, for example

/ the orders?
minCost = n

minCost

A GET request on a collection resource may return a large number of items. You should design a Web API to limit the amount of data returned by any single request. Consider supporting query strings that specify the maximum number of items to retrieve and the starting offset in the collection. Such as:

/orders? limit=25&offset=50

A cap on the number of returned items should also be considered to help prevent denial of service attacks. To help client applications, GET requests that return paging data should also include some form of metadata to indicate the total number of resources available in the collection.

By providing a sort parameter that takes the field name as the value, for example

/ the orders?
sort = ProductID

If each item contains a large amount of data, you can extend this method to limit the fields returned for each item. For example, you can use the query string argument, which accepts a comma-separated list of fields, for example

/ the orders?
Fields = ProductID, Quantity

Provide meaningful defaults for all optional parameters in the query string. For example, if paging is implemented, the limit parameter is set to 10, the offset parameter is set to 0, if sorting is implemented, the sort parameter is set to the key of the resource, and the fields parameter is set to all fields in the resource if projection is supported.

Partial responses to large binary resources are supported

Resources may contain large binary fields, such as files or images. To overcome the problems caused by unreliable and intermittent connections and improve response times, consider enabling such resources to be retrieved in chunks. To do this, the Web API should support the Accept-ranges header for GET requests for large resources. This header indicates that the GET operation supports part of the request. A client application can submit a GET request that returns a subset of resources specified as a range of bytes.

Also, consider implementing HTTP HEAD requests for these resources. A HEAD request is similar to a GET request, except that it only returns an HTTP header describing the resource and the message body is empty. A client application can make a HEAD request to determine whether to obtain a resource by using a partial GET request. Such as:

HEADhttps://adventure-works.com/products/10?fields=productImage HTTP / 1.1

Here is a sample response message:

HTTP / 1.1 200 OK

Accept-Ranges: bytes

Content-Type: image/jpeg

Content-Length: 4580


The Content-Length header provides the total size of the resource, and the Accept-ranges header indicates that the corresponding GET operation supports partial results. Client applications can use this information to retrieve images in smaller chunks. The first request gets the first 2500 bytes by using the Range header:

HTTP / 1.1 303 See Other

Location: /api/orders/12345

Filter and paging data

Exposing a collection of resources through a single URI can result in an application getting a lot of data when only a fraction of the information is needed. For example, suppose a client application needs to find all orders that cost more than a certain value. It might go from

/ orders

Instead, the API can allow filters to be passed in the query string of the URI, for example

/ the orders?
minCost = n

minCost

A GET request on a collection resource may return a large number of items. You should design a Web API to limit the amount of data returned by any single request. Consider supporting query strings that specify the maximum number of items to retrieve and the starting offset in the collection. Such as:

/orders? limit=25&offset=50

A cap on the number of returned items should also be considered to help prevent denial of service attacks. To help client applications, GET requests that return paging data should also include some form of metadata to indicate the total number of resources available in the collection.

By providing a sort parameter that takes the field name as the value, for example

/ the orders?
sort = ProductID

If each item contains a large amount of data, you can extend this method to limit the fields returned for each item. For example, you can use the query string argument, which accepts a comma-separated list of fields, for example

/ the orders?
Fields = ProductID, Quantity

Provide meaningful defaults for all optional parameters in the query string. For example, if paging is implemented, the limit parameter is set to 10, the offset parameter is set to 0, if sorting is implemented, the sort parameter is set to the key of the resource, and the fields parameter is set to all fields in the resource if projection is supported.

Partial responses to large binary resources are supported

Resources may contain large binary fields, such as files or images. To overcome the problems caused by unreliable and intermittent connections and improve response times, consider enabling such resources to be retrieved in chunks. To do this, the Web API should support the Accept-ranges header for GET requests for large resources. This header indicates that the GET operation supports part of the request. A client application can submit a GET request that returns a subset of resources specified as a range of bytes.

Also, consider implementing HTTP HEAD requests for these resources. A HEAD request is similar to a GET request, except that it only returns an HTTP header describing the resource and the message body is empty. A client application can make a HEAD request to determine whether to obtain a resource by using a partial GET request. Such as:

HEADhttps://adventure-works.com/products/10?fields=productImage HTTP/1.1 Here is a sample response message:

HTTP / 1.1 200 OK


Accept-Ranges: bytes

Content-Type: image/jpeg

Content-Length: 4580


The Content-Length header provides the total size of the resource, and the Accept-ranges header indicates that the corresponding GET operation supports partial results. Client applications can use this information to retrieve images in smaller chunks. The first request gets the first 2500 bytes by using the Range header:

GEThttps://adventure-works.com/products/10?fields=productImage HTTP / 1.1

Range: bytes=0-2499

The response message indicates that it is a partial response by returning the HTTP status code 206. The Content-Length header specifies the actual number of bytes (not the size of the resource) to be returned in the message body, while the Content-range header indicates which response. This is part of the resource (bytes 0-2499 in 4580) :

HTTP / 1.1 206 Partial Content


Accept-Ranges: bytes

Content-Type: image/jpeg

Content-Length: 2500

Content-Range: bytes 0-2499/4580

Subsequent requests from the client application can retrieve the rest of the resource.

Use HATEOAS to enable navigation of related resources

One of the main motivations behind REST is that it should be possible to browse the entire resource set without prior knowledge of the URI scheme. Each HTTP GET request should return the information necessary to find the resources directly related to the requested object through the hyperlinks contained in the response, and it should also be provided with information describing each of the available operations within those resources. This principle is called HATEOAS, or hypertext called the application state engine. The system is essentially a finite state machine, and the response to each request contains the information needed to move from one state to another. No additional information is required.

Version control RESTful Web apis

The chances of a Web API remaining static are slim. As business requirements change, new collections of resources may be added, relationships between resources may change, and data structures within resources may be modified. While updating the Web API to handle new or different requirements is a relatively simple process, you must consider the impact such changes will have on client applications that use the Web API. The problem is that while the developer who designs and implements the Web API has complete control over the API, the developer has varying degrees of control over the client application, which can be built by a third-party organization running remotely.

Through versioning, a Web API can indicate the functionality and resources it exposes, and client applications can submit requests for a specific version of that functionality or resource. The following sections describe several different approaches, each with its own advantages and trade-offs.

No version control

This is the simplest approach and may be acceptable for some internal apis. Significant changes can be represented as new resources or new links. Adding content to an existing resource may not make a significant difference because it will be ignored by client applications that do not want to see it.

For example, the request to the URI https://www.abc.com/customers/3 should return a single customer containing details of id, name and address stated by the client application fields:

HTTP / 1.1 200 OK

Content-Type: application/json; charset=utf-8 {“id”:3,”name”:”Contoso LLC”,”address”:”1 Microsoft Way Redmond WA 98053″}


If the DateCreated field is added to the schema of the customer resource, the response will look like this:

HTTP / 1.1 200 OK

Content-Type: application/json; charset=utf-8

{“id”:3,”name”:”Contoso LLC”,”dateCreated”:” 2014-09-04T12:11:38.0376089z “,”address”:”1 Microsoft Way Redmond WA 98053″}

If existing client applications are able to ignore unrecognized fields, they may continue to function normally, and new client applications can be designed to handle this new field. However, if more fundamental changes are made to the schema of the resource (for example, deleting or renaming fields), or if the relationship between the resources changes, those changes may constitute significant changes that prevent existing client applications from functioning properly. In these cases, you should consider using one of the following methods.

URI Versioning

Each time you modify a Web API or change a resource schema, you add a version number to the URI of each resource. Pre-existing URIs should continue to operate as before, returning resources that match their original schema.

Extending the previous example, if the address field is reorganized to contain each component of the address (such as subfields streetAddress, City, state, and zipCode), the version of the resource that can be exposed by URI contains a version number, Such as https://adventure-works.com/v2/customers/3:

HTTP / 1.1 200 OK

Content-Type: application/json; charset=utf-8

{” id “: 3,” name “:” Contoso LLC “, “dateCreated” : “the 2014-09-04 T12: though. 0376089 z”, “address” : {” streetAddress “:” 1 Microsoft Way”,”city”:”Redmond”,”state”:”WA”,”zipCode”:98053}

This versioning mechanism is quite simple, but depends on the server routing requests to the appropriate endpoint. However, as the Web API matures over many iterations, and the server must support many different versions, it can become unwieldy. Also, from a purist’s point of view, in all cases the client application is extracting the same data (client 3), so the URI should not really depend on the version. This scenario also complicates the implementation of HATEOAS because all links need to include the version number in their URI.

Query string version control

You can use the attached to the parameters in the query string of the HTTP request (for example) to specify the version of resource, rather than provide multiple URI https://www.abc.com/customers/3?version=2. If the version parameter is omitted from an older client application, the version parameter defaults to a meaningful value, such as 1.

This approach has the semantic advantage of always retrieving the same resource from the same URI, but it depends on the code that processes the request to parse the query string and send back the appropriate HTTP response. This approach also suffers from the same complexity of implementing HATEOAS as the URI versioning mechanism.

Header versioning

Instead of appending the version number as a query string parameter, you can implement a custom header that indicates the version of the resource. This approach requires the client application to add the appropriate header to any request, although if the version header is omitted, the code that handles the client request can use the default (version 1). The following example uses the name

Custom-Header
head


Version 1:

GEThttps://www.abc.com/customers/3 HTTP / 1.1

Custom-Header: api-version=1


HTTP / 1.1 200 OK

Content-Type: application/json; charset=utf-8

{“id”:3,”name”:”Contoso LLC”,”address”:”1 Microsoft Way Redmond WA 98053″}


Version 2:

GEThttps://www.abc.com/customers/3 HTTP / 1.1

Custom-Header: api-version=2


HTTP / 1.1 200 OK

Content-Type: application/json; charset=utf-8

{” id “: 3,” name “:” Contoso LLC “, “dateCreated” : “the 2014-09-04 T12: though. 0376089 z”, “address” : {” streetAddress “:” 1 Microsoft Way”,”city”:”Redmond”,”state”:”WA”,”zipCode”:98053}}

As with the first two approaches, implementing HATEOAS requires that appropriate custom headers be included in any link.

Media type version control

When a client application sends an HTTP GET request to a Web server, it should specify the content format that it can process using the Accept header, as described earlier in this guide. In general,

Accept
application / vnd.adventure-works.v1 + json
Accept
vnd.adventure-works.v1
json

GEThttps://adventure-works.com/customers/3 HTTP / 1.1

Accept: application/vnd.adventure-works.v1+json


The code that handles the request takes care of that

Accept

Accept

Format). The Web server validates the format of the data in the response body by using the Content-Type header:

HTTP / 1.1 200 OK

Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

{“id”:3,”name”:”Contoso LLC”,”address”:”1 Microsoft Way Redmond WA 98053″}

If the Accept header does not specify any known media type, the Web server can generate an HTTP 406 (unacceptable) response message or return a message with the default media type.

This approach is arguably the purest version control mechanism, and is a natural fit for HATEOAS, which can include MIME types of associated data in resource links.

Note: When choosing a versioning policy, you should also consider the impact on performance, especially caching on Web servers. URI versioning and query string versioning schemes are cache-friendly because the same URI/query string combination references the same data each time. Header versioning and media type versioning mechanisms often require additional logic to check for values in custom or "receive" headers. In a large-scale environment, many clients using different versions of the Web API can result in large amounts of duplicate data in the server-side cache. This problem can become serious if the client application communicates with the Web server through a proxy that implements caching and only forwards requests to the Web server if it does not currently keep a copy of the request data in its cache.Copy the code


Thanks for reading!



If you like this article, welcome to “ISevena”.