+ All Categories
Home > Documents > Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits...

Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits...

Date post: 21-Sep-2020
Category:
Upload: others
View: 3 times
Download: 0 times
Share this document with a friend
180
Transcript
Page 1: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 2: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 3: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Node.jsEssentials

Page 4: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

TableofContents

Node.jsEssentials

Credits

AbouttheAuthor

AbouttheReviewer

www.PacktPub.com

Supportfiles,eBooks,discountoffers,andmore

Whysubscribe?

FreeaccessforPacktaccountholders

Preface

Whatthisbookcovers

Whatyouneedforthisbook

Whothisbookisfor

Conventions

Readerfeedback

Customersupport

Downloadingtheexamplecode

Errata

Piracy

Questions

1.GettingStarted

Settingup

Hellorequire

Hellonpm

Summary

2.SimpleHTTP

Introducingrouting

Summary

3.Authentication

Basicauthentication

Page 5: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Bearertokens

OAuth

Summary

4.Debugging

Logging

Errorhandling

Summary

5.Configuration

JSONfiles

Environmentalvariables

Arguments

Summary

6.LevelDBandNoSQL

LevelDB

MongoDB

Summary

7.Socket.IO

Rooms

Authentication

Summary

8.CreatingandDeployingPackages

Creatingnpmpackages

Summary

9.UnitTesting

Installingmocha

Chai

Stubbingmethods

Summary

10.UsingMoreThanJavaScript

CoffeeScript

Codeblocksandfunctions

Page 6: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Theexistentialoperator

Objectsandarrays

Classes

Summary

Index

Page 7: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 8: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Node.jsEssentials

Page 9: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 10: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Node.jsEssentialsCopyright©2015PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:November2015

Productionreference:1301015

PublishedbyPacktPublishingLtd.

LiveryPlace

35LiveryStreet

BirminghamB32PB,UK.

ISBN978-1-78528-492-2

www.packtpub.com

Page 11: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 12: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

CreditsAuthor

FabianCook

Reviewers

ShoubhikBose

GlennGeenen

CommissioningEditor

EdwardGordan

AcquisitionEditor

DivyaPoojari

ContentDevelopmentEditor

AthiraLaji

TechnicalEditor

NaveenkumarJain

CopyEditor

SnehaSingh

ProjectCoordinator

HarshalVed

Proofreader

SafisEditing

Indexer

HemanginiBari

ProductionCoordinator

ShantanuN.Zagade

CoverWork

ShantanuN.Zagade

Page 13: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 14: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

AbouttheAuthorFabianCookisanexperiencedJavaScriptdeveloperwholivesinHawkesBay,NewZealand.HebeganworkingwithJavaandC#veryearlyinhislife,whichleadtousingNode.jsinanopensourcecontext.HeisnowcurrentlyworkingforaNewZealandISP,knownasNOWNZwheretheyareutilizingthefullpowerofNode.js,DockerandCoreOS.

Page 15: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 16: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

AbouttheReviewerGlennGeenenisaNode.jsdeveloperwithabackgroundingameandmobiledevelopment.HehasmostlyworkedasaniOSconsultantbeforebecomingaNode.jsconsultantforhiscompany,GeenenTijd.

Page 17: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 18: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

www.PacktPub.com

Page 19: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Supportfiles,eBooks,discountoffers,andmoreForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www2.packtpub.com/books/subscription/packtlib

DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.

Page 20: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 21: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.

Page 22: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 23: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

PrefaceNode.jsissimplyatoolthatletsyouuseJavaScriptontheserverside.However,itactuallydoesmuchmorethanthat–byextendingJavaScript,itallowsforamuchmoreintegratedandefficientapproachtodevelopment.Itcomesasnosurprisethatit’safundamentaltoolforfull-stackJavaScriptdevelopers.Whetheryouworkonthebackendorfrontend,youadoptamuchmorecollaborativeandagilewayofworkingusingNode.js,sothatyouandyourteamcanfocusondeliveringaqualityendproduct.Thiswillensurethatyou’rereadytotakeonanynewchallengethatgetsthrownatyou.

Thisbookwillbefastpacedandcoverdependencymanagement,runningyourownHTTPserver,realtimecommunication,andeverythinginbetweenthatisneededtogetupandrunningwithNode.js.

Page 24: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

WhatthisbookcoversChapter1,GettingStarted,coversthesetupofNode.js.Youwillalsocoverhowtoutilizeandmanagedependencies.

Chapter2,SimpleHTTP,covershowtorunasimpleHTTPserverandhelpsyouunderstandroutingandutilizationofmiddleware.

Chapter3,Authentication,coverstheutilizationofmiddlewareandJSONWebTokentoauthenticateusers.

Chapter4,Debugging,coverstheintegrationofpost-mortemtechniquesinyourdevelopmenttasksandhowtodebugyourNode.jsprograms.

Chapter5,Configuration,coverstheconfigurationandmaintenanceofyoursoftwareusingcentralizedconfigurationoptions,arguments,andenvironmentalvariables.

Chapter6,LevelDBandNoSQL,coverstheintroductionofNoSQLdatabases,suchasLevelDBandMongoDB.Italsocoverstheuseofthesimplekey/valuestoreandamorecompletedocumentdatabase.

Chapter7,Socket.IO,exploresthereal-timecommunicationbetweenclients,servers,andbackagainandalsohowitauthenticatesandnotifiestheusers.

Chapter8,CreatingandDeployingPackages,focusesonsharingthemodulesandcontributingtotheeco-system

Chapter9,UnitTesting,testsyourcodeusingMocha,Sinon,andChanceandalsocovershowtousemockswithfunctionsandgeneraterandomvaluestotestyourcode

Chapter10,UsingMoreThanJavaScript,explainstheusageofCoffeeScriptwithNode.jstoexpandlanguagecapabilities.

Page 25: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 26: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

WhatyouneedforthisbookYouwillneedacomputerthatrunsUnix(Macintosh),LinuxorWindows,alongwithyourpreferredIntegratedDevelopmentEnvironment.Ifyoudon’thaveanIDEthenyouhaveafewoptions,suchas:

Atom:https://atom.io/Sublime:http://www.sublimetext.com/Cloud9:https://c9.io/

Page 27: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 28: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

WhothisbookisforThebookwillbehelpfultoanybodywhowantstohaveknowledgeofNode.js(whatNode.jsisabout,howtouseit,whereit’susefulandwhentouseit).Familiaritywithserver-sideandNode.jsisaprerequisite.

Page 29: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 30: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ConventionsInthisbook,youwillfindanumberofstylesoftextthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestyles,andanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Wecanincludeothercontextsthroughtheuseoftheincludedirective.”

Ablockofcodeissetasfollows:

<scripttype='application/javascript'src='script_a.js'></script>

<scripttype='application/javascript'src='script_b.js'></script>

Anycommand-lineinputoroutputiswrittenasfollows:

[~]$npminstall-gn

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,inmenusordialogboxesforexample,appearinthetextlikethis:“Iftheuserhasn’tpassedboththeusernameandpasswordtheserverwillreturn500BadRequest“.

NoteWarningsorimportantnotesappearinaboxlikethis.

TipTipsandtricksappearlikethis.

Page 31: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 32: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.

Tosendusgeneralfeedback,simplysendane-mailto<[email protected]>,andmentionthebooktitleviathesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.

Page 33: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 34: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 35: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Page 36: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyouwouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheerratasubmissionformlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedonourwebsite,oraddedtoanylistofexistingerrata,undertheErratasectionofthattitle.Anyexistingerratacanbeviewedbyselectingyourtitlefromhttp://www.packtpub.com/support.

Page 37: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

PiracyPiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucomeacrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.

Page 38: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

QuestionsYoucancontactusat<[email protected]>ifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.

Page 39: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 40: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter1.GettingStartedEveryWebdevelopermusthavecomeacrossiteveryonceinawhile,eveniftheyjustdabbleinsimpleWebpages.WheneveryouwanttomakeyourWebpagealittlemoreinteractive,yougrabyourtrustworthyfriends,suchasJavaScriptandjQuery,andhacktogethersomethingnew.YoumighthavedevelopedsomeexcitingfrontendapplicationsusingAngularJSorBackboneandwanttolearnmoreaboutwhatelseyoucandowithJavaScript.

WhiletestingyourwebsiteonmultiplebrowsersyoumusthavecomeacrossGoogleChromeatsomepointandyoumighthavenoticedthatitisagreatplatformforJavaScriptapplications.

GoogleChromeandNode.jshavesomethingverybigincommon:theybothworkonGoogle’shigh-performanceV8JavaScriptengine,thisgivesusthesameengineinthebrowserthatwewillbeusinginthebackend,prettycool,right?

Page 41: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SettingupInordertogetstartedanduseNode.js,weneedtodownloadandinstallNode.js.Thebestwaytoinstallitwillbetoheadovertohttps://nodejs.org/anddownloadtheinstaller.

Atthetimeofwriting,thecurrentversionofNode.jsis4.2.1.

Toensureconsistency,wearegoingtouseanpmpackagetoinstallthecorrectversionofNode.JSand,forthis,wearegoingtousethenpackagedescribedathttps://www.npmjs.com/package/n.

Currently,thispackagehassupportonlyfor*nixmachines.ForWindows.seenvm-windowsordownloadthebinaryfor4.2.1fromhttps://nodejs.org/dist/v4.2.1/.

OnceyouhaveNode.jsinstalled,openaterminalandrun:

[~]$npminstall-gn

The–gargumentwillinstallthepackagegloballysowecanusethepackageanywhere.

Linuxusersmayneedtoruncommandsthatinstallglobalpackagesassudo.

Usingtherecentlyinstallpackage,run:

[~]$n

Thiswilldisplayascreenwiththefollowingpackages:

node/0.10.38

node/0.11.16

node/0.12.0

node/0.12.7

node/4.2.1

Ifnode/4.2.1isn’tmarkedwecansimplyrunthefollowingpackages;thiswillensurethatnode/4.2.1getsinstalled:

[~]$sudon4.2.1

Toensurethatthenodeisgood-to-go,letscreateandrunasimplehelloworldexample:

[~/src/examples/example-1]$touchexample.js

[~/src/examples/example-1]$echo"console.log(\"Helloworld\")">

example.js

[~/src/examples/example-1]$nodeexample.js

HelloWorld

Cool,itworks;nowlet’sgetdowntobusiness.

Page 42: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 43: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

HellorequireIntheprecedingexample,wejustloggedasimplemessage,nothinginteresting,solet’sdiveabitdeeperinthissection.

Whenusingmultiplescriptsinthebrowser,weusuallyjustincludeanotherscripttagsuchas:

<scripttype='application/javascript'src='script_a.js'></script>

<scripttype='application/javascript'src='script_b.js'></script>

Boththesescriptssharethesameglobalscope,thisusuallyleadstosomeunusualconflictswhenpeoplewanttogivevariablesthesamename.

//script_a.js

functionrun(){

console.log("I'mrunningfromscript_a.js!");

}

$(run);

//script_b.js

functionrun(){

console.log("I'mrunningfromscript_b.js!");

}

$(run);

Thiscanleadtoconfusion,andwhenmanyfilesareminifiedandcrammedtogetheritcausesaproblem;script_adeclaresaglobalvariable,whichisthendeclaredagaininscript_band,onrunningthecode,weseethefollowingontheconsole:

>I'mrunningfromscript_b.js!

>I'mrunningfromscript_b.js!

Themostcommonmethodtogetaroundthisandtolimitthepollutionoftheglobalscopeistowrapourfileswithananonymousfunction,asshown:

//script_a.js

(function($,undefined){

functionrun(){

console.log("I'mrunningfromscript_a.js!");

}

$(run);

})(jQuery);

//script_b.js

(function($,undefined){

functionrun(){

console.log("I'mrunningfromscript_b.js!");

}

$(run);

})(jQuery);

Nowwhenwerunthis,itworksasexpected:

>I'mrunningfromscript_a.js!

Page 44: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

>I'mrunningfromscript_b.js!

Thisisgoodforcodethatisn’tdependeduponexternally,butwhatdowedoforthecodethatis?Wejustexportit,right?

Somethingsimilartothefollowingcodewilldo:

(function(undefined){

functionLogger(){

}

Logger.prototype.log=function(message/*...*/){

console.log.apply(console,arguments);

}

this.Logger=Logger;

})()

Now,whenwerunthisscript,wecanaccessLoggerfromtheglobalscope:

varlogger=newLogger();

logger.log("This","is","pretty","cool")

>Thisisprettycool

Sonowwecanshareourlibrariesandeverythingisgood;ButwhatifsomeoneelsealreadyhasalibrarythatexposesthesameLoggerclass.

Whatdoesnodedotosolvethisissue?Hellorequire!

Node.jshasasimplewaytobringinscriptsandmodulesfromexternalsources,comparabletorequireinPHP.

Letscreateafewfilesinthisstructure:

/example-2

/util

index.js

logger.js

main.js

/*util/index.js*/

varlogger=newLogger()

varutil={

logger:logger

};

/*util/logger.js*/

functionLogger(){

}

Logger.prototype.log=function(message/*...*/){

console.log.apply(console,arguments);

};

/*main.js*/

util.logger.log("Thisisprettycool");

Wecanseethatmain.js.isdependentonutil/index.js,whichisinturndependentonutil/logger.js.

Page 45: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Thisshouldjustworkright?Maybenot.Let’srunthecommand:

[~/src/examples/example-2]$nodemain.js

ReferenceError:loggerisnotdefined

atObject.<anonymous>(/Users/fabian/examples/example-2/main.js:1:63)

/*Removedforsimplicity*/

atNode.js:814:3

Sowhyisthis?Shouldn’ttheybesharingthesameglobalscope?Well,inNode.jsthestoryisabitdifferent.Rememberthoseanonymousfunctionsthatwewerewrappingourfilesinearlier?Node.jswrapsourscriptsinthemautomaticallyandthisiswhererequirefitsin.

Letsfixourfiles,asshown:

/*util/index.js*/

Logger=require("./logger")

/*main.js*/

util=require("./util");

Ifyounotice,Ididn’tuseindex.jswhenrequiringutil/index.js;thereasonforthisisthatwhenyouarequireafolderratherthanafileyoucanspecifyanindexfilethatcanrepresentthatfolder’scode.Thiscanbehandyforsomethingsuchasamodelfolderwhereyouexposeallyourmodelsinonerequireratherthanhavingaseparaterequireforeachmodel.

Sonow,wehaverequiredourfiles.Butwhatdowegetback?

[~/src/examples/example-2]$node

>varutil=require("./util");

>console.log(util);

{}

Still,thereisnologger.Wehavemissedanimportantstep;wehaven’ttoldNode.jswhatwewanttoexposeinourfiles.

ToexposesomethinginNode.js,weuseanobjectcalledmodule.exports.Thereisashorthandreferencetoitthatisjustexports.Whenourfileiswrappedinananonymousfunction,bothmoduleandexportsarepassedasaparameter,asshowninthefollowingexample:

functionModule(){

this.exports={};

}

functionrequire(file){

//.....

returnsmodule.exports;

}

varmodule=newModule();

varexports=module.exports;

(function(exports,require,module){

Page 46: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

exports="Valuea"

module.exports="Valueb"

})(exports,require,module);

console.log(module.exports);

//Valueb

TipDownloadingtheexamplecode

YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.

Theexampleshowsthatexportsisinitiallyjustareferencetomodule.exports.Thismeansthat,ifyouuseexports={},thevalueyousetitaswon’tbeaccessibleoutsidethefunction’sscope.However,whenyouaddpropertiestoanexportsobject,youareactuallyaddingpropertiestothemodule.exportsobjectastheyareboththesamevalue.Assigningavaluetomodule.exportswillexportthatvaluebecauseitisaccessibleoutsidethefunction’sscopethroughthemodule.

Withthisknowledge,wecanfinallyrunourscriptinthefollowingmanner:

/*util/index.js*/

Logger=require("./logger.js");

exports.logger=newLogger();

/*util/logger.js*/

functionLogger(){

}

Logger.prototype.log=(message/*...*/){

console.log.apply(console,arguments);

};

module.exports=Logger;

