This article is an intro into 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 collection of resources or a single resource. In other words this could be used for searching purposes. However the more advanced search criteria I will describe in next article.

To design a service I will use two most commonly used specifications such as RAML and OpenAPI.

Tools

I am going to use following tools:

  • Anypoint Platform for service design
  • Swagger for service design using OpenAPI specification

Use Case

Let’s imagine that we are responsible for a design of a completely new service. This service should allow client to manage accounts of a domain system. All data are stored within NoSQL database – this is just an 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.

In our scenario account has only following parameters:

  • login of type string
  • name of type string
  • surname of type string
  • role of type string
  • email of type string but not mandatory
  • rank of type integer

Service Design

Get 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 singular noun. It depends from the project’s domain perspective. Let’s say I would like to publish configuration resource. This resource stores global config. In other words only one exists. So I would set 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 situation where we split nouns by slash in URL like the following examples:

/ldap/accounts
/db/accounts
/email/accounts

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": "aabacki",
 "name": "a",
 "surname": "abacki",
 "role": "ADMIN",
 "rank": 20
 }]

In both cases http status 200 should be returned.

Get single resource by id

Previous operation allowed 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 after slash like bellow

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

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

Encoding characters

In given example id contains characters but it can contains digits, and special characters like spaces. When special characters occurs 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 first example nothing was changed. However in 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": "aabacki",
 "name": "a",
 "surname": "abacki",
 "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 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 field. So how does do URL looks like?

/accounts?surname=Abacki&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 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 one representation that 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 idea about GET method, we are ready to design simple service using both RAML and Open API specification. I will design simple service using Anypoint Platform and Swagger.

We need to perform following actions:

  • create 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 following link. After that we login an and clicking Design button under Design Center header.

Design Center’s projects

In the following screen we add new API specification in my case it is accounts-service. We should be welcomed with editor where we will design our service. 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
Design Center’s main screen

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-service

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 is optional.

Get the list of accounts

It is time to define first operation which is get 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 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 request body we need to define response in responses property(line 19). I have defined two different http status codes for response in line 20 and 30. We will return either 200 which mean everything went ok or 415 when 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 (line 22 and 26). For each returned response we need to specify what it will contain. For both response’s format we return array (line 23). Each item within array is of type account (line 25).

So far we got following defition and when we hit /accounts endpoint we should be able recive an array of accounts. Here is an example of possible outcome:

Get the list of accounts

Simple accounts’ filter

It is time to add some basic filters.  We need to add queryParamters 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 account by login. We have basic get /accounts (defined in line 17, 18). Next we need to define a placeholder for login. In order to do it we enclose placeholder with curly brackets like in line 20. The reason why we used 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’s types like json or xml. Line 26 show that service will return single account by specifying 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:

 

Swagger

If you do not have an account you can create one, free of charge, under following link. After that we login and we should see list of all defined services.

Swagger’s MY hub

In order to create new API click create new API. In the popup screen we may fill just API name. Canvas is  divided into two sections:

  1. Design canvas
  2. User friendly API graphical representation

Resource definition

In first four lines we define basic information about our service like:

  • swagger version
  • version of our AP1
  • title of our API

First we need to specify our reusable type. Here is the code that should be added:

swagger: '2.0'
info:
  version: 1.0.0
  title: accounts-service

definitions:
  Account:
    type: object
    properties:
      login:
        type: string
      surname:
        type: string
      name:
        type: string
      role:
        type: string
      email:
        type: string
      rank:
        type: integer
        default: 0
    required:
      - login
      - surname
      - name
      - role
      - rank

All types are described within definitions property. We defined new account type in types (line 7). Next we specified that account is an object (line 8). This means that our account will contain properties. We define them after keyword properties in line 9. From lines 10 to 22 we define properties’ names and their types. After the keyword required (line 23) we mark which properties should be mandatory. required keyword holds an array of values. In YAML language array’s item is preceded with hyphen character.

JSON Schema

JSON Schema describes JSON types and validate them. As JSON is JavaScript Object Notation JSON Schema is defined using JavaScript language as well. Those of you who are familiar with JSON Schema should notice some similarity with OpenAPI specification. Definition from above is equivalent of the following schema file:


"definitions": {
  "account":  {
    "type": "object",
    "properties": {
       ...
    },
    "required": [...]
  }
}

Get the list of accounts

It is time to define first operation which is getting the list of accounts.

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

paths:
  /accounts:
    get:
      produces: 
        - application/json
        - application/xml
      responses:
        200:
          description: Accounts list
          schema:
            type: array
            items:
              $ref: '#/definitions/Account'

All paths we define under paths property. As you may see we start with our resource name and in the next line we define which verb is available under this resource. Our service will return either json or xml response (line 33, 34). This is defined in produces array. While get does not have request body we need to define response (line 35). We will return 200 which means everything went ok. For each returned response we need to specify what it will contain. For both response’s format we return type array (line 39). Each item within array is of type account (line 41). This notation is used in JSON Schema. $ref is used to made a reference to known element within schema. I will break this url down:

  • #/ means that we would like to search current file,
  • definitions made us to look at properties under definitions property,
  • and there finally Account.

So far we got definition and when we hit /accounts endpoint we should be able receive an array of accounts.

Simple accounts’ filter

It is time to add some basic filters.  In order to do in we need to add parameters (line 35). Parameters is an array of objects. Each item of this array contains:

  • in specifying if item is a query or URI parameter
  • name specifying name of the parameter
  • description
  • required specifying if parameter is mandatory or not
  • type like integer or string

Base on this description you should conclude that parameters are just query parameters of name surname, role and rank and all are optional.

 produces: 
   - application/json
   - application/xml
 parameters:
   - in: query
     name: surname
     required: false
     type: string
   - in: query
     name: role
     required: false
     type: string
   - in: query
     name: rank
     required: false
     type: integer
 responses:

Getting specific resource

The last operation but not least is getting account by login. We need to define a placeholder for login. In order to do it we enclose placeholder with curly brackets like in line 55. The reason why we used placeholder is fact that this part of URI will change. As URI parameter is used we need to define it within parameters array in line 60. Bare in mind that this parameters should be marked as mandatory (line 63). Line 69 depicts that service will return single account by specifying type to be account.

/accounts/{login}:
  get:
    produces: 
      - application/json
      - application/xml
    parameters:
      - in: path
        name: login
        required: true
        type: string
    responses:
      200:
        description: Account
        schema:
          $ref: '#/definitions/Account'

Summary

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

Specifications

Service’s specifications are available at github.

GET an HTTP method for retrieving resources
Tagged on:             

Leave a Reply

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