Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication...

180

Transcript of Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication...

Page 1: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 2: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 3: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Node.jsEssentials

Page 4: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Theexistentialoperator

Objectsandarrays

Classes

Summary

Index

Page 7: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 8: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Node.jsEssentials

Page 9: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 10: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 12: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 14: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 15: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 16: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

AbouttheReviewerGlennGeenenisaNode.jsdeveloperwithabackgroundingameandmobiledevelopment.HehasmostlyworkedasaniOSconsultantbeforebecomingaNode.jsconsultantforhiscompany,GeenenTijd.

Page 17: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 18: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

www.PacktPub.com

Page 19: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 21: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.

Page 22: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 23: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 26: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 28: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 29: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 30: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 32: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforustodeveloptitlesthatyoureallygetthemostoutof.

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

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideonwww.packtpub.com/authors.

Page 33: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 34: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 35: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 36: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluablecontent.

Page 38: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

QuestionsYoucancontactusat<[email protected]>ifyouarehavingaproblemwithanyaspectofthebook,andwewilldoourbesttoaddressit.

Page 39: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 40: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

WhiletestingyourwebsiteonmultiplebrowsersyoumusthavecomeacrossGoogleChromeatsomepointandyoumighthavenoticedthatitisagreatplatformforJavaScriptapplications.

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

Page 41: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 43: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

>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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

>util.log('Thisisprettycoolaswell')

01Jan00:00:00-Thisisprettycoolaswell

Page 48: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 49: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 53: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

WehavecoveredhoweasyitistoexposeandprotectpublicandprivatecodecomparedtoregularJavaScriptcodeinthebrowser,wheretheglobalscopecangetverypolluted.

Wealsoknowhowtoincludepackagesandcodefromexternalsourcesandhowtoensurethatthepackagesincludedareconsistent.

Asyoucanseethereisahugeecosystemofpackagesinoneofthemanypackagemanagers,suchasnpm,justwaitingforustouseandconsume.

Inthenextchapter,wewillfocusoncreatingasimpleservertoroute,authenticate,andconsumerequests.

Page 54: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 55: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

'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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 65: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryNowwehaveeverythingweneedtomakesomeprettycoolservices.WecannowcreateanHTTPfromscratch,routeourrequests,andcreateaRESTfulinterface.

ThiswillhelpyouwiththecreationofcompleteNode.JSservices.Inthenextchapter,wewillcoverauthentication.

Page 66: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 67: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Chapter3.AuthenticationWecannowcreateRESTfulAPIs,butwedon’twanteveryonetoaccesseverythingweexpose.Wewanttheroutestobesecureandtobeabletotrackwhoisdoingwhat.

Passportisagreatmoduleandanothermiddlewarethathelpsusauthenticaterequests.

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

Page 68: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

)

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

[~/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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 72: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

//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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

-H'Authorization:Bearer<token>'

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

Page 76: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 77: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 80: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryWecannowauthenticateusers;thisisgreataswecannowfigureoutwhothepeopleareandthenlimittheuserstocertainresources.

Inthenextchapterwewillcoverdebugging,wemayneedtouseitifourusersaren’tbeingauthenticated.

Page 81: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 82: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 83: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

[~/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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

[~/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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

[~/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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

(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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

//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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 93: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 96: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryInthischapterwehavecoveredafewpost-mortemdebuggingmethodstohelpusuncoverbugsincludinglogging,namingpractices,andsufficienterrorhandling.

Inthenextchapter,wewillcoverconfigurationofourapplications.

Page 97: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 98: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 99: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Config.server.port

Anexamplewherethiswillbeusefulcouldbeforaserverthatconnectstoadatabase,astheycanacceptboththeportandhostastheparameters:

{

"server":{

"host":"localhost",

"port":8000

},

"database":{

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

"port":27017

}

}

Page 101: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 102: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 104: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 106: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryThat’sallforconfiguration.Inthischapteryoulearnedaboutthethreemethodsthatyoucanusetocreateadynamicapplication.Welearnedthatweshouldnameourconfigurationkeysinawaythatwecanidentifywhatthevaluesarechangingtoandhowtheywillaffectourapplication.Wealsolearnedabouthowwecanpasssimpleargumentstoourapplicationusingenvironmentalvariablesandargv.

Withthisinformation,wecanmoveforwardtoconnectingandutilizingdatabasesinthenextchapter.

Page 107: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 108: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Page 109: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

});

});

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

.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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 113: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 117: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryNowwehaveplaceswherewecanstoreourdata.Ononeendwehaveasimplekey/valuestorethatprovidesuswithasuper-convenientwaytostoredataandontheotherendwehaveafeaturefulldatabasethatprovidesuswithafullsetofqueryoperators.

Boththesedatabaseswillhelpusinthenextchaptersaswemoveclosertocreatingourfullstackapplication.

InthenextchapterwewillcoverSocket.IO,areal-timecommunicationframeworkbuiltontopofWebSockets.

Page 118: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 119: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

aswellasreconnectingifneeded.Thistakesallthepainoutofusingsockets,aswehavenoneofthesetroubles.

Page 123: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

.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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 126: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

"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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

)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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 130: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Wearenowreadytobuildreal-timeapplications!

