SlideShare a Scribd company logo
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
Building Awesome APIs in Grails
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
Ad

Recommended

Building a scalable API with Grails
Building a scalable API with Grails
Tanausu Cerdeña
 
API Prefetching - HTML5DevConf - Oct. 21, 2014
API Prefetching - HTML5DevConf - Oct. 21, 2014
JonAbrams
 
Building a REST Service in minutes with Spring Boot
Building a REST Service in minutes with Spring Boot
Omri Spector
 
Alfresco Process Services REST API - Alfresco DevCon 2018
Alfresco Process Services REST API - Alfresco DevCon 2018
Dennis Koch
 
Building RESTful APIs w/ Grape
Building RESTful APIs w/ Grape
Daniel Doubrovkine
 
Why your next serverless project should use AWS AppSync
Why your next serverless project should use AWS AppSync
Yan Cui
 
Web API Test Automation using Frisby & Node.js
Web API Test Automation using Frisby & Node.js
Chi Lang Le Vu Tran
 
Mobile APIs in Practice
Mobile APIs in Practice
Tyler Singletary
 
The API Facade Pattern: Common Patterns - Episode 2
The API Facade Pattern: Common Patterns - Episode 2
Apigee | Google Cloud
 
Scaling with swagger
Scaling with swagger
Tony Tam
 
Design mobile efficient Apis
Design mobile efficient Apis
Mobile Rtpl
 
Selenium-4-and-appium-2
Selenium-4-and-appium-2
Manoj Kumar Kumar
 
Data normalization across API interactions
Data normalization across API interactions
Cloud Elements
 
RESTful Api practices Rails 3
RESTful Api practices Rails 3
Anton Narusberg
 
WordPress REST API
WordPress REST API
Anthony Montalbano
 
Raml part 1
Raml part 1
venkata20k
 
Huge: Running an API at Scale
Huge: Running an API at Scale
Apigee | Google Cloud
 
API Façade Pattern
API Façade Pattern
Nabeel Yoosuf
 
Server Installation and Configuration with Chef
Server Installation and Configuration with Chef
Raimonds Simanovskis
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
崇之 清水
 
Ruby On Grape
Ruby On Grape
Andrii Furmanets
 
"Design First" APIs with Swagger
"Design First" APIs with Swagger
scolestock
 
Designing 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 3
Apigee | Google Cloud
 
Rails as iOS Application Backend
Rails as iOS Application Backend
maximeguilbot
 
Chef introduction
Chef introduction
FENG Zhichao
 
Designing and Running a GraphQL API
Designing and Running a GraphQL API
Atlassian
 
Mobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many Devices
Apigee | Google Cloud
 
RESTful API - Best Practices
RESTful API - Best Practices
Tricode (part of Dept)
 
REST Api Tips and Tricks
REST Api Tips and Tricks
Maksym Bruner
 

More Related Content

What's hot (20)

The API Facade Pattern: Common Patterns - Episode 2
The API Facade Pattern: Common Patterns - Episode 2
Apigee | Google Cloud
 
Scaling with swagger
Scaling with swagger
Tony Tam
 
Design mobile efficient Apis
Design mobile efficient Apis
Mobile Rtpl
 
Selenium-4-and-appium-2
Selenium-4-and-appium-2
Manoj Kumar Kumar
 
Data normalization across API interactions
Data normalization across API interactions
Cloud Elements
 
RESTful Api practices Rails 3
RESTful Api practices Rails 3
Anton Narusberg
 
WordPress REST API
WordPress REST API
Anthony Montalbano
 
Raml part 1
Raml part 1
venkata20k
 
Huge: Running an API at Scale
Huge: Running an API at Scale
Apigee | Google Cloud
 
API Façade Pattern
API Façade Pattern
Nabeel Yoosuf
 
Server Installation and Configuration with Chef
Server Installation and Configuration with Chef
Raimonds Simanovskis
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
崇之 清水
 
Ruby On Grape
Ruby On Grape
Andrii Furmanets
 
"Design First" APIs with Swagger
"Design First" APIs with Swagger
scolestock
 
Designing 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 3
Apigee | Google Cloud
 
Rails as iOS Application Backend
Rails as iOS Application Backend
maximeguilbot
 
Chef introduction
Chef introduction
FENG Zhichao
 
Designing and Running a GraphQL API
Designing and Running a GraphQL API
Atlassian
 
Mobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many Devices
Apigee | Google Cloud
 
The API Facade Pattern: Common Patterns - Episode 2
The API Facade Pattern: Common Patterns - Episode 2
Apigee | Google Cloud
 
Scaling with swagger
Scaling with swagger
Tony Tam
 
Design mobile efficient Apis
Design mobile efficient Apis
Mobile Rtpl
 
Data normalization across API interactions
Data normalization across API interactions
Cloud Elements
 
