Best practices to design REST GET method on Anypoint Platform

Last Updated on 30/05/2021 by Patryk Bandurski

This article is an intro to a blog series about HTTP methods usage in RESTful services. As a GET is the most common, I will start with it. This method is used for retrieving the collection of resources or a single resource. In other words, GET can be used for searching purposes. However, the more advanced search criteria I will describe in the next article.

To design a service I will use just RAML specification. You can easily convert it to OpenAPI using Anypoint Exchange or any other tool.

Use Case

Let’s imagine that we are responsible for a design of a completely new service. This service should allow clients to manage accounts of a domain system. All data are stored within the NoSQL database – this is just implementation details that do not change our design. By manage I think about listing all available accounts, finding a particular one by id, or searching by a given constraint. As you may expect the primary resource would be an account. On the right-hand side, you can see the data model.

Service Design

Get the list of resources

One of the most obvious scenario would be to get all possible resources. For given resource we set url with pluralized noun

/accounts

Of course, they are some exceptions to this rule. When we have a resource that we know that only one entry exists we may use a singular noun. It depends on the project’s domain perspective. Let’s say I would like to publish configuration resources. This resource stores global config. In other words, only one exists. So I would set the following URL:

/configuration

On the other hand when we have compound nouns like LDAP account, DB account, or Email account we should not make compound resource name like /ldapaccounts. It is easier to read and maintain a situation where we split nouns by slash in URL like the following examples:

/accounts/ldap
/accounts/db
/accounts/emails

When we hit url like /accounts we may encounter two different situations. First is an empty response like below [ ].

and the second one is an array of resources:

[{
 "login": "adoe",
 "name": "adrian",
 "surname": "Doe",
 "role": "ADMIN",
 "rank": 20
 }]

In both cases http status 200 should be returned.

Get single resource by id

The previous operation allowed us to get all available resources. How about getting a particular one? For each record unique id should be selected. By this id, we will search the list. We need to append id like bellow

/resource/{id}
/accounts/adoe
/applications/872233

First example mean that we would like to get account which id is equal to adoe and the second one application identified by 872233.

Advice on encoding characters

In the given example id contains characters but it can contain digits and special characters like spaces. When special characters occur we need to encode given URI. Here are a couple of examples.

/accounts/12345  ⇒ /accounts/12345
/accounts/123 45 ⇒ /accounts/123%2045
/accounts/mail@domain.pl ⇒ /accounts/mail%40domain.pl


In the first example, nothing was changed. However, in the second and third some changes occurred. Space was replaced with %20 and @ character with %40.

When we try to get such resource we should receive its content with http status 200 like below:

{
 "login": "adoe",
 "name": "adrian",
 "surname": "Doe",
 "role": "ADMIN",
 "rank": 20
 }

A key difference here is that we received a single object within curly bracket. No like in the previous call, array of objects.

When the id is not known we should receive an empty body with http status 404.

Get resource(s) by field

There are some situations where we do not know the unique identifier, however, we do know other attributes. We can solve this problem in two ways by either query or uri parameters.

Searching by query parameters

We may search by any field we would like to, although there are some limitations. We need to decide by which fields our resource should be queried. For an account, I have decided to query by surname, role, and rank fields. So how does do URL look like?

/accounts?surname=adoe&role=ADMIN

So let’s break this structure down. This URI contains:

  • resource name like accounts
  • question mark that separate query parameters with name of the resource
  • query parameter
    • name of the parameter like surname
    • equals character =
    • value to compare

Query parameter should appear at least once after the question mark. In the given example we have two query parameters surname and role.

Searching by uri parameters

URI parameters are hidden within the address. Following address /accounts/2017/admin may return accounts created in 2017 that belonged to administrators. However, the search parameters are not optional in comparison to query parameters and must follow in that exact order.

Headers

The majority of RESTful services will return JSON content. However, the JSON content is not the only representation that the REST service can return. It may be as well XML or any other. What is more, service can accept more the one representation. In other words, it can return JSON and XML content for example.

How do we inform service that we would like specific representation? We do this by specifying Accept header.

Accept: application/json
Accept: application/xml

When you do not specify this header you will receive default service representation.

Design

