Skip to main content

API Strategy

  • API Design, Service Platform, API Economy

API Design​

  • First step, needs to have a stable good maintainable API

Service Platform​

  • Once design is covered, that's when it becomes a platform and can be used throughout the organization

API Economy​

Is when an economic opportunity becomes available, allowing the business to offer services in exchange to data access or price fee !ss_api_strategy.png

API Design (Principles/Foundations of REST)​

  • From Roy Thomas Fielding, who coined the HTTP design !ss_principles_rest.png

Levels of API​

  • Level 0: Swamp of POX (Single URI, Sinhle HTTP method, potentially using SOAP)
    • Example:
      • /getAllEmployees
      • /getEmployeeWithId?id=1
  • Level 1: Resources - different URIs, doesn't take command of HTTP verbs
  • Level 2: HTTP verbs - good URIs + http verbs: GET, POST, DELETE, PUT
  • Level 3: Hypermedia - HATEOAS, Content Negotiation.

Content Negotiation - allows you to specify for example what the content-type you want, ie: xml vs json and the api can handle such request. Another example is GraphQL

HTTP verbs:​

Common Verb usages:

UsageVerbDescriptionIdempotencySafe
ListingGETQuery a resource or collectionYesYes
CreatingPOSTInsert a single resource or perform a controller operationNoNo
ReplacingPUTreplace an entire resourceYesNo
UpdatingPATCHUpdate 1 or many fields of a resourceNoNo
DeletingDELETEDelete a single resourceYesNo

Safe methods are HTTP methods that do not modify the resource

Starting Point on Designing APIs​

  • Have a matrix on Who + What + How (inputs + Outputs) = Goals
WhoWhatHowInputOutputGoals
CustomerBuy ProductSearch ProductCatalog, Free QueryProductSearch Product in Catalog using free query
Add Product to CartProduct, CartAdding Product to Cart
AdminManage CatagogAdd product to CatalogCatalog, ProductAdd product to Catalog

Error Codes

  • Generally using HTTP standard codes is good practice
  • Good format:
    "error": {
"id": UUID,
"code": 401,
"message": "Request had invalid credentials.",
"status": "UNAUTHENTICATED",
"details": \[{
"@type": "type.googleapis.com/google.rpc.RetryInfo",
...
}\]
}
}
  • Generally should contain:
    • ID - for tracing purposes
    • Code HTTP Code
    • Status
    • Title
    • Link - to documentation for troubleshooting
    • Details

URI Design

  • Use uuid as path params rather than id
  • Use URL encode
  • Query params are using used to extend the api for example pagination, searching, filtering

Cheat sheet:

DomainExample
Collection/products
Pagination?start=10&row=10
Document/products/8d8d123a-ba34-4c42-8f35-3c0169f3972f
Search/Filtering/products?q=description:consigned or /search?q=consigned
Relations/products/8d8d123a-ba34-4c42-8f35-3c0169f3972f/reviews
Partial Content/products?q=description:analgesic&field=id,name
HTTP verbsGET PUT PATH POST DELETE
ContentJSON XML
Versioning/v2/products

Authentication

  • Basic Auth - User and Password
  • OAuth 2.0 - Access Token
  • Mutual Authentication - Assymetric certificates. 2 way SSL
  • Open ID - Federated access Token (Facebook, Google, Social media tokens)

Typical Flow OAuth 2.0 flow​

!ss_oauth2_flow.png

Tokens​

  • Opaque vs Transparent | Opaque | Transparent| | --- | --- | | Stateful | Stateless| | Meaningless content, it needs an Auth Server to validate if its valid | Meaningful content, Resource can decrypt it to get value| | Can check validate/introspect if revoked etc | No central service to validate/invalidate. Self validation | | Example Technology: APIGEE | JWT |

Typical JWT Flow​

!ss_stateless_token.png

Should API have a different version?​

  • If you dont control every client using the API
  • Change in the contract or removing critical portion of the API
  • Consider extending existing API, add new endpoints, add query params.
  • Remember to retire old APIs that are no longer used