RESTful Api practices Rails 3
RESTful Api practices Rails 3
Anton Narusberg
 
Server Installation and Configuration with Chef
Server Installation and Configuration with Chef
Raimonds Simanovskis
 
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
崇之 清水
 
"Design First" APIs with Swagger
"Design First" APIs with Swagger
scolestock
 
Designing 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 3
Apigee | Google Cloud
 
Rails as iOS Application Backend
Rails as iOS Application Backend
maximeguilbot
 
Designing and Running a GraphQL API
Designing and Running a GraphQL API
Atlassian
 
Mobile APIs: Optimizing APIs for Many Devices
Mobile APIs: Optimizing APIs for Many Devices
Apigee | Google Cloud
 

Similar to Building Awesome APIs in Grails (20)

RESTful API - Best Practices
RESTful API - Best Practices
Tricode (part of Dept)
 
REST Api Tips and Tricks
REST Api Tips and Tricks
Maksym Bruner
 
Consumer centric api design v0.4.0
Consumer centric api design v0.4.0
mustafa sarac
 
Architect's Guide to Building an API Program
Architect's Guide to Building an API Program
clatimer
 
Building a REST API for Longevity
Building a REST API for Longevity
MuleSoft
 
Android App Development 06 : Network &amp; Web Services
Android App Development 06 : Network &amp; Web Services
Anuchit Chalothorn
 
Beautiful APIs - SOSE2021 Keynote
Beautiful APIs - SOSE2021 Keynote
Cesare Pautasso
 
Api crash
Api crash
Hoang Nguyen
 
Api crash
Api crash
Luis Goldster
 
Api crash
Api crash
Harry Potter
 
Api crash
Api crash
Fraboni Ec
 
Api crash
Api crash
Tony Nguyen
 
Api crash
Api crash
Young Alista
 
Api crash
Api crash
James Wong
 
Session 8 Android Web Services - Part 1.pdf
Session 8 Android Web Services - Part 1.pdf
EngmohammedAlzared
 
Building Successful APIs Overnight - Orlando K - Codemotion Rome 2015
Building Successful APIs Overnight - Orlando K - Codemotion Rome 2015
Codemotion
 
Rest APIs Training
Rest APIs Training
Shekhar Kumar
 
A Deep Dive into RESTful API Design Part 2
A Deep Dive into RESTful API Design Part 2
VivekKrishna34
 
The DNA of a great API
The DNA of a great API
Ciprian Sorlea CSM-CSPO
 
REST APIs
REST APIs
Arthur De Magalhaes
 
REST Api Tips and Tricks
REST Api Tips and Tricks
Maksym Bruner
 
Consumer centric api design v0.4.0
Consumer centric api design v0.4.0
mustafa sarac
 
Architect's Guide to Building an API Program
Architect's Guide to Building an API Program
clatimer
 
Building a REST API for Longevity
Building a REST API for Longevity
MuleSoft
 
Android App Development 06 : Network &amp; Web Services
Android App Development 06 : Network &amp; Web Services
Anuchit Chalothorn
 
Beautiful APIs - SOSE2021 Keynote
Beautiful APIs - SOSE2021 Keynote
Cesare Pautasso
 
Session 8 Android Web Services - Part 1.pdf
Session 8 Android Web Services - Part 1.pdf
EngmohammedAlzared
 
Building Successful APIs Overnight - Orlando K - Codemotion Rome 2015
Building Successful APIs Overnight - Orlando K - Codemotion Rome 2015
Codemotion
 
A Deep Dive into RESTful API Design Part 2
A Deep Dive into RESTful API Design Part 2
VivekKrishna34
 
Ad

Recently uploaded (20)

Decipher SEO Solutions for your startup needs.
Decipher SEO Solutions for your startup needs.
mathai2
 
Artificial Intelligence Workloads and Data Center Management
Artificial Intelligence Workloads and Data Center Management
SandeepKS52
 
Who will create the languages of the future?
Who will create the languages of the future?
Jordi Cabot
 
Porting Qt 5 QML Modules to Qt 6 Webinar
Porting Qt 5 QML Modules to Qt 6 Webinar
ICS
 
Women in Tech: Marketo Engage User Group - June 2025 - AJO with AWS
Women in Tech: Marketo Engage User Group - June 2025 - AJO with AWS
BradBedford3
 
Emvigo Capability Deck 2025: Accelerating Innovation Through Intelligent Soft...
Emvigo Capability Deck 2025: Accelerating Innovation Through Intelligent Soft...
Emvigo Technologies
 
Advanced Token Development - Decentralized Innovation
Advanced Token Development - Decentralized Innovation
arohisinghas720
 
Rierino Commerce Platform - CMS Solution
Rierino Commerce Platform - CMS Solution
Rierino
 
