/users 2 0 0 { " u s e r _ i d s " : [ " 1 3 " , " 4 2 " ] } POST /users 2 0 1 { " i d " : " 1 3 " } GET /users/:id 2 0 0 { " i d " : " 1 3 " , " n a m e " : " b a s t i a n PUT /users/:id 2 0 0 { " i d " : " 1 3 " , " n a m e " : " f r e d " } GET /users/:id/posts 2 0 0 { " p o s t s " : [ . . . ] } POST /users/:id/posts 2 0 1 { " t i t l e " : " T e s t " , " c o n t e n t " :
Clients need out-of-band information: Needs to know each URI Needs to know (in advance) which method works on which resource Tight coupling between client and server
fixed resource names or hierarchies (an obvious coupling of client and server). [...] Instead, allow servers to instruct clients on how to construct appropriate URIs, [...] by defining those instructions within media types and link relations.
> u s e r _ u r l - > - > g i s t s _ u r l - > - > $ [ 0 ] . u r l - > https://api.github.com/ https://api.github.com/users/basti1302 https://api.github.com/users/basti1302/gists https://api.github.com/gists/7166618
s t = r e q u i r e ( ' r e q u e s t ' ) r e q u e s t . g e t ( ' h t t p s : / / a p i . g i t h u b . c o m / ' , f u n c t i o n ( e r r , r e s p o n s e ) { i f ( e r r ) { r e t u r n c o n s o l e . l o g ( e r r ) } v a r u r i = g e t U r i ( r e s p o n s e , ' u s e r _ u r l ' , { u s e r : ' b a s t i 1 3 0 2 ' } ) r e q u e s t . g e t ( u r i , f u n c t i o n ( e r r , r e s p o n s e ) { i f ( e r r ) { r e t u r n c o n s o l e . l o g ( e r r ) } u r i = g e t U r i ( r e s p o n s e , ' g i s t s _ u r l ' ) r e q u e s t . g e t ( u r i , f u n c t i o n ( e r r , r e s p o n s e ) { i f ( e r r ) { r e t u r n c o n s o l e . l o g ( e r r ) } u r i = n e x t U r i ( r e s p o n s e , ' $ [ 0 ] . u r l ' ) r e q u e s t . g e t ( u r i , f u n c t i o n ( e r r , r e s p o n s e ) { i f ( e r r ) { r e t u r n c o n s o l e . l o g ( e r r ) } v a r r e s o u r c e = J S O N . p a r s e ( r e s p o n s e . b o d y ) c o n s o l e . l o g ( r e s o u r c e ) } ) } ) } ) } )
e q u i r e ( ' t r a v e r s o n ' ) . j s o n . f r o m ( ' h t t p s : / / a p i . g i t h u b . c o m / ' ) a p i . n e w R e q u e s t ( ) . w i t h T e m p l a t e P a r a m e t e r s ( { u s e r : ' b a s t i 1 3 0 2 ' } ) . f o l l o w ( ' u s e r _ u r l ' , ' g i s t s _ u r l ' , ' $ [ 0 ] . u r l ' ) . g e t R e s o u r c e ( f u n c t i o n ( e r r , r e s o u r c e ) { c o n s o l e . l o g ( r e s o u r c e ) } )
/ a p i . e x a m p l e . c o m { " u s e r _ l o o k u p " : " h t t p : / / a p i . e x a m p l e . c o m / u s e r s / { u s e r _ n a m e } " } / / h t t p : / / a p i . e x a m p l e . c o m / u s e r s / b a s t i a n { " p o s t _ l o o k u p " : " h t t p : / / a p i . e x a m p l e . c o m / u s e r s / b a s t i a n / p o s t s / { p o s t _ i d } " } / / h t t p : / / a p i . e x a m p l e . c o m / u s e r s / b a s t i a n / p o s t s / 1 3 0 2 { " c o n t e n t " : " L o r e m i p s u m d o l o r s i t a m e t " } a p i . f o l l o w ( ' u s e r _ l o o k u p ' , ' p o s t _ l o o k u p ' ) . w i t h T e m p l a t e P a r a m e t e r s ( { u s e r _ n a m e : ' b a s t i a n ' , p o s t _ i d : 1 3 0 2 } ) . g e t R e s o u r c e ( f u n c t i o n ( e r r o r , d o c u m e n t ) { / / . . . } )
: { " n e s t e d " : { " l i n k " : " h t t p : / / a p i . e x a m p l e . c o m / c o n g r a t s / y o u / h a v e / f o u n d / m e " } } } a p i . n e w R e q u e s t ( ) . f o l l o w ( ' $ . d e e p l y . n e s t e d . l i n k ' ) . g e t R e s o u r c e ( f u n c t i o n ( e r r o r , d o c u m e n t ) { . . . } )
: { " s e l f " : { " h r e f " : " / o r d e r s " } , " n e x t " : { " h r e f " : " / o r d e r s ? p a g e = 2 " } , " f i n d " : { " h r e f " : " / o r d e r s { ? i d } " , " t e m p l a t e d " : t r u e } } , " _ e m b e d d e d " : { " o r d e r s " : [ { " _ l i n k s " : { " s e l f " : { " h r e f " : " / o r d e r s / 1 2 3 " } } , " t o t a l " : 3 0 . 0 0 , " s t a t u s " : " s h i p p e d " } ] } , " s h i p p e d T o d a y " : 2 0 }
h a l . e x a m p l e . c o m / : { " _ l i n k s " : { " f i n d " : { " h r e f " : " / o r d e r s { ? i d } " , " t e m p l a t e d " : t r u e } } } / / h t t p : / / h a l . e x a m p l e . c o m / o r d e r s ? 4 2 : { " _ l i n k s " : { " i t e m s " : { " h r e f " : " / o r d e r s / 4 2 / i t e m s " } } } / / h t t p : / / h a l . e x a m p l e . c o m / o r d e r s / 4 2 / i t e m s : { . . . } } v a r a p i = r e q u i r e ( ' t r a v e r s o n ' ) . j s o n H a l . f r o m ( ' h t t p : / / a p i . e x a m p l e . c o m / ' ) a p i . n e w R e q u e s t ( ) . f o l l o w ( ' f i n d ' , ' i t e m s ' ) . w i t h T e m p l a t e P a r a m e t e r s ( { i d : ' 4 7 1 1 ' } ) . g e t R e s o u r c e ( f u n c t i o n ( e r r o r , d o c u m e n t ) { / / . . . } )
npm) Grunt Build Run Mocha Tests in Node.js Browserify Everything -> Single File With UMD Shim some modules (reduces file size) Uglify it Start Test Server Run Mocha Tests in Browser (PhantomJS) ci.testling for cross-browser compatibility