/*main.js*/

util=require("./utils");

util.logger.log("Thisisprettycool");

Runningmain.js:

[~/src/examples/example-2]$nodemain.js

Thisisprettycool

Requirecanalsobeusedtoincludemodulesinourcode.Whenrequiringmodules,wedon’tneedtouseafilepath,wejustneedthenameofthenodemodulethatwewant.

Node.jsincludesmanyprebuiltcoremodules,oneofwhichistheutilmodule.Youcanfinddetailsontheutilmoduleathttps://nodejs.org/api/util.html.

Let’sseetheutilmodulecommand:

[~]$node

>varutil=require("util")

Page 47: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

>util.log('Thisisprettycoolaswell')

01Jan00:00:00-Thisisprettycoolaswell

Page 48: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 49: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

HellonpmAlongwithinternalmodulesthereisalsoanentireecosystemofpackages;themostcommonpackagemanagerforNode.jsisnpm.Atthetimeofwriting,thereareatotalof192,875packagesavailable.

Wecanusenpmtoaccesspackagesthatdomanythingsforus,fromroutingHTTPrequeststobuildingourprojects.Youcanalsobrowsethepackagesavailableathttps://www.npmjs.com/.

Usingapackagemanageryoucanbringinothermodules,whichisgreatasyoucanspendmoretimeworkingonyourbusinesslogicratherthanreinventingthewheel.

Let’sdownloadthefollowingpackagetomakeourlogmessagescolorful:

[~/src/examples/example-3]$npminstallchalk

Now,touseit,createafileandrequireit:

[~/src/examples/example-3]$touchindex.js

/*index.js*/

varchalk=require("chalk");

console.log("Iamjustnormaltext")

console.log(chalk.blue("Iambluetext!"))

Onrunningthiscode,youwillseethefirstmessageinadefaultcolorandthesecondmessageinblue.Let’slookatthecommand:.

[~/src/examples/example-3]$nodeindex.js

Iamjustnormaltext

Iambluetext!

Havingtheabilitytodownloadexistingpackagescomesinhandywhenyourequiresomethingthatsomeoneelsehasalreadyimplemented.Aswesaidearlier,therearemanypackagesouttheretochoosefrom.

Weneedtokeeptrackofthesedependenciesandthereisasimplesolutiontothat:package.json.

Usingpackage.jsonwecandefinethings,suchasthenameofourproject,whatthemainscriptis,howtoruntests,ourdependencies,andsoon.Youcanfindafulllistofpropertiesathttps://docs.npmjs.com/files/package.json.

npmprovidesahandycommandtocreatethesefilesanditwillaskyoutherelevantquestionsneededtocreateyourpackage.jsonfile:

[~/src/examples/example-3]$npminit

Theprecedingutilitywillwalkyouthroughthecreationofapackage.jsonfile.

Itonlycoversthemostcommonitemsandtriestoguessvaliddefaults.

Runthenpmhelpjsoncommandfordefinitivedocumentationonthesefieldsandtoknowwhattheydoexactly.

Page 50: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Afterwards,usenpmandinstall<pkg>--savetoinstallapackageandsaveitasadependencyinthepackage.jsonfile.

Press^Ctoquitatanytime:

name:(example-3)

version:(1.0.0)

description:

entrypoint:(main.js)

testcommand:

gitrepository:

keywords:

license:(ISC)

Abouttowriteto/examples/example-3/package.json:

{

"name":"example-3",

"version":"1.0.0",

"description":"",

"main":"main.js",

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1"

},

"author":"....",

"license":"ISC"

}

Isthisok?(yes)

Theutilitywillprovideyouwithdefaultvalues,soitiseasiertojustskipthroughthemusingtheEnterkey.

Nowwheninstallingourpackagewecanusethe--saveoptiontosavechalkasadependency,asshown:

[~/src/examples/example-3]$npminstall--savechalk

Wecanseechalkhasbeenadded:

[~/examples/example-3]$catpackage.json

{

"name":"example-3",

"version":"1.0.0",

"description":"",

"main":"main.js",

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1"

},

"author":"...",

"license":"ISC",

"dependencies":{

"chalk":"^1.0.0"

}

}

Wecanaddthesedependenciesmanuallybymodifyingpackage.json;thisisthemostcommonmethodtosavedependenciesoninstallation.

Page 51: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Youcanreadmoreaboutthepackagefileat:https://docs.npmjs.com/files/package.json.

Ifyouarecreatingaserveroranapplicationratherthanamodule,youmostlikelywanttofindawaytostartyourprocesswithouthavingtogiveapathtoyourmainfileallthetime;thisiswherethescriptobjectinyourpackage.jsonfilecomesintoplay.

Tosetyourstartupscript,youjustneedtosetthestartpropertyinthescriptsobject,asshown:

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1",

"start":"nodeserver.js"

}

Now,allweneedtodoisrunnpmstartandthennpmwillrunthestartscriptwehavealreadyspecified.

Wecandefinemorescripts,forexampleifwewantastartscriptforthedevelopmentenvironmentwecanalsodefineadevelopmentproperty;withnon-standardscriptnameshowever,insteadofjustusingnpm<script>,weneedtousenpmrun<script>.Forexample,ifwewanttorunournewdevelopmentscriptwewillhavetousenpmrundevelopment.

npmhasscriptsthataretriggeredatdifferenttimes.Wecandefineapostinstallscriptthatrunsafterwerunnpminstall;wecanusethisifwewanttotriggerapackagemanagertoinstallthemodules(forexample,bower)

Youcanreadmoreaboutthescriptsobjecthere:https://docs.npmjs.com/misc/scripts.

Youneedtodefineapackageifyouareworkinginateamofdeveloperswheretheprojectistobeinstalledondifferentmachines.Ifyouareusingasourcecontroltoolsuchasgit,itisrecommendedthatyouaddthenode_modulesdirectoryintoyourignorefile,asshown:

[~/examples/example-3]$echo"node_modules">.gitignore

[~/examples/example-3]$cat.gitignore

node_modules

Page 52: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 53: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryThatwasquick,wasn’tit?WehavecoveredthefundamentalsofNode.js,whichweneedtocontinueonourjourney.

WehavecoveredhoweasyitistoexposeandprotectpublicandprivatecodecomparedtoregularJavaScriptcodeinthebrowser,wheretheglobalscopecangetverypolluted.

Wealsoknowhowtoincludepackagesandcodefromexternalsourcesandhowtoensurethatthepackagesincludedareconsistent.

Asyoucanseethereisahugeecosystemofpackagesinoneofthemanypackagemanagers,suchasnpm,justwaitingforustouseandconsume.

Inthenextchapter,wewillfocusoncreatingasimpleservertoroute,authenticate,andconsumerequests.

Page 54: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 55: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter2.SimpleHTTPNowthatwehaveunderstoodthebasics,wecanmoveontosomethingabitmoreuseful.Inthischapter,wewilllookatcreatinganHTTPserverandroutingrequests.WhileworkingwithNode.jsyouwillcomeacrossHTTPveryoften,asserversidescriptingisoneofthecommonusesofNode.js.

Node.jscomeswithabuiltinHTTPserver;allyouneedtodoisrequiretheincludedhttppackageandcreateaserver.Youcanreadmoreaboutthepackageathttps://nodejs.org/api/http.html.

varHttp=require('http');

varserver=Http.createServer();

ThiswillcreateyourveryownHTTPserverthatisreadytoroll.Inthisstate,though,itwon’tbelisteningforanyrequests.Wecanstartlisteningonanyportorsocketwewish,aslongasitisavailable,asshown:

varHttp=require('http');

varserver=Http.createServer();

server.listen(8080,function(){

console.log('Listeningonport8080');

});

Let’ssavetheprecedingcodetoserver.jsandrunit:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

Bynavigatingtohttp://localhost:8080/onyourbrowseryouwillseethattherequesthasbeenacceptedbuttheserverisn’tresponding;thisisbecausewehaven’thandledtherequestsyet,wearejustlisteningforthem.

Whenwecreatetheserverwecanpassacallbackthatwillbecalledeachtimethereisarequest.Theparameterspassedwillbe:request,response.

functionrequestHandler(request,response){

}

varserver=Http.createServer(requestHandler);

Noweachtimewegetarequestwecandosomething:

varcount=0;

functionrequestHandler(request,response){

varmessage;

count+=1;

response.writeHead(201,{

'Content-Type':'text/plain'

});

message='Visitorcount:'+count;

console.log(message);

Page 56: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

response.end(message);

}

Let’srunthescriptandrequestthepagefromthebrowser;youshouldseeVisitorcount:1returnedtothebrowser:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

Visitorcount:1

Visitorcount:2

Somethingweirdhashappenedthough:anextrarequestgetsgenerated.Whoisvisitor2?

Thehttp.IncomingMessage(theparameterrequest)exposesafewpropertiesthatcanbeusedtofigurethisout.Thepropertywearemostinterestedinrightnowisurl.Weareexpectingjust/toberequested,solet’saddthistoourmessage:

message='Visitorcount:'+count+',path:'+request.url;

Nowyoucanrunthecodeandseewhat’sgoingon.Youwillnoticethat/favicon.icohasbeenrequestedaswell.IfyouarenotabletoseethisthenyoumustbewonderingwhatIhavebeengoingonaboutorifyourbrowserhasbeentohttp://localhost:8080recentlyandhasacachediconalready.Ifthisisthecase,thenyoucanrequesttheiconmanually,forexamplefromhttp://localhost:8080/favicon.ico:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

Visitorcount:1,path:/

Visitorcount:2,path:/favicon.ico

Wecanalsoseethatifwerequestanyotherpagewewillgetthecorrectpath,asshown:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

Visitorcount:1,path:/

Visitorcount:2,path:/favicon.ico

Visitorcount:3,path:/test

Visitorcount:4,path:/favicon.ico

Visitorcount:5,path:/foo

Visitorcount:6,path:/favicon.ico

Visitorcount:7,path:/bar

Visitorcount:8,path:/favicon.ico

Visitorcount:9,path:/foo/bar/baz/qux/norf

Visitorcount:10,path:/favicon.ico

Thisisn’tthedesiredoutcomethough,foreverythingbutafewrouteswewanttoreturn404:NotFound.

Page 57: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

IntroducingroutingRoutingisessentialforalmostallNode.jsservers.First,wewillimplementourownsimpleversionandthenmoveontothemorecomplexrounting.

Wecanimplementourownsimplerouterusingaswitchstatement,suchas:

functionrequestHandler(request,response){

varmessage,

status=200;

count+=1;

switch(request.url){

case'/count':

message=count.toString();

break;

case'/hello':

message='World';

break;

default:

status=404;

message='NotFound';

break;

}

response.writeHead(201,{

'Content-Type':'text/plain'

});

console.log(request.url,status,message);

response.end(message);

}

Let’srunthefollowingexample:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

/foo404NotFound

/bar404NotFound

/world404NotFound

/count2004

/hello200World

/count2006

Youcanseethecountincreasingwitheachrequest;however,itisn’treturnedeachtime.Ifwehaven’tdefinedacasespecificallyforthatroute,wereturn404:NotFound.

ForservicesthatimplementaRESTfulinterface,wewanttobeabletorouterequestsbasedontheHTTPmethodaswell.Therequestobjectexposesthisusingthemethodproperty.

Addingthistothelogwecanseethis:

console.log(request.method,request.url,status,message);

Page 58: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Runtheexampleandexecuteyourrequests,youcanuseaRESTclienttoinvokeaPOSTrequest:

[~/examples/example-4]$nodeserver.js

Listeningonport8080

GET/count2001

POST/count2002

PUT/count2003

DELETE/count2004

Wecanimplementaroutertoroutebasedonamethod,buttherearepackagesthatdothisforusalreadyoutthere.Fornowwewilluseasimplepackagecalledrouter:

[~/examples/example-5]$npminstallrouter

Now,wecandosomemorecomplexroutingofourrequests:

Let’screateasimpleRESTfulinterface.

First,weneedtocreatetheserver,asshown:

/*server.js*/

varHttp=require('http'),

Router=require('router'),

server,

router;

router=newRouter();

server=Http.createServer(function(request,response){

router(request,response,function(error){

if(!error){

response.writeHead(404);

}else{

//Handleerrors

console.log(error.message,error.stack);

response.writeHead(400);

}

response.end('\n');

});

});

server.listen(8080,function(){

console.log('Listeningonport8080');

});

Runningtheservershouldshowthattheserverislistening.

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Wewanttodefineasimpleinterfacetoread,save,anddeletemessages.Wemightwanttoreadindividualmessagesaswellasalistofmessages;thisessentiallydefinesasetofRESTfulendpoints.

RESTstandsforRepresentationalStateTransfer;itisaverysimpleandcommonstyle

Page 59: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

usedbymanyHTTPprogramminginterfaces.

Theendpointswewanttodefineare:

HTTPMethod Endpoint Usedto

POST /message Createmessage

GET /message/:id Readmessage

DELETE /message/:id Deletemessage

GET /message Readmultiplemessages

ForeachHTTPmethod,therouterhasamethodtouseformappingaroute.Thisinterfaceisintheformof:

router.<HTTPmethod>(<path>,[...<handler>])

Wecandefinemultiplehandlersforeachroute,butwewillcomebacktothatinamoment.

Wewillgothrougheachroute,createanimplementation,andappendthecodetotheendofserver.js.

Wewanttostoreourmessagessomewhere,andintherealworldwewillstoretheminadatabase;however,forsimplicitywewilluseanarraywithasimplecounter,asshown:

varcounter=0,

messages={};

Ourfirstroutewillbeusedtocreatemessages:

functioncreateMessage(request,response){

varid=counter+=1;

console.log('Createmessage',id);

response.writeHead(201,{

'Content-Type':'text/plain'

});

response.end('Message'+id);

}

router.post('/message',createMessage);

WecanensurethatthisrouteworksbyrunningtheserveranddoingaPOSTrequesttohttp://localhost:8000/message.

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Createmessage1

Createmessage2

Createmessage3

Wecanalsoconfirmthatthecounterisincrementing,astheidincreaseseachtimewemakearequest.Wewilldothistokeepatrackofthecountofmessagesandtogiveauniqueidtoeachmessage.

Page 60: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Nowthatthisisworking,weneedtobeabletoreadthemessagetextandtodothisweneedtobeabletoreadtherequestbodythatwassentbytheclient.Thisiswheremultiplehandlerscomeintoplay.Wecouldtacklethisintwodifferentways,ifwewerereadingthebodyinonlyonerouteorifweweredoingsomeotheractionspecifictoaroute,forinstanceauthorization,wewilladdanadditionalhandlertotheroute,suchas:

router.post('/message',parseBody,createMessage)

Theotherwaywecoulddoitisbyaddingahandlerforallmethodsandroutes;thiswillbeexecutedfirstbeforetheroutehandlers,thesearecommonlyreferredtoasmiddleware.Youcanthinkofhandlersasbeingachainoffunctionswhereeachoneiscallingthenext,onceitisfinishedwithitstasks.Withthisinmindyoushouldnotethattheorderinwhichyouaddahandler,bothmiddlewareandroute,willdictatetheorderofoperations.Thismeansthat,ifweareregisteringahandlerthatisexecutedforallmethods,wemustdothisfirst.

Therouterexposesafunctiontoaddthefollowinghandlers:

router.use(function(request,response,next){

console.log('middlewareexecuted');

//Nullastherewerenoerrors

//Iftherewasanerrorthenwecouldcall`next(error);`

next(null);

});

YoucanaddthiscodejustaboveyourimplementationofcreateMessage:

Onceyouhavedonethat,runtheserverandmakethefollowingrequest:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

middlewareexecuted

Createmessage1

Youcanseethatthemiddlewaregetsexecutedbeforetheroutehandler.

Nowthatweknowhowmiddlewareworks,wecanusethemasfollows:

[~/examples/example-5]$npminstallbody-parser

Replaceourcustommiddlewarewith:

varBodyParser=require('body-parser');

router.use(BodyParser.text());

Atthisstage,wejustwanttoreadallrequestsasplaintext.

NowwecanretrievethemessageincreateMessage:

