REST API Tutorial

  • REST
  • JSON
  • Dark Mode

How to design a REST API

Learning REST in pieces is one thing, while applying all those learned concepts into real application design is completely another challenge. In this tutorial, we will learn to design REST APIs for a network based application. Please note that the takeaway from this whole exercise is the learning of how to apply REST principles in design process.

Steps in designing REST Services
Identify Object Model
Create Model URIs
Determine Representations
Assign HTTP Methods
More Actions

Identify Object Model

The very first step in designing a REST API based application is – identifying the objects which will be presented as resources.

For a network based application, object modeling is pretty much simpler. There can be many things such as devices, managed entities, routers, modems etc. For simplicity sake, we will consider only two resources i.e.

  • Devices
  • Configurations

Here configuration is sub-resource of a device. A device can have many configuration options.

Note that both objects/resources in our above model will have a unique identifier, which is the integer id property.

Create Model URIs

Now when object model is ready, it’s time to decide the resource URIs. At this step, while designing the resource URIs – focus on the relationship between resources and its sub-resources. These resource URIs are endpoints for RESTful services.

In our application, a device is a top-level resource. And configuration is sub-resource under device. Let’s write down the URIs.

/devices
/devices/{id}

/configurations
/configurations/{id}

/devices/{id}/configurations
/devices/{id}/configurations/{id}

Notice that these URIs do not use any verb or operation. It’s very important to not include any verb in URIs. URIs should all be nouns only.

Determine Representations

Now when resource URIs have been decided, let’s work on their representations. Mostly representations are defined in either XML or JSON format. We will see XML examples as its more expressive on how data is composed.

Collection of Device Resource

When returning a collection resource, include only most important information about resource. This will keep the size of payload small, and so will improve the performance of REST APIs.

<devices size="2"> 

	<link rel="self" href="/devices"/>

	<device id="12345"> 
		<link rel="self" href="/devices/12345"/>
		<deviceFamily>apple-es</deviceFamily> 
		<OSVersion>10.3R2.11</OSVersion> 
		<platform>SRX100B</platform> 
		<serialNumber>32423457</serialNumber> 
		<connectionStatus>up</connectionStatus> 
		<ipAddr>192.168.21.9</ipAddr> 
		<name>apple-srx_200</name> 
		<status>active</status>
	</device> 

	<device id="556677"> 
		<link rel="self" href="/devices/556677"/>
		<deviceFamily>apple-es</deviceFamily> 
		<OSVersion>10.3R2.11</OSVersion> 
		<platform>SRX100B</platform> 
		<serialNumber>6453534</serialNumber> 
		<connectionStatus>up</connectionStatus> 
		<ipAddr>192.168.20.23</ipAddr> 
		<name>apple-srx_200</name> 
		<status>active</status>
	</device> 

</devices>

Single Device Resource

Opposite to collection URI, here include complete information of a device in this URI. Here, also include a list of links for sub-resources and other supported operations. This will make your REST API HATEOAS driven.

<device id="12345"> 
	<link rel="self" href="/devices/12345"/>

	<id>12345</id> 
	<deviceFamily>apple-es</deviceFamily> 
	<OSVersion>10.0R2.10</OSVersion> 
	<platform>SRX100-LM</platform> 
	<serialNumber>32423457</serialNumber> 
	<name>apple-srx_100_lehar</name> 
	<hostName>apple-srx_100_lehar</hostName> 
	<ipAddr>192.168.21.9</ipAddr> 
	<status>active</status>

	<configurations size="2">
		<link rel="self" href="/configurations" />

		<configuration id="42342">
			<link rel="self" href="/configurations/42342" />
		</configuration>

		<configuration id="675675">
			<link rel="self" href="/configurations/675675" />
		</configuration>
	</configurations>

	<method href="/devices/12345/exec-rpc" rel="rpc"/> 
	<method href="/devices/12345/synch-config"rel="synch device configuration"/> 
</device>

Configuration Resource Collection

Similar to device collection representation, create configuration collection representation with only minimal information.

<configurations size="20">
	<link rel="self" href="/configurations" />

	<configuration id="42342">
		<link rel="self" href="/configurations/42342" />
	</configuration>

	<configuration id="675675">
		<link rel="self" href="/configurations/675675" />
	</configuration>
	...
	...
</configurations>

Please note that configurations collection representation inside device is similar to top-level configurations URI. Only difference is that configurations for a device are only two, so only two configuration items are listed as subresource under device.

Single Configuration Resource

Now, single configuration resource representation must have all possible information about this resource – including relevant links.

