Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk...

180

Transcript of Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk...

Page 1: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 2: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 3: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Node.jsEssentials

Page 4: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Theexistentialoperator

Objectsandarrays

Classes

Summary

Index

Page 7: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 8: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Node.jsEssentials

Page 9: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 10: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 12: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 14: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 15: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 16: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

AbouttheReviewerGlennGeenenisaNode.jsdeveloperwithabackgroundingameandmobiledevelopment.HehasmostlyworkedasaniOSconsultantbeforebecomingaNode.jsconsultantforhiscompany,GeenenTijd.

Page 17: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 18: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

www.PacktPub.com

Page 19: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 21: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.

Page 22: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 23: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 26: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 28: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 29: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 30: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 32: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.

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

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.

Page 33: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 34: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 35: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 36: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.

Page 38: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

QuestionsYoucancontactusat<[email protected]>ifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.

Page 39: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 40: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

WhiletestingyourwebsiteonmultiplebrowsersyoumusthavecomeacrossGoogleChromeatsomepointandyoumighthavenoticedthatitisagreatplatformforJavaScriptapplications.

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

Page 41: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 43: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

>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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

>util.log('Thisisprettycoolaswell')

01Jan00:00:00-Thisisprettycoolaswell

Page 48: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 49: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 53: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

WehavecoveredhoweasyitistoexposeandprotectpublicandprivatecodecomparedtoregularJavaScriptcodeinthebrowser,wheretheglobalscopecangetverypolluted.

Wealsoknowhowtoincludepackagesandcodefromexternalsourcesandhowtoensurethatthepackagesincludedareconsistent.

Asyoucanseethereisahugeecosystemofpackagesinoneofthemanypackagemanagers,suchasnpm,justwaitingforustouseandconsume.

Inthenextchapter,wewillfocusoncreatingasimpleservertoroute,authenticate,andconsumerequests.

Page 54: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 55: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

'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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 65: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryNowwehaveeverythingweneedtomakesomeprettycoolservices.WecannowcreateanHTTPfromscratch,routeourrequests,andcreateaRESTfulinterface.

ThiswillhelpyouwiththecreationofcompleteNode.JSservices.Inthenextchapter,wewillcoverauthentication.

Page 66: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 67: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Chapter3.AuthenticationWecannowcreateRESTfulAPIs,butwedon’twanteveryonetoaccesseverythingweexpose.Wewanttheroutestobesecureandtobeabletotrackwhoisdoingwhat.

Passportisagreatmoduleandanothermiddlewarethathelpsusauthenticaterequests.

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

Page 68: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

)

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

[~/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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 72: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

//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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

-H'Authorization:Bearer<token>'

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

Page 76: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 77: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 80: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryWecannowauthenticateusers;thisisgreataswecannowfigureoutwhothepeopleareandthenlimittheuserstocertainresources.

Inthenextchapterwewillcoverdebugging,wemayneedtouseitifourusersaren’tbeingauthenticated.

Page 81: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 82: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 83: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

[~/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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

[~/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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

[~/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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

(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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

//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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 93: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 96: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryInthischapterwehavecoveredafewpost-mortemdebuggingmethodstohelpusuncoverbugsincludinglogging,namingpractices,andsufficienterrorhandling.

Inthenextchapter,wewillcoverconfigurationofourapplications.

Page 97: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 98: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 99: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Config.server.port

Anexamplewherethiswillbeusefulcouldbeforaserverthatconnectstoadatabase,astheycanacceptboththeportandhostastheparameters:

{

"server":{

"host":"localhost",

"port":8000

},

"database":{

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

"port":27017

}

}

Page 101: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 102: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 104: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 106: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryThat’sallforconfiguration.Inthischapteryoulearnedaboutthethreemethodsthatyoucanusetocreateadynamicapplication.Welearnedthatweshouldnameourconfigurationkeysinawaythatwecanidentifywhatthevaluesarechangingtoandhowtheywillaffectourapplication.Wealsolearnedabouthowwecanpasssimpleargumentstoourapplicationusingenvironmentalvariablesandargv.

Withthisinformation,wecanmoveforwardtoconnectingandutilizingdatabasesinthenextchapter.

Page 107: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 108: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Page 109: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

});

});

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

.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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 113: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 117: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryNowwehaveplaceswherewecanstoreourdata.Ononeendwehaveasimplekey/valuestorethatprovidesuswithasuper-convenientwaytostoredataandontheotherendwehaveafeaturefulldatabasethatprovidesuswithafullsetofqueryoperators.

Boththesedatabaseswillhelpusinthenextchaptersaswemoveclosertocreatingourfullstackapplication.

InthenextchapterwewillcoverSocket.IO,areal-timecommunicationframeworkbuiltontopofWebSockets.

Page 118: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 119: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

aswellasreconnectingifneeded.Thistakesallthepainoutofusingsockets,aswehavenoneofthesetroubles.

Page 123: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

.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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 126: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

"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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

)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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 130: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Wearenowreadytobuildreal-timeapplications!