functioncreateMessage(request,response){

varid=counter+=1,

message=request.body;

console.log('Createmessage',id,message);

messages[id]=message;

response.writeHead(201,{

Page 61: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

'Content-Type':'text/plain',

'Location':'/message/'+id

});

response.end(message);

}

Runserver.jsandPOSTacoupleofmessagestohttp://localhost:8080/message;youwillseesomethingsimilartothesemessages:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Createmessage1Hellofoo

Createmessage2Hellobar

Ifyounotice,youwillseethataheaderreturnswithanewlocationofthemessageanditsid,Ifwerequesthttp://localhost:8080/message/1,thecontentfromthefirstmessageshouldbereturned.

However,thereissomethingdifferentwiththisroute;ithasakeythatisgeneratedeachtimeamessageiscreated.Wedon’twanttosetupanewrouteforeachnewmessageasitwillbehighlyinefficient.Instead,wecreatearoutethatmatchesapattern,suchas/message/:id.ThisisacommonwaytodefineadynamicrouteinNode.js.

Theidpartoftherouteiscalledaparameter.Wecandefineasmanyoftheseaswewantinourrouteandreferthemusingtherequest;forexamplewecanhavearoutesimilarto/user/:id/profile/:attribute.

WiththisinmindwecancreateourreadMessagehandler,asshown:

functionreadMessage(request,response){

varid=request.params.id,

message=messages[id];

console.log('Readmessage',id,message);

response.writeHead(200,{

'Content-Type':'text/plain'

});

response.end(message);

}

router.get('/message/:id',readMessage);

Nowlet’ssavetheprecedingcodeintheserver.jsfileandruntheserver:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Createmessage1Hellofoo

Readmessage1Hellofoo

Createmessage2Hellobar

Readmessage2Hellobar

Readmessage1Hellofoo

Wecanseeit’sworkingbysendingafewrequeststotheserver.

Deletingmessagesisalmostthesameasreadingthem;butwedon’treturnanythingandnullouttheoriginalmessagevalue:

Page 62: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

functiondeleteMessage(request,response){

varid=request.params.id;

console.log('Deletemessage',id);

messages[id]=undefined;

response.writeHead(204,{});

response.end('');

}

router.delete('/message/:id',deleteMessage)

First,runtheserver,thencreate,read,anddeleteamessage,asshown:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Deletemessage1

Createmessage1Hello

Readmessage1Hello

Deletemessage1

Readmessage1undefined

Thatlooksgood;however,wehaverunintoaproblem.Weshouldn’tbeabletoreadamessageagainafterdeletingit;wewillreturn404inboththereadanddeletehandlersifwecan’tfindamessage.Wecandothisbyaddingthefollowingcodetoourreadanddeletehandlers:

varid=request.params.id,

message=messages[id];

if(typeofmessage!=='string'){

console.log('Messagenotfound',id);

response.writeHead(404);

response.end('\n');

return;

}

Nowlet’ssavetheprecedingcodeintheserver.jsfileandruntheserver:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Messagenotfound1

Createmessage1Hello

Readmessage1Hello

Lastly,wewanttobeabletoreadallmessagesandreturnalistofallmessagevalues:

functionreadMessages(request,response){

varid,

message,

messageList=[],

messageString;

for(idinmessages){

Page 63: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

if(!messages.hasOwnProperty(id)){

continue;

}

message=messages[id];

//Handledeletedmessages

if(typeofmessage!=='string'){

continue;

}

messageList.push(message);

}

console.log('Readmessages',JSON.stringify(

messageList,

null,

''

));

messageString=messageList.join('\n');

response.writeHead(200,{

'Content-Type':'text/plain'

});

response.end(messageString);

}

router.get('/message',readMessages);

Nowlet’ssavetheprecedingcodeintheserver.jsfileandruntheserver:

[~/examples/example-5]$nodeserver.js

Listeningonport8080

Createmessage1Hello1

Createmessage2Hello2

Createmessage3Hello3

Createmessage4Hello4

Createmessage5Hello5

Readmessages[

"Hello1",

"Hello2",

"Hello3",

"Hello4",

"Hello5"

]

Awesome;nowwehaveafullRESTfulinterfacetoreadandwritemessages.But,wedon’twanteveryonetobeabletoreadourmessages;theyshouldbesecureandwealsowanttoknowwhoiscreatingthemessages,wewillcoverthisinthenextchapter.

Page 64: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 65: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryNowwehaveeverythingweneedtomakesomeprettycoolservices.WecannowcreateanHTTPfromscratch,routeourrequests,andcreateaRESTfulinterface.

ThiswillhelpyouwiththecreationofcompleteNode.JSservices.Inthenextchapter,wewillcoverauthentication.

Page 66: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 67: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter3.AuthenticationWecannowcreateRESTfulAPIs,butwedon’twanteveryonetoaccesseverythingweexpose.Wewanttheroutestobesecureandtobeabletotrackwhoisdoingwhat.

Passportisagreatmoduleandanothermiddlewarethathelpsusauthenticaterequests.

PassportexposesasimpleAPIforproviderstoexpandonandcreatestrategiestoauthenticateusers.Atthetimeofwriting,thereare307officiallysupportedstrategies;however,thereisnoreasonwhyyoucan’twriteyourownstrategyandpublishitforotherstouse.

Page 68: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

BasicauthenticationThesimpleststrategyforpassportisthelocalstrategythatacceptsausernameandpassword.

Wewillintroducetheexpressframeworkfortheseexamplesand,nowthatyouknowthebasicsofhowitallworksunderneath,wecanputitalltogether.

Youcaninstallexpress,body-parser,passport,andpassport-local.Expressisabatteries-includedWebframeworkforNode.js,andincludesroutingandtheabilitytousemiddleware:

[~/examples/example-19]$npminstallexpressbody-parserpassportpassport-

local

Fornow,wecanstoreourusersinasimpleobjecttoreferencelater,asshown:

varusers={

foo:{

username:'foo',

password:'bar',

id:1

},

bar:{

username:'bar',

password:'foo',

id:2

}

}

Oncewehaveafewusers,weneedtosetuppassport.Whenwecreateaninstanceofthelocalstrategy,weneedtoprovideaverifycallbackwherewechecktheusernameandpassword,whilereturningauser:

varPassport=require('passport'),

LocalStrategy=require('passport-local').Strategy;

varlocalStrategy=newLocalStrategy({

usernameField:'username',

passwordField:'password'

},

function(username,password,done){

user=users[username];

if(user==null){

returndone(null,false,{message:'Invaliduser'});

}

if(user.password!==password){

returndone(null,false,{message:'Invalidpassword'});

}

done(null,user);

}

Page 69: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

)

Theverifycallbackinthiscaseisexpectingdonetobecalledwithauser.Italsoallowsustoprovideinformationiftheuserwasinvalidorthepasswordwaswrong.

Now,thatwehaveastrategywecanpassthistopassport,whichallowsustoreferenceitlateranduseittoauthenticateourrequests,asfollows:

Passport.use('local',localStrategy);

Youcanusemultiplestrategiesperapplicationandreferenceeachonebythenameyoupassed,inthiscase'local'.

Now,let’screateourserver,asshownhere:

varExpress=require('express');

varapp=Express();

Wewillhavetousethebody-parsermiddleware.Thiswillensurethat,whenweposttoourloginroute,wecanreadourbody;wealsoneedtoinitializepassport:

varBodyParser=require('body-parser');

app.use(BodyParser.urlencoded({extended:false}));

app.use(BodyParser.json());

app.use(Passport.initialize());

Tologintoourapplication,weneedtocreateapostroutethatusesauthenticationasoneofthehandlers.Thecodeforthisisasfollows:

app.post(

'/login',

Passport.authenticate('local',{session:false}),

function(request,response){

}

);

Now,whenwesendaPOSTrequestto/logintheserverwillauthenticateourrequests.

Onceauthenticated,theuserpropertywillbepopulatedontherequestobject,asfollows:

app.post(

'/login',

Passport.authenticate('local',{session:false}),

function(request,response){

response.send('UserId'+request.user.id);

}

);

Lastly,weneedtolistenforrequests,aswithalltheotherservers:

app.listen(8080,function(){

console.log('Listeningonport8080');

});

Letsruntheexample:

Page 70: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

[~/examples/example-19]$nodeserver.js

Listeningonport8080

Now,wecanauthenticateuserswhenwesendaPOSTrequestatourserver.Iftheuserhasn’tpassedboththeusernameandpasswordtheserverwillreturn400BadRequest.

TipIfyouaren’tfamiliarwithcurlyoucoulduseatool,suchasAdvancedRESTClient:

https://chromerestclient.appspot.com/

InthefollowingexamplesIwillbeusingthecommandlineinterfacecurl.

WecanexecutealoginrequestbyexecutingaPOSTto/logincommand:

[~]$curl-XPOSThttp://localhost:8080/login-v

<HTTP/1.1400BadRequest

Iftheuserprovidesthewrongdetailsthen401Unauthorizedwillbereturned:

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"foo","password":"foo"}'\

-v

<HTTP/1.1401Unauthorized

Ifweprovidethecorrectdetailsthenwecanseeourhandlerwascalledandthecorrectdatawasreturned:

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"foo","password":"bar"}'

UserId1

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"bar","password":"foo"}'

UserId2

Page 71: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 72: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

BearertokensNowthatwehaveanauthenticateduser,wecangenerateatokenthatcanbeusedwiththerestofourrequestsratherthanpassingourusernameandpasswordeverywhere.ThisiscommonlyknownasaBearertokenand,conveniently,thereisapassportstrategyforthis.

Forourtokens,wewillusesomethingcalledaJSONWebToken(JWT).JWTallowsustoencodetokensfromJSONobjectsandthendecodethemandverifythem.Thedatastoredinthemisopenandsimpletoread,sopasswordsshouldn’tbestoredinthem;however,itmakesverifyingauserverysimple.Wecanalsoprovidethesetokenswithexpirydates,whichhelpslimittheseverityoftokensbeingexposed.

YoucanreadmoreaboutJWTathttp://jwt.io/.

WecaninstallJWTusingthefollowingcommand:

[~/examples/example-19]$npminstalljsonwebtoken

Onceauserisauthenticated,wecansafelyprovidethemwithatokentouseinfuturerequests:

varJSONWebToken=require('jsonwebtoken'),

Crypto=require('crypto');

vargenerateToken=function(request,response){

//Thepayloadjustcontainstheidoftheuser

//andtheirusername,wecanverifywhethertheclaim

//iscorrectusingJSONWebToken.verify

varpayload={

id:user.id,

username:user.username

};

//Generatearandomstring

//Usuallythiswouldbeanappwideconstant

//Butcanbedonebothways

varsecret=Crypto.randomBytes(128)

.toString('base64');

//Createthetokenwithapayloadandsecret

vartoken=JSONWebToken.sign(payload,secret);

//Theuserisstillreferencingthesameobject

//inusers,sononeedtosetitagain

//Ifwewereusingadatabase,wewouldsave

//ithere

request.user.secret=secret

returntoken;

}

vargenerateTokenHandler=function(request,response){

varuser=request.user;

//Generateourtoken

vartoken=generateToken(user);

Page 73: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

//Returntheuseratokentouse

response.send(token);

};

app.post(

'/login',

Passport.authenticate('local',{session:false}),

generateTokenHandler

);

Now,whentheuserlogsintheywillbepresentedwithatokentousethatwecanverify.

LetsrunourNode.jsserver:

[~/examples/example-19]$nodeserver.js

Listeningonport8080

Whenweloginnowwereceiveatoken:

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"foo","password":"bar"}'

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZC

I6MSwidXNlcm5hbWUiOiJmb28iLCJpYXQiOjE0MzcyO

TQ3OTV9.iOZO7oCIceZl6YvZqVP9WZLRx-XVvJFMF1p

pPCEsGGs

Wecanenterthisintothedebuggerathttp://jwt.io/andseethecontents,asshown:

{

"id":1,

"username":"foo",

"iat":1437294795

}

Ifwehadthesecretwecouldverifythatthetokeniscorrect.Thesignaturechangeseverytimewerequestatoken:

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"foo","password":"bar"}'

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZC

I6MSwidXNlcm5hbWUiOiJmb28iLCJpYXQiOjE0MzcyO

TQ5OTl9.n1eRQVOM9qORTIMUpslH-ycTNEYdLDKa9lU

pmhf44s0

Wecanauthenticateauserusingpassport-bearer;itissetupverysimilartopassport-local.However,ratherthanacceptingausernameandpasswordfromthebody,weacceptabearertoken;thiscanbepassedusingthequerystring,body,ortheAuthorizationheader:

Firstwemustinstallpassport-http-bearer:

[~/examples/example-19]$npminstallpassport-http-bearer

Thelet’screateourverifier.Therearetwosteps:thefirstisensuringthedecodedinformationmatchesouruser,thiswillbewhereweusuallyretrieveouruser;then’once

Page 74: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

wehaveauserandit’svalid,wecancheckwhetherthetokenisvalidbasedontheuser’ssecret:

varBearerStrategy=require('passport-http-bearer').Strategy;

varverifyToken=function(token,done){

varpayload=JSONWebToken.decode(token);

varuser=users[payload.username];

//Ifwecan'tfindauser,ortheinformation

//doesn'tmatchthenreturnfalse

if(user==null||

user.id!==payload.id||

user.username!==payload.username){

returndone(null,false);

}

//Ensurethetokenisvalidnowwehaveauser

JSONWebToken.verify(token,user.secret,function(error,decoded){

if(error||decoded==null){

returndone(error,false);

}

returndone(null,user);

});

}

varbearerStrategy=newBearerStrategy(

verifyToken

)

Wecanregisterthisstrategyasthebearersowecanuseitlater:

Passport.use('bearer',bearerStrategy);

Wecancreateasimpleroutewhereweretrieveuserdetailsforanauthenticateduser:

app.get(

'/userinfo',

Passport.authenticate('bearer',{session:false}),

function(request,response){

varuser=request.user;

response.send({

id:user.id,

username:user.username

});

}

);

Let’sruntheNode.jsserver:

[~/examples/example-19]$nodeserver.js

Listeningonport8080

Oncewereceiveatoken:

[~]$curl-XPOSThttp://localhost:8080/login\

-H'Content-Type:application/json'\

-d'{"username":"foo","password":"bar"}'

Wecanusetheresultinourrequests:

Page 75: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

[~]$curl-XGEThttp://localhost:8080/userinfo\

-H'Authorization:Bearer<token>'

{"id":1,"username":"foo"}

Page 76: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 77: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

OAuthOAuthprovidesmanyadvantages;forinstance,itdoesnotneedtodealwiththeactualidentificationofusers.Wecanletusersloginusingservicestheytrust,suchasGoogle,Facebook,orAuth0.

Forthefollowingexamples,IwillbeusingAuth0.Theyprovideafreeaccountforyoutogetup-and-running:https://auth0.com/.

Youwillneedtosignupandcreateanapi(chooseAngularJS+Node.js),thengotoSettingsandtakedownthedomain,clientid,andclientsecret.YouwillneedthesetosetupOAuth.

WecanauthenticateusingOAuthusingpassport-oauth2:

[~/examples/example-19]$npminstall--savepassport-oauth2

Aswithourbearertokens,wewanttovalidatewhattheserverreturns,whichwillbeauserobjectthathasanid.Wewillmatchthiswithauserthatisinourdataorcreateanewuser:

varvalidateOAuth=function(accessToken,refreshToken,profile,done){

varkeys=Object.keys(users),user=null;

for(variKey=0;iKey<keys.length;i+=1){

user=users[key];

if(user.thirdPartyId!==profile.user_id){continue;}

returndone(null,user);

}

users[profile.name]=user={

username:profile.name,

id:keys.length,

thirdPartyId:profile.user_id

}

done(null,user);

};

OncewehaveafunctiontovalidateouruserswecanputtogethertheoptionsforourOAuthstrategy:

varoAuthOptions={

authorizationURL:'https://<domain>.auth0.com/authorize',

tokenURL:'https://<domain>.auth0.com/oauth/token',

clientID:'<clientid>',

clientSecret:'<clientsecret>',

callbackURL:"http://localhost:8080/oauth/callback"

}

