2.3 HTTP Verbs

REQUIRED

Access to REST APIs must be via the standard HTTP verbs: GET, PUT, POST, DELETE in line with the W3C standard, namely:

  • GET
  • POST
  • PUT
  • DELETE
  • OPTIONS 

2.3.1 GET

GET is used:

  • For retrieval of information
  • If the interaction is more like a question (i.e. it is a safe operation such as a query)

The HTTP GET method is used to read (or retrieve) a representation of a resource. It can be used to retrieve information about an individual resource (e.g. customers/123456) or to retrieve a collection (list) of resources (e.g. customers).

In the “happy” (non-error) path, GET returns a representation in JSON and an HTTP response code of 200 (OK). In an error case, it most often returns a 404 (NOT FOUND) or 400 (BAD REQUEST) if the requested resource does not exist.

According to the HTTP specification, GET (along with HEAD) requests are used only to read data and not change it. Therefore, they are considered safe when used this way. That is, they can be called without risk of data modification or corruption - calling it once has the same effect as calling it 10 times. GET is idempotent, which means that making multiple identical requests ends up having the same result as a single request. Do not expose unsafe operations via GET - it should never modify any resources on the server.

Examples:

GET https://api.example.govt.nz/passengers/12345

Accept: application/json,version=1.*

Accept-Encoding: gzip,deflate

Host: api.example.com

 

#Success Response

Content-Length: 612

Location: https://api.example.govt.nz/passengers/12345

Date: Fri Jul 08 16:52:21 NZST 2016

CorrelationID: 65347f577ade5f7750234abb

Transaction-Id: 65347f577bde5949ac7bd448

{

  "id": 12345

  "names": {

    "firstName": "John",

    "middleName": "Howard",

    "lastName": "Doe",

    "salutation": "Mr John Doe",

    "title": "Mr"

  },

  "addresses": [

    {

      "type": "home",

      "address1": "1 Some Street",

      "address2": "Some Suburb",

      "address3": "Some City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 1111

    },

    {

      "type": "business",

      "address1": "2 Work Street",

      "address2": "Work Suburb",

      "address3": "Work City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 2222

    }

  ]

}

 

GET https://api.example.govt.nz/passengers/12345/flights

GET https://api.example.govt.nz/schools/98765/learners

Some API GET calls will return a single resource item whilst some calls will return a collection of resource items. See section 2.6.6 Singletons vs. Collections for more details

2.3.2 POST

POST is used:

  • To create a piece of information or resource item
  • To spawn an action 
  • If the interaction is like placing an order
  • If the interaction changes the state of the resource in a way that the user would perceive (e.g. a subscription to a service)
  • If the user be held accountable for the results of the interaction

The POST verb is most-often utilized to create new resources. In particular, it is used to create sub-resources: that is, subordinate to some other (e.g. parent) resource. In other words, when creating a new resource, POST to the parent and the API takes care of associating the new resource with the parent, assigning an ID (new resource URI), etc. On successful creation, POST returns an HTTP status 201, with a Location header containing a link to the newly created resource.

POST is neither safe nor idempotent. It is therefore recommended for non-idempotent resource requests. Making two identical POST requests will most-likely result in two resources containing the same information.

The body content of a POST request will differ to that of a PUT, as the provider will create certain values such as a unique identifier for the resource and its creation date.

In many cases a POST may have a small subset of the data that can exist on a resource – a subsequent PUT to the resource can be performed to populate the data.

Examples:

#Create a new passenger resource

POST https://api.example.govt.nz/passengers

{

  "names": {

    "firstName": "John",

    "middleName": "Howard",

    "lastName": "Doe",

    "salutation": "Mr John Doe",

    "title": "Mr"

  },

  "addresses": [

    {

      "type": "home",

      "address1": "1 Some Street",

      "address2": "Some Suburb",

      "address3": "Some City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 1111

    },

    {

      "type": "business",

      "address1": "2 Work Street",

      "address2": "Work Suburb",

      "address3": "Work City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 2222

    }

  ]

}

 

#Successful resource creation response

HTTP/1.1 201 Created

Location: https://api.example.govt.nz/passengers/12345

Content-Location: https://api.example.govt.nz/passengers/12345

Content-Type: application/json

{

  "id": 12345,

  "names": {

    "firstName": "John",

    "middleName": "Howard",

    "lastName": "Doe",

    "salutation": "Mr John Doe",

    "title": "Mr"

  },

  "addresses": [

    {

      "type": "home",

      "address1": "1 Some Street",

      "address2": "Some Suburb",

      "address3": "Some City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 1111

    },

    {

      "type": "business",

      "address1": "2 Work Street",

      "address2": "Work Suburb",

      "address3": "Work City",

      "address4": null,

      "country": "New Zealand",

      "postcode": 2222

    }

  ]

}

2.3.3 PUT

PUT is used:

  • To update an existing resource item
  • To replace an existing resource item

PUT is most-often utilized to update information, PUT-ing to a known resource URI with the request body containing the newly updated representation of the original resource.

However, PUT can also be used to create a resource in the case where the resource ID is chosen by the consumer instead of by the API itself: where PUT is to a URI that contains the value of a non-existent resource ID. Again, the request body contains a resource representation. This method of creation should be avoided if possible, or used sparingly. If consumer-defined resource IDs are required, use POST to create the new resource and provide the consumer-defined ID in the body representation (see POST).

