Builiding API using great combination of tools and workflows how we can vastly speedup and improve our API development, without compromising stability and testability.
DB MIGRATION Version control for you DB schema Laravel Schema facade provides database agnostic support for creating and manipulating tables / / C r e a t e n e w m i g r a t i o n p h p a r t i s a n m a k e : m i g r a t i o n c r e a t e _ u s e r s _ t a b l e / / R u n m i g r a t i o n p h p a r t i s a n m i g r a t e / / R o l l b a c k m i g r a t i o n p h p a r t i s a n m i g r a t e
DB SEED Sample/Test data for your DB / / C r e a t e n e w s e e d e r p h p a r t i s a n m a k e : s e e d e r U s e r s T a b l e S e e d e r / / R u n s e e d e r s p h p a r t i s a n d b : s e e d / / R u n s e e d e r s v 2 p h p a r t i s a n m i g r a t e - - s e e d / / R u n s i n g l e s e e d e r p h p a r t i s a n d b : s e e d - - c l a s s = U s e r s T a b l e S e e d e r
MODEL FACTORY Default model attributes Uses Faker for generating values $ f a c t o r y - > d e f i n e ( A p p \ U s e r : : c l a s s , f u n c t i o n ( F a k e r \ G e n e r a t o r $ f a k e r ) { r e t u r n [ ' n a m e ' = > $ f a k e r - > n a m e , ' e m a i l ' = > $ f a k e r - > e m a i l , ' p a s s w o r d ' = > b c r y p t ( s t r _ r a n d o m ( 1 0 ) ) , ' r e m e m b e r _ t o k e n ' = > s t r _ r a n d o m ( 1 0 ) , ] ; } ) ;
Multiple Factory Types - multiple factories for the same Eloquent model class $ f a c t o r y - > d e f i n e A s ( A p p \ U s e r : : c l a s s , ' a d m i n ' , f u n c t i o n ( $ f a k e r ) u s e ( $ f a c t o r y $ u s e r = $ f a c t o r y - > r a w ( A p p \ U s e r : : c l a s s ) ; r e t u r n a r r a y _ m e r g e ( $ u s e r , [ ' a d m i n ' = > t r u e ] ) ; } ) ;
Attach relationships $ f a c t o r y - > d e f i n e ( A p p \ P o s t : : c l a s s , f u n c t i o n ( $ f a k e r ) { r e t u r n [ ' t i t l e ' = > $ f a k e r - > t i t l e , ' c o n t e n t ' = > $ f a k e r - > p a r a g r a p h , ' u s e r _ i d ' = > f u n c t i o n ( ) { r e t u r n f a c t o r y ( A p p \ U s e r : : c l a s s ) - > c r e a t e ( ) - > i d ; } , ' u s e r _ t y p e ' = > f u n c t i o n ( a r r a y $ p o s t ) { r e t u r n A p p \ U s e r : : f i n d ( $ p o s t [ ' u s e r _ i d ' ] ) - > t y p e ; } ] ; } ) ;
Using factories / / C r e a t e t h r e e A p p \ U s e r i n s t a n c e s . . . $ u s e r s = f a c t o r y ( A p p \ U s e r : : c l a s s , 3 ) - > m a k e ( ) ; / / C r e a t e t h r e e A p p \ U s e r " a d m i n " i n s t a n c e s . . . $ u s e r s = f a c t o r y ( A p p \ U s e r : : c l a s s , ' a d m i n ' , 3 ) - > m a k e ( ) ; / / C r e a t e A p p \ U s e r u s e r w i t h g i v e n n a m e . . . $ u s e r = f a c t o r y ( A p p \ U s e r : : c l a s s ) - > m a k e ( [ ' n a m e ' = > ' A b i g a i l ' , ] ) ; / / P e r s i s t i n g F a c t o r y M o d e l s $ u s e r = f a c t o r y ( A p p \ U s e r : : c l a s s ) - > c r e a t e ( ) ;
FORM REQUESTS Special class for validating request params and authorizing Each class has rules() (returns array) and authorize() (returns boolean) methods Type Hinting in Controller methods
p u b l i c f u n c t i o n r u l e s ( ) { r e t u r n [ ' t i t l e ' = > ' r e q u i r e d | u n i q u e : p o s t s | m a x : 2 5 5 ' , ' b o d y ' = > ' r e q u i r e d ' , ] ; } p u b l i c f u n c t i o n a u t h o r i z e ( ) { $ c o m m e n t I d = $ t h i s - > r o u t e ( ' c o m m e n t ' ) ; r e t u r n C o m m e n t : : w h e r e ( ' i d ' , $ c o m m e n t I d ) - > w h e r e ( ' u s e r _ i d ' , A u t h : : i d ( ) ) - > e x i s t s ( ) ; }
TESTING PHPUnit is included out of the box Laravel is built with testing in mind It's also ships with convenient helper methods Uses Mockery as mock object framework
CREATING HTTP REQUEST / / M a i n m e t h o d p u b l i c f u n c t i o n c a l l ( $ m e t h o d , $ u r i , $ p a r a m e t e r s = [ ] , $ c o o k i e s = [ ] , $ f i l e s = / / T h e r e a r e a l s o m e t h o d s f o r o t h e r h t t p v e r b s p u b l i c f u n c t i o n p o s t ( $ u r i , a r r a y $ d a t a = [ ] , a r r a y $ h e a d e r s = [ ] ) / / V i s i t t h e g i v e n U R I w i t h a J S O N r e q u e s t . p u b l i c f u n c t i o n j s o n ( $ m e t h o d , $ u r i , a r r a y $ d a t a = [ ] , a r r a y $ h e a d e r s = [ ] ) { / / r e m o v e d c o d e f r o m o r i g i n a l m e t h o d $ c o n t e n t = j s o n _ e n c o d e ( $ d a t a ) ; $ h e a d e r s = a r r a y _ m e r g e ( [ ' C O N T E N T _ L E N G T H ' = > m b _ s t r l e n ( $ c o n t e n t , ' 8 b i t ' ) , ' C O N T E N T _ T Y P E ' = > ' a p p l i c a t i o n / j s o n ' , ' A c c e p t ' = > ' a p p l i c a t i o n / j s o n ' , ] , $ h e a d e r s ) ;
DINGO API " r e q u i r e " : { " d i n g o / a p i " : " 1 . 0 . * @ d e v " } Provides set of tools to easily and quickly build your own API Requires Laravel 5.1+ or Lumen 5.1+ and PHP 5.5.9+
CONFIGURATION Much of the package comes preconfigured Use your .env file to configure most of the package Finer tuning of the package will require publishing the configuration file
Configure: Name Default Version Authentication Provider Throttling / Rate Limiting (Disabled by default) Response Transformer (Fractal is the default) Response Format (JSON and a JSON response format is registered by default) Error Format
CREATING API ENDPOINTS / / T o a v o i d c o m p l i c a t i o n s w i t h y o u r m a i n a p p l i c a t i o n r o u t e s t h i s p a c k a g e u t i l $ a p i = a p p ( ' D i n g o \ A p i \ R o u t i n g \ R o u t e r ' ) ; $ a p i - > v e r s i o n ( ' v 1 ' , f u n c t i o n ( $ a p i ) { $ a p i - > p o s t ( ' r e g i s t e r ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ R e g i s t e r C o n t r o l l e r $ a p i - > p o s t ( ' l o g i n ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ L o g i n C o n t r o l l e r @ l o g i n $ a p i - > g e t ( ' b l o c k s ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r @ g e t A $ a p i - > g e t ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r $ a p i - > p o s t ( ' b l o c k s ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r @ c r e $ a p i - > p u t ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r $ a p i - > d e l e t e ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l } ) ;
RESPONSES There's a number of different ways to return responses Use Response Builder Provides a fluent interface to easily build a more customizable response Generally used in conjunction with transformers. Use the Dingo\Api\Routing\Helpers trait
u s e D i n g o \ A p i \ R o u t i n g \ H e l p e r s ; u s e I l l u m i n a t e \ R o u t i n g \ C o n t r o l l e r ; c l a s s A p i C o n t r o l l e r e x t e n d s C o n t r o l l e r { u s e H e l p e r s ; }
RESPONDING WITH AN ARRAY p u b l i c f u n c t i o n s h o w ( $ i d ) { $ b l o c k = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > a r r a y ( $ b l o c k - > t o A r r a y ( ) ) ; }
RESPONDING WITH A SINGLE ITEM p u b l i c f u n c t i o n s h o w ( $ i d ) { $ b l o c k = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ b l o c k , n e w B l o c k T r a n s f o r m e r ) ; }
RESPONDING WITH A COLLECTION OF ITEMS p u b l i c f u n c t i o n s h o w ( $ i d ) { $ b l o c k s = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > c o l l e c t i o n ( $ b l o c k s , n e w B l o c k T r a n s f o r m e r ) ; }
RESPONDING WITH PAGINATED ITEMS p u b l i c f u n c t i o n s h o w ( $ i d ) { $ b l o c k s = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > p a g i n a t i o n ( $ b l o c k s , n e w B l o c k T r a n s f o r m e r ) ; }
OTHER RESPONSES / / R e s p o n d i n g W i t h N o C o n t e n t r e t u r n $ t h i s - > r e s p o n s e - > n o C o n t e n t ( ) ; / / R e s p o n d i n g W i t h C r e a t e d R e s p o n s e r e t u r n $ t h i s - > r e s p o n s e - > c r e a t e d ( ) ; / / A g e n e r i c e r r o r w i t h c u s t o m m e s s a g e a n d s t a t u s c o d e . r e t u r n $ t h i s - > r e s p o n s e - > e r r o r ( ' T h i s i s a n e r r o r . ' , 4 0 4 ) ; / / A n o t f o u n d e r r o r w i t h a n o p t i o n a l m e s s a g e a s t h e f i r s t p a r a m e t e r . r e t u r n $ t h i s - > r e s p o n s e - > e r r o r N o t F o u n d ( ) ; / / T h e r e a r e a l s o e r r o r B a d R e q u e s t , e r r o r F o r b i d d e n , e r r o r I n t e r n a l , e r r o r U n a u t h
Adding Additional Headers r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > w i t h H e a d e r ( ' X - F o o ' Adding Meta Data r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > a d d M e t a ( ' f o o ' / / s e t a n a r r a y o f m e t a d a t a i n s t e a d o f c h a i n i n g m u l t i p l e m e t h o d c a l l s r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > s e t M e t a ( $ m e t a Setting Response Status Code r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > s e t S t a t u s C o d e
TRANSFORMERS Easily and consistently transform objects into an array Type-cast integers and booleans, include pagination results, and nest relationships Fractal is the default transformation layer used by Dingo
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( ) { $ t h i s - > a v a i l a b l e I n c l u d e s = [ ' p r e s e n t a t i o n s ' , ' b l o c k _ s e t t i n g s ' , ] ; $ t h i s - > d e f a u l t I n c l u d e s = [ ' b l o c k _ s e t t i n g s ' , ] ; } p u b l i c f u n c t i o n t r a n s f o r m ( B l o c k $ b l o c k ) { r e t u r n [ ' i d ' = > ( i n t ) $ b l o c k - > i d , ' n a m e ' = > $ b l o c k - > n a m e ,
FORM REQUEST END ERROR RESPONSES Extend the base API form request class Implement your own class Dingo\Api\Exception\ValidationHttpException should be thrown
“ Dredd is a language-agnostic command-line tool for validating API documentation written in API Blueprint format against its backend implementation. ” Dredd reads your API description Validates whether API replies with expected responses Continuous Integration Support Hooks — a glue code for each test setup and teardown Use of Gavel.js library Do not install dredd manually on Homestead - use a er.sh
HEADERS EXPECTATIONS All headers given in example must be present in the response Only values of headers significant for content negotiation are validated All other headers values can differ
BODY EXPECTATIONS All JSON keys on any level given in the example must be present in the response JSON Response JSON values must be of the same JSON primitive type All JSON values can differ Arrays can have additional items, type or structure is not validated. Plain text must match perfectly
PHP Dredd hook handler Provides a bridge between the Dredd API Testing Framework and PHP environment " r e q u i r e - d e v " : { " d d e l n a n o / d r e d d - h o o k s - p h p " : " ~ 1 . 0 . 0 " , }
HOOKS USAGE Loading db fixtures Cleanup a er test step or steps Handling authentication and sessions Passing data between transactions (saving state from responses to stash) Modifying request generated from blueprint Changing generated expectations Setting custom expectations Debugging via logging stuff
r e q u i r e _ _ D I R _ _ . ' / . . / . . / . . / v e n d o r / a u t o l o a d . p h p ' ; $ a p p = r e q u i r e _ _ D I R _ _ . ' / . . / . . / . . / b o o t s t r a p / a p p . p h p ' ; $ a p p - > m a k e ( \ I l l u m i n a t e \ C o n t r a c t s \ C o n s o l e \ K e r n e l : : c l a s s ) - > b o o t s t r a p ( ) ; A r t i s a n : : c a l l ( ' m i g r a t e ' ) ; $ u s e r = f a c t o r y ( \ E m a t e r i a l s \ A p i \ M o d e l s \ U s e r : : c l a s s ) - > c r e a t e ( [ ' e m a i l ' = > ' k n o w n u s e r @ e x a m p l e . d e v ' ] ) ; H o o k s : : b e f o r e E a c h ( f u n c t i o n ( & $ t r a n s a c t i o n ) u s e ( $ a p p ) { $ a p p - > m a k e ( ' d b ' ) - > b e g i n T r a n s a c t i o n ( ) ; } ) ; H o o k s : : a f t e r E a c h ( f u n c t i o n ( & $ t r a n s a c t i o n ) u s e ( $ a p p ) {
RUN DREDD d r e d d d o c u m e n t a t i o n . a p i b h t t p : / / l o c a l h o s t : 8 0 0 1 - - s e r v e r " p h p - S 0 . 0 . 0 . 0 : 8 0 0 1 - t p u b l i c / " - - l a n g u a g e v e n d o r / b i n / d r e d d - h o o k s - p h p - - h o o k f i l e s t e s t s / d r e d d / h o o k s / h o o k f i l e . p h p
REPOSITORIES " r e q u i r e " : { " b o s n a d e v / r e p o s i t o r i e s " : " 0 . * " } Container where data access logic is stored Hides the details of data access logic from business logic
i n t e r f a c e R e p o s i t o r y I n t e r f a c e { p u b l i c f u n c t i o n a l l ( $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n p a g i n a t e ( $ p e r P a g e = 1 5 , $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n c r e a t e ( a r r a y $ d a t a ) ; p u b l i c f u n c t i o n u p d a t e ( a r r a y $ d a t a , $ i d ) ; p u b l i c f u n c t i o n d e l e t e ( $ i d ) ; p u b l i c f u n c t i o n f i n d ( $ i d , $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n f i n d B y ( $ f i e l d , $ v a l u e , $ c o l u m n s = a r r a y ( ' * ' ) ) ; }
u s e A p p \ R e p o s i t o r i e s \ F i l m R e p o s i t o r y a s F i l m ; c l a s s F i l m s C o n t r o l l e r e x t e n d s C o n t r o l l e r { / * * * @ v a r F i l m * / p r i v a t e $ f i l m ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( F i l m $ f i l m ) { $ t h i s - > f i l m = $ f i l m ; } p u b l i c f u n c t i o n i n d e x ( ) { r e t u r n \ R e s p o n s e : : j s o n ( $ t h i s - > f i l m - > a l l ( ) ) ;
Basic actions are just enough for simple querying Added CriteriaInterface i n t e r f a c e C r i t e r i a I n t e r f a c e { p u b l i c f u n c t i o n s k i p C r i t e r i a ( $ s t a t u s = t r u e ) ; p u b l i c f u n c t i o n g e t C r i t e r i a ( ) ; p u b l i c f u n c t i o n g e t B y C r i t e r i a ( C r i t e r i a $ c r i t e r i a ) ; p u b l i c f u n c t i o n p u s h C r i t e r i a ( C r i t e r i a $ c r i t e r i a ) ; p u b l i c f u n c t i o n a p p l y C r i t e r i a ( ) ; }
c l a s s L e n g t h O v e r T w o H o u r s i m p l e m e n t s C r i t e r i a I n t e r f a c e { p u b l i c f u n c t i o n a p p l y ( $ m o d e l , R e p o s i t o r y $ r e p o s i t o r y ) { $ q u e r y = $ m o d e l - > w h e r e ( ' l e n g t h ' , ' > ' , 1 2 0 ) ; r e t u r n $ q u e r y ; } }
u s e A p p \ R e p o s i t o r i e s \ C r i t e r i a \ F i l m s \ L e n g t h O v e r T w o H o u r s ; u s e A p p \ R e p o s i t o r i e s \ F i l m R e p o s i t o r y a s F i l m ; c l a s s F i l m s C o n t r o l l e r e x t e n d s C o n t r o l l e r { / * * * @ v a r F i l m * / p r i v a t e $ f i l m ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( F i l m $ f i l m ) { $ t h i s - > f i l m = $ f i l m ; } p u b l i c f u n c t i o n i n d e x ( ) {
CORS (CROSS-ORIGIN RESOURCE SHARING) " r e q u i r e " : { " b a r r y v d h / l a r a v e l - c o r s " : " ^ 0 . 7 . 2 " } Allows you to send CORS headers with ACL-style per-url configuration. Supports Laravel & Lumen