Thenwecreateourstrategy,asfollows:

varOAuth2Strategy=require('passport-oauth2').Strategy;

Page 78: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

oAuthStrategy=newOAuth2Strategy(oAuthOptions,validateOAuth);

BeforeweuseourstrategyweneedtoducktypethestrategiesuserProfilemethodwithourown,thisissowecanrequesttheuserobjecttouseinvalidateOAuth:

varparseUserProfile=function(done,error,body){

if(error){

returndone(newError('Failedtofetchuserprofile'))

}

varjson;

try{

json=JSON.parse(body);

}catch(error){

returndone(error);

}

done(null,json);

}

vargetUserProfile=function(accessToken,done){

oAuthStrategy._oauth2.get(

"https://<domain>.auth0.com/userinfo",

accessToken,

parseUserProfile.bind(null,done)

)

}

oAuthStrategy.userProfile=getUserProfile

Wecanregisterthisstrategyasoauthsowecanuseitlater:

Passport.use('oauth',oAuthStrategy);

WeneedtocreatetworoutestohandleourOAuthauthentication:oneroutetostarttheflowandtheotherfortheidentificationservertoreturnto:

app.get('/oauth',Passport.authenticate('oauth',{session:false}));

WecanuseourgenerateTokenHandlerhere,asourrequestwillhaveauseronit.

app.get('/oauth/callback',

Passport.authenticate('oauth',{session:false}),

generateTokenHandler

);

Wecannowstartourserverandrequesthttp://localhost:8080/oauth;theserverwillredirectyoutoAuth0.Onceloggedin,youwillreceiveatokenthatyoucanusewith/userinfo.

Ifyouwereusingsessions,youcouldsavetheusertothesessionandredirectthembacktoyourfrontpage(orthedefaultpagesetforaloggedinuser).Forasingle-pageapp,whenusingsomethinglikeAngular,youmaywanttoredirecttheuserwithatokenintheURLfortheclientframeworktograbontoandsave.

Page 79: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 80: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryWecannowauthenticateusers;thisisgreataswecannowfigureoutwhothepeopleareandthenlimittheuserstocertainresources.

Inthenextchapterwewillcoverdebugging,wemayneedtouseitifourusersaren’tbeingauthenticated.

Page 81: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 82: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter4.DebuggingAtsomepointinyourjourneywithNode.js,itisinevitablethatyouwillhavetodebugsomenastybugs.So,let’sexpectthembeforehandandplanforthatday.

Page 83: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

LoggingThereareafewmethodsthatwecanusetodebugoursoftware;thefirstonewearegoingtolookatislogging.Thesimplestwaytologamessageistouseconsole.InmostofthepreviousexamplesconsolehasbeenusedtoportraywhatisgoingonwithoutneedingtoseetheentireHTTPrequestandresponse,thusmakingthingsalotmorereadableandsimple.

Anexampleofthisis:

varHttp=require('http');

Http.createServer(function(request,response){

console.log(

'Receivedrequest',

request.method,

request.url

)

console.log('Returning200');

response.writeHead(200,{'Content-Type':'text/plain'});

response.end('HelloWorld\n');

}).listen(8000);

console.log('Serverrunningonport8000');

Runningthisexamplewilllogrequestsandresponsesontheconsole:

[~/examples/example-6]$nodeserver.js

Serverrunningonport8000

ReceivedrequestGET/

Returning200

ReceivedrequestGET/favicon.ico

Returning200

ReceivedrequestGET/test

Returning200

ReceivedrequestGET/favicon.ico

Returning200

Ifweareusingaframeworkthatacceptsmiddleware,suchasexpress,wecoulduseasimplenpmpackagecalledmorgan;youcanfindthepackageathttps://www.npmjs.com/package/morgan:

[~/examples/example-7]$npminstallmorgan

[~/examples/example-7]$npminstallrouter

Wecanuseitbyusingrequiretobringitintoourcodeandaddingitasmiddleware:

varMorgan=require('morgan'),

Router=require('router'),

Http=require('http');

Page 84: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

router=newRouter();

router.use(Morgan('tiny'));

/*Simpleserver*/

Http.createServer(function(request,response){

router(request,response,function(error){

if(!error){

response.writeHead(404);

}else{

//Handleerrors

console.log(error.message,error.stack);

response.writeHead(400);

}

response.end('\n');

});

}).listen(8000);

console.log('Serverrunningonport8000');

functiongetInfo(request,response){

varinfo=process.versions;

info=JSON.stringify(info);

response.writeHead(200,{'Content-Type':'application/json'});

response.end(info);

}

router.get('/info',getInfo);

Whentheserverisrunning,wecanseeeachrequestandresponsewithouthavingtoaddloggingtoeachhandler:

[~/examples/example-7]$nodeserver.js

Serverrunningonport8000

GET/test404--4.492ms

GET/favicon.ico404--2.281ms

GET/info200--1.120ms

GET/info200--1.120ms

GET/test404--0.199ms

GET/info200--0.494ms

GET/test404--0.162ms

Thiskindofloggingisasimplewaytoseewhatisbeingusedontheserverandhowlongeachrequestistaking.Here,youcanseethatthefirstrequeststookthelongestandthentheygotalotfaster.Thedifferenceisonlyof3ms;ifthetimewaslarger,itcouldhavebeenabigproblem.

Wecanincreasetheinformationthat’sloggedbychangingtheformatwepasstomorgan,asshown:

router.use(Morgan('combined'));

Byrunningtheserveryouwillseemoreinformation,suchastheremoteuser,dateandtimeoftherequest,amountofcontentthatwasreturned,andtheclienttheyareusing.

Page 85: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

[~/examples/example-7]$nodeserver.js

Serverrunningonport8000

::1--[07/Jun/2015:11:09:03+0000]"GET/infoHTTP/1.1"200-"-""--

REMOVED---"

Timingisdefinitelyanimportantfactorasitcanbehelpfulwhensiftingthroughthemountainsoflogsthatyouwillobtain.Somebugscanbelikeatickingtime-bombwaitingtoexplodeat3AMonaSaturdaynight.Alltheselogsmeannothingtousiftheprocesshasdiedandthelogshavedisappeared.Thereisanotherpopularandusefulpackagecalledbunyan,whichwrapsmanyloggingmethodsintoone.

Bunyanbringstothetabletheadvantageofwriteablestreamstowritelogs,whetheritisafileondiskorstdout.Thisallowsustopersistourlogsforpostmortemdebugging.Youcanfindmoredetailsaboutbunyanathttps://www.npmjs.com/package/bunyan.

Now,let’sinstallthepackage.Wewantitinstalledbothlocallyandgloballysothatwecanalsouseitasacommandlinetool:

[~/examples/example-8]$npminstall–gbunyan

[~/examples/example-8]$npminstallbunyan

Now,letsdosomelogging:

varBunyan=require('bunyan'),

logger;

logger=Bunyan.createLogger({

name:'example-8'

});

logger.info('Hellologging');

Runningourexample:

[~/examples/example-8]$nodeindex.js