<configuration id="42342">
	<link rel="self" href="/configurations/42342" />
	<content><![CDATA[...]]></content>
	<status>active</status>
	<link  rel="raw configuration content" href="/configurations/42342/raw" /> 
</configuration>

Configuration Resource Collection Under Single Device

This resource collection of configurations will be a subset of primary collection of configurations, and will be specific a device only. As it is the subset of primary collection, DO NOT create a different representation data fields than primary collection. Use same presentation fields as primary collection.

<configurations size="2">
	<link rel="self" href="/devices/12345/configurations" />

	<configuration id="53324">
		<link rel="self" href="/devices/12345/configurations/53324" />
		<link rel="detail" href="/configurations/53324" />
	</configuration>

	<configuration id="333443">
		<link rel="self" href="/devices/12345/configurations/333443" />
		<link rel="detail" href="/configurations/333443" />
	</configuration>
</configurations>

Notice that this subresource collection has two links. One for its direct representation inside sub-collection i.e. /devices/12345/configurations/333443 and other pointing to its location in primary collection i.e. /configurations/333443.

Having two links is important as you can provide access to a device specific configuration in more unique manner, and you will have ability to mask some fields (if design require it) which shall not be visible in a secondary collection.

Single Configuration Resource Under Single Device

This representation should have either exactly similar representation as of Configuration representation from primary collection; OR you may mask few fields.

This subresource representation will also have an additional link to its primary presentation.

<configuration id="11223344">
	<link rel="self" href="/devices/12345/configurations/11223344" />
	<link rel="detail" href="/configurations/11223344" />
	<content><![CDATA[...]]></content>
	<status>active</status>
	<link rel="raw configuration content" href="/configurations/11223344/raw" /> 
</configuration>

Now, before moving forward to next section, let’s note down few observations so you don’t miss them.

  • Resource URIs are all nouns.
  • URIs are usually in two forms – collection of resources and singular resource.
  • Collection may be in two forms primary collection and secondary collection. Secondary collection is sub-collection from a primary collection only.
  • Each resource/collection contain at least one link i.e. to itself.
  • Collections contain only most important information about resources.
  • To get complete information about a resource, you need to access through its specific resource URI only.
  • Representations can have extra links (i.e. methods in single device). Here method represent a POST method. You can have more attributes or form links in altogether new way also.
  • We have not talked about operations on these resources yet.

Assign HTTP Methods

So our resource URIs and their representation are fixed now. Let’s decide the possible operations in application and map these operations on resource URIs. A user of network application can perform browse, create, update or delete operations. So let’s map them.

Browse all devices or configurations [Primary Collection]

HTTP GET /devices
HTTP GET /configurations

If the collection size is large, you can apply paging and filtering as well. e.g. Below requests will fetch first 20 records from collection.

HTTP GET /devices?startIndex=0&size=20
HTTP GET /configurations?startIndex=0&size=20

Browse all devices or configurations [Secondary Collection]

HTTP GET /devices/{id}/configurations

It will be mostly a small size collection – so no need to enable filtering or soring here.

Browse single device or configuration [Primary Collection]

To get the complete detail of a device or configuration, use GET operation on singular resource URIs.

HTTP GET /devices/{id}
HTTP GET /configurations/{id}

Browse single device or configuration [Secondary Collection]

HTTP GET /devices/{id}/configurations/{id}

Subresource representation will be either same as or subset of primary presentation.

Create a device or configuration

Create is not idempotent operation, and in HTTP protocol – POST is also not idempotent. So use POST.

HTTP POST /devices
HTTP POST /configurations

Please note that request payload will not contain any id attribute, as server is responsible for deciding it. Response of create request will look like this:

HTTP/1.1 201 Created
Content-Type: application/xml
Location: http://example.com/network-app/configurations/678678

<configuration id="678678">
	<link rel="self" href="/configurations/678678" />
	<content><![CDATA[...]]></content>
	<status>active</status>
	<link  rel="raw configuration content" href="/configurations/678678/raw" /> 
</configuration>

Update a device or configuration

Update operation is an idempotent operation and HTTP PUT is also is idempotent method. So we can use PUT method for update operations.

HTTP PUT /devices/{id}
HTTP PUT /configurations/{id}

PUT response may look like this.

HTTP/1.1 200 OK
Content-Type: application/xml

<configuration id="678678">
	<link rel="self" href="/configurations/678678" />
	<content><![CDATA[. updated content here .]]></content>
	<status>active</status>
	<link  rel="raw configuration content" href="/configurations/678678/raw" /> 
</configuration>

Remove a device or configuration

Removing is always a DELETE operation.

HTTP DELETE /devices/{id}
HTTP DELETE /configurations/{id}