As we have some basic ideas about the GET method, we are ready to design simple services using RAML specifications. I will design a simple service using the Anypoint Platform.

We need to perform following actions:

  • create a reusable definition of our entity – account
  • create get /accounts operation
  • add optional query parameters to get /accounts operation
  • create get /accounts by login operation

Anypoint Platform

If you do not have an account you can create one, free of charge, under the following link. After that we login in and clicking the Design button under the Design Center header.

Design Centers' API specifications
Design Centers’ API specifications

In the following screen, we add a new API specification in my case it is Accounts SAPI. We should be welcomed by the editor where we will design our service. The screen is dived into three sections:

  1. File’s explorer
  2. Design canvas in the middle
  3. API console – displaying in a user friendly manner our API
Main screen during API specification definition in Design Center
Main screen during API specification definition in Design Center

Resource definition

As a default, we got information that we are going to design using RAML specification in version 1.0. First, we need to specify our reusable type. Here is the code that should be added:

#%RAML 1.0
title: Accounts SAPI

types:
  account:
    type: object
    properties:
      login:
      name:
      surname:
      role:
      email?:
      rank:
        type: integer
        default: 0

We defined type under types keyword by name (line 4). Next, we specified what kind of type we are defining by setting property type. In line 6 we set it to object. This means that our account will contain properties. Since it is an object we define properties in properties property. From lines 8 to 12 we do not specify anything except property name. This is a shortcut for defining string property. We could define each property in the following manner:

properties:
  login:
    type: string

Line 12 depict how to mark that property as optional.

Get the list of accounts

It is time to define the first operation which gets the list of accounts. But wait a moment. We defined just an object account not an array of objects. In RAML version 1.0 we are able to reuse object definition for creating an array. It will become clear in the next paragraph.

First we will define simple get /accounts just like blow:

/accounts:
  get:
     responses:
       200:
         body:
           application/json:
             type: array
             items:
               type: account
           application/xml:
             type: array
             items:
               type: account
       415:
         description: Unsupported Media Type

As you may see we start with our resource name and in the next line we define which verb is available under this resource. While GET does not have a request body we need to define response in responses property(line 19). I have defined two different HTTP status codes for response in lines 20 and 30. We will return either 200 which means everything went ok or 415 when the client will try to get our resource using unsupported media type. For each return status, we need to specify what its body will contain. Our service will return either JSON or XML response (lines 22 and 26). For each returned response we need to specify what it will contain. For both response formats, we return an array (line 23). Each item within the array is of type account (line 25).

So far we got the following definition and when we hit /accounts endpoint we should be able to recive an array of accounts. Here is an example of a possible outcome (using mocking service):

Get the list of accounts from mocking service enabled for API specification on Anypoint Platform
Get the list of accounts from mocking service enabled for API specification on Anypoint Platform

Simple accounts’ filter

It is time to add some basic filters.  We need to add queryParamters a section under our resource like in line 19. As with type definition, we need to define what parameters are available like name, type, and if it is mandatory or not.

/accounts:
  get:
    queryParameters:
      surname?:
      role?:
      rank?:
        type: integer
    responses:
      200:

Getting specific resource

The last operation but not least is getting an account by the login. We have basic get /accounts (defined in lines 17, 18). Next, we need to define a placeholder for login. In order to do it, we enclose a placeholder with curly brackets like in line 20. The reason why we used a placeholder is fact that this part of URI will change. After that line, we perform similar steps as with GET /accounts. We define possible body types like JSON or XML. Line 26 show that the service will return a single account by specifying the type to be account.

/accounts:
  get:
  ...
  /{login}:
   get:
     responses:
       200:
         body:
           application/json:
             type: account
           application/xml:
              type: account

Here is an example of the URI and possible response:

Get the account base on login from mocking service enabled for API specification on Anypoint Platform
Get the account base on login from mocking service enabled for API specification on Anypoint Platform

Summary

  • GET is used for retrieving resources
  • Resources are available under pluralized noun URI like /accounts
  • Filtering is possible by using either query or URI parameters
  • RAML specification is a concise form describing the service in the YAML language

Specifications

API specification is available as a Gist.

Best practices to design REST GET method on Anypoint Platform

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top