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
- Example:
- 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:
Usage | Verb | Description | Idempotency | Safe |
---|---|---|---|---|
Listing | GET | Query a resource or collection | Yes | Yes |
Creating | POST | Insert a single resource or perform a controller operation | No | No |
Replacing | PUT | replace an entire resource | Yes | No |
Updating | PATCH | Update 1 or many fields of a resource | No | No |
Deleting | DELETE | Delete a single resource | Yes | No |
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
Who | What | How | Input | Output | Goals |
---|---|---|---|---|---|
Customer | Buy Product | Search Product | Catalog, Free Query | Product | Search Product in Catalog using free query |
Add Product to Cart | Product, Cart | Adding Product to Cart | |||
Admin | Manage Catagog | Add product to Catalog | Catalog, Product | Add 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:
Domain | Example |
---|---|
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 verbs | GET PUT PATH POST DELETE |
Content | JSON 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