A successful response SHOULD be 202 (Accepted) if resource has been queues for deletion (async operation), or 200 (OK) / 204 (No Content) if resource has been deleted permanently (sync operation).

In case of async operation, application shall return a task id which can be tracked for success/failure status.

Please note that you should put enough analysis in deciding the behavior when a subresource is deleted from system. Normally, you may want to SOFT DELETE a resource in these requests – in other words, set their status INACTIVE. By following this approach, you will not need to find and remove its references from other places as well.

Applying or Removing a configuration from a device

In real application, you will need to apply the configuration on device – OR you may want to remove the configuration from device (not from primary collection). You shall use PUT and DELETE methods in this case, because of its idempotent nature.

//Apply Configuration on a device
HTTP PUT /devices/{id}/configurations 		

//Remove Configuration on a device	
HTTP DELETE /devices/{id}/configurations/{id}		

More Actions

So far we have designed only object model, URIs and then decided HTTP methods or operations on them. You need to work on other aspects of the application as well:

1) Logging
2) Security
3) Discovery etc.

In next article, we will create this application in java to get more detailed understanding.

TwitterFacebookLinkedinReddit

14
Leave a Reply

This comment form is under antispam protection
5 Comment threads
9 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
11 Comment authors
This comment form is under antispam protection
  Subscribe  
newest oldest most voted
Notify of
Rodrigo R. Magalhães

Hello there,

The POST HTTP method has been indicated as a preferred to perform creation of objects on a RESTFULL interfce. However, on https://restfulapi.net/idempotent-rest-apis/, it is indicated that PUT should be preferred in detriment to the POST method due to the idempotent aspect and the fact that there would occur repeated calls (intentional or not) and the interface would then render prone to errors.
So, for the sake of consistency, shouldn’t this aspect be noted on this page as well? I’d go for PUT rather than POST, anyway.

Vote Up0Vote Down  Reply
2 months ago
ronnakrit

What if I have setup API for all dropdown options when I about to edit device, do I need to do like this

HTTP GET /devices/info —— return all available status and all other options

above URI will be crashing directly with HTTP GET /devices/{id} which is quite ambiguous.

or

HTTP GET /device_infos —— return all available status and all other options

which is the right design to follow?

Vote Up0Vote Down  Reply
10 months ago
Admin

If you apply right level of caching at server side, no GET URI will be crashing. I will say the first design is more suitable.

Vote Up0Vote Down  Reply
10 months ago
flaviu

How about restricting “id” a bit?
instead of {id:.*}, use {id:\d+} or {id:!info} or anything that will exclude “info” as a valid ID and fallback to a different controller.
As far as design goes, /devices/info should return a list of “info” resources on all devices.

GET /devices HTTP/1.1 – return all devices
GET /devices/123 HTTP/1.1 – return device with ID 123
GET /devices/123/info HTTP/1.1 – return all info on device with id 123
GET /info HTTP/1.1 – return all available info
GET /devices/info HTTP/1.1 – this returns… all available info (as above)?

This all depends on how you data is structured as well

Vote Up0Vote Down  Reply
9 months ago
Vernon

Great article. It’s only a pity it addresses only a many to many relationship between devices and configurations. I am also looking for something with a one to one and a one to many relationship. Anyone have any ideas for these? I am also a little disappointed in the response “Not really. Until I apply configuration to device, how configuration id will be created?”
It sounds like configuration has 2 ids. one for it’s primary existence and one for it’s many to many relationship with device.

Vote Up0Vote Down  Reply
1 year ago
Admin

Exactly. Configuration has 2 ids in that sense. One primary id and another id which helps in finding the device and configuration mapping.

Vote Up0Vote Down  Reply
1 year ago
Mark

I see where you’re coming from, Vernon. I was confused when I saw PUT used to create a configuration resource. Earlier, we read that POST is proper for a create operation, and PUT is proper for an update operation. And then, right at the end of the article, we see PUT used to create a resource. I wondered if that might be because it’s an update, but that would mean we have an id.

Clearly, we don’t have an id. This leads me to believe that the PUT is going to create a resource if no configuration exists or update if a configuration does exist. Allowing PUT to create a resource as a side effect is problematic because it leaves the API user no mechanism to explicitly update a resource without possibly creating a resource. I’ve used APIs that allow PUT to create when no resource is found, and that forced me to test the result of each PUT and DELETE if a resource was created. Ugh.

I’m very clear in my API designs. POST creates. PUT updates. If PUT finds no resource, it fails. No side effects and no surprises.

