bit.ly/fun-apis
APIS
Who uses REST?
Do you know that REST is dead?
Context!
(crazy Cyndi Lauper slides)
GraphQL & REST are tools
GraphQL
REST
Tools
Needs
“If all you have is a hammer, everything looks like a nail”
Needs
Tools
Common sense
The WEB in 2017
google.com
twitter.com
cyliconvalley.es
wecodefest.com
browser for
cyliconvalley.es
google.com
twitter.com
cyliconvalley.es
wecodefest.com
browser for
google.com
browser for
twitter.com
browser for
wecodefest.com
browser for
cyliconvalley.es
google.com
twitter.com
cyliconvalley.es
wecodefest.com
browser for
google.com
browser for
twitter.com
browser for
wecodefest.com
APIs in 2017
twitter.com
Twitter clients
Contract
API exposes data
Client consumes it
Cylicon
v1
v2
v3
We want evolvable APIs
¿How do we design APIs to be able to evolve?
These are NOT new concepts
REST style is an abstraction of the architectural elements within a distributed hypermedia system
-- Roy Fielding, 2000
What is Hypermedia?
Pagination (HAL)
{
"total": 43,
"count": 30,
"_links": {
"first": {
"href": "http://liferay.org.es/p/blogs?page=1&per_page=30"
},
"next": {
"href": "http://liferay.org.es/p/blogs?page=2&per_page=30"
},
"last": {
"href": "http://liferay.org.es/p/blogs?page=2&per_page=30"
}
}
Standard links (Link Relations IANA)
{
"total": 43,
"count": 30,
"_links": {
"first": {
"href": "http://liferay.org.es/p/blogs?...
},
"next": {
"href": "http://liferay.org.es/p/blogs?page=2&per_page=30"
},
"last": {
"href": "http://liferay.org.es/p/blogs?page=2&per_page=30"
}
}
Over 80 standard types
Entry URL with all the endpoints (JSON HOME)
{
"resources": {
"people": {
"href": "http://localhost/o/api/people/"
},
"web-sites": {
"href": "http://localhost/o/api/web-sites/"
},
"blogs": {
"href": "http://localhost/o/api/blogs/"
}
}
}
Clients can browse the urls.
They don’t need to know more.
Actions (Siren)
{
"class": "BlogPosting",
"actions": [
{
"name": "delete-item",
"title": "Delete Blog Posting",
"method": "DELETE",
"href": "http://localhost:8080/o/p/blogs/32400"
}
],
"properties": {
"headline": "Hello Valladolid!"
}
}
Forms (Siren)
{
"class": "BlogPosting",
"actions": [
{
"name": "add-blog-posting",
"title": "Add Blog Posting",
"method": "POST",
"href": "http://localhost:8080/o/p/blogs",
"fields": [
{
"name": "headline",
"type": "text"
},
],
Clients don’t need to know the fields in advance, just support the types.
Embedded fields and selection
{
"headline": "Hello CyLicon Valley!",
"_embedded": {
"author": {
"_links": {
"self": {
"href": "http://localhost:8080/o/p/people/30765"
}
},
"name": "Shalinie Kooman"
}
GET /blogs/33100?embedded=author
GET /blogs/33100?fields=[headline, author]
URI templates
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "IriTemplate",
"template": "http://api.example.com/issues{?q}",
"variableRepresentation": "BasicRepresentation",
"mapping": [
{
"@type": "IriTemplateMapping",
"variable": "q",
"property": "hydra:freetextQuery",
"required": true
}
]
}
We don’t need to hardcode “concepts” (like embedded)
What are shared vocabularies?
schema.org & ...
Internal Models
597 types
867 properties
Microformats
Never expose internal models
Customer language
Mapping internals with standards
Internal | Schema.org |
BlogsEntry | BlogsPosting |
headline | title |
alternativeHeadline | subtitle |
description | description |
user | creator |
user | author |
articleBody | content |
aggregateRating | ratings |
Internal | Schema.org+custom |
Group | WebSite |
name | name |
groupKey | alternateName |
user | creator |
Ratings Service | aggregateRatings |
friendlyURL | _self / @id |
BlogsEntry Service | blogs |
manualMembership | ¿? |
What do I have to do to use it?
Ease development of common cases (Collection+json)
Automatic support for multiple
message formats
(representor pattern)
Documentation generated automatically
APIO
Support for
several formats
Representor
public Representor<Blogs, LongId> representor(
Builder<Blogs, LongId> builder) {
return builder
.types("BlogPosting")
.identifier(blogs -> blogs::getEntryId)
.addString("articleBody", Blogs::getContent)
.addString("headline", Blogs::getTitle)
.addDate("createDate", Blogs::getCreateDate)
.addLink("license", "https://creativecommons.org/licenses/by/4.0")
.addLinkedModel("creator", User.class, this::_getUserOptional)
.addRelatedCollection("comment", Comment.class, CommentableIdentifier::create)
.addBidirectionalModel("web", "blogs", Web.class, this::_getWeb, Web::getWebId)
.build();
}
Routes (collection)
@Override
public NestedRoutes<Blogs> collectionRoutes(
NestedRoutes.Builder<Blogs, LongIdentifier> builder) {
return builder
.addGetter(this::_getPageItems)
.addCreator(this::_addBlogs)
.build();
}
Routes (item)
@Override
public ItemRoutes<BlogsEntry> itemRoutes(
ItemRoutes.Builder<BlogsEntry, LongIdentifier> builder) {
return builder
.addGetter(this::_getBlogsEntry)
.addRemover(this::_deleteBlogsEntry)
.addUpdater(this::_updateBlogsEntry)
.build();
}
Interactive documentation generated automatically
"descriptor" : [
{
"id" : "people",
"type" : "semantic",
"href" : "http://schema.org/Person"
"doc" : { "format" : "text", "value" : "A person" },
"descriptor" : [
{
"id" : "givenName",
"type" : "semantic",
"href" : "http://schema.org/givenName"
},
{
"id" : "familyName",
"type" : "semantic",
"href" : "http://schema.org/familyName"
}
An hypermedia consumer is like a game loop
4 - receive user interactors and browse URL
1 - URL request
2 - Identify response
and redirect to processor
3 - render data
and controls
Automatic processing
of schema.org types
Implementation
of the control loop
Offline support
Visual components
Clients in Java (+Android), iOS & Javascript
val id =
"http://.../o/api/p/groups/5745/blogs"
thing.load(id, credentials)
Demo time!
evolvable-apis.org
Manifiesto
Guidelines/tutorials
Libraries
Join us!
questions?
References
Image References
Liferay
We are hiring! :D
Javier Gamarra / @nhpatt
APIS