On a successful update, PUT returns 200 (or 204 if not returning any content in the body). If using PUT to create, return HTTP status 201 on successful creation. A body in the response is optional. It is not necessary to return a link via a Location header in the creation case since the consumer already knows the resource ID, having set it themselves.

PUT is not a safe operation: it modifies (or creates) state on the server, but it is idempotent. I.e. if you create or update a resource using PUT and then make that same call again, the resource is still there and still has the same state as it did with the first call. However, were the PUT call to increment a counter within the resource, the call is no longer idempotent. Sometimes this is necessary behaviour, and it may be enough to document that the call is not idempotent. However, it's recommended to keep PUT requests idempotent. It is strongly recommended to use POST for non-idempotent requests.

It is good practice to perform a GET on a resource before you perform a PUT. This means that the consuming application has the latest representation of that resource. PUT should contain the entire resource content in the message body. By performing a GET prior to the PUT the consuming application can re-send the results of the GET with any modifications.

When using a resource response cache, any PUT to a resource should invalidate that cache or re-populate the cache with the updated resource.

Examples:

# Get a resource

GET https://api.example.govt.nz/passengers/12345/1w3r4y6u

# The response can be cached in a response cache with a cache key of the resource e.g. cachekey__12345_1w3r4y6u

# Response Body

{

  "id": "1w3r4y6u",

  "passenger_name": "SMITH/NICOLAS",

  "pnr_number": "CG4X7U",

  "travel_class": "business",

  "seat": "74J",

  "auxiliary_fields": [

    {

      "label": "Terminal",

      "value": "T1"

    },

    {

      "label": "Departure",

      "value": "30OCT 19:05"

    }

  ],

  "secondary_fields": [

    {

      "label": "Boarding",

      "value": "18:30"

    },

    {

      "label": "Gate",

      "value": "D57"

    },

    {

      "label": "Seat",

      "value": "74J"

    },

    {

      "label": "Sec.Nr.",

      "value": "003"

    }

  ],

  "flight_info": {

    "flight_number": "KL0642",

    "departure_airport": {

      "airport_code": "JFK",

      "city": "New York",

      "terminal": "T1",

      "gate": "D57"

    },

    "arrival_airport": {

      "airport_code": "AMS",

      "city": "Amsterdam"

    },

    "flight_schedule": {

      "departure_time": "2016-01-02T19:05",

      "arrival_time": "2016-01-05T17:30"

    }

  }

}

 

# Put to a resource (update) changing the seat number

PUT https://api.example.govt.nz/passengers/12345/flights/1w3r4y6u

#The api invalidates cache object with key cachekey__12345_1w3r4y6u

{

  "id": "1w3r4y6u",

  "passenger_name": "SMITH/NICOLAS",

  "pnr_number": "CG4X7U",

  "travel_class": "business",

  "seat": "10J",

  "auxiliary_fields": [

    {

      "label": "Terminal",

      "value": "T2"

    },

    {

      "label": "Departure",

      "value": "30OCT 19:05"

    }

  ],

  "secondary_fields": [

    {

      "label": "Boarding",

      "value": "18:30"

    },

    {

      "label": "Gate",

      "value": "D57"

    },

    {

      "label": "Seat",

      "value": "10J"

    },

    {

      "label": "Sec.Nr.",

      "value": "003"

    }

  ],

  "flight_info": {

    "flight_number": "KL0642",

    "departure_airport": {

      "airport_code": "JFK",

      "city": "New York",

      "terminal": "T1",

      "gate": "D57"

    },

    "arrival_airport": {

      "airport_code": "AMS",

      "city": "Amsterdam"

    },

    "flight_schedule": {

      "departure_time": "2016-01-02T19:05",

      "arrival_time": "2016-01-05T17:30"

    }

  }

}

# Response that update was successful

HTTP/1.1 204 No Content

Content-Location: /passengers/12345/flights/1w3r4y6u

2.3.4 DELETE

DELETE is used:

  • To delete a resource item

DELETE is only used to delete a resource identified by a URI.

On successful deletion, DELETE returns an HTTP status 200 (OK) along with a response body, and optionally the representation of the deleted item, or a wrapped response. Alternatively, DELETE can return HTTP status 204 (NO CONTENT) with no response body. 

DELETE operations are idempotent. If you DELETE a resource, it is removed. Repeatedly calling DELETE on that resource ends up as the same result: the resource is gone. However, calling DELETE on a resource a second time will often return a 404 (NOT FOUND) since it was already removed and therefore is no longer findable. This, in some opinion, makes DELETE operations no longer idempotent, but the end-state of the resource is the same. Returning a 404 is acceptable and communicates accurately the status of the call. If calling DELETE were to decrement a counter (within the resource), the DELETE call is no longer idempotent. 

Example:

#Delete a resource

DELETE https://api.example.govt.nz/passengers/12345/flights/1w3r4y6u

 

#Response

HTTP/1.1 204 No Content

Content-Location: /passengers/12345/flights/1w3r4y6u

2.3.5 OPTIONS

OPTIONS is used to retrieve information about what the consumer is allowed to do with the Resource

2.3.6 Other

PATCH is a valid HTTP verb, but is still in draft and its use is discouraged due to complexity.

HEAD is also rarely used, but should retrieve metadata about a Resource, such as a hash of the data or when it was last updated.

Page last updated: 18/12/2016