SlideShare ist ein Scribd-Unternehmen logo
1 von 91
Downloaden Sie, um offline zu lesen
Building Awesome APIs in Grails 
By Chris Latimer 
Š 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
2 
What makes an 
API awesome?
3 
Is it using JSON 
payloads 
instead of XML?
Using this Template 
4 
Is it strict adherence to REST principles?
5 
API Fielding Score
6
7 Predictable and Consistent
8 
"uri": 
"/categories/activism", 
"name": 
"Activism 
& 
Non 
Profits", 
"link": 
“https://vimeo.com/…”, 
… 
"metadata": 
{ 
"connections": 
{…} 
} 
Category ! 
Response: 
"uri": 
"/channels/804185", 
"name": 
"School 
Intercom", 
"link": 
“https://vimeo.com/…”, 
… 
"metadata": 
{ 
"connections": 
{…} 
} 
Channel ! 
Response:
9 
<photo 
id="2636" 
owner="47058503995@N01" 
secret="a123456" 
server=“2" 
title=“test_04” 
ispublic=“1" 
isfriend="0" 
isfamily="0" 
/> 
<contact 
nsid="12037949629@N01" 
username="Eric" 
iconserver="1" 
realname="Eric 
Costello" 
friend="1" 
family="0" 
ignored="1" 
/>
10 
Stable Versions 
URI Based Accept Header 
/v1/endpoint 
! 
/v2/endpoint 
Accept-­‐Version: 
1.0 
! 
Accept-­‐Version: 
1.1 
Content Type 
Accept: 
application/vnd.your.api.v2+json 
! 
Accept: 
application/vnd.your.api.v2.1+json
11 
Predictable Response Codes 
2xx Successful 4xx Client Error 
400 
Bad 
Request 
401 
Unauthorized 
403 
Forbidden 
404 
Not 
Found 
5xx Server Error 
500 
Server 
Error 
502 
Bad 
Gateway 
503 
Unavailable 
200 
Success 
201 
Created 
!
12 
Intuitive Structure
13 
Intuitive URI Structure 
URI Description 
/group/{id} A Facebook group 
/group/{id}/feed This group’s feed 
/group/{id}/files Files uploaded to this group 
/group/{id}/events This group’s events
14 
Intuitive Navigation 
Pagination 
"total": 
659212, 
"page": 
2, 
"per_page": 
10, 
"paging": 
{ 
"next": 
"/channels?page=3", 
"previous": 
"/channels?page=1", 
"first": 
"/channels?page=1", 
"last": 
"/channels?page=65922" 
}
15 
Intuitive Navigation 
Related Resources 
{ 
“uri": 
"/categories/experimental", 
"name": 
"Experimental", 
"subcategories": 
[ 
{ 
"uri": 
“/categories/experimental/animation", 
"name": 
"Animation", 
"link": 
“https://vimeo.com/categories/…” 
}… 
] 
}
Flexible Responses
17 
Partial Responses 
Get Full Response 
/feeds/api/users/default/uploads 
Get Partial Response 
/feeds/api/users/default/uploads? 
 
