it doesnt mean napping :(
Ovia Innovation Day - 3/08/2023
Rest-ish with a mix of something called GraphQL
(we can save GraphQL for another time).
curl -H "Host: testapi.oviahealth.com" \
-H "accept: */*" \
-H "ovuline_mode: 3" \
-H "accept-language: en-US;q=1, es-US;q=0.9, en;q=0.8" \
-H "authorization: Bearer VEdQV29rYVN3ejFmanY3YVA1WTR5ZlNnYkFLWWlFZDJNT2VibG1GVA==" \
-H "user-agent: Pregnancy/20846 (iPhone; iOS 16.0; Scale/3.00)" \
-H "ovuline_user_type: 4" \
--compressed "https://testapi.oviahealth.com/v2/latest_value/1041,1042?&timezone=America/Chicago" | jq
The biggest issue with this is it isn't intuitive to know what 1041 and 1042 are requesting.
{
"type": 246949,
"value": "Videos series: Infant care",
"image": "https://s3.amazonaws.com/Ovuline/content_platform/34944/article_246949_InfantCare_600x600_5_Nails.jpg"
},
{
"type": 246950,
"value": "Videos series: Breastfeeding bootcamp",
"image": "https://s3.amazonaws.com/Ovuline/content_platform/34945/article_246950_BreastfeedingBootcamp_noplay_600x600_1_Benefits.jpg"
},
{
"type": 246951,
"value": "Videos series: Pregnancy by week",
"image": "https://www.oviahealth.com/images/article_images/article_20019_s_640_427.jpg"
},
{
"type": 246952,
"value": "Videos series: Moms at work",
"image": "https://s3.amazonaws.com/Ovuline/content_platform/27801/article_105868.jpg"
},
{
"type": 246977,
"value": "Video series: Bringing home baby",
"image": "https://s3.amazonaws.com/Ovuline/content_platform/35192/article_246977.jpg"
}
Matt Trask
@matthewtrask
Concepts
URIs
The Verbs
The Status Codes
The Headers
API Contracts and Tooling
But first... some background.
Matt Trask
@matthewtrask
Matt Trask
@matthewtrask
What do we mean by REST-ish?
Anything but full Hypermedia, which means no links giving directions to the next resource
Matt Trask
@matthewtrask
GET /characters
{
"action": "POST",
"data": {
"user": {
"id": 1,
"name": "Luke Skywalker",
"affilations": [
{
"Jedi",
"Rebels",
"Resistance"
}
]
}
}
}
Matt Trask
@matthewtrask
GET /star-wars-characters
{
"data": {
"user": {
{
"id": 1,
"name": "Luke Skywalker",
"affilations": [
{
"Jedi",
"Rebels",
"Resistance"
}
],
"roles": [
"star pilot",
"death star killer"
],
"weapon": "Lightsaber",
"friends": [
"Obi-Wan",
"Biggs Darklighter",
"Princess Leia",
"Chewie",
"Han Solo"
]
},
{
"id": 2,
"name": "Anakin Skywalker",
"affilations": [
{
"Jedi",
"Sith",
"Empire",
"Galatic Republic"
}
],
"roles": [
"general",
"star pilot",
"pod racing",
"youngling slayer"
],
"weapon": "Lightsaber",
"friends": [
"Obi-Wan",
"Palpatine",
"Ahsoka Tano",
"R2-D2",
"Captain Rex"
]
}
}
}
}
Matt Trask
@matthewtrask
GET /characters
200 OK
{
"error": "There were no characters to be found"
}
We want to avoid this.
Matt Trask
@matthewtrask
Representation State Transfer (REST)
Matt Trask
@matthewtrask
6 Architectural Constraints
1. Client Server Architecture
2. Statelessness
3. Cacheability
4. Layered System
5. Code On Demand (optional)
6. Uniform Interface
Theorized in a doctoral thesis by Roy Fielding in 2000.
Matt Trask
@matthewtrask
1. Client Server Architecture
Client and Server are independent of each other.
Allows for independent
client (iOS, Android, Web) implementations.
Matt Trask
@matthewtrask
2. Statelessness
Server maintains intrinsic (resource) state
Client maintains extrinsic state, and is responsible for passing state to server (filters, limits, etc)
Matt Trask
@matthewtrask
3. Cacheability
Clients and intermediaries can cache responses.
Responses must, implicitly or explicitly, define themselves as cacheable or non-cacheable.
Matt Trask
@matthewtrask
4. Layered System
Client can not discern whether it has connected to the server directly, or through an intermediary like a proxy or load balancer
Matt Trask
@matthewtrask
5. Code On Demand
Ability to send code on demand, such as some Javascript, to the client to be executed.
*Optional
Matt Trask
@matthewtrask
6. Uniform Interface
1.Resource Identification in requests
2. Resource Manipulation Through Representations
3. Self-descriptive messages
4. Hypermedia as the engine of application state
*Fundamental
Matt Trask
@matthewtrask
Matt Trask
@matthewtrask
The Entry Point to your API
Best Practices
Resource Based instead Action Based
Pluralized
Hyphened instead of underscored
URIs should describe relationships
Matt Trask
@matthewtrask
What does resource based mean?
Resource based means that by looking at the URI, I can reasonably determine what resource I am about to consume/manipulate/remove
Also allows you to manage relationships in the resource collection
Matt Trask
@matthewtrask
GOOD https://api.starwars.com/characters
BAD https://api.starwars.com/get_characters
GOOD https://api.starwars.com/characters/ac5b605a-661e-4083-857f-d65e30e3467a
tip: you dont need to use the full uuid
BAD https://api.starwars.com/characters/1
Matt Trask
@matthewtrask
GET https://api.fancypantsbank.com/accounts
GET https://api.fancypantsbank.com/accounts/checking-account
POST https://api.fancypantsbank.com/accounts/checking-account
PUT https://api.fancypantsbank.com/accounts/checking-account
DELETE https://api.fancypantsbank.com/accounts/checking-account
Matt Trask
@matthewtrask
Get
Post
Put
Patch
Delete
Options
Matt Trask
@matthewtrask
requests a representation of the specified resource
No Request Body
Response will have body
Safe
Idempotent
Cacheable
Matt Trask
@matthewtrask
GET /api/users
200 OK
Content-type: application/json
Etag: 0bba161a7165a211c7435c950ee78438
Cache-control: max-age=3600 // 1 hour
{
"data": [
{
"id": 1,
"name": "Luke Skywalker",
"links": {
"rel": "self",
"href": "/api/users/1",
"metbod": "GET"
}
},
{
"id": 2,
"name": "Anakin Skywalker",
"links": {
"rel": "self",
"href": "/api/users/2",
"method": "GET"
}
}
]
}
Matt Trask
@matthewtrask
send data to the server to create a resource
Request has body
Response may have body
Not Safe
Not Idempotent
Cacheable If Freshness Headers is provided
Matt Trask
@matthewtrask
POST /api/users
{
"name": "Wedge Antilles",
"species": "Corellian",
"age": 26,
"affiliations": [
"rebels",
"resistance"
],
"callsign": "Red 2"
"ship": "X-Wing"
}
201 Created
Location: /api/users/3
Content-Location: /api/users/3
Last-Modified: Wed, 24 Mar 2021 12:00:00 GMT
Matt Trask
@matthewtrask
update or create data on the server
Request will have a body
Successful response will not have body
Not Safe
Idempotent
Not cacheable
Status Codes: 201, 204
Matt Trask
@matthewtrask
PUT /api/users/3
{
"name": "Wedge Antilles",
"species": "Corellian",
"age": 30,
"affiliations": [
"rebels",
"resistance"
],
"callsign": "Red Leader"
"ship": "X-Wing"
}
200 OK
Location: /api/users/3
Content-Location: /api/users/3
Last-Modified: Mon, 15 Nov 2017 12:00:00 GMT
Matt Trask
@matthewtrask
partial update of a resource on a server
Request will have a body
Response may have body
Not Safe
Not Idempotent
Cacheable If Freshness Data is provided
May be restricted via headers
Status Codes: 200, 204
Matt Trask
@matthewtrask
PATCH /api/users/3
{
"callsign": "Red Leader"
}
204 No Content
Location: /api/users/3
Content-Location: /api/users/3
Last-Modified: Wed, 24 Mar 2021 12:01:00 GMT
Matt Trask
@matthewtrask
complete removal of a resource on a server
Request may have body
Response may have body
Not Safe
Not Idempotent
Not Cacheable
Status Codes: 200, 202, 204
Matt Trask
@matthewtrask
DELETE /api/users/3
{
"name": "Wedge Antilles",
"species": "Corellian",
"age": 30,
"affiliations": [
"rebels",
"resistance"
],
"callsign": "Red Leader"
"ship": "X-Wing"
}
204 No Content
200 OK
{
"message": "Resource deleted",
}
Matt Trask
@matthewtrask
requests permitted communication options for a given URL or server
Request will not have body
Response will have body
Safe
Idempotent
Not cacheable
Status Codes: 204
Matt Trask
@matthewtrask
Non-CORS
OPTIOMS /api/users
HTTP/1.1 204 No Content
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Wed, 24 Mar 2021 11:45:00 GMT
Server: EOS (lax004/2813)
CORS
OPTIONS https://starwars.com/api/users/ HTTP/1.1
Host: bar.example
Accept: text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://starwars.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Matt Trask
@matthewtrask
Numbers and Phrases to give quick context for a request
// brew install httpie
http https://www.matthewtraskphotography.com
HTTP/1.1 200 OK
Matt Trask
@matthewtrask
100s - Informational
Matt Trask
@matthewtrask
200s - Success
Matt Trask
@matthewtrask
300s - Redirection
Matt Trask
@matthewtrask
400s - Client Errors
Matt Trask
@matthewtrask
500s - Server Error
Matt Trask
@matthewtrask
They aren't Pokemon, so you dont need to catch them all.
Matt Trask
@matthewtrask
Information sent with the request and response
4 Types of Headers
General Headers
Request Headers
Response Headers
Entity Headers
Matt Trask
@matthewtrask
Accept | Content-type
# Request
Accept: application/json
Accept: application/vnd.github+json
Accept: application/xml
Accept: text/plain
Accept: text/csv
# Response
Content-type: application/json
Content-type: text/html
Content-type: text/csv
Content-type: application/vnd.github+json
These headers allow for content negotiation: where the server decides between the mime types which is best to send
Matt Trask
@matthewtrask
Authorization
# Request
Authorization: Bearer 5eb63bbbe01eeed093cb22bb8f5acdc3
Authorization: Basic a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
Matt Trask
@matthewtrask
Cache-Control
# Request
Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>
Cache-Control: no-cache
Cache-Control: no-store
Cache-Control: no-transform
Cache-Control: only-if-cached
# Response
Cache-Control: no-cache
Cache-Control: no-store
Cache-Control: no-transform
Cache-Control: public
Cache-Control: private
Cache-Control: proxy-revalidate
Cache-Control: max-age=<seconds>
Cache-Control: s-maxage=<seconds>
Matt Trask
@matthewtrask
Location
# Response
Content-Location: /api/characters/3
Location: /api/characters/3
Matt Trask
@matthewtrask
Etag | Age | Last-Modified
# Response
Etag: 5eb63bbbe01eeed093cb22bb8f5acdc3
Age: 60 // in seconds
Last-modified: Wed, 24 Mar 2021-03-24 12:00:00 GMT // “HTTP-date” format as defined by RFC 7231
Matt Trask
@matthewtrask
Content-length | Content-language | Expires
# Response
Content-length: 128 // represented by 8-bit bytes
Content-language: en
Expires: Tue, 29 Mar 2021 23:59:59 GMT
Matt Trask
@matthewtrask
CORS
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Matt Trask
@matthewtrask
Matt Trask
@matthewtrask
What Is An API Contract?
It is something that both API provider and API consumer can agree upon, and get to work developing and delivering, and then integrating and consuming. An API contract is a shared understanding of what the capabilities of a digital interface are, allowing for applications to be programmed on top of.
Matt Trask
@matthewtrask
A description language that allows for documentation generation, testing, SDK generation, and more.
The resulting file creates the "contract" that both the server and client can reliable consume.
The artist formerly known as Swagger
Matt Trask
@matthewtrask
Matt Trask
@matthewtrask
https://github.com/matthewtrask/national-parks-api/blob/main/contract/openapi.yml
Matt Trask
@matthewtrask
Matt Trask
@matthewtrask
JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.
Matt Trask
@matthewtrask
{
"$id": "https://example.com/address.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "An address similar to http://microformats.org/wiki/h-card",
"type": "object",
"properties": {
"post-office-box": {
"type": "string"
},
"extended-address": {
"type": "string"
},
"street-address": {
"type": "string"
},
"locality": {
"type": "string"
},
"region": {
"type": "string"
},
"postal-code": {
"type": "string"
},
"country-name": {
"type": "string"
}
},
"required": [ "locality", "region", "country-name" ],
"dependentRequired": {
"post-office-box": [ "street-address" ],
"extended-address": [ "street-address" ]
}
}
Matt Trask
@matthewtrask
OpenAPI 3.1 finally worked through some minor issues and now OpenAPI and JSON Schema play nice together!
Matt Trask
@matthewtrask
Postman - HTTP Testing
Paw (Mac Only) - HTTP Testing
HTTPie (CLI) - HTTP Testing
Insomnia - HTTP Testing
Postwoman - HTTP Testing
Stoplight Studio - Documentation
Stoplight Spectral - Linting
Dredd.io - Testing
jq - CLI JSON map/filter/slice