API versioning strategies (knot, point to point, and compatible)

!ss_api_versioning_strategy.png

Pagination

Page-Based :​

limit=20&page=1

  • Pros
    • easy to implement, SQL backed
    • Stateless
    • works regardless of sort_by
  • Cons:
    • Not performant on offset values, as Database still queries the first set and throws it away, 100000 would be really slow

Example Query: ORDER BY column_list [ASC |DESC] OFFSET offset_row_count {ROW | ROWS} FETCH {FIRST | NEXT} fetch_row_count {ROW | ROWS} ONLY

Keyset-Based:​

  • limit=20&since_id=20
  • The sort_by needs to be consistent, i.e i can query where id >20 so it will always be consistent
  • Pros
    • works with existing filters only need additional URL param
    • New records inserted on previous pages will update accordingly
    • Consistent performance
  • Cons
    • Tight Coupling on paging mechanism on Sorting
    • Does not work with string fields or enums (low cardinality fields: boolean, string as how do you sort by string)
    • Complicated for API users when using custom sort_by

Example Query:

SELECT * FROM t AS OF SYSTEM TIME ${time}
WHERE key > ${value}
ORDER BY key
LIMIT ${amount}

Cursor-Based​

Response contains the following: if we had requested: ?limit=20 the response would include:

{
results:[],
users:21
}
  • in the intial query, we return the last value of the original response as the next_cursor:
SELECT * FROM users
ORDER BY id DESC
LIMIT %limit+1
  • we do not include the data from limit+1 in the response but only to keep track of the next cursor, on the subsequent call:
SELECT * FROM users
AND id <= %cursor
ORDER BY id DESC
LIMIT %limit
  • Another way to implement cursor is have a hash of the next cursor as an identifier.

For example you had a list of names: β•‘ Bagshot β•‘ Bathilda β•‘
β•‘ Black β•‘ Sirius β•‘
β•‘ Brown β•‘ Lavender β•‘
β•‘ Chang β•‘ Cho β•‘
β•‘ Creevey β•‘ Colin β•‘
β•‘ Crouch β•‘ Bartemius β•‘
β•‘ Delacour β•‘ Fleur β•‘
β•‘ Diggle β•‘ Dedalus β•‘
β•‘ Diggory β•‘ Cedric β•‘
β•‘ Dumbledore β•‘ Aberforth β•‘
β•‘ Dumbledore β•‘ Albus β•‘
β•‘ Dursley β•‘ Dudley β•‘
β•‘ Dursley β•‘ Petunia β•‘
β•‘ Dursley β•‘ Vernon β•‘
β•‘ Filch β•‘ Argus β•‘
β•‘ Finnigan β•‘ Seamus β•‘
β•‘ Fletcher β•‘ Mundungus

you can have the cursor as:

    β€œcursor”: {
β€œprevious_page”: null,
β€œnext_page”: "next___Creevey"
}
}{
β€œcursor”: {
β€œprevious_page”: "prev___Crouch" ,
β€œnext_page”: "next___Dumbledore"
}
}

your query then says get all the names next to Creevey. gives you more flexibility with the sorts rather than the keyset pagination.

  • Pros

    • More flexibility on filter logic
    • Consistent ordering
    • Consistent performance
  • Cons

    • Potentially more complex to implement

H-Factor

  • Things to consider on Level 3 APIs (HATEOAS)
    • LE - Embed Links
    • LO - Outbound Links
    • LT - Templated Links
    • LI - Idempotent Links
    • LN - Non-idempotent links
    • CR - Read Controls
    • CU - Update Controls
    • CM - Method COntrols
    • CL - Link annotations

API Gateway patterns

!ss_api_gateway.png

API Different Aspects

  • Concepts
  • Readability
  • Error Handling
  • URI Design
  • Authentication
  • Versioning
  • Data Handling

API Scaling