fields=entry(title,gd:comments,yt:statistics)
18 
Result Filtering 
Get List of Videos 
/feeds/api/videos?q=surfing&max-­‐results=10 
Get Videos with 1,000,000+ Views 
/feeds/api/videos?q=surfing&max-­‐results=10 
&fields=entry[yt:statistics/@viewCount 
> 
1000000]
19 
Customized Responses 
ItemLookup - Default 
ItemId=B00008OE6I 
ItemLookup - Default With Reviews 
ItemId=B00008OE6I 
&ResponseGroup=Reviews 
ItemLookup - Large With Reviews and Offers 
ItemId=B00008OE6I 
&ResponseGroup=Large,Reviews,Offers
20 
Easy to Learn and 
Experiment With
21
22
23 
Designing an 
awesome API
Apps API
G
Phone Shopping App 
Potential Resources 
/phones 
! 
/devices 
! 
/manufacturers
Phone Shopping App 
API Features 
Pagination 
! 
Filtering 
! 
Versioning
Phone Shopping App 
Potential Resources 
/phoneVariations 
! 
/phone/{id} 
! 
/phone/{id}/variations
Phone Shopping App 
API Features 
Complete 
vs. 
Compact 
representations 
! 
Including 
related 
entities 
! 
Linking 
to 
other 
resources
Phone API Endpoints 
URI Verb Description 
/phones GET Get a list of phones 
/phones/{id} GET Get phone details 
/phones/{id}/manufacturer GET Get phone’s manufacturer 
/phones/{id}/variations GET Get variations of a phone
Phone API Versioning 
Header Based 
Accept-­‐Version: 
1.0 
! 
Accept-­‐Version: 
2.0
General Response Structure Patterns 
{ 
“entity” 
: 
{ 
“attr1” 
: 
“value1”, 
“attr2” 
: 
“value2”, 
… 
}, 
“metadata” 
: 
{ 
} 
} 
{ 
“attr1” 
: 
“value1”, 
“attr2” 
: 
“value2”, 
… 
}
General Response Structure Patterns 
{ 
“entities” 
: 
[ 
{ 
“attr1” 
: 
“value1”, 
“attr2” 
: 
“value2”, 
… 
},{…},{…}… 
], 
“metadata” 
: 
{ 
} 
} 
[ 
{ 
“attr1” 
: 
“value1”, 
“attr2” 
: 
“value2”, 
… 
}, 
{…},{…}… 
]
API Formats 
JSON Default 
“phones” 
: 
[ 
{ 
“name”:“iPhone 
5s”, 
“basePrice”:599.99, 
… 
} 
] 
XML by Request 
<phones> 
<phone> 
<name>iPhone 
5s</name> 
… 
</phone> 
… 
</phones>
Intuitive Response Structures 
Single Response Collection Response 
{ 
{ 
“phones” 
: 
[ 
{…}, 
{…}, 
…], 
“paging” 
: 
{ 
… 
} 
} 
phone: 
{ 
“key1” 
: 
“value1”, 
… 
“keyN” 
: 
“valueN”, 
“links” 
: 
[ 
{…},{…}…] 
} 
}
Complete and Compact Representations 
Attribute Compact Complete 
name 
basePrice 
variations 
manufacturer
Customized Responses 
GET 
/phones/1 
{ 
“name” 
: 
“iPhone5s”, 
… 
} 
GET 
/phones/1?include=accessories 
{ 
“name” 
: 
“iPhone5s”, 
… 
“includes” 
: 
[ 
{ 
“accessories” 
: 
[…] 
} 
] 
}
Pagination 
GET 
/phones 
{ 
“phones” 
: 
[ 
{…}, 
{…}, 
…], 
“paging” 
: 
{ 
“totalCount” 
: 
233, 
“currentMax” 
: 
10, 
“currentOffset” 
: 
40 
} 
}
Linking to Related Entities 
GET 
/phones/1 
{ 
…, 
“links” 
: 
[ 
{ 
“rel” 
: 
“self”, 
“href” 
: 
“http://…”, 
}, 
{ 
“rel” 
: 
“manufacturer”, 
“href” 
: 
“http://…” 
} 
] 
}
Building an 
awesome API using
Phone 
Manufacturer 
Variation 
Domain Model
API Features 
• Accept Header Versioning 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links
Out of the Box APIs 
@Resource(uri="/phones", 
formats=["json", 
"xml"]) 
class 
Phone 
{ 
… 
}
@Resource URL Mappings 
URL Mapping Verb Action 
/phones GET List of phones 
/phones POST Create new phone 
/phones/{id} GET Get single phone 
/phones/{id} PUT Update phone 
/phones/{id} DELETE Delete phone
Read-Only URL Mappings 
@Resource(uri="/phones", 
formats=["json", 
"xml"], 
readOnly=true) 
URL Mapping Verb Action 
/phones GET List of phones 
/phones/{id} GET Get single phone
Link is on Twitter - @chrislatimer
Demo 
> 
git 
clone 
https://github.com/chrislatimer/gmobile.git 
! 
> 
cd 
gmobile 
! 
> 
git 
checkout 
api-­‐step-­‐1
API Features 
• Accept Header Versioning 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs
Implementing API Versioning 
Version 1! 
Controller 
Version 2! 
Controller 
API Requests URL Mappings
Controller Namespaces 
Version 
1 
Controller 
class 
PhoneController 
extends 
RestfulController<Phone> 
{ 
static 
namespace 
= 
'v1' 
} 
Version 
2 
Controller 
class 
PhoneController 
extends 
RestfulController<Phone> 
{ 
static 
namespace 
= 
'v2' 
}
URL Mappings 
"/phones"(version:'1.0', 
resources:"phone", 
namespace:'v1') 
! 
"/phones"(version:'2.0', 
resources:"phone", 
namespace:'v2') 
Version 1! 
Controller 
Version 2! 
Controller 
Accept-­‐Version: 
1.0 
Accept-­‐Version: 
2.0
Demo 
> 
git 
checkout 
api-­‐step-­‐2
API Features 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning
Peanut - Ugliest Dog 2014
! 
"class": 
"org.gmobile.Phone", 
"id": 
1, 
"description": 
null, 
"manufacturer": 
{ 
"class": 
"org.gmobile.Manufacturer", 
"id": 
1 
}, 
"name": 
"Xtreme 
Photon 
Z5", 
"variations": 
[ 
{ 
"class": 
"org.gmobile.Variation", 
"id": 
1 
},… 
] 
Ugliest JSON 2014?
API Features 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links 
• Prettier JSON 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning
How can we improve our ! 
response payload?
Out of the Box Customization 
include 
attributes exclude 
attributes 
beans 
{ 
bookRenderer(XmlRenderer, 
Book) 
{ 
includes 
= 
[‘title’] 
} 
} 
beans 
{ 
bookRenderer(XmlRenderer, 
Book) 
{ 
excludes 
= 
[‘title’] 
} 
} 
This can be useful for very! 
simple customization
Simplified Picture of the Response Flow 
Controller Renderer Converter Marshaller 
as 
JSON/XML 
respond 
render 
value 
marshal
Creating a Custom Marshaller 
class 
PhoneMarshallerJson 
extends 
ClosureObjectMarshaller<JSON> 
{ 
! 
static 
final 
marshal 
= 
{ 
Phone 
phone 
-­‐> 
def 
map 
= 
[:] 
… 
map 
} 
! 
public 
PhoneMarshallerJson() 
{ 
super(Phone, 
marshal) 
} 
}
Creating a Custom Marshaller 
class 
PhoneMarshallerXml 
extends 
ClosureObjectMarshaller<XML>{ 
! 
def 
static 
final 
marshal 
= 
{ 
Phone 
phone, 
XML 
xml 
-­‐> 
xml.build 
{ 
name(phone.name) 
} 
} 
! 
public 
PhoneMarshallerXml() 
{ 
super(Phone, 
marshal) 
} 
}
Registering a Custom Marshaller 
beans 
= 
{ 
customPhoneJsonMarshaller(ObjectMarshallerRegisterer) 
{ 
marshaller 
= 
new 
PhoneMarshallerJson() 
converterClass 
= 
JSON 
priority 
= 
1 
} 
! 
customPhoneXmlMarshaller(ObjectMarshallerRegisterer) 
{ 
marshaller 
= 
new 
PhoneMarshallerXml() 
converterClass 
= 
XML 
priority 
= 
1 
} 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐3
API Features 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning 
• Prettier JSON
Creating Named Marshallers 
class 
PhoneMarshallerJsonCompact 
extends 
ClosureObjectMarshaller<JSON>{ 
! 
public 
static 
final 
marshal 
= 
{ 
Phone 
phone 
-­‐> 
def 
map 
= 
[:] 
map.id 
= 
phone.id 
map.name 
= 
phone.name 
map 
} 
! 
public 
PhoneMarshallerJsonCompact() 
{ 
super(Phone, 
marshal) 
} 
! 
}
Registering Named Marshallers 
def 
init 
= 
{ 
JSON.createNamedConfig('compact') 
{ 
it.registerObjectMarshaller(Phone, 
PhoneMarshallerJsonCompact.marshal) 
} 
! 
JSON.createNamedConfig('complete') 
{ 
it.registerObjectMarshaller(Phone, 
PhoneMarshallerJson.marshal) 
} 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐4
API Features 
• Pagination 
• Custom Responses 
• Related Links 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning 
• Prettier JSON 
• Multiple Representations
Creating a Custom Renderer 
class 
ApiJsonRenderer<T> 
extends 
AbstractRenderer<T> 
{ 
! 
public 
ApiJsonRenderer(Class<T> 
targetClass) 
{ 
super(targetClass, 
MimeType.JSON); 
} 
! 
@Override 
void 
render(T 
object, 
RenderContext 
context) 
{ 
// 
rendering 
logic 
} 
}
Using a Custom Renderer 
def 
show(Phone 
phone) 
{ 
def 
detail 
= 
params.detail 
?: 
"complete" 
withFormat 
{ 
json 
{ 
respond(phone, 
[detail:detail]) 
} 
xml 
{ 
XML.use(params?.detail?.toLowerCase() 
?: 
"complete") 
{ 
respond 
phone 
} 
} 
} 
}
Registering a Custom Renderer 
beans 
= 
{ 
phoneRenderer(ApiJsonRenderer, 
Phone) 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐5
The monkey wrench in ! 
grails.converters.JSON
Creating a Custom Converter 
class 
ApiJSON 
extends 
JSON 
{ 
… 
public 
void 
renderPartial(JSONWriter 
out) 
{ 
initWriter(out) 
super.value(target) 
} 
protected 
initWriter(JSONWriter 
out) 
{ 
writer 
= 
out 
referenceStack 
= 
new 
Stack<Object>(); 
} 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐6
Rendering Paging Info 
def 
index() 
{ 
… 
withFormat 
{ 
json 
{ 
respond 
Phone.list(params), 
[detail:detail, 
paging:[totalCount: 
Phone.count(), 
currentMax: 
params.max, 
curentOffset:offset]] 
} 
…
Rendering Paging Info 
void 
render(T 
object, 
RenderContext 
context) 
{ 
… 
if(context.arguments?.paging) 
{ 
writer.key("paging") 
converter 
= 
context.arguments.paging 
as 
ApiJSON 
converter.renderPartial(writer) 
} 
… 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐7
API Features 
• Custom Responses 
• Related Links 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning 
• Prettier JSON 
• Multiple Representations 
• Pagination
Customizing Responses 
def 
show(Phone 
phone) 
{ 
… 
withFormat 
{ 
json 
{ 
respond(phone, 
[detail:detail, 
include:params?.list('include')]) 
} 
… 
} 
}
Rendering Custom Responses 
void 
render(T 
object, 
RenderContext 
context) 
{ 
… 
if(context.arguments?.include) 
{ 
writer.key("include") 
writer.array() 
context.arguments?.include.each 
{ 
includeProp 
-­‐> 
JSON.use("compact") 
{ 
converter 
= 
object.properties.get(includeProp) 
as 
ApiJSON 
} 
writer.object() 
writer.key(includeProp) 
converter.renderPartial(writer) 
writer.endObject() 
} 
writer.endArray() 
} 
… 
}
Demo 
> 
git 
checkout 
api-­‐step-­‐8
API Features 
• Predictable Response Codes • Related Links 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning 
• Prettier JSON 
• Multiple Representations 
• Pagination 
• Custom Responses
Including Related Links 
static 
final 
Closure 
marshal 
= 
{ 
LinkGenerator 
linkGenerator, 
Phone 
phone 
-­‐> 
def 
json 
= 
[:] 
json.id 
= 
phone.id 
json.name 
= 
phone.name 
json.links 
= 
[] 
json.links 
<< 
[rel:"self", 
href:linkGenerator.link(resource: 
phone, 
method: 
HttpMethod.GET, 
absolute: 
true)] 
json 
}
Including Related Links 
static 
final 
Closure 
marshal 
= 
{ 
LinkGenerator 
linkGenerator, 
Phone 
phone 
-­‐> 
def 
json 
= 
[:] 
json.id 
= 
phone.id 
json.name 
= 
phone.name 
json.links 
= 
[] 
json.links 
<< 
[rel:"self", 
href:linkGenerator.link(resource: 
phone, 
method: 
HttpMethod.GET, 
absolute: 
true)] 
json 
}
Including Related Links 
closure.curry(linkGenerator)
Demo 
> 
git 
checkout 
api-­‐step-­‐9
API Features 
• Predictable Response Codes 
• JSON and XML 
• RESTful URIs 
• Accept Header Versioning 
• Prettier JSON 
• Multiple Representations 
• Pagination 
• Custom Responses 
• Related Links
Is this API Awesome? 
It’s getting there…
Follow up questions? 
@chrislatimer 
clatimer@apigee.com

Weitere ähnliche Inhalte

Was ist angesagt?

RESTful Api practices Rails 3
RESTful Api practices Rails 3RESTful Api practices Rails 3
RESTful Api practices Rails 3
Anton Narusberg
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile apps
Mugunth Kumar
 
The API Facade Pattern: Technology - Episode 3
The API Facade Pattern: Technology - Episode 3The API Facade Pattern: Technology - Episode 3
The API Facade Pattern: Technology - Episode 3
Apigee | Google Cloud
 
Mobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many DevicesMobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many Devices
Apigee | Google Cloud
 

Was ist angesagt? (20)

The API Facade Pattern: Common Patterns - Episode 2
The API Facade Pattern: Common Patterns - Episode 2The API Facade Pattern: Common Patterns - Episode 2
The API Facade Pattern: Common Patterns - Episode 2
 
Scaling with swagger
Scaling with swaggerScaling with swagger
Scaling with swagger
 
Design mobile efficient Apis
Design mobile efficient ApisDesign mobile efficient Apis
Design mobile efficient Apis
 
Selenium-4-and-appium-2
Selenium-4-and-appium-2Selenium-4-and-appium-2
Selenium-4-and-appium-2
 
Data normalization across API interactions
Data normalization across API interactionsData normalization across API interactions
Data normalization across API interactions
 
RESTful Api practices Rails 3
RESTful Api practices Rails 3RESTful Api practices Rails 3
RESTful Api practices Rails 3
 
WordPress REST API
WordPress REST APIWordPress REST API
WordPress REST API
 
Raml part 1
Raml part 1Raml part 1
Raml part 1
 
Huge: Running an API at Scale
Huge: Running an API at ScaleHuge: Running an API at Scale
Huge: Running an API at Scale
 
API Façade Pattern
API Façade PatternAPI Façade Pattern
API Façade Pattern
 
Server Installation and Configuration with Chef
Server Installation and Configuration with ChefServer Installation and Configuration with Chef
Server Installation and Configuration with Chef
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
 
Ruby On Grape
Ruby On GrapeRuby On Grape
Ruby On Grape
 
"Design First" APIs with Swagger
"Design First" APIs with Swagger"Design First" APIs with Swagger
"Design First" APIs with Swagger
 
Designing your API Server for mobile apps
Designing your API Server for mobile appsDesigning your API Server for mobile apps
Designing your API Server for mobile apps
 
The API Facade Pattern: Technology - Episode 3
The API Facade Pattern: Technology - Episode 3The API Facade Pattern: Technology - Episode 3
The API Facade Pattern: Technology - Episode 3
 
Rails as iOS Application Backend
Rails as iOS Application BackendRails as iOS Application Backend
Rails as iOS Application Backend
 
Chef introduction
Chef introductionChef introduction
Chef introduction
 
Designing and Running a GraphQL API
Designing and Running a GraphQL APIDesigning and Running a GraphQL API
Designing and Running a GraphQL API
 
Mobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many DevicesMobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many Devices
 

Ähnlich wie Building Awesome APIs in Grails

WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIsThe liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
Jorge Ferrer
 

Ähnlich wie Building Awesome APIs in Grails (20)

Api development with rails
Api development with railsApi development with rails
Api development with rails
 
Api's and ember js
Api's and ember jsApi's and ember js
Api's and ember js
 
Mashing Up The Guardian
Mashing Up The GuardianMashing Up The Guardian
Mashing Up The Guardian
 
Prairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API ResponsesPrairie DevCon 2015 - Crafting Evolvable API Responses
Prairie DevCon 2015 - Crafting Evolvable API Responses
 
Cloud2Car - Force.com and the Internet of Things
Cloud2Car - Force.com and the Internet of ThingsCloud2Car - Force.com and the Internet of Things
Cloud2Car - Force.com and the Internet of Things
 
Mashing Up The Guardian
Mashing Up The GuardianMashing Up The Guardian
Mashing Up The Guardian
 
What Makes a Great Open API?
What Makes a Great Open API?What Makes a Great Open API?
What Makes a Great Open API?
 
Don't screw it up! How to build durable API
Don't screw it up! How to build durable API Don't screw it up! How to build durable API
Don't screw it up! How to build durable API
 
How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016
How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016
How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016
 
David GĂłmez G. - Hypermedia APIs for headless platforms and Data Integration ...
David GĂłmez G. - Hypermedia APIs for headless platforms and Data Integration ...David GĂłmez G. - Hypermedia APIs for headless platforms and Data Integration ...
David GĂłmez G. - Hypermedia APIs for headless platforms and Data Integration ...
 
Cdm mil-18 - hypermedia ap is for headless platforms and data integration
Cdm mil-18 - hypermedia ap is for headless platforms and data integrationCdm mil-18 - hypermedia ap is for headless platforms and data integration
Cdm mil-18 - hypermedia ap is for headless platforms and data integration
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
Going FaaSter, Functions as a Service at Netflix
Going FaaSter, Functions as a Service at NetflixGoing FaaSter, Functions as a Service at Netflix
Going FaaSter, Functions as a Service at Netflix
 
Lets dance- Dutch Architecture Conference (LAC) 2018
Lets dance- Dutch Architecture Conference (LAC) 2018Lets dance- Dutch Architecture Conference (LAC) 2018
Lets dance- Dutch Architecture Conference (LAC) 2018
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfony
 
What Makes a Great Open API?
What Makes a Great Open API?What Makes a Great Open API?
What Makes a Great Open API?
 
Swift meetup22june2015
Swift meetup22june2015Swift meetup22june2015
Swift meetup22june2015
 
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIsThe liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
 
Continuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWSContinuous Integration and Deployment Best Practices on AWS
Continuous Integration and Deployment Best Practices on AWS
 
Mobile Development integration tests
Mobile Development integration testsMobile Development integration tests
Mobile Development integration tests
 

KĂźrzlich hochgeladen

Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Bert Jan Schrijver
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 

KĂźrzlich hochgeladen (20)

%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 

Building Awesome APIs in Grails

  • 1. Building Awesome APIs in Grails By Chris Latimer Š 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 2. 2 What makes an API awesome?
  • 3. 3 Is it using JSON payloads instead of XML?
  • 4. Using this Template 4 Is it strict adherence to REST principles?
  • 6. 6
  • 7. 7 Predictable and Consistent
  • 8. 8 "uri": "/categories/activism", "name": "Activism & Non Profits", "link": “https://vimeo.com/…”, … "metadata": { "connections": {…} } Category ! Response: "uri": "/channels/804185", "name": "School Intercom", "link": “https://vimeo.com/…”, … "metadata": { "connections": {…} } Channel ! Response:
  • 9. 9 <photo id="2636" owner="47058503995@N01" secret="a123456" server=“2" title=“test_04” ispublic=“1" isfriend="0" isfamily="0" /> <contact nsid="12037949629@N01" username="Eric" iconserver="1" realname="Eric Costello" friend="1" family="0" ignored="1" />
  • 10. 10 Stable Versions URI Based Accept Header /v1/endpoint ! /v2/endpoint Accept-­‐Version: 1.0 ! Accept-­‐Version: 1.1 Content Type Accept: application/vnd.your.api.v2+json ! Accept: application/vnd.your.api.v2.1+json
  • 11. 11 Predictable Response Codes 2xx Successful 4xx Client Error 400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 5xx Server Error 500 Server Error 502 Bad Gateway 503 Unavailable 200 Success 201 Created !
  • 13. 13 Intuitive URI Structure URI Description /group/{id} A Facebook group /group/{id}/feed This group’s feed /group/{id}/files Files uploaded to this group /group/{id}/events This group’s events
  • 14. 14 Intuitive Navigation Pagination "total": 659212, "page": 2, "per_page": 10, "paging": { "next": "/channels?page=3", "previous": "/channels?page=1", "first": "/channels?page=1", "last": "/channels?page=65922" }
  • 15. 15 Intuitive Navigation Related Resources { “uri": "/categories/experimental", "name": "Experimental", "subcategories": [ { "uri": “/categories/experimental/animation", "name": "Animation", "link": “https://vimeo.com/categories/…” }… ] }
  • 17. 17 Partial Responses Get Full Response /feeds/api/users/default/uploads Get Partial Response /feeds/api/users/default/uploads? fields=entry(title,gd:comments,yt:statistics)
  • 18. 18 Result Filtering Get List of Videos /feeds/api/videos?q=surfing&max-­‐results=10 Get Videos with 1,000,000+ Views /feeds/api/videos?q=surfing&max-­‐results=10 &fields=entry[yt:statistics/@viewCount > 1000000]
  • 19. 19 Customized Responses ItemLookup - Default ItemId=B00008OE6I ItemLookup - Default With Reviews ItemId=B00008OE6I &ResponseGroup=Reviews ItemLookup - Large With Reviews and Offers ItemId=B00008OE6I &ResponseGroup=Large,Reviews,Offers
  • 20. 20 Easy to Learn and Experiment With
  • 21. 21
  • 22. 22
  • 23. 23 Designing an awesome API
  • 25. G
  • 26. Phone Shopping App Potential Resources /phones ! /devices ! /manufacturers
  • 27. Phone Shopping App API Features Pagination ! Filtering ! Versioning
  • 28. Phone Shopping App Potential Resources /phoneVariations ! /phone/{id} ! /phone/{id}/variations
  • 29. Phone Shopping App API Features Complete vs. Compact representations ! Including related entities ! Linking to other resources
  • 30. Phone API Endpoints URI Verb Description /phones GET Get a list of phones /phones/{id} GET Get phone details /phones/{id}/manufacturer GET Get phone’s manufacturer /phones/{id}/variations GET Get variations of a phone
  • 31. Phone API Versioning Header Based Accept-­‐Version: 1.0 ! Accept-­‐Version: 2.0
  • 32. General Response Structure Patterns { “entity” : { “attr1” : “value1”, “attr2” : “value2”, … }, “metadata” : { } } { “attr1” : “value1”, “attr2” : “value2”, … }
  • 33. General Response Structure Patterns { “entities” : [ { “attr1” : “value1”, “attr2” : “value2”, … },{…},{…}… ], “metadata” : { } } [ { “attr1” : “value1”, “attr2” : “value2”, … }, {…},{…}… ]
  • 34. API Formats JSON Default “phones” : [ { “name”:“iPhone 5s”, “basePrice”:599.99, … } ] XML by Request <phones> <phone> <name>iPhone 5s</name> … </phone> … </phones>
  • 35. Intuitive Response Structures Single Response Collection Response { { “phones” : [ {…}, {…}, …], “paging” : { … } } phone: { “key1” : “value1”, … “keyN” : “valueN”, “links” : [ {…},{…}…] } }
  • 36. Complete and Compact Representations Attribute Compact Complete name basePrice variations manufacturer
  • 37. Customized Responses GET /phones/1 { “name” : “iPhone5s”, … } GET /phones/1?include=accessories { “name” : “iPhone5s”, … “includes” : [ { “accessories” : […] } ] }
  • 38. Pagination GET /phones { “phones” : [ {…}, {…}, …], “paging” : { “totalCount” : 233, “currentMax” : 10, “currentOffset” : 40 } }
  • 39. Linking to Related Entities GET /phones/1 { …, “links” : [ { “rel” : “self”, “href” : “http://…”, }, { “rel” : “manufacturer”, “href” : “http://…” } ] }
  • 40. Building an awesome API using
  • 42. API Features • Accept Header Versioning • Predictable Response Codes • JSON and XML • RESTful URIs • Multiple Representations • Pagination • Custom Responses • Related Links
  • 43. Out of the Box APIs @Resource(uri="/phones", formats=["json", "xml"]) class Phone { … }
  • 44. @Resource URL Mappings URL Mapping Verb Action /phones GET List of phones /phones POST Create new phone /phones/{id} GET Get single phone /phones/{id} PUT Update phone /phones/{id} DELETE Delete phone
  • 45. Read-Only URL Mappings @Resource(uri="/phones", formats=["json", "xml"], readOnly=true) URL Mapping Verb Action /phones GET List of phones /phones/{id} GET Get single phone
  • 46.
  • 47. Link is on Twitter - @chrislatimer
  • 48. Demo > git clone https://github.com/chrislatimer/gmobile.git ! > cd gmobile ! > git checkout api-­‐step-­‐1
  • 49. API Features • Accept Header Versioning • Multiple Representations • Pagination • Custom Responses • Related Links • Predictable Response Codes • JSON and XML • RESTful URIs
  • 50. Implementing API Versioning Version 1! Controller Version 2! Controller API Requests URL Mappings
  • 51. Controller Namespaces Version 1 Controller class PhoneController extends RestfulController<Phone> { static namespace = 'v1' } Version 2 Controller class PhoneController extends RestfulController<Phone> { static namespace = 'v2' }
  • 52. URL Mappings "/phones"(version:'1.0', resources:"phone", namespace:'v1') ! "/phones"(version:'2.0', resources:"phone", namespace:'v2') Version 1! Controller Version 2! Controller Accept-­‐Version: 1.0 Accept-­‐Version: 2.0
  • 53. Demo > git checkout api-­‐step-­‐2
  • 54. API Features • Multiple Representations • Pagination • Custom Responses • Related Links • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning
  • 55. Peanut - Ugliest Dog 2014
  • 56. ! "class": "org.gmobile.Phone", "id": 1, "description": null, "manufacturer": { "class": "org.gmobile.Manufacturer", "id": 1 }, "name": "Xtreme Photon Z5", "variations": [ { "class": "org.gmobile.Variation", "id": 1 },… ] Ugliest JSON 2014?
  • 57. API Features • Multiple Representations • Pagination • Custom Responses • Related Links • Prettier JSON • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning
  • 58. How can we improve our ! response payload?
  • 59. Out of the Box Customization include attributes exclude attributes beans { bookRenderer(XmlRenderer, Book) { includes = [‘title’] } } beans { bookRenderer(XmlRenderer, Book) { excludes = [‘title’] } } This can be useful for very! simple customization
  • 60. Simplified Picture of the Response Flow Controller Renderer Converter Marshaller as JSON/XML respond render value marshal
  • 61. Creating a Custom Marshaller class PhoneMarshallerJson extends ClosureObjectMarshaller<JSON> { ! static final marshal = { Phone phone -­‐> def map = [:] … map } ! public PhoneMarshallerJson() { super(Phone, marshal) } }
  • 62. Creating a Custom Marshaller class PhoneMarshallerXml extends ClosureObjectMarshaller<XML>{ ! def static final marshal = { Phone phone, XML xml -­‐> xml.build { name(phone.name) } } ! public PhoneMarshallerXml() { super(Phone, marshal) } }
  • 63. Registering a Custom Marshaller beans = { customPhoneJsonMarshaller(ObjectMarshallerRegisterer) { marshaller = new PhoneMarshallerJson() converterClass = JSON priority = 1 } ! customPhoneXmlMarshaller(ObjectMarshallerRegisterer) { marshaller = new PhoneMarshallerXml() converterClass = XML priority = 1 } }
  • 64. Demo > git checkout api-­‐step-­‐3
  • 65. API Features • Multiple Representations • Pagination • Custom Responses • Related Links • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning • Prettier JSON
  • 66. Creating Named Marshallers class PhoneMarshallerJsonCompact extends ClosureObjectMarshaller<JSON>{ ! public static final marshal = { Phone phone -­‐> def map = [:] map.id = phone.id map.name = phone.name map } ! public PhoneMarshallerJsonCompact() { super(Phone, marshal) } ! }
  • 67. Registering Named Marshallers def init = { JSON.createNamedConfig('compact') { it.registerObjectMarshaller(Phone, PhoneMarshallerJsonCompact.marshal) } ! JSON.createNamedConfig('complete') { it.registerObjectMarshaller(Phone, PhoneMarshallerJson.marshal) } }
  • 68. Demo > git checkout api-­‐step-­‐4
  • 69. API Features • Pagination • Custom Responses • Related Links • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning • Prettier JSON • Multiple Representations
  • 70. Creating a Custom Renderer class ApiJsonRenderer<T> extends AbstractRenderer<T> { ! public ApiJsonRenderer(Class<T> targetClass) { super(targetClass, MimeType.JSON); } ! @Override void render(T object, RenderContext context) { // rendering logic } }
  • 71. Using a Custom Renderer def show(Phone phone) { def detail = params.detail ?: "complete" withFormat { json { respond(phone, [detail:detail]) } xml { XML.use(params?.detail?.toLowerCase() ?: "complete") { respond phone } } } }
  • 72. Registering a Custom Renderer beans = { phoneRenderer(ApiJsonRenderer, Phone) }
  • 73. Demo > git checkout api-­‐step-­‐5
  • 74. The monkey wrench in ! grails.converters.JSON
  • 75. Creating a Custom Converter class ApiJSON extends JSON { … public void renderPartial(JSONWriter out) { initWriter(out) super.value(target) } protected initWriter(JSONWriter out) { writer = out referenceStack = new Stack<Object>(); } }
  • 76. Demo > git checkout api-­‐step-­‐6
  • 77. Rendering Paging Info def index() { … withFormat { json { respond Phone.list(params), [detail:detail, paging:[totalCount: Phone.count(), currentMax: params.max, curentOffset:offset]] } …
  • 78. Rendering Paging Info void render(T object, RenderContext context) { … if(context.arguments?.paging) { writer.key("paging") converter = context.arguments.paging as ApiJSON converter.renderPartial(writer) } … }
  • 79. Demo > git checkout api-­‐step-­‐7
  • 80. API Features • Custom Responses • Related Links • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning • Prettier JSON • Multiple Representations • Pagination
  • 81. Customizing Responses def show(Phone phone) { … withFormat { json { respond(phone, [detail:detail, include:params?.list('include')]) } … } }
  • 82. Rendering Custom Responses void render(T object, RenderContext context) { … if(context.arguments?.include) { writer.key("include") writer.array() context.arguments?.include.each { includeProp -­‐> JSON.use("compact") { converter = object.properties.get(includeProp) as ApiJSON } writer.object() writer.key(includeProp) converter.renderPartial(writer) writer.endObject() } writer.endArray() } … }
  • 83. Demo > git checkout api-­‐step-­‐8
  • 84. API Features • Predictable Response Codes • Related Links • JSON and XML • RESTful URIs • Accept Header Versioning • Prettier JSON • Multiple Representations • Pagination • Custom Responses
  • 85. Including Related Links static final Closure marshal = { LinkGenerator linkGenerator, Phone phone -­‐> def json = [:] json.id = phone.id json.name = phone.name json.links = [] json.links << [rel:"self", href:linkGenerator.link(resource: phone, method: HttpMethod.GET, absolute: true)] json }
  • 86. Including Related Links static final Closure marshal = { LinkGenerator linkGenerator, Phone phone -­‐> def json = [:] json.id = phone.id json.name = phone.name json.links = [] json.links << [rel:"self", href:linkGenerator.link(resource: phone, method: HttpMethod.GET, absolute: true)] json }
  • 87. Including Related Links closure.curry(linkGenerator)
  • 88. Demo > git checkout api-­‐step-­‐9
  • 89. API Features • Predictable Response Codes • JSON and XML • RESTful URIs • Accept Header Versioning • Prettier JSON • Multiple Representations • Pagination • Custom Responses • Related Links
  • 90. Is this API Awesome? It’s getting there…
  • 91. Follow up questions? @chrislatimer clatimer@apigee.com