When it comes to 1-1 or many-many, I’ve done both. For many-many, the device has an id and the resource has an id. There’s no reason for the relationship to be exclusive. In fact, the relationship between the two is usually captured in an associative table. By convention, the associative table is named using the two participating resources, so that would be DEVICE_CONFIGURATION. In the associative table, we would keep the id for the device, the id for the configuration, and the id for the entity containing both. One row from that table looks like:

{
  &amp;amp;quot;deviceConfigurations&amp;amp;quot;: {
    &amp;amp;quot;id&amp;amp;quot;: &amp;amp;quot;devcon123&amp;amp;quot;,
    &amp;amp;quot;deviceID&amp;amp;quot;: &amp;amp;quot;dev123&amp;amp;quot;,
    &amp;amp;quot;configurationID&amp;amp;quot;: &amp;amp;quot;con123&amp;amp;quot;
  }
}

Holding true to form with POST and PUT, a POST on a configuration would create both a configuration and an deviceConfiguration. A PUT would fail if there is no deviceConfiguration containing the device id and the configuration id.

For 1-1, I represent that as a contained resource. It looks like:

{
...
  &amp;amp;quot;devices&amp;amp;quot; : {
    &amp;amp;quot;id&amp;amp;quot;: &amp;amp;quot;dev123&amp;amp;quot;,
    &amp;amp;quot;configurations&amp;amp;quot;: {
      &amp;amp;quot;id&amp;amp;quot;: &amp;amp;quot;con123&amp;amp;quot;
  }
}

Feedback is welcome. Does anyone have a different approach that they prefer?

Vote Up0Vote Down  Reply
1 month ago
Admin

POST creates. PUT updates. If PUT finds no resource, it fails. No side effects and no surprises.

Practically, I have also followed same approach in all my designs.

Vote Up0Vote Down  Reply
1 month ago
baabaa

Should

HTTP PUT /devices/{id}/configurations

be

HTTP PUT /devices/{id}/configurations/{id}

?

Vote Up0Vote Down  Reply
1 year ago
Admin

Not really. Until I apply configuration to device, how configuration id will be created?

Vote Up0Vote Down  Reply
1 year ago
Peprikus

It’s a PUT – you already know the id. You want update a specific configuration…

Vote Up0Vote Down  Reply
1 year ago
Uddeshya

PUT request, is only used if we try to update something. Here you said yourself that you want to add/apply a configuration to a specific device. So shouldn’t the URI be like this: PUT /devices/{id}
and we should provide that Configuration id/information(already existed in the primary collection of configurations) in the request body.
OR
If the configuration does not already exist in the primary collection of configurations, the configuration should be created first and then added/applied to the device like this:
POST /configurations——Response body contains configuration id
PUT /devices/{id}———-Request body contains configuration id

I think this is the better way for designing these type of REST resources.

Vote Up0Vote Down  Reply
11 months ago
ergusto

| Should
|
| HTTP PUT /devices/{id}/configurations
| be
|
| HTTP PUT /devices/{id}/configurations/{id}
| ?

Yes, it absolutely should. PUT method is used to create or overwrite a resource *at a particular URI*, and crucially that means *that it is already known by the client*. HTTP POST /devices/{id}/configurations would be perfectly valid. However a PUT to that URI implies you are overwriting the resource *at that specific URI*, and not a child resource. Not sure why `Admin` is purporting to be an expert on this subject and authoring a website about REST when they clearly aren’t familiar with even the most rudimentary characteristics of REST.

Vote Up0Vote Down  Reply
9 months ago
vinothini

Thank you, It’s Very useful

Vote Up0Vote Down  Reply
1 year ago

Learn REST

  • What is REST?
  • REST Constraints
  • REST Resource Naming Guide

Guides

  • Caching
  • Compression
  • Content Negotiation
  • HATEOAS
  • Idempotence
  • Security Essentials
  • Versioning
  • Statelessness

Tech – How To

  • REST API Design Tutorial
  • Create REST APIs with JAX-RS 2.0

FAQs

  • PUT vs POST
  • N+1 Problem
  • ‘q’ Parameter

Resources

  • Comparing SOAP vs REST APIs
  • HTTP Methods
  • Richardson Maturity Model
  • HTTP Response Codes
    • 200 (OK)
    • 201 (Created)
    • 202 (Accepted)
    • 204 (No Content)

References

  • The dissertation by Roy Thomas Fielding
  • Uniform Resource Identifier (URI, URL, URN) [RFC 3986]
  • Internet MediaTypes
  • Web Application Description Language (WADL)

Meta Links

  • About
  • Contact Us
  • Privacy Policy

Blogs

  • How To Do In Java
Creative Commons License
This work by RESTfulAPI.net is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
wpDiscuz