Download Adobe Illustrator Crack free for Windows 2025?
Download Adobe Illustrator Crack free for Windows 2025?
grete1122g
 
SAP Datasphere Catalog L2 (2024-02-07).pptx
SAP Datasphere Catalog L2 (2024-02-07).pptx
HimanshuSachdeva46
 
GDG Douglas - Google AI Agents: Your Next Intern?
GDG Douglas - Google AI Agents: Your Next Intern?
felipeceotto
 
Meet You in the Middle: 1000x Performance for Parquet Queries on PB-Scale Dat...
Meet You in the Middle: 1000x Performance for Parquet Queries on PB-Scale Dat...
Alluxio, Inc.
 
Zoneranker’s Digital marketing solutions
Zoneranker’s Digital marketing solutions
reenashriee
 
Open Source Software Development Methods
Open Source Software Development Methods
VICTOR MAESTRE RAMIREZ
 
Transmission Media. (Computer Networks)
Transmission Media. (Computer Networks)
S Pranav (Deepu)
 
dp-700 exam questions sample docume .pdf
dp-700 exam questions sample docume .pdf
pravkumarbiz
 
SAP PM Module Level-IV Training Complete.ppt
SAP PM Module Level-IV Training Complete.ppt
MuhammadShaheryar36
 
On-Device AI: Is It Time to Go All-In, or Do We Still Need the Cloud?
On-Device AI: Is It Time to Go All-In, or Do We Still Need the Cloud?
Hassan Abid
 
Application Modernization with Choreo - The AI-Native Internal Developer Plat...
Application Modernization with Choreo - The AI-Native Internal Developer Plat...
WSO2
 
Wondershare PDFelement Pro 11.4.20.3548 Crack Free Download
Wondershare PDFelement Pro 11.4.20.3548 Crack Free Download
Puppy jhon
 
Decipher SEO Solutions for your startup needs.
Decipher SEO Solutions for your startup needs.
mathai2
 
Artificial Intelligence Workloads and Data Center Management
Artificial Intelligence Workloads and Data Center Management
SandeepKS52
 
Who will create the languages of the future?
Who will create the languages of the future?
Jordi Cabot
 
Porting Qt 5 QML Modules to Qt 6 Webinar
Porting Qt 5 QML Modules to Qt 6 Webinar
ICS
 
Women in Tech: Marketo Engage User Group - June 2025 - AJO with AWS
Women in Tech: Marketo Engage User Group - June 2025 - AJO with AWS
BradBedford3
 
Emvigo Capability Deck 2025: Accelerating Innovation Through Intelligent Soft...
Emvigo Capability Deck 2025: Accelerating Innovation Through Intelligent Soft...
Emvigo Technologies
 
Advanced Token Development - Decentralized Innovation
Advanced Token Development - Decentralized Innovation
arohisinghas720
 
Rierino Commerce Platform - CMS Solution
Rierino Commerce Platform - CMS Solution
Rierino
 
Download Adobe Illustrator Crack free for Windows 2025?
Download Adobe Illustrator Crack free for Windows 2025?
grete1122g
 
SAP Datasphere Catalog L2 (2024-02-07).pptx
SAP Datasphere Catalog L2 (2024-02-07).pptx
HimanshuSachdeva46
 
GDG Douglas - Google AI Agents: Your Next Intern?
GDG Douglas - Google AI Agents: Your Next Intern?
felipeceotto
 
Meet You in the Middle: 1000x Performance for Parquet Queries on PB-Scale Dat...
Meet You in the Middle: 1000x Performance for Parquet Queries on PB-Scale Dat...
Alluxio, Inc.
 
Zoneranker’s Digital marketing solutions
Zoneranker’s Digital marketing solutions
reenashriee
 
Open Source Software Development Methods
Open Source Software Development Methods
VICTOR MAESTRE RAMIREZ
 
Transmission Media. (Computer Networks)
Transmission Media. (Computer Networks)
S Pranav (Deepu)
 
dp-700 exam questions sample docume .pdf
dp-700 exam questions sample docume .pdf
pravkumarbiz
 
SAP PM Module Level-IV Training Complete.ppt
SAP PM Module Level-IV Training Complete.ppt
MuhammadShaheryar36
 
On-Device AI: Is It Time to Go All-In, or Do We Still Need the Cloud?
On-Device AI: Is It Time to Go All-In, or Do We Still Need the Cloud?
Hassan Abid
 
Application Modernization with Choreo - The AI-Native Internal Developer Plat...
Application Modernization with Choreo - The AI-Native Internal Developer Plat...
WSO2
 
Wondershare PDFelement Pro 11.4.20.3548 Crack Free Download
Wondershare PDFelement Pro 11.4.20.3548 Crack Free Download
Puppy jhon
 
Ad

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
  • 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…