Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NodeJS Development Best Practices

NodeJS Development Best Practices

Best Practices for NodeJS Development.

Here's a sample/starter project implemented based on these best practices https://github.com/kabirbaidhya/nodejs-sample-app.

Kabir Baidhya

November 11, 2016
Tweet

More Decks by Kabir Baidhya

Other Decks in Technology

Transcript

  1. What is NodeJS? A JavaScript runtime built on Chrome's V8

    engine. Used primarily for server­side application development, building cross­platform tools & applications, and much more.
  2. How does it work? c o n s o l

    e . l o g ( ' A ' ) ; c o n s o l e . l o g ( ' B ' ) ; s e t T i m e o u t ( ( ) = > { c o n s o l e . l o g ( ' C ' ) ; } , 1 0 0 0 ) ; s e t T i m e o u t ( ( ) = > { c o n s o l e . l o g ( ' D ' ) ; } ) ; c o n s o l e . l o g ( ' E ' ) ;
  3. Think asynchronously Avoid using synchronous or blocking code like this.

    v a r f s = r e q u i r e ( ' f s ' ) ; f o r ( v a r i = 0 ; i < f i l e s . l e n g t h ; i + + ) { / / C a l l i n g s y n c h r o n o u s f u n c t i o n d a t a = f s . r e a d F i l e S y n c ( f i l e s [ i ] ) ; c o n s o l e . l o g ( ' C o n t e n t s = ' , d a t a ) ; } Consider using async alternatives instead. f s . r e a d F i l e ( f i l e n a m e , ' u t f 8 ' , ( e r r , d a t a ) = > { / / D o s o m e t h i n g h e r e } ) ;
  4. Put require() at the top r e q u i

    r e ( ) & i m p o r t calls belong to the top; not in the middle of code. a p p . g e t ( " / a p i / s o m e t h i n g " , f u n c t i o n ( r e q , r e s ) { v a r d a t a s t o r e = r e q u i r e ( " m y D a t a S t o r e D e p " ) ( s o m e C o n f i g ) ; d a t a s t o r e . g e t ( r e q . q u e r y . s o m e K e y ) . . . } ) ; Why? Because they're synchronous and blocking.
  5. What about CPU intensive jobs? The Non­Blocking anync IO is

    awesome for IO bound tasks. But what about CPU intensive works? Delegate them to queues or worker processes dedicated to do those work. PS: Avoid thinking of multi­threading as a workaround.
  6. Callback Hell? Asynchronity does imply use of callbacks everywhere and

    could lead to callback hell. g e t D a t a ( f u n c t i o n ( x ) { g e t M o r e D a t a ( x , f u n c t i o n ( y ) { g e t M o r e D a t a ( y , f u n c t i o n ( z ) { g e t E v e n t M o r e D a t a ( z , f u n c t i o n ( ) { / / A n d e v e n m o r e . . . } ) ; } ) ; } ) ; } ) ;
  7. Use Promises Promises does save us from callback hell e

    x p o r t f u n c t i o n g e t P r o j e c t s B y C o m p a n i e s ( a u t h , c o m p a n y I d s ) l e t p r o m i s e s = c o m p a n y I d s . m a p ( c o m p a n y I d = > g e t P r o j e c t s B y C o m p a n y ( a u t h , c o m p a n y I d ) ) ; r e t u r n P r o m i s e . a l l ( p r o m i s e s ) . t h e n ( f o r m a t R e s u l t s ) ; } f u n c t i o n g e t P r o j e c t s B y C o m p a n y ( t o k e n , c o m p a n y I d ) { l e t u r l = ` / p r o j e c t s ? c o m p a n y _ i d = $ { c o m p a n y I d } ` ; r e t u r n r p ( { m e t h o d : ' G E T ' , u r l : B A S E _ U R I + u r l } ) ; } f u n c t i o n f o r m a t R e s u l t s ( d a t a ) { r e t u r n d a t a . r e d u c e ( ( p r e v , c u r ) = > p r e v . c o n c a t ( c u r ) , [ ] ) ; }
  8. And a s y n c / a w a

    i t And the ES7 adds a better syntatic sugar to the language using a s y n c / a w a i t . i m p o r t r p f r o m ' r e q u e s t ­ p r o m i s e ' ; a s y n c f u n c t i o n r e q u e s t A l l ( u r l s ) { l e t r e s u l t = a w a i t P r o m i s e . a l l ( u r l s . m a p ( u r l = > r p ( u r l ) ) ) ; r e t u r n r e s u l t ; } c o n s t u r l s = [ ' h t t p s : / / w w w . g o o g l e . c o m ' , ' h t t p s : / / w w w . a p p l e . c o m ' , ' h t t p s : / / w w w . y a h o o . c o m ' ] ; l e t s i t e s = r e q u e s t A l l ( u r l s ) ;
  9. Use n p m i n i t Always use

    n p m i n i t (or y a r n i n i t ) to initialize your new project.
  10. Use n p m s c r i p t

    s for your tasks You might not task runners like g u l p all the time. Mostly n p m s c r i p t s would suffice. " s c r i p t s " : { " s t a r t " : " n o d e m o n ­ ­ e x e c b a b e l ­ n o d e s r c / i n d e x . j s " , " s t a r t : s y s t e m d " : " s y s t e m c t l s t a r t o n t a r g e t ­ m o d e l a p i . s e r v i c " l i n t " : " e s l i n t . " , " c l e a n " : " r m r a f d i s t / " , " b u i l d " : " r u n ­ s c l e a n b a b e l " , " r e s t a r t " : " s y s t e m c t l r e s t a r t o n t a r g e t ­ m o d e l a p i . s e r v i c e " " s t o p " : " s y s t e m c t l s t o p o n t a r g e t ­ m o d e l a p i . s e r v i c e " , " l o g s " : " j o u r n a l c t l ­ f ­ u o n t a r g e t ­ m o d e l a p i . s e r v i c e " , " b a b e l " : " b a b e l s r c / ­ d d i s t / " } , $ n p m s t a r t # o r y a r n s t a r t
  11. More sophisticated scripts # ! / u s r /

    b i n / e n v b a s h # G e t t h e b r a n c h n a m e f r o m t h e e n v i r o n m e n t , d e f a u l t s t o ' d e v ' i f [ ­ z " $ B R A N C H " ] ; t h e n B R A N C H = " d e v " f i # C h e c k o u t t o t h e s p e c i f i e d b r a n c h g i t f e t c h g i t c h e c k o u t " $ B R A N C H " # P u l l t h e l a t e s t c h a n g e s g i t p u l l o r i g i n " $ B R A N C H " # B u i l d t h e a p p l i c a t i o n n p m r u n b u i l d ; s y s t e m c t l r e s t a r t " $ S E R V I C E " . . .
  12. Register them as n p m s c r i

    p t s Create your complex scripts in separate files and register n p m s c r i p t s for them. You may use s h , p y t h o n , j s etc anything to write your scripts. " s c r i p t s " : { " d e p l o y " : " . / s c r i p t s / d e p l o y . s h " " , " b u i l d " : " . / s c r i p t s / b u i l d . p y " , " a b c " : " . / s c r i p t s / a b c . j s " } Now run your script $ n p m r u n d e p l o y You might want to use packages like shelljs for these.
  13. Do lint your code Make sure you l i n

    t your javascript code with something like e s l i n t . And ensure the linter doesn't throw any errors before you commit. p a r s e r : ' b a b e l ­ e s l i n t ' e n v : e s 6 : t r u e n o d e : t r u e p a r s e r O p t i o n s : e c m a V e r s i o n : 6 , s o u r c e T y p e : ' m o d u l e ' e c m a F e a t u r e s : m o d u l e s : t r u e
  14. Why linting? Improves code quality. Enforces rules for the coding

    standards. Code consistency. Helps in avoiding bad practices Prevents some potential bugs.
  15. Project Directory Structure Keep your root directories simple like these:

    s r c / ­ your source files s e r v e r / ­ server side code a p p / ­ client­side app code (if in the same repository) d i s t / or b u i l d / ­ compiled files from the source (in case you use ES6+, typescript, coffeescriptetc). And, should be added to g i t i g n o r e . p u b l i c / ­ in case you have files to be served publicly. r e s o u r c e s / ­ other miscellaneous projects resources or files
  16. Example project structure So your project might look similar to

    this: ­ s r c / ­ s e r v e r / ­ c o n f i g / ­ c o n t r o l l e r s / ­ s e r v i c e s ­ m i d d l e w a r e s ­ m o d e l s / ­ v i e w s / ­ i n d e x . j s ­ a p p / ­ i n d e x . j s ­ d i s t / ­ p u b l i c / ­ b u i l d / ­ i n d e x . h t m l ­ r e s o u r c e s / ­ s c r i p t s / ­ d a t a /
  17. Application Configuration Create an application central config file to put

    all your config params. / / c o n f i g . j s m o d u l e . e x p o r t s = { b u c k e t P o l i c y : p r o c e s s . e n v . B U C K E T _ P O L I C Y | | ' t r a n s i e n t ' n i o s : { b a s e U r i : p r o c e s s . e n v . N I O S _ B A S E _ U R I , . . . } , p r o c o r e : { c l i e n t S e c r e t : p r o c e s s . e n v . P R O C O R E _ C L I E N T _ S E C R E T , r e d i r e c t U r i : p r o c e s s . e n v . P R O C O R E _ R E D I R E C T _ U R I , . . . } , . . . } ;
  18. Deployment Specific Credentials Put them in g i t i

    g n o r e d files or use environment variables. NEVER store your deploy specific credentials eg: database login, Google OAuth credentials, S3 client secret etc. in your codebase. Firstly, it's not secure. Secondly, they might be different for each deployment. And, it makes deploying really difficult.
  19. Use environment variables Use environment variables to store the your

    deploy specific configurations & credentials. N O D E _ E N V = d e v e l o p m e n t D B _ U S E R N A M E = k a b i r D B _ P A S S W O R D = k a b i r . . . Now in your application you can do m o d u l e . e x p o r t s = { e n v : p r o c e s s . e n v . N O D E _ E N V , d a t a b a s e : { n a m e : p r o c e s s . e n v . D B _ N A M E , u s e r n a m e : p r o c e s s . e n v . D B _ U S E R N A M E , . . . } . . . } ;
  20. Use n o d e ­ f o r e

    m a n $ y a r n a d d f o r e m a n ­ ­ d e v Now you may put your environment variables in a g i t i g n o r e d . e n v file. And f o r e m a n would load them into p r o c e s s . e n v if you run your application using n f s t a r t . (development only) $ n f s t a r t [ O K A Y ] L o a d e d E N V . e n v F i l e a s K E Y = V A L U E F o r m a t 1 0 : 4 4 : 1 3 P M w e b . 1 | > n o d e j s ­ s a m p l e ­ a p p @ 0 . 0 . 1 s t a r t / h o m e / k a b 1 0 : 4 4 : 1 3 P M w e b . 1 | > n o d e m o n ­ ­ e x e c b a b e l ­ n o d e s r c / i n d e x . j s 1 0 : 4 4 : 1 3 P M w e b . 1 | [ n o d e m o n ] 1 . 1 1 . 0 1 0 : 4 4 : 1 3 P M w e b . 1 | [ n o d e m o n ] t o r e s t a r t a t a n y t i m e , e n t e r ` 1 0 : 4 4 : 1 3 P M w e b . 1 | [ n o d e m o n ] w a t c h i n g : * . * 1 0 : 4 4 : 1 3 P M w e b . 1 | [ n o d e m o n ] s t a r t i n g ` b a b e l ­ n o d e s r c / i n d e x . 1 0 : 4 4 : 1 4 P M w e b . 1 | i n f o : L i s t e n i n g o n p o r t 3 0 0 0
  21. Start using ES6+ features i m p o r t

    c o r s f r o m ' c o r s ' ; i m p o r t e x p r e s s f r o m ' e x p r e s s ' ; i m p o r t m o r g a n f r o m ' m o r g a n ' ; i m p o r t P r o m i s e f r o m ' b l u e b i r d ' ; i m p o r t c o n f i g f r o m ' . / c o n f i g / c o n f i g ' ; i m p o r t r o u t e s f r o m ' . / r o u t e s ' ; i m p o r t { s t a r t } f r o m ' . / s e r v i c e / s e r v e r ' ; i m p o r t { e n s u r e E v e r y t h i n g G o o d } f r o m ' . / s e r v i c e / e n s u r e ' ; l e t a p p = e x p r e s s ( ) ; c o n s t P O R T = p r o c e s s . e n v . P O R T | | 8 0 0 0 ; a p p . u s e ( m o r g a n ( c o n f i g . m o r g o n F o r m a t ( a p p . g e t ( ' e n v ' ) ) ) ) ; a p p . u s e ( c o r s ( ) ; a p p . u s e ( ' / a p i ' , r o u t e s ) ; . . .
  22. Transpile your code Current version of NodeJS supports most of

    the ES2015 features but not all of them. But we can still use ES6+ features in our code using transpilers like b a b e l $ y a r n a d d ­ ­ d e v b a b e l ­ c l i b a b e l ­ p r e s e t ­ e s 2 0 1 5
  23. Some useful packages Some of the most common npm packages

    for NodeJS projects e x p r e s s c o r s m o r g a n b l u e b i r d r e q u e s t r e q u e s t ­ p r o m i s e l o d a s h m o m e n t b o d y ­ p a r s e r w i n s t o n m u l t e r
  24. Packages for better development Some of the packages that could

    improve your development experience b a b e l ­ c l i e s l i n t n o d e m o n f o r e m a n n p m ­ r u n ­ a l l s h e l l j s p m 2 d e v t o o l n o d e ­ i n s p e c t o r