Page 131: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 132: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Chapter8.CreatingandDeployingPackagesNowthatwehaveallofthepiecesthatareneededtocreateNode.jsapplicationsandservers,wewillnowfocusmoreonsharingourmodulesandcontributingtotheeco-system.

Allthepackagesonnpmhavebeenuploaded,maintained,andcontributedbysomeoneinthecommunity,solet’shavealookathowwecandothesameourselves.

Page 133: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 137: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

Inthenextchapter,wewillcoveronemoreconceptweneedtoknowabout:unittesting.

Page 138: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 139: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

TherearemanytestingframeworksforNode.jsandinthischapterwewillbecoveringMocha.

Page 140: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 143: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 145: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

}

}

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 149: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SummaryEverythingweneedtocreateaNode.jsapplicationisnowatourfingertips.Testingisjustoneofthosethingsthatareessentialtoanysuccessfulsoftware.Wecoveredusingmochaasatestingframeworkandchaiasanassertionframework.

Inthenextchapter,wewillcoverusinganotherlanguagewithNode.js,CoffeeScript!

Page 150: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 151: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 155: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

if(date==null)

{

date=newDate();

}

returnkeys[key]=date;

};

Essentially,allCoffeeScriptdoesischeckifthevalueisnullorundefined.

Page 157: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 158: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 160: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 163: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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 Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 166: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies
Page 167: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

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

YoucanlearnmoreaboutthespecificsyntaxofCoffeeScriptathttp://coffeescript.org/.

Inthischapterwehavecoveredutilizinganotherlanguage.ThiscanhelpalleviatethestruggleswithJavaScript’sstyleorsyntaxforbeginners.Forpeoplewhoareusedtomorelanguagefeatures,thisisabigadvantageasithelpsremovethepitfallsthatpeopleusuallycomeacross.

Page 168: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

IndexA

argumentsabout/Arguments

authenticationbasicauthentication/Basicauthentication

Page 169: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

BBDD(BehaviorDrivenDevelopment)/InstallingmochaBearertoken

about/Bearertokensbunyan

reference/Logging

Page 170: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Cchai

about/ChaiURL/Chai

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

configurationJSONfiles/JSONfilesenvironmentalvariables/Environmentalvariablesarguments/Arguments

Page 171: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Eenvironmentalvariables

about/Environmentalvariableserrorhandling

about/Errorhandlingexistentialoperator

about/Theexistentialoperator

Page 172: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

HHellorequireexample

about/HellorequireHTTPmethods

POST/IntroducingroutingGET/IntroducingroutingDELETE/Introducingrouting

Page 173: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

JJSONfiles

about/JSONfilesJSONWebToken(JWT)

about/BearertokensURL/Bearertokens

Page 174: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

LLevelDB

about/LevelDBlogging

about/Logging

Page 175: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Mmethods

stubbing/Stubbingmethodsmocha

installing/Installingmochareference,forinterfaces/InstallingmochaURL/Installingmocha

MongoDBabout/MongoDB

MongoLababout/MongoDB

morganabout/Loggingreference/Logging

Page 176: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

NNode.js

settingup/SettingupURL/Settingup

npmabout/HellonpmURL/Hellonpmscriptsobject/Hellonpm

npmpackagescreating/Creatingnpmpackages

Page 177: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

OOAuth

about/OAuthURL/OAuth

Page 178: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Rrouting

about/Introducingrouting

Page 179: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

SSinon.js/StubbingmethodsSocket.IO

rooms/Roomsauthentication/Authentication

spy/Stubbingmethodsstubfunction/Stubbingmethods

Page 180: Node.js Essentials Essentials.pdf · Chapter 7, Socket.IO, explores the real-time communication between clients, servers, and back again and also how it authenticates and notifies

Ttry/catchfunction/Errorhandling