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

More Related Content

What's hot (20)

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

Similar to Building Awesome APIs in Grails (20)

KEY
Api development with rails
Edwin Cruz
 
PDF
Api's and ember js
Edwin Cruz
 
PDF
Mashing Up The Guardian
Michael Brunton-Spall
 
PPTX
Prairie DevCon 2015 - Crafting Evolvable API Responses
darrelmiller71
 
PPT
Cloud2Car - Force.com and the Internet of Things
Salesforce Developers
 
PDF
Mashing Up The Guardian
Michael Brunton-Spall
 
PDF
What Makes a Great Open API?
John Musser
 
PDF
Don't screw it up! How to build durable API
Alessandro Cinelli (cirpo)
 
PDF
How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016
Codemotion
 
PDF
David Gómez G. - Hypermedia APIs for headless platforms and Data Integration ...
Codemotion
 
PDF
Cdm mil-18 - hypermedia ap is for headless platforms and data integration
David Gómez García
 
PDF
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
PDF
Going FaaSter, Functions as a Service at Netflix
Yunong Xiao
 
PDF
Lets dance- Dutch Architecture Conference (LAC) 2018
Yenlo
 
PPS
Simplify your professional web development with symfony
Francois Zaninotto
 
PDF
What Makes a Great Open API?
Salesforce Developers
 
PPTX
Swift meetup22june2015
Claire Townend Gee
 
PDF
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
Jorge Ferrer
 
PDF
Continuous Integration and Deployment Best Practices on AWS
Danilo Poccia
 
PDF
Mobile Development integration tests
Kenneth Poon
 
Api development with rails
Edwin Cruz
 
Api's and ember js
Edwin Cruz
 
Mashing Up The Guardian
Michael Brunton-Spall
 
Prairie DevCon 2015 - Crafting Evolvable API Responses
darrelmiller71
 
Cloud2Car - Force.com and the Internet of Things
Salesforce Developers
 
Mashing Up The Guardian
Michael Brunton-Spall
 
What Makes a Great Open API?
John Musser
 
Don't screw it up! How to build durable API
Alessandro Cinelli (cirpo)
 
How To Structure Go Applications - Paul Bellamy - Codemotion Milan 2016
Codemotion
 
David Gómez G. - Hypermedia APIs for headless platforms and Data Integration ...
Codemotion
 
Cdm mil-18 - hypermedia ap is for headless platforms and data integration
David Gómez García
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
Going FaaSter, Functions as a Service at Netflix
Yunong Xiao
 
Lets dance- Dutch Architecture Conference (LAC) 2018
Yenlo
 
Simplify your professional web development with symfony
Francois Zaninotto
 
What Makes a Great Open API?
Salesforce Developers
 
Swift meetup22june2015
Claire Townend Gee
 
The liferay case: lessons learned evolving from RPC to Hypermedia REST APIs
Jorge Ferrer
 
Continuous Integration and Deployment Best Practices on AWS
Danilo Poccia
 
Mobile Development integration tests
Kenneth Poon
 
Ad

Recently uploaded (20)

PPTX
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
PPTX
A Complete Guide to Salesforce SMS Integrations Build Scalable Messaging With...
360 SMS APP
 
PDF
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
PPTX
Human Resources Information System (HRIS)
Amity University, Patna
 
PPTX
Engineering the Java Web Application (MVC)
abhishekoza1981
 
PDF
Alexander Marshalov - How to use AI Assistants with your Monitoring system Q2...
VictoriaMetrics
 
PPTX
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
PPTX
MailsDaddy Outlook OST to PST converter.pptx
abhishekdutt366
 
PDF
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
PPTX
Revolutionizing Code Modernization with AI
KrzysztofKkol1
 
PDF
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
PPTX
Tally software_Introduction_Presentation
AditiBansal54083
 
PDF
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
PDF
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
PPTX
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
PDF
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
PPTX
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
PPTX
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
PDF
Linux Certificate of Completion - LabEx Certificate
VICTOR MAESTRE RAMIREZ
 
PDF
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
How Apagen Empowered an EPC Company with Engineering ERP Software
SatishKumar2651
 
A Complete Guide to Salesforce SMS Integrations Build Scalable Messaging With...
360 SMS APP
 
GetOnCRM Speeds Up Agentforce 3 Deployment for Enterprise AI Wins.pdf
GetOnCRM Solutions
 
Human Resources Information System (HRIS)
Amity University, Patna
 
Engineering the Java Web Application (MVC)
abhishekoza1981
 
Alexander Marshalov - How to use AI Assistants with your Monitoring system Q2...
VictoriaMetrics
 
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
MailsDaddy Outlook OST to PST converter.pptx
abhishekdutt366
 
Alarm in Android-Scheduling Timed Tasks Using AlarmManager in Android.pdf
Nabin Dhakal
 
Revolutionizing Code Modernization with AI
KrzysztofKkol1
 
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
Tally software_Introduction_Presentation
AditiBansal54083
 
Capcut Pro Crack For PC Latest Version {Fully Unlocked} 2025
hashhshs786
 
Revenue streams of the Wazirx clone script.pdf
aaronjeffray
 
Writing Better Code - Helping Developers make Decisions.pptx
Lorraine Steyn
 
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
Java Native Memory Leaks: The Hidden Villain Behind JVM Performance Issues
Tier1 app
 
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
Linux Certificate of Completion - LabEx Certificate
VICTOR MAESTRE RAMIREZ
 
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
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…