The document discusses best practices for designing RESTful APIs including using RESTful standards like HATEOAS, representing resources with plural nouns, using HTTP verbs for actions, and returning appropriate status codes. It also covers OData conventions for querying data through URLs with syntax like $filter, $select, $expand and $orderby. Webhooks are described for asynchronous notifications. Overall it provides guidance on RESTful API design and considerations for data retrieval.
5. • Pluralized nouns for entities
/users
• Actions using HTTP methods
GET /users
• Instances vs Collections
GET /users (returns a collection)
GET /users/1a (returns an instance)
6. • GET : Retrieve an instance or collection.
• POST : Creates an entity record.
• PUT : Updates a wholesome entity given its
ID.
• DELETE: Removes an entity given its ID
• PATCH: Partially updates an entity given its ID
• OPTIONS: Returns list of HTTP methods
available for an entity
10. 200 OK – ResponseOK. Should be used in GET (or PUT calls containing modified entity)
201 Created – Returned by a synchronous POST call creates an entity.
202 Accepted – Result of an long running operation by a POST call.
204 No Content – Result of a synchronous operation by a DELETE or PUT/PATCH
304 Not Modified – A cached response is returned
400 Bad Request – A malformed JSON request is sent
401 Unauthorized – API user is not authenticated. Bad credentials.
403 Forbidden – API user is not authorized. User roles don’t allow invoking an endpoint
409 Conflict – A race condition is found (validation of updated/modified timestamp
failed)
404 Not Found – Record for provided ID is not found, in case of GET, DELETE, PUT,
PATCH
408 RequestTimeout – Server couldn’t process the request in a specified time limit
414 URIToo Long – When the URL length limit of 2083 is hit
429Too Many Requests –Throttled API response, as a result of rate-limiting feature
500 Internal Server Error
501 Not Implemented – A requested method/operation is not implemented by service
503 Service Unavailable (with details if in debug mode) - In case of service based errors
11. • Repeated calls to the same resource must
recreate entities
• E.g. Repeatedly calling PUT /players/1a should
just update the record
• PATCH special case
12. • HypermediaAsThe Engine Of Application
State
• Sticky links in API responses
• Includes schema details
• Provides navigational elements (links) to
entities used in an API response
14. • Open Data Framework (odata.org)
• Standard, predictable method to query
• SQL-like syntax in HTTP URLs
• Works atop RESTful GET endpoints
• Reduces cognitive dissonance in developers
• Server and client side libraries exist (Olingo for
Java,ASP.NET in .NET, and others)
15. • $select - Filter the list of attributes from an instance (SELECT)
• $filter - Expressions to filter list of records from a collection
(WHERE)
• $top - Number of records to retrieve (FETCH orTOP or LIMIT)
• $offset - Skip to this record number (OFFSET)
• $expand - Expand body of an aggregation/reference entity
(similar to adding a JOIN)
• $orderby - Sort the collections based on given column
name(s) (ORDER BY)
• $count - Return only the count of records in case of a
collections call. (COUNT)
16. • $select - Filter the list of attributes from an instance (SELECT)]
GET /players?$select=name
{
"links":[
{
"href":"<baseURL>/players?$select=name",
"schema":"<baseURL>/schemas/$players",
"rel":"players | self"
},
{
"href":"<baseURL>/games/1g",
"schema":"<baseURL>/schemas/$games",
"rel":"games"
}
],
"data":[
{
"name":"A B"
}
]
}
17. • Filter the list of attributes from an instance (SELECT)]
query {
players {
name
}
}
{
"data":{
"players ” : [ {
"name":"A B"
}
]
}
18. • $filter - Expressions to filter list of records from a collection (WHERE)
GET /players?$filter=name eq ‘A B’&$select=id,name
{
"links":[
{
"href":"<baseURL>/players?$filter=name eq ‘A B’&$select=id,name ",
"schema":"<baseURL>/schemas/$players",
"rel":"players | self"
},
{
"href":"<baseURL>/games/1g",
"schema":"<baseURL>/schemas/$games",
"rel":"games"
}
],
"data":[
{
"id":"1a",
"name":"A B"
}
]
}
19. • Expressions to filter list of records from a collection (WHERE)
query {
players(filter:{ name : {eq : “A B”}}) {
id,
name
}
}
query {
player(id:”1a”) {
id,
name
}
}
{
"data":{
"players ” : [ {
"id":"1a",
"name":"A B"
}
]
}
{
"data":{
"player” : {
"id":"1a",
"name":"A B"
}
}
20. • $top - Number of records to retrieve (FETCH orTOP or LIMIT)
• $offset - Skip to this record number (OFFSET)
• GET /players?$top=1&$offset=2 (Skips two records and takes 1)
{
"links":[
{
"href":"<baseURL> /players?$top=1&$offset=2",
"schema":"<baseURL>/schemas/$players",
"rel":"players | self"
},
{
"href":"<baseURL>/games/1g",
"schema":"<baseURL>/schemas/$games",
"rel":"games"
}
],
"data":[
{
"id":"1a",
"name":"A B",
"country_code":"US",
"game_id":"1g"
}
]
}
21. • first - Number of records to retrieve (FETCH orTOP or LIMIT)
• offset - Skip to this record number (OFFSET)
query {
players(first:1 offset:2)
{
id,
name,
country_code,
game_id
}
}
{
"data": {
"players": [
{
"id":"1a",
"name":"A B",
"country_code":"US",
"game_id":"1g"
}
]
}
}
22. • $expand - Expand body of an aggregation/reference entity (similar to adding a JOIN)
• GET /players/1a?$expand=game
{
"links":[
{
"href":"<baseURL>/players/1a?$expand=game",
"schema":"<baseURL>/schemas/$players",
"rel":"players | self"
},
{
"href":"<baseURL>/games/1g",
"schema":"<baseURL>/schemas/$games",
"rel":"games"
}
],
"data":
{
"id":"1a",
"name":"A B",
"country_code":"US",
"game":{
"id":"1g",
"name":"Contra"
}
}
}
23. • Expand body of an aggregation/reference entity (similar to adding a JOIN)
query {
player(id:”1a”)
{
id,
name,
country_code,
game {
id,
name
}
}
}
{
"data": {
"player":
{
"id":"1a",
"name":"A B",
"country_code":"US",
"game":{
"id":"1g",
"name":"Contra"
}
}
}
}
24. • $orderby – sort an entity result using one or more fields
• GET /players?$orderby=name
{
"links":[
{
"href":"<baseURL>/players?$orderby=name ",
"schema":"<baseURL>/schemas/$players",
"rel":"players | self"
},
{
"href":"<baseURL>/games/1g",
"schema":"<baseURL>/schemas/$games",
"rel":"games"
}
],
"data":[
{
"id":"1a",
"name":"A B",
"country_code":"US",
"game_id":"1g"
}
}
]
}
26. • Consider this in C#:
players.Any(p => p.address.city ==
"Foster City" &&
p.validateTrophies()).ToList<Player>();
• To Lambda expression in ODATA
/players?$filter=players/any(p:p/address/
city eq 'Foster City' and
p.validateTrophies())
28. • Use RESTful standards
• Use ODATA for predictable retrieval
• Use appropriate status codes
• Make sure to account for
idempotency and concurrency
• Quick comparison of ODATA and
GraphQL