building awesome apis with grails
DESCRIPTION
Speaker: Chris Latimer Essential Grails Track Grails provides rapid API capabilities out of the box, but the developing an API that is ready for public consumption takes a little work.TRANSCRIPT
![Page 1: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/1.jpg)
© 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
Building Awesome APIs in GrailsBy Chris Latimer
![Page 2: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/2.jpg)
2
What makes an API awesome?
![Page 3: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/3.jpg)
3
Is it using JSON payloads
instead of XML?
![Page 4: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/4.jpg)
Using this Template
4
Is it strict adherence to REST principles?
![Page 5: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/5.jpg)
5
API Fielding Score
![Page 6: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/6.jpg)
6
![Page 7: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/7.jpg)
7Predictable and Consistent
![Page 8: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/8.jpg)
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:
![Page 9: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/9.jpg)
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" />
![Page 10: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/10.jpg)
10
Stable Versions
/v1/endpoint
/v2/endpoint
Accept-‐Version: 1.0
Accept-‐Version: 1.1
URI Based Accept Header
Accept: application/vnd.your.api.v2+json
Accept: application/vnd.your.api.v2.1+json
Content Type
![Page 11: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/11.jpg)
11
Predictable Response Codes
400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found
2xx Successful 4xx Client Error
500 Server Error 502 Bad Gateway 503 Unavailable
5xx Server Error
200 Success 201 Created
![Page 12: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/12.jpg)
12Intuitive Structure
![Page 13: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/13.jpg)
13
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
Intuitive URI Structure
![Page 14: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/14.jpg)
14
Intuitive Navigation
"total": 659212, "page": 2, "per_page": 10, "paging": { "next": "/channels?page=3", "previous": "/channels?page=1", "first": "/channels?page=1", "last": "/channels?page=65922" }
Pagination
![Page 15: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/15.jpg)
15
Intuitive Navigation
{ “uri": "/categories/experimental", "name": "Experimental", "subcategories": [ { "uri": “/categories/experimental/animation", "name": "Animation", "link": “https://vimeo.com/categories/…” }… ] }
Related Resources
![Page 16: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/16.jpg)
Flexible Responses
![Page 17: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/17.jpg)
17
Partial Responses
/feeds/api/users/default/uploads
Get Full Response
/feeds/api/users/default/uploads? \ fields=entry(title,gd:comments,yt:statistics)
Get Partial Response
![Page 18: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/18.jpg)
18
Result Filtering
/feeds/api/videos?q=surfing&max-‐results=10
Get List of Videos
/feeds/api/videos?q=surfing&max-‐results=10 &fields=entry[yt:statistics/@viewCount > 1000000]
Get Videos with 1,000,000+ Views
![Page 19: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/19.jpg)
19
Customized Responses
ItemId=B00008OE6I
ItemLookup - Default
ItemId=B00008OE6I &ResponseGroup=Reviews
ItemLookup - Default With Reviews
ItemId=B00008OE6I &ResponseGroup=Large,Reviews,Offers
ItemLookup - Large With Reviews and Offers
![Page 20: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/20.jpg)
20
Easy to Learn and Experiment With
![Page 21: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/21.jpg)
21
![Page 22: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/22.jpg)
22
![Page 23: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/23.jpg)
23
Designing an awesome API
![Page 24: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/24.jpg)
Apps API
![Page 25: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/25.jpg)
G
![Page 26: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/26.jpg)
Phone Shopping App
/phones
/devices
/manufacturers
Potential Resources
![Page 27: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/27.jpg)
Phone Shopping App
Pagination
Filtering
Versioning
API Features
![Page 28: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/28.jpg)
Phone Shopping App
/phoneVariations
/phone/{id}
/phone/{id}/variations
Potential Resources
![Page 29: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/29.jpg)
Phone Shopping App
Complete vs. Compact representations
Including related entities
Linking to other resources
API Features
![Page 30: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/30.jpg)
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 Endpoints
![Page 31: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/31.jpg)
Phone API Versioning
Accept-‐Version: 1.0
Accept-‐Version: 2.0
Header Based
![Page 32: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/32.jpg)
{ “entity” : { “attr1” : “value1”, “attr2” : “value2”, … }, “metadata” : { } }
{ “attr1” : “value1”, “attr2” : “value2”, … }
General Response Structure Patterns
![Page 33: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/33.jpg)
{ “entities” : [ { “attr1” : “value1”, “attr2” : “value2”, … },{…},{…}… ], “metadata” : { } }
[ { “attr1” : “value1”, “attr2” : “value2”, … }, {…},{…}… ]
General Response Structure Patterns
![Page 34: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/34.jpg)
API Formats
“phones” : [ { “name”:“iPhone 5s”, “basePrice”:599.99, … } ]
JSON Default<phones> <phone> <name>iPhone 5s</name> … </phone> … </phones>
XML by Request
![Page 35: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/35.jpg)
Intuitive Response Structures
{ “phones” : [ {…}, {…}, …], “paging” : { … } }
Collection ResponseSingle Response{ phone: { “key1” : “value1”, … “keyN” : “valueN”, “links” : [ {…},{…}…] } }
![Page 36: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/36.jpg)
Complete and Compact Representations
Attribute Compact Complete
name
basePrice
variations
manufacturer
![Page 37: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/37.jpg)
Customized Responses
{ “name” : “iPhone5s”, … }
GET /phones/1
{ “name” : “iPhone5s”, … “includes” : [ { “accessories” : […] } ] }
GET /phones/1?include=accessories
![Page 38: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/38.jpg)
PaginationGET /phones{ “phones” : [ {…}, {…}, …], “paging” : { “totalCount” : 233, “currentMax” : 10, “currentOffset” : 40 } }
![Page 39: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/39.jpg)
GET /phones/1{ …, “links” : [ { “rel” : “self”, “href” : “http://…”, }, { “rel” : “manufacturer”, “href” : “http://…” } ] }
Linking to Related Entities
![Page 40: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/40.jpg)
Building an awesome API using
![Page 41: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/41.jpg)
Phone
Manufacturer
Variation
Domain Model
![Page 42: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/42.jpg)
API Features
• Accept Header Versioning
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
![Page 43: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/43.jpg)
Out of the Box APIs
@Resource(uri="/phones", formats=["json", "xml"]) class Phone { … }
![Page 44: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/44.jpg)
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
@Resource URL Mappings
![Page 45: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/45.jpg)
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
![Page 46: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/46.jpg)
![Page 47: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/47.jpg)
Link is on Twitter - @chrislatimer
![Page 48: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/48.jpg)
Demo
> git clone https://github.com/chrislatimer/gmobile.git
> cd gmobile
> git checkout api-‐step-‐1
![Page 49: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/49.jpg)
API Features
• Accept Header Versioning
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
• Predictable Response Codes
• JSON and XML
• RESTful URIs
![Page 50: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/50.jpg)
Implementing API Versioning
Version 1Controller
Version 2Controller
URL MappingsAPI Requests
![Page 51: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/51.jpg)
Controller Namespaces
class PhoneController extends RestfulController<Phone> { static namespace = 'v1' }
Version 1 Controller
Version 2 Controllerclass PhoneController extends RestfulController<Phone> { static namespace = 'v2' }
![Page 52: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/52.jpg)
URL Mappings"/phones"(version:'1.0', resources:"phone", namespace:'v1')
"/phones"(version:'2.0', resources:"phone", namespace:'v2')
Version 1Controller
Version 2Controller
Accept-‐Version: 1.0
Accept-‐Version: 2.0
![Page 53: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/53.jpg)
Demo
> git checkout api-‐step-‐2
![Page 54: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/54.jpg)
API Features
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
![Page 55: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/55.jpg)
Peanut - Ugliest Dog 2014
![Page 56: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/56.jpg)
"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?
![Page 57: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/57.jpg)
API Features
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
• Prettier JSON
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
![Page 58: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/58.jpg)
How can we improve our response payload?
![Page 59: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/59.jpg)
Out of the Box Customization
beans { bookRenderer(XmlRenderer, Book) { includes = [‘title’] } }
include attributes exclude attributes
This can be useful for verysimple customization
beans { bookRenderer(XmlRenderer, Book) { excludes = [‘title’] } }
![Page 60: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/60.jpg)
Simplified Picture of the Response Flow
Controller Renderer Converter Marshaller
as JSON/XML
respond
render
valuemarshal
![Page 61: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/61.jpg)
class PhoneMarshallerJson extends ClosureObjectMarshaller<JSON> {
static final marshal = { Phone phone -‐> def map = [:] … map }
public PhoneMarshallerJson() { super(Phone, marshal) } }
Creating a Custom Marshaller
![Page 62: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/62.jpg)
class PhoneMarshallerXml extends ClosureObjectMarshaller<XML>{
def static final marshal = { Phone phone, XML xml -‐> xml.build { name(phone.name) } }
public PhoneMarshallerXml() { super(Phone, marshal) } }
Creating a Custom Marshaller
![Page 63: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/63.jpg)
beans = { customPhoneJsonMarshaller(ObjectMarshallerRegisterer) { marshaller = new PhoneMarshallerJson() converterClass = JSON priority = 1 }
customPhoneXmlMarshaller(ObjectMarshallerRegisterer) { marshaller = new PhoneMarshallerXml() converterClass = XML priority = 1 } }
Registering a Custom Marshaller
![Page 64: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/64.jpg)
Demo
> git checkout api-‐step-‐3
![Page 65: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/65.jpg)
API Features
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
• Prettier JSON
![Page 66: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/66.jpg)
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) }
}
Creating Named Marshallers
![Page 67: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/67.jpg)
def init = { JSON.createNamedConfig('compact') { it.registerObjectMarshaller(Phone, PhoneMarshallerJsonCompact.marshal) }
JSON.createNamedConfig('complete') { it.registerObjectMarshaller(Phone, PhoneMarshallerJson.marshal) } }
Registering Named Marshallers
![Page 68: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/68.jpg)
Demo
> git checkout api-‐step-‐4
![Page 69: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/69.jpg)
API Features
• Pagination
• Custom Responses
• Related Links
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
• Prettier JSON
• Multiple Representations
![Page 70: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/70.jpg)
class ApiJsonRenderer<T> extends AbstractRenderer<T> {
public ApiJsonRenderer(Class<T> targetClass) { super(targetClass, MimeType.JSON); }
@Override void render(T object, RenderContext context) { // rendering logic } }
Creating a Custom Renderer
![Page 71: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/71.jpg)
def show(Phone phone) { def detail = params.detail ?: "complete" withFormat { json { respond(phone, [detail:detail]) } xml { XML.use(params?.detail?.toLowerCase() ?: "complete") { respond phone } } } }
Using a Custom Renderer
![Page 72: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/72.jpg)
beans = { phoneRenderer(ApiJsonRenderer, Phone) }
Registering a Custom Renderer
![Page 73: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/73.jpg)
Demo
> git checkout api-‐step-‐5
![Page 74: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/74.jpg)
The monkey wrench in grails.converters.JSON
![Page 75: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/75.jpg)
class ApiJSON extends JSON { … public void renderPartial(JSONWriter out) { initWriter(out) super.value(target) } protected initWriter(JSONWriter out) { writer = out referenceStack = new Stack<Object>(); } }
Creating a Custom Converter
![Page 76: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/76.jpg)
Demo
> git checkout api-‐step-‐6
![Page 77: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/77.jpg)
def index() { … withFormat { json { respond Phone.list(params), [detail:detail, paging:[totalCount: Phone.count(), currentMax: params.max, curentOffset:offset]] } …
Rendering Paging Info
![Page 78: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/78.jpg)
void render(T object, RenderContext context) { … if(context.arguments?.paging) { writer.key("paging") converter = context.arguments.paging as ApiJSON converter.renderPartial(writer) } … }
Rendering Paging Info
![Page 79: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/79.jpg)
Demo
> git checkout api-‐step-‐7
![Page 80: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/80.jpg)
API Features
• Custom Responses
• Related Links
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
• Prettier JSON
• Multiple Representations
• Pagination
![Page 81: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/81.jpg)
def show(Phone phone) { … withFormat { json { respond(phone, [detail:detail, include:params?.list('include')]) } … } }
Customizing Responses
![Page 82: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/82.jpg)
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() } … }
Rendering Custom Responses
![Page 83: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/83.jpg)
Demo
> git checkout api-‐step-‐8
![Page 84: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/84.jpg)
API Features
• Related Links• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
• Prettier JSON
• Multiple Representations
• Pagination
• Custom Responses
![Page 85: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/85.jpg)
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
![Page 86: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/86.jpg)
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
![Page 87: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/87.jpg)
closure.curry(linkGenerator)
Including Related Links
![Page 88: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/88.jpg)
Demo
> git checkout api-‐step-‐9
![Page 89: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/89.jpg)
API Features
• Predictable Response Codes
• JSON and XML
• RESTful URIs
• Accept Header Versioning
• Prettier JSON
• Multiple Representations
• Pagination
• Custom Responses
• Related Links
![Page 90: Building Awesome APIs with Grails](https://reader035.vdocuments.site/reader035/viewer/2022070322/559444571a28ab06308b47a5/html5/thumbnails/90.jpg)
Is this API Awesome?
It’s getting there…