Page 131: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 132: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Chapter8.CreatingandDeployingPackagesNowthatwehaveallofthepiecesthatareneededtocreateNode.jsapplicationsandservers,wewillnowfocusmoreonsharingourmodulesandcontributingtotheeco-system.

Allthepackagesonnpmhavebeenuploaded,maintained,andcontributedbysomeoneinthecommunity,solet’shavealookathowwecandothesameourselves.

Page 133: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 137: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

Inthenextchapter,wewillcoveronemoreconceptweneedtoknowabout:unittesting.

Page 138: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 139: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

TherearemanytestingframeworksforNode.jsandinthischapterwewillbecoveringMocha.

Page 140: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 143: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 145: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

}

}

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 149: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SummaryEverythingweneedtocreateaNode.jsapplicationisnowatourfingertips.Testingisjustoneofthosethingsthatareessentialtoanysuccessfulsoftware.Wecoveredusingmochaasatestingframeworkandchaiasanassertionframework.

Inthenextchapter,wewillcoverusinganotherlanguagewithNode.js,CoffeeScript!

Page 150: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 151: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 155: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

if(date==null)

{

date=newDate();

}

returnkeys[key]=date;

};

Essentially,allCoffeeScriptdoesischeckifthevalueisnullorundefined.

Page 157: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 158: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 160: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 163: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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 - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 166: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer
Page 167: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

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

YoucanlearnmoreaboutthespecificsyntaxofCoffeeScriptathttp://coffeescript.org/.

Inthischapterwehavecoveredutilizinganotherlanguage.ThiscanhelpalleviatethestruggleswithJavaScript’sstyleorsyntaxforbeginners.Forpeoplewhoareusedtomorelanguagefeatures,thisisabigadvantageasithelpsremovethepitfallsthatpeopleusuallycomeacross.

Page 168: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

IndexA

argumentsabout/Arguments

authenticationbasicauthentication/Basicauthentication

Page 169: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

BBDD(BehaviorDrivenDevelopment)/InstallingmochaBearertoken

about/Bearertokensbunyan

reference/Logging

Page 170: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Cchai

about/ChaiURL/Chai

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

configurationJSONfiles/JSONfilesenvironmentalvariables/Environmentalvariablesarguments/Arguments

Page 171: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Eenvironmentalvariables

about/Environmentalvariableserrorhandling

about/Errorhandlingexistentialoperator

about/Theexistentialoperator

Page 172: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

HHellorequireexample

about/HellorequireHTTPmethods

POST/IntroducingroutingGET/IntroducingroutingDELETE/Introducingrouting

Page 173: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

JJSONfiles

about/JSONfilesJSONWebToken(JWT)

about/BearertokensURL/Bearertokens

Page 174: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

LLevelDB

about/LevelDBlogging

about/Logging

Page 175: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Mmethods

stubbing/Stubbingmethodsmocha

installing/Installingmochareference,forinterfaces/InstallingmochaURL/Installingmocha

MongoDBabout/MongoDB

MongoLababout/MongoDB

morganabout/Loggingreference/Logging

Page 176: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

NNode.js

settingup/SettingupURL/Settingup

npmabout/HellonpmURL/Hellonpmscriptsobject/Hellonpm

npmpackagescreating/Creatingnpmpackages

Page 177: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

OOAuth

about/OAuthURL/OAuth

Page 178: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Rrouting

about/Introducingrouting

Page 179: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

SSinon.js/StubbingmethodsSocket.IO

rooms/Roomsauthentication/Authentication

spy/Stubbingmethodsstubfunction/Stubbingmethods

Page 180: Node.js Essentials - the-eye.euthe-eye.eu › public › Site-Dumps › index-of › index-of.co.uk › Cloud Tec… · Node.js Essentials Credits About the Author About the Reviewer

Ttry/catchfunction/Errorhandling