{"name":"example-

8","hostname":"macbook.local","pid":2483,"level":30,"msg":"Hello

logging","time":"2015-06-07T11:35:13.973Z","v":0}

Thisdoesn’tlookverypretty,doesit?BunyanusesasimplestructuredJSONstringtosavemessages;thismakesiteasytoparse,extend,andread.BunyancomeswithaCLIutilitytomakeeverythingniceandpretty.

Ifweruntheexamplewiththeutility,thenwewillseethattheoutputisnicelyformatted:

[~/examples/example-8]$nodeindex.js|bunyan

[2015-06-07T11:38:59.698Z]INFO:example-8/2494onmacbook.local:Hello

logging

Ifweaddafewmorelevels,youwillseeonyourconsolethateachiscoloreddifferentlytohelpusidentifythem:

varBunyan=require('bunyan'),

logger;

logger=Bunyan.createLogger({

name:'example-8'

});

Page 86: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

logger.trace('Trace');

logger.debug('Debug');

logger.info('Info');

logger.warn('Warn');

logger.error('Error');

logger.fatal('Fatal');

logger.fatal('Wegotafatal,letsexit');

process.exit(1);

Let’sruntheexample:

[~/examples/example-8]$nodeindex.js|bunyan

[2015-06-07T11:39:55.801Z]INFO:example-8/2512onmacbook.local:Info

[2015-06-07T11:39:55.811Z]WARN:example-8/2512onmacbook.local:Warn

[2015-06-07T11:39:55.814Z]ERROR:example-8/2512onmacbook.local:Error

[2015-06-07T11:39:55.814Z]FATAL:example-8/2512onmacbook.local:Fatal

[2015-06-07T11:39:55.814Z]FATAL:example-8/2512onmacbook.local:Wegota

fatal,letsexit

Ifyounotice,traceanddebugweren’toutputtedontheconsole.Thisisbecausetheyareusedtofollowtheflowoftheprogramratherthanthekeyinformationandareusuallyverynoisy.

Wecanchangetheleveloflogswewanttoseebypassingthisasanoptionwhenwecreatethelogger:

logger=Bunyan.createLogger({

name:'example-8',

level:Bunyan.TRACE

});

Now,whenweruntheexample:

[~/examples/example-8]$nodeindex.js|bunyan

[2015-06-07T11:55:40.175Z]TRACE:example-8/2621onmacbook.local:Trace

[2015-06-07T11:55:40.177Z]DEBUG:example-8/2621onmacbook.local:Debug

[2015-06-07T11:55:40.178Z]INFO:example-8/2621onmacbook.local:Info

[2015-06-07T11:55:40.178Z]WARN:example-8/2621onmacbook.local:Warn

[2015-06-07T11:55:40.178Z]ERROR:example-8/2621onmacbook.local:Error

[2015-06-07T11:55:40.178Z]FATAL:example-8/2621onmacbook.local:Fatal

[2015-06-07T11:55:40.178Z]FATAL:example-8/2621onmacbook.local:Wegota

fatal,letsexit

Weusuallydon’twanttoseelogsthatarelowerthantheinfolevel,asanyinformationthatisusefulforpost-mortemdebuggingshouldhavebeenloggedusingtheinfoorhigher.

Bunyan’sapiisgoodforthefunctionofloggingerrorsandobjects.ItsavesthecorrectstructuresinitsJSONoutput,whichisreadyfordisplay:

try{

ref.go();

}catch(error){

logger.error(error);

}

Let’sruntheexample:

Page 87: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

[~/examples/example-9]$nodeindex.js|bunyan

[2015-06-07T12:00:38.700Z]ERROR:example-9/2635onmacbook.local:refis

notdefined

ReferenceError:refisnotdefined

atObject.<anonymous>(~/examples/example-8/index.js:9:2)

atModule._compile(module.js:460:26)

atObject.Module._extensions..js(module.js:478:10)

atModule.load(module.js:355:32)

atFunction.Module._load(module.js:310:12)

atFunction.Module.runMain(module.js:501:10)

atstartup(node.js:129:16)

atnode.js:814:3

Ifwelookattheexampleandpretty-printit,wewillseethattheysaveitasanerror:

[~/examples/example-9]$npminstall-gprettyjson

[~/examples/example-9]$nodeindex.js|prettyjson

name:example-9

hostname:macbook.local

pid:2650

level:50

err:

message:refisnotdefined

name:ReferenceError

stack:

"""

ReferenceError:refisnotdefined

atObject.<anonymous>(~/examples/example-8/index.js:9:2)

atModule._compile(module.js:460:26)

atObject.Module._extensions..js(module.js:478:10)

atModule.load(module.js:355:32)

atFunction.Module._load(module.js:310:12)

atFunction.Module.runMain(module.js:501:10)

atstartup(node.js:129:16)

atnode.js:814:3

"""

msg:refisnotdefined

time:2015-06-07T12:02:33.875Z

v:0

Thisisusefulbecause,ifyoujustloganerror,youwilleithergetanemptyobjectifyouusedJSON.stringifyorjustthemessageifyouusedtoString:

try{

ref.go();

}catch(error){

console.log(JSON.stringify(error));

console.log(error);

console.log({

message:error.message

name:error.name

stack:error.stack

});

}

Let’sruntheexample:

Page 88: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

[~/examples/example-10]$nodeindex.js

{}

[ReferenceError:refisnotdefined]

{message:'refisnotdefined',

name:'ReferenceError',

stack:'--REMOVED--'}

Itistolotsimplerandcleanertouselogger.error(error)thanlogger.error({message:error.message/*,...*/});.

Asmentionedearlier,bunyanusestheconceptofstreams,whichmeansthatwecanwritetoafile,stdout,oranyotherservicewewishtoextendto.

Towritetoafile,allweneedtodoisaddittotheoptionspassedtobunyanatsetup:

varBunyan=require('bunyan'),

logger;

logger=Bunyan.createLogger({

name:'example-11',

streams:[

{

level:Bunyan.INFO,

path:'./log.log'

}

]

});

logger.info(process.versions);

logger.info('Applicationstarted');

Byrunningtheexample,youwon’tseeanylogsbeingoutputtedtotheconsolebuttheywillbewrittentofileinstead:

[~/examples/example-11]$nodeindex.js

Ifyoulistwhat’sinthedirectoryyouwillseeanewfilehasbeencreated:

[~/examples/example-11]$ls

index.jslog.lognode_modules

Ifyoureadwhat’sinthefileyouwillseethatthelogshavealreadybeenwritten:

[~/examples/example-11]$catlog.log

{"name":"example-

11","hostname":"macbook.local","pid":3614,"level":30,"http_parser":"2.3","n

ode":"0.12.2","v8":"3.28.73","uv":"1.4.2-

node1","zlib":"1.2.8","modules":"14","openssl":"1.0.1m","msg":"","time":"20

15-06-07T12:29:46.606Z","v":0}

{"name":"example-

11","hostname":"macbook.local","pid":3614,"level":30,"msg":"Application

started","time":"2015-06-07T12:29:46.608Z","v":0}

Wecanrunthisthroughbunyaninordertoprintitoutnicely:

[~/examples/example-11]$catlog.log|bunyan

[~/examples/example-11]$catlog.log|bunyan

[2015-06-07T12:29:46.606Z]INFO:example-11/3614onmacbook.local:

Page 89: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

(http_parser=2.3,node=0.12.2,v8=3.28.73,uv=1.4.2-node1,zlib=1.2.8,

modules=14,openssl=1.0.1m)

[2015-06-07T12:29:46.608Z]INFO:example-11/3614onmacbook.local:

Applicationstarted

Nowthatwecanlogtoafile,wealsowanttobeabletoseethemessagesastheyaredisplayed.Ifwewerejustloggingtoafile,wecoulduse:

[~/examples/example-11]$tail-flog.log|bunyan

Thiswilllogtostdoutasthefileitisbeingwrittento;alternativelywecouldjustaddanotherstreamtobunyan:

logger=Bunyan.createLogger({

name:'example-11',

streams:[

{

level:Bunyan.INFO,

path:'./log.log'

},

{

level:Bunyan.INFO,

stream:process.stdout

}

]

});

Runningtheexamplewilldisplaythelogstotheconsole:

[~/examples/example-11]$nodeindex.js|bunyan

[2015-06-07T12:37:19.857Z]INFO:example-11/3695onmacbook.local:

(http_parser=2.3,node=0.12.2,v8=3.28.73,uv=1.4.2-node1,zlib=1.2.8,

modules=14,openssl=1.0.1m)[2015-06-07T12:37:19.860Z]INFO:example-

11/3695onmacbook.local:Applicationstarted

Wecanalsoseethelogshavebeenappendedtothefile:

[~/examples/example-11]$catlog.log|bunyan

[2015-06-07T12:29:46.606Z]INFO:example-11/3614onmacbook.local:

(http_parser=2.3,node=0.12.2,v8=3.28.73,uv=1.4.2-node1,zlib=1.2.8,

modules=14,openssl=1.0.1m)

[2015-06-07T12:29:46.608Z]INFO:example-11/3614onmacbook.local:

Applicationstarted

[2015-06-07T12:37:19.857Z]INFO:example-11/3695onmacbook.local:

(http_parser=2.3,node=0.12.2,v8=3.28.73,uv=1.4.2-node1,zlib=1.2.8,

modules=14,openssl=1.0.1m)

[2015-06-07T12:37:19.860Z]INFO:example-11/3695onmacbook.local:

Applicationstarted

Great,nowwehavetheloggingdown,whatshallwedowithit?

Well,ithelpstoknowwhereourerrorsareoccurringanditstartstogetreallymessywhenyouhavelotsofanonymousfunctionsaroundtheplace.IfyounoticedintheexamplesthatcoveranHTTPserver,themajorityofthefunctionswerenamed.Thisisveryhelpfulintrackingdownerrorswhencallbacksareinvolved.

Let’slookatthisexample:

Page 90: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

try{

a=function(callback){

returnfunction(){

callback();

};

};

b=function(callback){

returnfunction(){

callback();

}

};

c=function(callback){

returnfunction(){

thrownewError("I'mjustmessingwithyou");

};

};

a(b(c()))();

}catch(error){

logger.error(error);

}

Itmightlookabitmessyandthat’sbecauseitis.Let’srunthefollowingexample:

[~/examples/example-12]$nodeindex.js|bunyan

[2015-06-07T12:51:11.665Z]ERROR:example-12/4158onmacbook.local:I'm

justmessingwithyou

Error:I'mjustmessingwithyou

at/Users/fabian/examples/example-12/index.js:19:10

at/Users/fabian/examples/example-12/index.js:14:4

at/Users/fabian/examples/example-12/index.js:9:4

atObject.<anonymous>(/Users/fabian/examples/example-

12/index.js:22:16)

atModule._compile(module.js:460:26)

atObject.Module._extensions..js(module.js:478:10)

atModule.load(module.js:355:32)

atFunction.Module._load(module.js:310:12)

atFunction.Module.runMain(module.js:501:10)

atstartup(node.js:129:16)

Youcanseethattherearenofunctionnamesinourcodeandalsothereisnonaminginthestacktraceunlikethefirstfewfunctions.InNode.js,thenamingoffunctionswillcomefromeitherthevariablenameortheactualfunctionname.Forexample,ifyouuseCls.prototype.functhenthenamewillbeCls.funcbutifyouusethefunctionfuncthenthenamewillbefunc.

Youcanseethatthereisaslightbenefitherebutthisbecomesveryusefulonceyoustartusingpatternsinvolvingasynccallbacks:

[~/examples/example-13]$npminstallq

Let’sthrowanerrorinacallback:

varQ=require('q');

Q()

.then(function(){

Page 91: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

//Promisedreturnedfromanotherfunction

returnQ()

.then(function(){

thrownewError('Helloerrors');

});

})

.fail(function(error){

logger.error(error);

});

Runningourexamplegivesus:

[~/examples/example-13]$nodeindex.js|bunyan

[2015-06-07T13:03:57.047Z]ERROR:example-13/4598onmacbook.local:Hello

errors

Error:Helloerrors

at/Users/fabian/examples/example-13/index.js:12:9

at_fulfilled(/Users/fabian/examples/example-

13/node_modules/q/q.js:834:54)

Thisiswhereitstartstogetdifficulttoread;assigningsimplenamestoourfunctionscanhelpusfindwheretheerroriscomingfrom:

returnQ()

.then(functionresultFromOtherFunction(){

thrownewError('Helloerrors');

});

Runningtheexample:

[~/examples/example-13]$nodeindex.js|bunyan

[2015-06-07T13:04:45.598Z]ERROR:example-13/4614onmacbook.local:Hello

errors

Error:Helloerrors

atresultFromOtherFunction(/Users/fabian/examples/example-

13/index.js:12:9)

at_fulfilled(/Users/fabian/examples/example-

13/node_modules/q/q.js:834:54)

Page 92: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 93: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ErrorhandlingAnotheraspectofdebuggingishandlingandexpectingerrorsbeforehand.Therearethreewaysinwhichwecanhandleourerrors:

asimpletry/catchcatchingthemattheprocesslevelcatchingerrorsonthedomainlevel

Atry/catchfunctionwillbesufficientifweexpectanerrortooccurandwewillbeabletocontinuewithoutknowingtheresultofwhateverwasbeingexecuted,orwecouldhandleandreturntheerror,asshown:

functionparseJSONAndUse(input){

varjson=null;

try{

json=JSON.parse(input);

}catch(error){

returnQ.reject(newError("Couldn'tparseJSON"));

}

returnQ(use(json));

}

Anothersimplewaytocatcherrorsistoaddanerrorhandlertoyourprocess;anyerrorsthatarecaughtatthislevelareusuallyfatalandshouldbetreatedassuch.Anexitoftheprocessshouldfollowandyoushouldbeusingapackage,suchasforeverorpm2:

process.on('uncaughtException',functionerrorProcessHandler(error){

logger.fatal(error);

logger.fatal('Fatalerrorencountered,exitingnow');

process.exit(1);

});

Youshouldalwaysexittheprocessfollowinganuncaughterror.Thefactthatitisuncaughtmeansthatyourapplicationisinanunknownstatewhereanythingcanhappen.Forexample,therecouldhavebeenanerrorinyourHTTProuterandnomorerequestscanberoutedtothecorrecthandlers.Youcanreadmoreaboutthisathttps://nodejs.org/api/process.html#process_event_uncaughtexception.

Abetterwaytohandleerrorsonagloballevelisusingdomain.Withdomainsyoucanalmostsandboxagroupofasynchronouscodetogether.

Let’sthinkinthecontextofarequesttoourserver.Wemakearequest,readfromadatabase,makecallstoexternalservices,writebacktoadatabase,dosomelogging,dosomebusinesslogic,andweexpectperfectdatacomingfromexternalsourcesallaroundthecode.However,intherealworlditisn’talwayssoandwecan’thandleeveryerrorthatcouldpossiblyoccur;moreover,wedon’twanttotakedownourentireserverjustbecauseofoneerrorforaveryspecificrequest.That’swhereweneeddomains.

Let’slookatthefollowingexample:

varDomain=require('domain'),

Page 94: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

domain;

domain=Domain.create();

domain.on('error',function(error){

console.log('Domainerror',error.message);

});

domain.run(function(){

//Runcodeinsidedomain

console.log(process.domain===domain);

thrownewError('Errorhappened');

});

Let’srunthecode:

[~/examples/example-14]$nodeindex.js

true

DomainerrorErrorhappened

Thereisaproblemwiththiscode;however,aswearerunningthissynchronouslywearestillputtingtheprocessintoabrokenstate.Thisisbecausetheerrorbubbleduptothenodeitselfandthenwaspassedtotheactivedomain.

Whenwearecreatingthedomaininanasynchronouscallback,wecanbesurethattheprocesscancontinue.Wecanmimicthisbyusingprocess.nextTick:

process.nextTick(function(){

domain.run(function(){

thrownewError('Errorhappened');

});

console.log("Iwon'texecute");

});

process.nextTick(function(){

console.log('Nexttickhappend!');

});

console.log('Ihappenedbeforeeverythingelse');

Runningtheexampleshoulddisplaythecorrectlogs:

[~/examples/example-15]$nodeindex.js

Ihappenedbeforeeverythingelse

DomainerrorErrorhappened

Nexttickhappend!

Page 95: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 96: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryInthischapterwehavecoveredafewpost-mortemdebuggingmethodstohelpusuncoverbugsincludinglogging,namingpractices,andsufficienterrorhandling.

Inthenextchapter,wewillcoverconfigurationofourapplications.

Page 97: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 98: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter5.ConfigurationAsourapplicationsgetlargerandlarger,westarttolosesightofwhatisconfiguredtodowhat;wemayalsogetintoasituationwherewehavecoderunningin12differentplaces,eachneedingabitofcodethathastobechangedtodosomethingelse,forexampleconnectingtoadifferentdatabase.Then,foreachofthose12environments,wehavethreeversions:production,staging,anddevelopment.Allofasudden,itgetsverycomplicated.Thisiswhyweneedtobeabletoconfigureourcodefromahigher-levelsothatwedon’tbreakanythingintheprocess.

Page 99: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

JSONfilesThereareafewwaysinwhichwecanconfigureourapplication.ThefirstwaythatwewilllookatisasimpleJSONfile.

Ifwelookattheextensionsrequiresupportsbydefault,wecanseethatwecanimportJSONrightintoourcode,asshown:

[~/examples/example-16]$node

>require.extensions

{'.js':[Function],

'.json':[Function],

'.node':[Function:dlopen]}

Let’screateasimpleserverwithaconfigurationfileratherthanahardcodedfile:

First,wehavetocreatetheconfigurationfile:

{

"host":"localhost",

"port":8000

}

Withthis,wecannowcreateourserver:

varConfig=require('./config.json'),

Http=require('http');

Http.createServer(function(request,response){

}).listen(Config.port,Config.host,function(){

console.log('Listeningonport',Config.port,'andhost',Config.host);

});

Now,wecanjustchangetheconfigfileinsteadofchangingthecodetochangetheportonwhichourserverisrunning.

Butourconfigfileisabittoogeneric;wehavenoideaastowhatisahostoraportandwhattheyarerelatedto.

Whileconfiguring,thekeysneedtobelessgenericsothatweknowwhattheyarebeingusedfor,unlessthecontextisgivendirectlybytheapplication.Forexample,iftheapplicationwastoservepurelystaticcontentthenitmaybeacceptabletohavemoregenerickeys.

Tomaketheseconfigurationkeyslessgeneric,wecanwrapthemallinaserverobject:

{

"server":{

"host":"localhost",

"port":8000

}

}

Sonow,inordertoknowabouttheportoftheserverweneedtousethefollowingcode:

Page 100: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Config.server.port

Anexamplewherethiswillbeusefulcouldbeforaserverthatconnectstoadatabase,astheycanacceptboththeportandhostastheparameters:

{

"server":{

"host":"localhost",

"port":8000

},

"database":{

"host":"db1.example.com",

"port":27017

}

}

Page 101: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 102: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

EnvironmentalvariablesAnotherwayinwhichwecanconfigureourapplicationsisthroughtheuseofenvironmentalvariables.

Thesecanbedefinedbytheenvironmentyouarerunningyourapplicationinorinthecommandthatyouareusingtostartyourprocesswith.

InNode.js,youcanaccesstheenvironmentalvariablesusingprocess.env.Whenusingenv,youdon’twanttobepollutingthisspacetoomuchandsoitisagoodideatoprefixthekeywithsomethingrelatedtoyourself—yourprogramorcompany.Forexample,Config.server.hostbecomesprocess.env.NAME_SERVER_HOST;thereasonforthisisthatwecanclearlyseewhatisrelatedtoyourprogramandwhatisn’t.

Usingenvironmentalvariablestoconfigureourserver,ourcodewilllookasfollows:

varHttp=require('http'),

server_port,

server_host;

server_port=parseInt(process.env.FOO_SERVER_PORT,10);

server_host=process.env.FOO_SERVER_HOST;

Http.createServer(function(request,response){

}).listen(server_port,server_host,function(){

console.log('Listeningonport',server_port,'andhost',server_host);

});

Torunthiscodewithourvariables,wewilluse:

[~/examples/example-17]$FOO_SERVER_PORT=8001\

FOO_SERVER_HOST=localhostnodeserver.js

Listeningonport8001andhostlocalhost

YouprobablynoticedthatIhadtouseparseIntforFOO_SERVER_PORT;thisisbecauseallvariablespassedinthismannerareessentiallystrings.Wecanseethisbyexecutingtypeofprocess.env.FOO_ENV:

[~/examples/example-17]$FOO_ENV=1234node

>typeofprocess.env.FOO_ENV

'string'

>typeofparseInt(process.env.FOO_ENV,10)

'number'

Althoughthiskindofconfigurationisverysimpletocreateandconsume,itmaynotbethebestmethod,asthevariablesarehardtokeeptrackofiftherearealotofthemandtheycanbedroppedveryeasily.

Page 103: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 104: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ArgumentsAnotherwayinwhichtheconfigurationcanbedoneisthroughtheuseofargumentsthatarepassedtoNode.jsastheprocessstarts,youcanaccesstheseusingprocess.argv,withargvstandingforargumentvector.

Thearraythatprocess.argvreturnswillalwayshaveanodeatindex0.Forexample,ifyourunnodeserver.jsthenprocess.argvwillhavethevalueof['node','/example/server.js'].

IfyoupassanargumenttoNode.jsthenitwillbeaddedtotheendofprocess.argv.

Ifyourunnodeserver.js--port=8001,theprocess.argvwillcontain['node','/example/server.js','--port=8001'],prettysimple,right?

Eventhoughwecanhaveallthisconfiguration,weshouldalwaysrememberthatconfigurationcanbesimplyexcludedandwewillstillwantourapplicationtorunwhenthishappens.Usually,youshouldprovidedefaulthardcodedvaluesasabackupwhenyouhaveconfigurationoptions.

Parameterssuchaspasswordsandprivatekeysshouldneverhaveadefaultvaluebutlinksandoptionsthatareusuallystandardshouldbegivendefaults.ItisprettyeasytogiveadefaultvalueinNode.js,allyouneedtodoisusetheORoperator.

value=value||'default';

Essentially,whatthisdoesischeckifthevalueisfalsy;ifitis,thenusethedefaultvalue.Youneedtowatchoutforvaluesthatyouknowcouldbefalsy,booleansandnumbersdefinitelyfallintothiscategory.

Inthesecasesyoucanuseanifstatementcheckingforanullvalue,asshown:

if(value==null)value=1

Page 105: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 106: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryThat’sallforconfiguration.Inthischapteryoulearnedaboutthethreemethodsthatyoucanusetocreateadynamicapplication.Welearnedthatweshouldnameourconfigurationkeysinawaythatwecanidentifywhatthevaluesarechangingtoandhowtheywillaffectourapplication.Wealsolearnedabouthowwecanpasssimpleargumentstoourapplicationusingenvironmentalvariablesandargv.

Withthisinformation,wecanmoveforwardtoconnectingandutilizingdatabasesinthenextchapter.

Page 107: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 108: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter6.LevelDBandNoSQLInthischapter,wewillcovertwovariationsofdatabasesthatcanbeusedwithNode.js;oneprovidesaverylightweightandsimplesetoffeatures,whiletheothergivesusmoreflexibilityandageneral-purposesetoffeatures.Inthischapter,wearegoingtocoverLevelDBandMongoDB

Page 109: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

LevelDBOneofthegreatthingswithNode.jsisthatweusethesamelanguageforboththefrontandbackendandthesamegoesforNoSQLdatabases.ThemajorityofthemsupportJSONrightoffthemark;thisisgreatforanyoneusingNode.jsasthereisnotimespentinmakingarelationalmodel,turningitintoaJSON-likestructure,passingittothebrowser,doingsomethingwithit,andreversingtheprocess.

WithadatabasethatsupportsJSONnatively,youcangetrightdowntobusinessandplayball.

GooglehasprovideduswithasimplehookintoaNoSQLdatabasethatcanbeinstalledandcanbemadereadytousewithjustonecommand:

[~/examples/example-18]$npminstalllevel

YouwillseethatthiswillinstallbothLevelDOWNandLevelUP.

LevelDOWNisthelow-levelbindingtoLevelDBandLevelUPisthesimplewrapperaroundthis.

LevelDBisverysimpleintermsofsetup.Onceitisinstalled,wejustcreateaninstanceofLevelUPandpassitwherewewantourdatabasetobestored:

varLevelUP=require('level'),

db=newLevelUP('./example-db');

Nowwehaveafastandsimplewaytostoredata.

AsLevelDBisjustasimplekey/valuestore,itdefaultstostringkeysandstringvalues.Thisisusefulifthat’salltheinformationyouwishtostore.Youcanalsouseitasasimplecachestore.IthasaverysimpleAPI,atthisstageweareonlygoingtofocusonfourmethods:put,get,del,andcreateReadStream;it’sprettyobviouswhatmostofthemdo:

Method Usedfor Arguments

put insertingpairs key,value,callback(error)

get fetchingpairs key,callback(error,value)

del deletingpairs key,callback(error)

createReadStream fetchingmanypairs

Toinsertdataoncewehavecreatedourdatabase,allweneedtodois:

db.put('key','value',function(error){

if(error)returnconsole.log('Error!',error)

db.get('key',function(error,value){

if(error)returnconsole.log('Error!',error)

console.log("key=",value)

Page 110: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

});

});

Ifwerunthecode,wewillseethatweinsertedandretrievedourvalue:

[~/examples/example-18]$nodeindex.js

key=value

Thisisn’toursimpleJSONstructure;however,it’sjustastring.TogetourstoretosaveJSON,allweneedtodoistopassthevalueencodingasanoptiontothedatabase,asshown:

varLevelUP=require('level'),

db=newLevelUP('./example-db',{

valueEncoding:'json'

});

NowwecanstoreJSONdata:

db.put('jsonKey',{inner:'value'},function(error){

if(error)returnconsole.log('Error!',error)

db.get('jsonKey',function(error,value){

if(error)returnconsole.log('Error!',error)

console.log("jsonKey=",value)

});

});

However,astringcanbestoredasJSONandwecanstillpassstringsasavalueandalsoretrieveitassuch.

Runningthisexamplewillshowthefollowing:

[~/examples/example-18]$nodeindex.js

key=value

jsonKey={inner:'value'}

Now,wehavethesimplemethodsdownandwecannowmoveontocreateReadStream.

ThisfunctionreturnsanobjectthatcanbecomparedtoNode.jsbuiltinReadableStream.Foreachkey/valuepairinourdatabase,itwillemitadataevent;italsoemitsotherevents,suchaserrorandend.Iferrordoesn’thaveaneventlistener,thenitwillpropagate,therebykillingyourentireprocess(ordomain),asshown:

db.put('key1',{inner:'value'},function(error){

if(error)returnconsole.log('Error!',error)

varstream=db.createReadStream();

stream

.on('data',function(pair){

console.log(pair.key,"=",pair.value);

})

.on('error',function(error){

console.log(error);

})

Page 111: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

.on('end',function(){

console.log('end');

});

});

Runningthisexample:

[~/examples/example-20]$nodeindex.js

key1={inner:'value'}

end

Ifweputmoredatainthedatabasewewillhavemultipledataeventsemitted:

[~/examples/example-20]$nodeindex.js

key1={inner:'value'}

key2={inner:'value'}

end

Page 112: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 113: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

MongoDBAsyoucansee,databaseswithNode.jscanbeverysimple.IfwewantsomethingabitmorecompletewecanuseanotherNoSQLdatabasecalledMongoDB–anotherverypopulardocument-baseddatabase.

Forthissetofexamples,youcaneitheruseahosteddatabaseusingaprovidersuchasMongoLab(theyprovideafreetierfordevelopment)oryoucansetupadatabaselocallyfollowingtheinstructionsathttp://docs.mongodb.org/manual/installation.

Wecancontinueonceyouhaveadatabasetoconnectto.

MongoDBhasseveralmodulesthatcanbeusedwithNode.js,themostpopularoneisMongoose;however,wewillbeusingthecoreMongoDBmodule:

[~/examples/example-21]$npminstallmongodb

Touseourdatabase,wefirstneedtoconnecttoit.Weneedtoprovidetheclientwithaconnectionstring,agenericURIwiththeprotocolofmongodb.

Ifyouhavealocalmongodatabaserunningwithnocredentialsyouwilluse:

mongodb://localhost:27017/database

Thedefaultportis27017,soyoudon’tneedtospecifythat;however,itisincludedforcompleteness.

IfyouareusingMongoLab,theywillprovideyouwithaconnectionstring;itshouldbeintheformatof:

mongodb://<dbuser>:<dbpassword>@<ds>.mongolab.com:<port>/<db>

Connectingtoourdatabaseisactuallyprettysimple.Allweneedtodoisprovidethedriverwithaconnectionstringandwegetbackadatabase:

varMongoDB=require('mongodb'),

MongoClient=MongoDB.MongoClient;

connection="mongodb://localhost:27017/database"

MongoClient.connect(connection,function(error,db){

if(error)returnconsole.log(error);

console.log('Wehaveaconnection!');

});

EachsetofdatainMongoDBisstoredinacollection.Oncewehaveadatabasewecanfetchacollectiontoruntheoperationson:

varcollection=db.collection('collection_name');

Inacollection,wehaveafewsimplemethodsthatholdlotsofpower,givingusafullCRUD“API”.

EachdocumentinMongoDBhasanid,whichisaninstanceofObjectId.Theproperty

Page 114: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

theyuseforthisidis_id.

Tosaveadocumentwejustneedtocallsave,itacceptsanobjectoranarrayofobjects.Asingleobjectinacollectionisreferredtoasadocument:

vardoc={

key:'value_1'

};

collection.save(doc,{w:1},function(){

console.log('Documentsaved')

});

IfwecallthesavefunctionwithadocumentthathasanIDthenthedocumentwillbeupdatedratherthaninserted:

varObjectId=MongoDB.ObjectId

//Thisdocumentalreadyexistsinmydatabase

vardoc_id={

_id:newObjectId("55b4b1ffa31f48c6fa33a62a"),

key:'value_2'

};

collection.save(doc_id,{w:1},function(){

console.log('DocumentwithIDsaved');

});

Nowthatwehavedocumentsinourdatabase,wecanqueryforthem,asshown:

collection.find().toArray(function(error,result){

console.log(result.length+"documentsinourdatabase!")

});

Ifnocallbackisprovidedtofindthenitwillreturnacursor;thisallowsustousemethodssuchaslimit,sort,andtoArray.

Youcanpassaquerytofindtolimitwhatisreturned.InordertofindanobjectbyitsIDweneedtousesomething,suchas:

collection.find(

{_id:newObjectId("55b4b1ffa31f48c6fa33a62a")},

function(error,documents){

console.log('Founddocument',documents[0]);

}

);

Wecanalsofilteritbyanyotherpropertyyoumightuse:

collection.find(

{key:'value'},

function(error,documents){

console.log('Found',documents.length,'documents');

}

);

IfyouhaveusedSQLbefore,youmusthavenoticedthelackofoperators,suchasOR,AND,orNOT.However,youdon’tneedtoworrybecausemongoprovidesmanyequivalents.

Youcanseeacompletelisthere:

Page 115: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

http://docs.mongodb.org/manual/reference/operator/query/.

Alloperatorsareprefixedwiththedollarsign,forexample$and,$or,$gt,and$lt.

Youcanseethespecificsyntaxtousethesebylookingatthedocumentation.

Tousean$orcondition,youneedtoincludeitasifitisaproperty:

collection.find(

{

$or:[

{key:'value'},

{key:'value_2'}

]

},

function(error,documents){

console.log('Found',documents.length,'documents');

}

);

UsingadatabasesuchasMongoDBgivesusmorepowertoretrieveourdataandcreateamorefeaturefullsoftware.

Page 116: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 117: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryNowwehaveplaceswherewecanstoreourdata.Ononeendwehaveasimplekey/valuestorethatprovidesuswithasuper-convenientwaytostoredataandontheotherendwehaveafeaturefulldatabasethatprovidesuswithafullsetofqueryoperators.

Boththesedatabaseswillhelpusinthenextchaptersaswemoveclosertocreatingourfullstackapplication.

InthenextchapterwewillcoverSocket.IO,areal-timecommunicationframeworkbuiltontopofWebSockets.

Page 118: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 119: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter7.Socket.IOSimpleHTTPisgreatforthingsthatdon’tneedreal-timedata,butwhataboutwhenweneedtoknowaboutthingsastheyhappen.Forexample,ifwewerecreatingawebsitethathadachatinterfaceorsimilar?

ThisiswhensomethinglikeWebsocketscomeintoplay.WebsocketsareusuallyreferredtoasWebSocketsandarefullduplexortwo-waylow-latencycommunicationchannels.Theyaregenerallyusedbymessagingapplicationsandgameswheremessagesneedtoberelayedbetweentheserverandclient.Thereisareallyhandynpmmodulecalledsocket.io,whichcanaddWebsocketstoanyNode.jsapplication.

Toinstallitwejustneedtorun:

[~/examples/example-27]npminstallsocket.io

Socket.IOcanbesetupverysimplytolistenforconnections.First,wewanttobeabletoserveoutastatichtmlpagetorunclientsidecodewith:

varHttp=require('http'),

FS=require('fs');

varserver=Http.createServer(handler);

server.listen(8080);

functionhandler(request,response){

varindex=FS.readFileSync('index.html');

index=index.toString();

response.writeHead(200,{

'Content-Type':'text/html',

'Content-Length':Buffer.byteLength(index)

});

response.end(index);

}

Now,letscreateanHTMLfileaswell,namedindex.html,inthesamedirectory:

<html>

<head>

<title>WSExample</title>

</head>

<body>

<h2>WSExample</h2>

<pid="output"></p>

<!--SocketIOClientlibrary-->

<scriptsrc="/socket.io/socket.io.js"></script>

<scripttype="application/javascript">

/*Ourclientsidecodewillgohere*/

</script>

</body>

</html>

Page 120: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Let’srunourexampleandensurethatwegetourpage,weshouldbeabletoseeWSExampleonscreen.Now,toaddsocketsupporttoourapplicationwejustneedtorequiresocket.ioandspecifywhathttpservertolistenwithtoIOServer:

varIOServer=require('socket.io');

vario=newIOServer(server);

Now,wheneverthereisanewsocketconnectionover8080wewillgetaconnectioneventonio:

io.on('connection',function(socket){

console.log('NewConnection');

});

Letsaddsomecodetotheclient.Socket.IOprovidesuswithaclientlibraryandtheyexposethisthroughtheendpoint/socket.io/socket.io.js.Thisisincludedintheprecedingindex.htmlfile.

TipAlltheclientsidecodeiscontainedwithinthesecondscripttagoftheindex.htmlfile.

Tocreateaconnectionwiththeserverallweneedtodoiscallio.connectandpassthelocation.Thiswillreturnasocketforuswithwhichwecancommunicatetoourserver.

WeareusingtheclientprovidedbySocket.IOhere,asitwilldetectwhetherWebSocketsareavailable,andifpossibleusethem.Otherwise,itwillutilizeothermethodssuchaspolling,whichmakessurethatitworkseverywhereratherthanjustonevergreenbrowsers:

varsocket=io.connect('http://localhost:8080');

Wewilluseapelementtologmessagestothescreenwith.Wecandothatwiththiscode,thenallweneedtodoiscalllogScreen:

varoutput=document.getElementById('output');

functionlogScreen(text){

vardate=newDate().toISOString();

line=date+""+text+"<br/>";

output.innerHTML=line+output.innerHTML

}

Onceaconnectionismade,justlikeontheserversideaconnectioneventisemitted,wecanlistentothisusingon:

socket.on('connection',function(){

logScreen('Connection!');

});

Now,wecanrunourserveroncewenavigatetohttp://localhost:8080.YoushouldbeabletoseeConnection!showingup:

Page 121: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Toreceiveamessageonserverside,wejustneedtolistenforthemessageevent.Fornow,wewilljustechothemessageback:

socket.on('connection',function(){

socket.on('message',function(message){

socket.send(message);

});

});

Ontheclientside,wejustneedtocallsendtosendamessageandwewanttodothisinsideourconnectionevent.Theapioneachsideisverysimilartoeachother,asyoucansee:

socket.send('Hello');

Ontheclientside,wealsowanttolistenformessagesandlogthemtothescreen:

socket.on('message',logScreen);

Oncewerestarttheserverandrefreshourpage,weshouldbeabletoseeanadditionalHellomessageappearonscreen.

[~/examples/example-27]$nodeindex.js

Hello

Thishappensbecausetheservercannowsendtheclientpacketsofdata.Italsomeansthatwecanupdatetheclientatanytime.Forexample,everysecondwecansendanupdatetotheclient:

socket.on('connection',function(){

functiononTimeout(){

socket.send('Update');

}

setInterval(onTimeout,1000);

});

Now,whenwerestartourserverweshouldbeabletoseeanupdatemessageeverysecond.

Youmighthavenoticedthatyoudidn’tneedtorefreshyourwebpagefortheconnectiontobeopenedagain.Thisisbecausesocket.iotransparentlykeepsourconnections“alive”

Page 122: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

aswellasreconnectingifneeded.Thistakesallthepainoutofusingsockets,aswehavenoneofthesetroubles.

Page 123: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

RoomsSocket.IOalsohastheconceptofrooms,wheremultipleclientscanbegroupedintodifferentrooms.Toemulatethis,allyouwillneedtodoisnavigatetohttp://localhost:8080inmultipletabs.

Onceaclientconnects,weneedtocallthejoinmethodtotellthesocketwhatroomtobein.Ifwewishtodosomethingsuchasagroupchatwithspecificusersonly,weneedhavearoomidentifierinadatabaseorcreateone.Fornowwewilljusthaveeveryonejointhesameroom:

socket.on('connection',function(){

console.log('NewConnection');

varroom='ourroom';

socket.join(room,function(error){

if(error)returnconsole.log(error);

console.log('Joinedroom!');

});

});

Foreverytabweopen,weshouldseeamessagethatwehavejoinedaroom:

[~/examples/example-27]$nodeindex.js

NewConnection

Joinedroom!

NewConnection

Joinedroom!

NewConnection

Joinedroom

Withthis,wecanbroadcastamessagetotheentireroom.Let’sdothiseverytimesomeonejoins.Withinthejoincallback:

socket

.to(room)

.emit(

'message',

socket.id+'joinedtheroom!'

);

Ifyoulookinyourbrowser,witheachconnectiontheotherclientsgetanotificationthatsomeoneelsehasjoined:

x3OwYOkOCSsa6Qt5AAAFjoinedtheroom!

mlx-Cy1k3szq8W8tAAAEjoinedtheroom!

Connection!

Connecting

Thisisgreat,wecannowcommunicatealmostdirectlybetweenbrowsers!

Ifwewanttoleavearoom,allweneedtodoiscallleave,wewillbroadcastthisbeforewecallthefunction:

socket

Page 124: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

.to(room)

.emit(

'message',

socket.id+'isleavingtheroom'

);

socket.leave(room);

Whilerunningthis,youwillnotseeanymessagesfromanotherclientbecauseyouareleavingrightaway:however,ifyouweretoputadelayonthisyoumightseeanotherclientcomeandgo:

leave=function(){

socket

.to(room)

.emit(

'message',

socket.id+'isleavingtheroom'

);

socket.leave(room);

};

setTimeout(leave,2000);

Page 125: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 126: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

AuthenticationForauthentication,wecanusethesamemethodthatweusedwithourHTTPserverandwecanacceptaJSONWebToken

Intheseexamples,forsimplicitywewilljusthaveasingleHTTProutetologin.WewillsignaJWTthatwewilllaterauthenticatebycheckingthesignature

Weneedtoinstallacoupleofextranpmmodulesforthis;wewillincludechancesothatwecangeneratesomerandomdata.

[~/examples/example-27]npminstallsocketio-jwtjsonwebtokenchance

First,wearegoingtoneedaroutetologin.Wewillmodifyourhandlertowatchfortheurl/login:

if(request.url==='/login'){

returngenerateToken(response)

}

OurnewfunctiongenerateTokenwillcreateaJSONWebTokenwithsomerandomdatausingchance.Wewillalsoneedasecretforourtokens:

varJWT=require('jsonwebtoken'),

Chance=require('chance').Chance();

varjwtSecret='Oursecret';

functiongenerateToken(response){

varpayload={

email:Chance.email(),

name:Chance.first()+''+Chance.last()

}

vartoken=JWT.sign(payload,jwtSecret);

response.writeHead(200,{

'Content-Type':'text/plain',

'Content-Length':Buffer.byteLength(token)

})

response.end(token);

}

Now,wheneverwerequesthttp://localhost:8080/loginwewillreceiveatokenthatwecanuse:

[~]$curl-XGEThttp://localhost:8080/login

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbW

joiR2VuZSBGbGVtaW5nIiwiaWF0IjoxNDQxMjcyMjM0

e1Y

Wecanenterthisintothedebuggerathttp://jwt.io/andseethecontents:

{

Page 127: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

"email":"[email protected]",

"name":"GeneFleming",

"iat":1441272234

}

Awesome,wehaveatokenandarandomuserbeinggeneratedforus.Now,wecanusethistoauthenticateourusers.Socket.IOhasamethodontheservertodothisandwejustneedtopassahandlertypefunctiontoit.Thisiswheresocketio-jwtcomesin,wepassitoursecretanditwillensureitisarealtoken,prettysimple:

varSocketIOJWT=require('socketio-jwt');

io.use(SocketIOJWT.authorize({

secret:jwtSecret,

handshake:true}));

Now,whenwetrytoconnecttoourserverfromtheclientitwillneveremittheconnectevent,asourclientisn’tauthenticated.Thisisexactlywhatwewant.

WefirstwanttowrapupourSocket.IOcode(wewillcallthislater);wealsowanttogiveitaparameteroftoken:

functionsocketIO(token){

varsocket=io.connect('http://localhost:8080');

varoutput=document.getElementById('output');

functionlogScreen(text){

vardate=newDate().toISOString();

line=date+""+text+"<br/>";

output.innerHTML=line+output.innerHTML

}

logScreen('Connecting');

socket.on('connect',function(){

logScreen('Connection!');

socket.send('Hello');

});

socket.on('message',logScreen);

}

Next,wewillcreatealoginfunction,thiswillrequesttheloginURLandthenpasstheresponsetothesocketIOfunction,asshown:

functionlogin(){

{

varrequest=newXMLHttpRequest();

request.onreadystatechange=function(){

if(

request.readyState!==4||

request.status!==200

Page 128: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

)return

socketIO(request.responseText);

}

request.open("GET","/login",true);

request.send(null);

}

Thenwewanttocalltheloginfunction:

login();

Wecanpassthetokenontotheserverbychangingtheconnectcalltopassaquerystring:

varsocket=io.connect('http://localhost:8080',{

query:'token='+token

});

Now,whenrunningourserverandnavigatingtoourclientweshouldbeabletoconnect—awesome!Sincewehaveauthenticatedwecanalsorespondwithapersonalizedmessageforeachuser,insideourserver-sideconnectioneventhandlerwewillemitamessagetotheclient.

Oursocketwillhaveanewpropertycalleddecoded_token;usingthiswewillbeabletoviewthecontentsofourtoken:

varpayload=socket.decoded_token;

varname=payload.name;

socket.emit('message','Hello'+name+'!');

Oncewejoinourroom,wecantelltherestoftheclientswhohavealsojoined:

socket

.to(room)

.emit(

'message',

name+'joinedtheroom!'

);

Page 129: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 130: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummarySocket.IObringsamazingcapabilitiestoourapplications.Wecannowinstantlycommunicatewithothers,eitherindividuallyorbybroadcastinginaroom.Withtheabilitytoidentifyusers,wecanrecordmessagesorthehistoryofthatuser,readytobeservedupbyaRESTfulAPI.

Wearenowreadytobuildreal-timeapplications!

Page 131: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 132: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter8.CreatingandDeployingPackagesNowthatwehaveallofthepiecesthatareneededtocreateNode.jsapplicationsandservers,wewillnowfocusmoreonsharingourmodulesandcontributingtotheeco-system.

Allthepackagesonnpmhavebeenuploaded,maintained,andcontributedbysomeoneinthecommunity,solet’shavealookathowwecandothesameourselves.

Page 133: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

CreatingnpmpackagesWecanstartwiththefollowingsteps:

Firstweneedtocreateauser:

[~]$npmadduser

Username:<username>

Password:

Email:(thisISpublic)<email>

Oncewehaveauserwehaveopenedthegatestonpm.

Now,let’screateapackage:

[~/examples/example-22]$npminit

{

"name":"njs-e-example-package",

"version":"1.0.0",

"description":"",

"main":"index.js",

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1"

},

"author":"",

"license":"ISC"

}

Topublishthispackageallweneedtodoisrunnpmpublish:

[~/examples/example-22]$npmpublish

[email protected]

Youcanseethatwehavepublishedourpackagesuccessfully,youcanviewtheoneIpublishedat:

https://www.npmjs.com/package/njs-e-example-package

Youwillhavetonameyourpackagesomethingelseinordertopublishit;otherwise,wewillhaveaconflict.

Nowwecanrunthefollowingcommand:

[~/examples/example-21]$npminstallnjs-e-example-package

[email protected]_modules/njs-e-example-package

Thenwewillhavethepackage!Isn’tthatprettycool?

Ifwetrytopublishagain,wewillgetanerrorbecauseversion1.0.2isalreadypublished,asshowninthefollowingscreenshot:

Page 134: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Toincrementourpackageversion,allweneedtodoisexecute:

[~/examples/example-22]$npmversionpatch

v1.0.1

Nowwecanpublishagain:

[~/examples/example-22]$npmpublish

[email protected]

Youcangotoyourpackagespageonnpmandyouwillseethattheversionnumberandreleasecounthasbeenupdated.

VersioninginNode.jsfollowsthesemverschema,whichismadeupofmajor,minor,andpatchversions.Whenthepatchversionisincremented,itmeansthattheAPIhasstayedthesamehoweversomethinghasbeenfixedbehindthescenes.Iftheminorversionhasbeenincremented,itmeansthatanon-breakingAPIchangehasoccurred,suchasamethodhasbeenadded.Ifthemajorversionisupdated,itmeansthattherehasbeenabreakingAPIchange;forexampleamethodhasbeendeletedoramethodsignaturehaschanged.

Sometimes,therearethingsinyourprojectthatyoudon’twanttobepushedoutforotherpeopletohave.Thiscouldbeanoriginalsource,somecertificates,ormaybesomekeysfordevelopment.Justlikewhenusinggit,wehaveanignorefilecalled.npmignore.

Bydefault,ifthereisno.npmignorebutthereisa.gitignore,npmwillignorewhatismatchedbythe.gitignorefile.Ifyoudon’tlikethisbehaviorthenyoucanjustcreateanempty.npmignorefile.

The.npmignorefilefollowsthesamerulesas.gitignore,whichareasfollows:

Blanklinesorlinesstartingwith#areignoredStandardglobpatternsworkYoucanendpatternswithaforwardslash/tospecifyadirectoryYoucannegateapatternbystartingitwithanexclamationpoint!

Forexample,ifwehadadirectoryofcertificatesinwhichwehadakey:

[~/examples/example-22]$mkdircertificates

[~/examples/example-22]$touchcertifticates/key.key

Weprobablydon’twantthistobepublished,soinourignorefilewewillhave:

Page 135: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

certificates/

Wealsodon’twantanykeyfilesthatwehavelyingaround,soweaddthisaswell:

*.key

Now,let’spublish:

[~/examples/example-22]$npmversionpatch

v1.0.2

[~/examples/example-22]$npmpublish

[email protected]

Now,let’sinstallourpackage:

[~/examples/example-23][email protected]

Now,whenwelistwhat’sinthedirectory,wedon’tseeallourcertificatesbeingpassedaround:

[~/examples/example-23]$lsnode_modules/njs-e-example-package

package.json

Thisisgreat,butwhatifwewanttoprotectourentirepackageandnotjustafewcertificates?

Allweneedtodoissetprivatetotrueinourpackage.jsonfileanditwillpreventnpmfrompublishingthemodulewhenwerunnpmpublish:

Ourpackage.jsonshouldlooksomethinglike:

{

"name":"example-23",

"version":"1.0.0",

"description":"",

"main":"index.js",

"scripts":{

"test":"echo\"Error:notestspecified\"&&exit1"

},

"author":"",

"license":"UNLICENSED",

"dependencies":{

"njs-e-example-package":"^1.0.2"

},

"private":true

}

Now,whenwerunnpmpublish:

[~/examples/example-23]$npmpublish

npmERR!Thispackagehasbeenmarkedasprivate

Awesome,that’sexactlywhatwewantedtosee.

Page 136: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 137: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryLookslikewearegettingclosertobeingreadywithallthingsaboutNode.js.Weknownowhowtosetup,debug,develop,anddistributeoursoftware.

Inthenextchapter,wewillcoveronemoreconceptweneedtoknowabout:unittesting.

Page 138: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 139: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter9.UnitTestingWehavecomethisfarbuthaven’tdoneanytesting!That’snotverygood,isit?Usually,ifnotalways,testingisamajorconcerninsoftwaredevelopment.Inthischapter,wewillcoverunittestingconceptswithNode.

TherearemanytestingframeworksforNode.jsandinthischapterwewillbecoveringMocha.

Page 140: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

InstallingmochaToensurethatmochagetsinstalledeverywhere,weneedtoinstallitglobally.Thiscanbedoneusingthe-gflagwithnpminstall:

[~/examples/example-24]$npminstall-gmocha

Now,wecanuseMochathroughtheterminalconsole.

Typically,wewillcontainallourtestingcodeinatestsub-directorywithintheproject.Allweneedtodotogetourcoderunningisrunmocha,assumingwehavewrittensometestsfirst.

Aswithmany(ifnotall)unittestingframeworks,Mochausesassertionstoensurethatatestrunscorrectly.Ifanerroristhrownandisnothandledthenthetestisconsideredtohavefailed.Whatassertionlibrariesdoisthrowerrorswhenanunexpectedvalueispassed,sothisworkswell.

Node.jsprovidesasimpleassertionmodule,let’shavealookatthefollowing:

[~/examples/example-24]$node

>assert=require('assert')

>expected=1

>actual=1

>assert.equal(actual,expected)

>actual=1

>assert.equal(actual,expected)

AssertionError:2==1

Aswecansee,anerroristhrowniftheassertiondoesn’tpass.However,theerrormessageprovidedwasn’tveryhandy;tofixthiswecanpassanerrormessageaswell:

>assert.equal(actual,expected,'Expected1')

AssertionError:Expected1

Withthiswecancreateatest.

Mochaprovidesmanywaysofcreatingtests,thesearecalledinterfacesandthedefaultiscalledBDD.

Youcanviewallinterfacesathttp://mochajs.org/#interfaces.

TheBDD(BehaviorDrivenDevelopment)interfacecanbecomparedtoGherkinwherewespecifyafeatureandasetofscenarios.Itprovidesmethodstohelpdefinethesesets,describeorcontextisusedtodefineafeature,andtheitorspecifyfunctionsareusedtodefineascenario.

Forexample,ifweweretohaveafunctionthatjoinssomeone’sfirstandlastname,thetestsmightlooksomethinglikethefollowing:

varGetFullName=require('../lib/get-full-name'),

assert=require('assert');

describe('Fetchfullname',function(){

Page 141: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

it('shouldreturnbothafirstandlastname',function(){

varresult=GetFullName({first:'Node',last:'JS'})

assert.equal(result,'NodeJS');

})

})

Wecanalsoaddafewmoretestsforthis;forexample,ifitthrewanerrorincaseofnoobjectbeingpassed:

it('shouldthrowanerrorwhenanobjectwasnotpassed',function(){

assert.throws(

function(){

GetFullName(null);

},

/Objectexpected/

)

})

Youcanexploremanymoremocha-specificfeaturesathttp://mochajs.org/.

Page 142: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 143: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ChaiAlongwiththemanytestingframeworks,therearealsomanyassertionframeworks,oneofwhichiscalledChai.Completedocumentationcanbefoundathttp://chaijs.com/.

Insteadofjustusingthebuilt-inassertionmoduleprovidedbyNode.js,wemaywanttouseamodulesuchasChaitoextendourpossibilities.

Chaihasthreesetsofinterfaces,should,expect,andassert.Inthischapter,wewillbecoveringexpect.

Whenusingexpect,youareusingnaturallanguagetodescribewhatyouwant;forexample,ifyouwantsomethingtoexist,youcansay,expect(x).to.existratherthanassert(!!x):

varExpect=require('chai').expect

varAssert=require('assert')

varvalue=1

Expect(value).to.exist

assert(!!value)

Usingnaturallanguagemakesthingsalotclearerforpeoplereadingyourtests.

Thislanguagecanbelinkedtogether;wehaveto,be,been,is,that,which,and,has,have,with,at,of,andsame,whichcanhelpustobuildsentenceslike:

Expect(value).to.be.ok.and.to.equal(1)

However,thesewordsareonlyforreliabilityandtheydon’tmodifytheresult.Therearealotofotherwordsthatcanbeusedtoassertthings,suchasnot,exists,ok,andmanymore.Youcanviewthemallathttp://chaijs.com/api/bdd/.

Someexamplesofchaiinuseare:

Expect(true).to.be.ok

Expect(false).to.not.be.ok

Expect(1).to.exists

Expect([]).to.be.empty

Expect('hi').to.equal('hi')

Expect(4).to.be.below(5)

Expect(5).to.be.above(4)

Expect(function(){}).to.be.instanceOf(Function)

Page 144: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 145: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

StubbingmethodsIfitlookslikeaduck,swimslikeaduck,andquackslikeaduck,thenitprobablyisaduck.

Asyouwriteyourtestsyouwanttobeonlybetestingunitsofcode.Generallythiswillbeamethod,provideitsomeinput,andexpectanoutputofsomekind,orifitisavoidfunction,expectnothingtobereturned.

Withthisinmind,youhavetothinkofyourapplicationasbeinginasandboxedstatewhereitcan’ttalktotheoutsideworld.Forexample,itmightnotbeabletotalktoadatabaseormakeanykindofexternalrequest.Havingthisassumptionisgreatifyouaregoingto(andyouusuallyshould)implementcontinuousintegrationanddeployment.ItalsomeansthattherearenoexternalrequirementsforthemachineyouaretestingonexceptforNode.jsandthetestingframework,whichcouldjustbeapartofyourpackageanyway.

Unlessthemethodyouaretestingisrathersimpleanddoesn’thaveanyexternaldependencies,youwillprobablywanttomockthemethodsthatyouknowitisgoingtoexecute.AgreatmoduletodothisiscalledSinon.js;itallowsyoutocreatestubsandspiestomakesurethatthecorrectdatareturnsfromothermethodsandtoensurethattheywerecalledinthefirstplace.

sinonprovidesmanyhelpers,asmentionedbeforeandoneoftheseiscalledaspy.Aspyisusedmainlytojustwrapafunctiontoseewhatitsinputandoutputwas.Onceaspyhasbeenappliedtoafunction,totheoutsideworlditbehavesexactlythesame.

varSinon=require('sinon');

varreturnOriginal=function(value){

returnvalue;

}

varspy=Sinon.spy(returnOriginal);

result=spy(1);

console.log(result);//Logs1

Wecanuseaspytocheckifafunctionwascalled:

assert(spy.called)

Orwhatargumentswerepassedforeachcall:

assert.equal(spy.args[0][0],1)

Ifweprovidedspywithanobjectandamethodtoreplacethenwecanrestoretheoriginaloncewearefinished.Wewillusuallydothisintheteardownofourtest:

varobject={

spyOnMe:function(value){

returnvalue;

Page 146: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

}

}

Sinon.spy(object,'spyOnMe')

varresult=object.spyOnMe(1)

assert(result.called)

assert.equal(result.args[0][0],1)

object.spyOnMe.restore()

Wealsohaveastubfunction,whichinheritsallthefunctionalityofspybutinsteadofcallingtheoriginalfunctionitcompletelyreplacesit.

Thisissowecandefinethebehavior,forexample,whatitreturns:

varstub=Sinon.stub().returns(42)

console.log(stub())//logs42

Wecanalsodefineareturnvalueforasetofargumentspassed:

varstub=Sinon.stub()

stub.withArgs(1,2,3).returns(42)

stub.withArgs(3,4,5).returns(43)

console.log(stub(1,2,3))//logs42

console.log(stub(3,4,5))//logs43

Let’ssaywehadthissetofmethods:

functionUsers(){

}

Users.prototype.getUser=function(id){

returnDatabase.findUser(id);

}

Users.prototype.getNameForUser=function(id){

varuser=this.getUser(id);

returnuser.name;

}

module.exports=Users

Now,weonlycareaboutthescenariowhereauserisreturned,asthegetUserfunctionwillthrowanerrorifitcan’tfindit.Knowingthis,wejustwanttotestthatwhenauserisfounditreturnstheirname.

Thisisaperfectexampleofwhenwewanttostubamethod:

varSinon=require('sinon');

varUsers=require('../lib/users');

varAssert=require('assert');

it('shouldreturnausersname',function(){

varname='NodeJS';

varuser={name:name};

varstub=Sinon.stub().returns(user);

Page 147: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

varusers=newUsers();

users.getUser=stub;

varresult=users.getNameForUser(1);

assert.equal(result,name,'Namenotreturned');

});

Insteadofreplacingthefunctionwecanalsopassthefunctionusingthescope,replacingthiswiththeobjectwepassed;eitherwayissufficient.

varresult=users.getNameForUser.call(

{

getUser:stub

},

1

);

Page 148: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 149: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryEverythingweneedtocreateaNode.jsapplicationisnowatourfingertips.Testingisjustoneofthosethingsthatareessentialtoanysuccessfulsoftware.Wecoveredusingmochaasatestingframeworkandchaiasanassertionframework.

Inthenextchapter,wewillcoverusinganotherlanguagewithNode.js,CoffeeScript!

Page 150: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 151: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Chapter10.UsingMoreThanJavaScriptThroughoutthisbookwehaveusedonlyJavaScript.Well,it’scalledNode.jsisn’tit?

However,thatdoesn’tmeanthatwecan’tuseotherlanguageswithit.WecanandaslongasitcompilestoJavaScriptyouaregoodtogo.

Thereisabiglistofcommonlanguagesthatareavailableat:https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-JS.

Ifyouaremissingyourstronglytypedlanguageorjustwantaslightlydifferentsyntax,thentherewillsurelybeoneoptionoutthereforyousomewhere.

AcoupleofcommonlanguagesincludeCoffeeScriptandTypeScript,theyworkgreatwithNode.jsastheybothcompiletoJavaScript.Inthischapter,wewillcovertheusageofCoffeeScript.TypeScriptissimilarinusage;however,thesyntaxfollowsasimilarpathtoC#andJava.

Page 152: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

CoffeeScriptIt’sverysimpletoinstallandstartusingadditionallanguages.Let’shavealookatCoffeeScript:

WeneedtoinstallCoffeeScriptglobally,sothatwecanuseacommandsimilartonode:

[~]npminstall-gcoffee-script

Nowwecanruncoffee:

[~]coffee

>

ThesyntaxisverysimilartoJavaScript:

[~]coffee

>1+1

2

>console.log('Hello')

Hello

Insteadofusingthe.jsextension,weuse.coffee.

First,wewillcreateaCoffeeScriptfile:

/*index.coffee*/

console.log('HelloCoffeeScript!')

Thentorunit,allweneedtodoisusethecoffeecommand,similartothenodecommand:

[~/examples/example-25]coffeeindex.coffee

HelloCoffeScript!

Tocompileour.coffeefilesinto.js,wecanuse-c.Oncecompiled,wecanrunthemdirectlywithNode.js:

[~/examples/example-25]coffee-cindex.coffee

[~/examples/example-25]nodeindex.js

HelloCoffeeScript!

IfwehaveabunchofCoffeeScriptthatwewanttocompiletoJavaScriptallatonce,wecanusecoffee-c-o./lib./src.Thiswilltakeall.coffeefilesfrom./src,compilethemto.jsandthenoutputthemto./lib.

YouwillneedtocompileallyourfilesforotheruserstouseourCoffeeScriptcodealongsidetheirJavaScriptcode.ThealternativeistoincludeCoffeeScriptasadependencyandrequiretheregisterfileintoyourapplication,asshown:

/*index.js*/

require('coffee-script/register');

require('./other.coffee');

YoumayneedtodothisifdonotyouwishtocompileyourCoffeeScript,orifyouareusingatoolthatrequiresaJavaScriptfilesuchasGulporGrunt.

Page 153: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

TipToseetheequivalentsbetweenJavaScriptandCoffeeScriptyoucanusethesitehttp://js2.coffee/,itprovidesasimplewaytocomparethetwoonthefly.

CoffeeScriptisbasicallyjustJavaScript;however,ithastargetedreadabilityandsimplicity.WithsimplicityitalsotriestolimittheuseofthebadpartsofJavaScriptandexposesthegoodparts.

UsingCoffeeScriptisusuallygreatforbeginners,(andforexperts),asitusesEnglishlanguageratherthancomputerlanguage.Forexample,insteadofusing===(tripleequals)tocheckiftwovaluesequal,wecanjustusetheEnglishwordis.So,x===ybecomesxisy,whichmeansthatthereisnotranslatingrequiredwhenreading.

Alongwithis,thereareotherkeywords,suchasisnt,not,or,and,yesandno.

Usingthesekeywordsinsteadofsymboloperatorsgivesclaritytothereadersandprogrammers.TheCoffeeScripthassimilarformattingtoPythoninthewayfunctionsandcodeblocksaredeclared;theindentationindicateswhentheblockendsandbegins

Page 154: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 155: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

CodeblocksandfunctionsInJavaScriptyouwillusuallygrouptogetherblocksusingcurlybraces,asshowninthefollowingexample:

if(true)

{

console.log('Itwastrue!')

}

WhereasinCoffeeScriptyouwillleaveoutallthecurlybraces,infactallthebracesareleftout:

iftrue

console.log('Itwastrue!')

Thesameistruewhendeclaringafunction,noticethatweareusinganarrowratherthanthekeywordfunction.Theparameterlistisonlyrequiredifyouwantnamedarguments:

func=->

console.log('Iexecuted')

CoffeeScripttriestoassumeasmuchaspossiblewhilestillgivingtheprogrammerenoughcontrol.

YoumayhavealsonoticedthatIdidn’tusethevarkeywordwhendeclaringafunction.Thisisbecauseitisimplicitlydeclared,asyoucanseebycompilingtheabovecodetoJavaScript:

varfunc;

func=function()

{

returnconsole.log('Iexecuted');

};

Youcanseeinthiscompiledcodethatthelaststatementinthefunctionisthereturnvalue,thismeansthatwedon’tneedtodeclarethereturnvalueandjustassumethatthelastvalueisreturned.Thismakesitverysimpletocreateonelinefunctions,suchas:

add=(a,b)->a+b

UnlikeJavaScript,youmayprovidedefaultargumentsforafunctionandthiscanbecomparedtoC#;however,it’snotlimitedtoonlyconstantsasitessentiallyexecutesthestatementwithinthefunction:

keys={}

func=(key,date=newDate)->

keys[key]=date

Youcanseethisbycompilingtheabovefunctionas:

varfunc,keys;

keys={};

func=function(key,date)

{

Page 156: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

if(date==null)

{

date=newDate();

}

returnkeys[key]=date;

};

Essentially,allCoffeeScriptdoesischeckifthevalueisnullorundefined.

Page 157: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 158: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

TheexistentialoperatorYoucanchecktoseeifavalueisnullorundefinedusingtheexistentialoperator,whichcheckstoseeifthevalueexists.Thisisindicatedbyusingthequestionmarksymbolafteravariable;thestatementwillbetrueifthevalueexistsandotherwisefalse.

Tousethisinanexpression:

date=null

ifnotdate?

date=newDate()

console.log(date)

Youcanusethisasashorthandoperatoraswell,forexample:

date?=newDate()

console.log(date)

Theabovetwoexamplesofcodewillbehaveexactlythesameandwillactuallycompiletogivethesamecode:

vardate;

date=null;

if(date==null)

{

date=newDate();

}

Youmayalsousetheexistentialoperatortoensureavalueexistsbeforeaccessingapropertyofit.Forexample,ifyouwanttogetthetimefromadate,or-1ifthedatedoesn’texist:

getTime=(date=null)->date?.getTime()?-1

Givingdatethenullvalueshowsthatwedon’tmindifnovalueispassed:

Whenanobjectdoesn’texistandtheoperatorisusedthenthereturnedvalueisundefined,thismeansthatwecanusethesameoperatoragaintoreturnadefaultvalue.

Page 159: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 160: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ObjectsandarraysAlongwithalltheassumptionsthatCoffeeScripttriestomake,itsurelydoestrytoremovealltheun-neededsyntaxplainJavaScriptrequires.Anotherinstanceofthiscanbeseenwhiledefiningarraysandobjectsinwhichtheuseofanewlinedeclaresanewitem.Forexample,youwillusuallydefineanarrayas:

array=[

1,

2,

3

]

Thisstillworks;however,withCoffeeScriptyoucanleaveoutthecommasseparatingeachitem:

array=[

1

2

3

]

Youcanalsomixthetwostylestogether:

array=[

'a','b','c'

1,2,3

true,false

]

Youcandothesamewithobjects,suchas:

object={

foo:1

bar:2

}

Withobjectsyoucanevenleaveoutthecurlybracesanduseindentationtoshowthedifferencesintheobject:

object=

foo:1

bar:2

foobar:

another:3

key:4

ToloopanarrayinCoffeeScript,allyouneedtodoisusethefor…inloop,suchas:

forvalue,indexinarray

console.log(value,index)

continueiftypeofvalueis'string'

console.log('Valuewasnotastring')

Ifyoudonotwishtousetheindexofyouritem,yousimplydon’taskforit:

Page 161: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

forvalueinarray

console.log(value)

AswithJavaScriptloops,youcanusebreakandcontinuetocontroltheflow.

ToloopanobjectinCoffeeScriptyoucanusethefor…ofloop,thisisabitdifferentfromthefor…ofloopprovidedbyJavaScript:

forkey,valueofobject

console.log(key,value)

Aswiththefor…inloop,ifyoudon’twantthevalue,excludeit:

forkeyofobject

console.log(key)

Forbothtypesofloops,thenamingisirrelevant:

forkey,valueofobject

#Notethatthiswillletdatesandarraysthrough(etc)

continueunlessvalueinstanceofObject

fornestedKey,nestedValueofvalue

console.log(nestedKey,nestedValue)

Page 162: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 163: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

ClassesUnlikeJavaScript,CoffeeScriptprovidesanaturalwaytodeclareclassesandinheritance.

TodefineaclassinJavaScript,youneedtodeclareafunctionfirst:

functionUser(username){

this.username=username;

}

Thenyouwilldeclaretheprototypemethods:

User.prototype.getUsername=function(){

returnthis.username;

}

Ifyouhaveastaticmethod,youcandefinethisonthefunctionratherthantheprototype:

User.createUser=function(username){

returnnewUser(username);

}

InCoffeeScriptyoucanusetheclasskeywordandgivetheclassaname.Youcanthendeclaretheconstructor,static,andinstance(prototype)methods:

classUser

@createUser:(username)->

returnnewUser(username)

constructor:(username)->

this.username=username

getUsername:->

returnthis.username

Usually,youplaceallyourstaticmethodsaboveyourconstructorsothattheystayseparatefromyourinstancemethods.Thisavoidsconfusion,youmayhavenoticedthatIdeclaredthestaticmethodcreateUserwitha@prefix,thisishowyoudefineastaticmethodinCoffeeScript.However,youcanalsousethetraditionalJavaScriptmethodofUser.createUser=->,eitherwaywillworkhere.

Thecodethatisrunwhentheinstanceisbeingcreatedorconstructediscalledtheconstructor.Thisisthesameterminologythatisusedinmanyotherlanguagessoitshouldbefamiliar.Aconstructorisessentiallyafunction.

Alltheinstancemethodsaredeclaredsimilarlytopropertiesofanobject.

Withclassescomesanothersymbol,[email protected],youcanuseittorefertothethiskeyword.Forexample,thegetUsernamemethodcanbewrittenas:

getUsername:->

return@username

Or,ifwewanttodropthereturnstatementandmakeitaoneliner:

Page 164: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

getUsername:->@username

The@symbolcanalsobeusedinparameterliststodeclarethatwewanttheinstancepropertytobesetasthepassedvalue.Forexample,ifwehadasetUsernamemethodwecaneitherdo:

setUsername:(username)->

@username=username

Orwecando:

setUsername:(@username)->

BoththemethodswillcompiletothesameJavaScriptcode.

Giventhefactthatwecanusethe@symbolinourparameterlist,wecanrefactorourconstructorfunctionto:

constructor:(@username)->

AnotheradvantageofusingCoffeeScriptclassisthatwecandefineinheritance.Todoso,allweneedtodoisusetheextendskeyword,thisissimilartootherlanguages.

Intheseexamples,wewanttohavetwoclasses,PersonandRobotthatextendthebaseUserclass.

Forourperson,wewanttobeabletogivethemanameandanagealongwiththeusernamethattheUserclassrequires.

First,weneedtodeclareourclass:

classPersonextendsUser

Thendeclareourconstructor.Inourconstructor,wewillcallthesuperfunction,thiswillexecutetheconstructoroftheparentclassUserandwewanttopasstheusernametoit,asshown:

constructor:(username,@name,@age)->

super(username)

Wethenaddtwomethods,getNameandgetAge:

getName:->@name

getAge:->@age

Next,wewilldothesameforRobot,exceptthistimeweonlywantausernameand@usage:

classRobotextendsUser

constructor:(username,@usage)–>

super(username)

getUsage:->@usage

Wecannowcreateinstancesofourclassesandcomparethem,asshownhere:

Page 165: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 166: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more
Page 167: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SummaryCoffeeScripttriestomakegoodassumptionswithyourcode.ThishelpstoremovesomeproblemsthatJavaScriptdeveloperscomeacross.Forexample,thedifferencebetween==and===.

YoucanlearnmoreaboutthespecificsyntaxofCoffeeScriptathttp://coffeescript.org/.

Inthischapterwehavecoveredutilizinganotherlanguage.ThiscanhelpalleviatethestruggleswithJavaScript’sstyleorsyntaxforbeginners.Forpeoplewhoareusedtomorelanguagefeatures,thisisabigadvantageasithelpsremovethepitfallsthatpeopleusuallycomeacross.

Page 168: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

IndexA

argumentsabout/Arguments

authenticationbasicauthentication/Basicauthentication

Page 169: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

BBDD(BehaviorDrivenDevelopment)/InstallingmochaBearertoken

about/Bearertokensbunyan

reference/Logging

Page 170: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Cchai

about/ChaiURL/Chai

CoffeeScriptabout/CoffeeScriptinstalling/CoffeeScriptreference/CoffeeScriptcodeblocks/Codeblocksandfunctionsfunctions/Codeblocksandfunctionsexistentialoperator,using/Theexistentialoperatorobjects/Objectsandarraysarrays/Objectsandarraysclasses/Classes

configurationJSONfiles/JSONfilesenvironmentalvariables/Environmentalvariablesarguments/Arguments

Page 171: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Eenvironmentalvariables

about/Environmentalvariableserrorhandling

about/Errorhandlingexistentialoperator

about/Theexistentialoperator

Page 172: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

HHellorequireexample

about/HellorequireHTTPmethods

POST/IntroducingroutingGET/IntroducingroutingDELETE/Introducingrouting

Page 173: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

JJSONfiles

about/JSONfilesJSONWebToken(JWT)

about/BearertokensURL/Bearertokens

Page 174: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

LLevelDB

about/LevelDBlogging

about/Logging

Page 175: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Mmethods

stubbing/Stubbingmethodsmocha

installing/Installingmochareference,forinterfaces/InstallingmochaURL/Installingmocha

MongoDBabout/MongoDB

MongoLababout/MongoDB

morganabout/Loggingreference/Logging

Page 176: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

NNode.js

settingup/SettingupURL/Settingup

npmabout/HellonpmURL/Hellonpmscriptsobject/Hellonpm

npmpackagescreating/Creatingnpmpackages

Page 177: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

OOAuth

about/OAuthURL/OAuth

Page 178: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Rrouting

about/Introducingrouting

Page 179: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

SSinon.js/StubbingmethodsSocket.IO

rooms/Roomsauthentication/Authentication

spy/Stubbingmethodsstubfunction/Stubbingmethods

Page 180: Node.js Essentials - luzala.orgluzala.org/Node.js Essentials.pdf · Node.js Essentials Credits About the Author About the Reviewer Support files, eBooks, discount offers, and more

Ttry/catchfunction/Errorhandling


Recommended