table of contents - alumnitutorial.com · most of node.js beginners in my country indonesia is...

105

Upload: duongthu

Post on 18-Mar-2019

219 views

Category:

Documents


0 download

TRANSCRIPT

1.1

1.2

1.2.1

1.2.2

1.3

1.3.1

1.3.2

1.4

1.4.1

1.5

1.6

1.6.1

1.6.2

1.7

1.7.1

1.7.2

1.8

1.8.1

1.8.1.1

1.8.1.2

1.8.2

1.9

1.9.1

1.9.1.1

1.9.1.2

1.9.1.2.1

1.9.2

1.9.2.1

1.9.3

1.9.3.1

TableofContentsIntroduction

Node.js

JavaScriptDiServer

Node.jsInAction

AsinkronI/O&Event

PHP&ServerHTTPApache

Javascript&Node.js

ServerHTTPDasar

MenjalankanServer

ServerFileStatis

PemrosesanDataFormHTML

URLEncode

MultipartData

Modulenpm

Konsep

Paketnpm

ExpressJS

ServerFile

Middleware

AksesServer

ServerREST

Database

SQLite

NodeSqlite3

Enkripsi

sqlcipher

MySQL

NodeMySQL

MongoDB

NodeMongoDB

2

1.9.3.2

1.10

1.10.1

1.10.2

1.11

1.11.1

1.11.2

1.11.3

1.12

1.12.1

1.12.2

1.12.3

1.13

1.14

1.15

Mongoose

Testing

REST

Automasi

ToDataURI

Penggunaan

todatauri.js

KoneksiMySQL

PersonRESTAPI

CaraKerja

Server

Pengetesan

ImageUploader

MemakaiES6

TentangPengarang

3

AplikasiWebNode.jsNote:ThisbookiswritteninBahasaIndonesiaandthemainreasonforthatisbecausemostofNode.jsbeginnersinmycountryIndonesiaishavingdifficultiestofindNode.jsresourceswritteninmynativelanguage.

BukuinicocokbagisiapasajayanginginmulaibelajarpemrogramandiplatformNode.jskhususnyauntukmembangunaplikasiweb.SyaratyangdibutuhkanadalahpembacasetidaknyapernahatausudahbisamemakaibahasapemrogramanJavaScript.

Ebookinibisaandaaksesdiduatempatyaitu:

GithubIDJS(versionline).Gitbook(versionline,PDF,Epub&Mobi).

FeedbackUntukpertanyaan,kesalahanketikataupermintaansilahkanmengisiGithubissue.

LisensiAplikasiWebNode.jsolehEquanPr.dikerjakandibawahlisensiCreativeCommonsAttribution-NonCommercial4.0InternationalLicense.

Introduction

4

Node.jsJavascriptmerupakanbahasapemrogramanyanglengkaphanyasajaselamainidipakaisebagaibahasauntukpengembanganaplikasiwebyangberjalanpadasisiclientataubrowsersaja.TetapisejakditemukannyaNode.jsolehRyanDhalpadatahun2009,JavascriptbisadigunakansebagaibahasapemrogramandisisiserversekelasdenganPHP,ASP,C#,RubydlldengankatalainNode.jsmenyediakanplatformuntukmembuataplikasiJavascriptdapatdijalankandisisiserver.

UntukmengeksekusiJavascriptsebagaibahasaserverdiperlukanengineyangcepatdanmempunyaiperformansiyangbagus.EngineJavascriptdariGooglebernamaV8yangdipakaiolehNode.jsyangmerupakanengineyangsamayangdipakaidibrowserGoogleChrome.

Node.js

5

JavaScriptDiServerTakterelakkanbahwaJavascriptmerupakanbahasapemrogramanyangpalingpopuler.JikaandasebagaideveloperpernahmengembangkanaplikasiwebmakapenggunaanJavascriptpastitidakterhindarkan.

SekarangdenganberjalannyaJavascriptdiserverlaluapakeuntunganyangandaperolehdenganmempelajariNode.js,kuranglebihsepertiini:

Pengembanghanyamemakaisatubahasauntukmengembangkanaplikasilengkapclient&serversehinggamengurangiLearningCurveuntukmempelajaribahasaserveryanglain.

Sharingkodeantaraclientdanserveratauistilahnyacodereuse.

JavascriptsecaranativemendukungJSONyangmerupakanstandartransferdatayangbanyakdipakaisaatinisehinggauntukmengkonsumsidata-datadaripihakketigapemrosesandiNode.jsakansangatmudahsekali.

DatabaseNoSQLsepertiMongoDBdanCouchDBmendukunglangsungJavascriptsehinggainterfacingdengandatabaseiniakanjauhlebihmudah.

Node.jsmemakaiV8yangselalumengikutiperkembanganstandarECMAScript,jaditidakperluadakekhawatiranbahwabrowsertidakakanmendukungfitur-fiturdiNode.js.

JavaScriptDiServer

6

Node.jsInActionSupayaandalebihtertarikdalambelajarNode.jsberikutbeberapawebsiteterkenalyangsudahmemakaiNode.js

www.myspace.com

www.yummly.com

www.shutterstock.com

Node.jsInAction

7

www.klout.com

www.geekli.st

Node.jsInAction

8

www.learnboost.com

ApakahmasihraguuntukmemakaiNode.js?...KalaumasihpenasaranapayangmembuatNode.jsberbedadaribackendpadaumumnya,silahkandilanjutkanmembaca:smile:

Node.jsInAction

9

AsinkronI/O&EventTidaksepertikebanyakanbahasabackendlainnyaoperasifungsidijavascriptlebihbersifatasinkrondanbanyakmenggunakaneventdemikianjugadenganNode.js.SebelumpenjelasanlebihlanjutmarikitalihatterlebihdahulutentangmetodesinkronsepertiyangdipakaipadaPHPdenganwebserverApache.

AsinkronI/O&Event

10

PHP&ServerHTTPApacheMarikitalihatcontohberikutyaituoperasifungsiakseskedatabaseMySQLolehPHPyangdilakukansecarasinkron

$hasil=mysql_query("SELECT*FROMTabelAnggota");

print_r($hasil);

pengambilandataolehmysql_query()diatasakandijalankandanoperasiberikutnyaprint_r()akandiblokatautidakakanberjalansebelumakseskedatabaseselesai.YangperlumenjadiperhatiandisiniyaituprosesInputOutputatauI/Oakseskedatabaseolehmysql_query()dapatmemakanwaktuyangrelatifmungkinbeberapadetikataumenittergantungdariwaktulatensidariI/O.Waktulatensiinitergantungdaribanyakhalseperti

QuerydatabaselambatakibatbanyakpenggunayangmengaksesKualitasjaringanuntukakseskedatabasejelekProsesbacatuliskediskkomputerdatabaseyangmembutuhkanwaktu...

SebelumprosesI/Oselesaimakaselamabeberapadetikataumenittersebutstatedariprosesmysql_query()bisadibilangidleatautidakmelakukanapa-apa.

LalujikaprosesI/Odiblokbagaimanajikaadarequestlagidariuser?apayangakandilakukanolehserveruntukmenanganirequestini?..penyelesaiannyayaitudenganmemakaipendekatanprosesmultithread.Melaluipendekataninitiapkoneksiyangterjadiakanditanganiolehthread.Threaddisinibisadikatakansebagaitaskyangdijalankanolehprosesorkomputer.

SepertinyapermasalahanI/Oyangterblokterselesaikandenganpendekatanmetodeinitetapidenganbertambahnyakoneksiyangterjadimakathreadakansemakinbanyaksehinggaprosesorakansemakinterbebani,belumlagiuntukswitchingantarthreadmenyebabkankonsumsimemory(RAM)komputeryangcukupbesar.

BerikutcontohbenchmarkantarawebserverApachedanNginx(serverHTTPsepertihalnyaApachehanyasajaNginxmemakaisistemasinkronI/OdaneventyangmiripNode.js).Gambarinidiambildarigoo.gl/pvLL4

PHP&ServerHTTPApache

11

BisadilihatbahwaNginxbisamenanganirequestyangjauhlebihbanyakdaripadawebserverApachepadajumlahkoneksibersamayangsemakinnaik.

PHP&ServerHTTPApache

12

Javascript&Node.jsKembalikeJavascript!.Untukmengetahuiapayangdimaksuddenganpemrogramanasinkronbisalebihmudahdenganmemakaipendekatancontohkode.PerhatikankodeJavascriptpadaNode.jsberikut

varfs=require('fs');

fs.readFile('./resource.json',function(err,data){

if(err)throwerr;

console.log(JSON.parse(data));

});

console.log('Selanjutnya...');

fungsireadFile()akanmembacamembacaisidarifileresource.jsonsecaraasinkronyangartinyaproseseksekusiprogramtidakakanmenunggupembacaanfileresource.jsonsampaiselesaitetapiprogramakantetapmenjalankankodeJavascriptselanjutnyayaituconsole.log('Selanjutnya...').Sekarnglihatapayangterjadijikakodejavascriptdiatasdijalankan

Jikaprosespembacaanfileresource.jsonselesaimakafungsicallbackpadareadFile()akandijalankandanhasilnyaakanditampilkanpadaconsole.Yah,fungsicallbackmerupakankonsepyangpentingdalamprosesI/OyangasinkronkarenamelaluifungsicallbackinidatadatayangdikembalikanolehprosesI/Oakandiproses.

LalubagaimanaplatformNode.jsmengetahuikalausuatuprosesitutelahselesaiatautidak?...jawabannyaadalahEventLoop.Event-eventyangterjadikarenaprosesasinkronsepertipadafungsifs.readFile()akanditanganiolehyangnamanyaEventLoopini.

Campuranteknologiantaraeventdrivendanprosesasinkroninimemungkinkanpembuatanaplikasidenganpenggunaandatasecaramasifdanreal-time.SifatkomunikasiNode.jsI/Oyangringandanbisamenanganiusersecarabersamaandalamjumlahrelatifbesartetapitetapmenjagastatedarikoneksisupayatetapterbukadandenganpenggunaanmemoriyangcukupkecilmemungkinkanpengembanganaplikasidenganpenggunaandatayangbesardankolaboratif...Yeah,Node.jsFTW!:metal:

Javascript&Node.js

13

Javascript&Node.js

14

ServerHTTPDasarPenggunaanNode.jsyangrevolusioneryaitusebagaiserver.Yup...mungkinkitaterbiasamemakaiserversepertiApache-PHP,Nginx-PHP,Java-Tomcat-ApacheatauIIS-ASP.NETsebagaipemrosesdatadisisiserver,tetapisekarangsemuaitubisatergantikandenganmemakaiJavaScript-Node.js!.LihatcontohdasardariserverNode.jsberikut(kodesumberpadadirektoricodepadarepositoriini)

server-http.js

varhttp=require('http'),

PORT=3400;

varserver=http.createServer(function(req,res){

varbody="<pre>HaruskahbelajarNode.js?</pre><p><h3>...YoMesto!</h3></p>"

res.writeHead(200,{

'Content-Length':body.length,

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

'Pesan-Header':'PengenalanNode.js'

});

res.write(body);

res.end();

});

server.listen(PORT);

console.log("Port"+PORT+":Node.jsServer...");

PakethttpmerupakanpaketbawaandariplatformNode.jsyangmendukungpenggunaanfitur-fiturprotokolHTTP.ObjectservermerupakanobjectyangdikembalikandarifungsicreateServer().

varserver=http.createServer([requestListener])

TiaprequestyangterjadiakanditanganiolehfungsicallbackrequestListener.Carakerjacallbackinihampirsamadenganketikakitamenekantombolbuttonhtmlyangmempunyaiatributeventonclick,jikaditekanmakafungsiyangteregistrasioleheventonclickyaituclickHandler(event)akandijalankan.

onclick-button.html

ServerHTTPDasar

15

<script>

functionclickHandler(event){

console.log(event.target.innerHTML+"Terus!");

}

</script>

<buttononclick="clickHandler(event)">TEKAN</button>

SamahalnyadengancallbackrequestListenerpadaobjectserverinijikaadarequestmakarequestListenerakandijalankan

function(req,res){

varbody="<pre>HaruskahbelajarNode.js?</pre><p><h3>...YoMesto!</h3></p>"

res.writeHead(200,{

'Content-Length':body.length,

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

'Pesan-Header':'PengenalanNode.js'

});

res.write(body);

res.end();

}

PakethttpNode.jsmemberikankeleluasanbagideveloperuntukmembangunservertingkatrendah.Bahkanmudahsajakalauharusmen-settingnilaifieldheaderdariHTTP.

SepertipadacontohdiatasagarrespondarirequestdiperlakukansebagaiHTMLolehbrowsermakanilaifieldContent-Typeharusberupatext/html.SettinginibisadilakukanmelaluimetodewriteHead(),res.setHeader(field,value)danbeberapametodelainnya.

Untukmembacaheaderbisadipakaifungsisepertires.getHeader(field,value)danuntukmenghapusfieldheadertertentudenganmemakaifungsires.removeHeader(field).Perludiingatbahwasettingheaderdilakukansebelumfungsires.write()ataures.end()dijalankankarenajikares.write()dijalankantetapikemudianadaperubahanfieldheadermakaperubahaniniakandiabaikan.

SatuhallagiyaitutentangkodestatusdariresponHTTP.Kodestatusinibisadisettingselain200(requesthttpsukses),misalnyabiladiperlukanhalamanerrordengankodestatus404.

ServerHTTPDasar

16

MenjalankanServerUntukmenjalankanserverNode.jsketikperintahberikutditerminal

$nodeserver-http.js

Port3400:Node.jsServer...

Bukabrowser(chrome)danbukaurlhttp://localhost:3400kemudianketikCTRL+SHIFT+IuntukmembukaChromeDevTool,dengantoolinibisadilihatresponheaderdariHTTPdimanabeberapafieldnyatelahdisetsebelumnya(lingkaranmerahpadascreenshotdibawahini).

MenjalankanServer

17

ServerFileStatisAplikasiwebmemerlukanfile-filestatissepertiCSS,fontdangambarataufile-filelibraryJavaScriptagaraplikasiwebbekerjasebagaimanamestinya.File-fileinisengajadipisahkanagarterstrukturdansecarabestpracticesfile-fileinimemangharusdipisahkan.LalubagaimanacaranyaNode.jsbisamenyediakanfile-fileini?...okmarikitabuatserverNode.jsyangfungsinyauntukmenyediakanfilestatis.

AgarserverNode.jsbisamengirimkanfilestatiskeklienmakaserverperlumengetahuipathatautempatdimanafiletersebutberada.

Node.jsbisamengirimkanfiletersebutsecarastreamingmelaluifungsifs.createReadStream().SebelumdijelaskanlebihlanjutmungkinbisadilihatataudicobasajaserverfileNode.jsdibawahini

server-file.js

ServerFileStatis

18

varhttp=require('http'),

parse=require('url').parse,

join=require('path').join,

fs=require('fs'),

root=join(__dirname,'www'),

PORT=3300,

server=http.createServer(function(req,res){

varurl=parse(req.url),

path=join(root,url.pathname),

stream=fs.createReadStream(path);

stream.on('data',function(bagian){

res.write(bagian);

});

stream.on('end',function(){

res.end();

});

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

res.setHeader('Content-Type','text/html');

varurl_demo="http://localhost:"+PORT+"/index.html";

res.write("cobabuka<ahref="+url_demo+">"+url_demo+"</a>");

res.end();

})

});

server.listen(PORT);

console.log('Port'+PORT+':ServerFile');

Berikutsedikitpenjelasandarikodediatas

__dirnamemerupakanvariabelglobalyangdisediakanolehNode.jsyangberisipathdirektoridarifileyangsedangaktifmengeksekusi__dirname.rootmerupakandirektorirootataureferensitempatdimanafile-fileyangakandikirimkanolehserverNode.js.Padakodeserverdiatasdirektorirootdisettingpadadirektoriwww.pathadalahpathfileyangbisadidapatkandenganmenggabungkanpathdirektorirootdanpathname.pathnameyangdimaksuddisinimisalnyajikaURLyangdimintayaituhttp://localhost:3300/index.htmlmakapathnameadalah/index.html.Nilaivariabelpathdihasilkandenganmemakaifungsijoin().

varpath=join(root,url.pathname)

ServerFileStatis

19

streamyangdikembalikanolehfungsifs.createReadStream()merupakanclassstream.Readable.Objekstreaminimengeluarkandatasecarastreaminguntukdiolahlebihlanjut.Perlumenjadicatatanbahwastream.Readabletidakakanmengeluarkandatajikalautidakdikehendaki.Nah...carauntukmendeteksidatastreaminginisudahsiapdikonsumsiataubelumadalahmelaluievent.

Eventyangdidukungolehclassstream.Readableadalahsebagaiberikut

Event:readableEvent:dataEvent:endEvent:errorEvent:close

Mungkinandabertanyakenapaserverfilestatisdiatasmemakaistream,bukankahmenyediakanfilesecaralangsungsajasudahbisa?jawabannyamemangbisa,tetapimungkintidakakanefisienkalaufileyangakandiberikankeclientmempunyaiukuranyangbesar.Cobalihatkodeberikut

varhttp=require('http');

varfs=require('fs');

varserver=http.createServer(function(req,res){

fs.readFile(__dirname+'/data.txt',function(err,data){

res.end(data);

});

});

server.listen(8000);

Jikafiledata.txtterlalubesarmakabufferyangdigunakanolehsistemjugabesardankonsumsimemorijugaakanbertambahbesarseiringsemakinbanyakpenggunayangmengaksesfileini.

JikaandainginlebihbanyakmendalamitentangNode.jsStreamsilahkanlihatresourceberikut(dalamBahasaInggris):

Node.jsAPIStreamStreamHandbook

ServerFileStatis

20

PemrosesanDataFormHTMLAplikasiwebmemperolehdatadaripenggunaumumnyamelaluipengisianformHTML.ContohnyasepertiketikaregistrasidimediasosialatauaktifitasupdatestatusdiFacebookmisalnyapastiakanmengisitextfielddankemudianmenekantombolsubmitataupunenteragardatabisaterkirimdandiprosesolehserver.

Datayangdikirimolehformbiasanyasalahsatudariduatipemimeberikut

application/x-www-form-urlencodedmultipart/form-data

Node.jssendirihanyamenyediakanparsingdatamelaluibodydarirequestsedangkanuntukvalidasiataupemrosesandataakandiserahkankepadakomunitas.

PemrosesanDataFormHTML

21

URLEncodeHanyaakandibahasuntuk2metodeHTTPyaituGETdanPOSTsaja.MetodeGETuntukmenampilkanformhtmldanPOSTuntukmenanganidataformyangdikirim

Oklangsungkitalihatkodeserversederhanauntukparsingdataformmelaluiobjekrequestdengandataformbertipeapplication/x-www-form-urlencodedyangmerupakandefaultdaritagform.

varhttp=require('http');

vardata=[];

varqs=require('querystring');

varserver=http.createServer(function(req,res){

if('/'==req.url){

switch(req.method){

case'GET':

tampilkanForm(res);

break;

case'POST':

prosesData(req,res);

break;

default:

badRequest(res);

}

}else{

notFound(res);

}

});

functiontampilkanForm(res){

varhtml='<html><head><title>DataHobiku</title></head><body>'

+'<h1>Hobiku</h1>'

+'<formmethod="post"action="/">'

+'<p><inputtype="text"name="hobi"></p>'

+'<p><inputtype="submit"value="Simpan"></p>'

+'</form></body></html>';

res.setHeader('Content-Type','text/html');

res.setHeader('Content-Length',Buffer.byteLength(html));

res.end(html);

}

functionprosesData(req,res){

varbody='';

req.setEncoding('utf-8');

req.on('data',function(chunk){

body+=chunk;

URLEncode

22

});

req.on('end',function(){

vardata=qs.parse(body);

res.setHeader('Content-Type','text/plain');

res.end('Hobiku:'+data.hobi);

});

}

functionbadRequest(res){

res.statusCode=400;

res.setHeader('Content-Type','text/plain');

res.end('400-BadRequest');

}

functionnotFound(res){

res.statusCode=404;

res.setHeader('Content-Type','text/plain');

res.end('404-NotFound');

}

server.listen(3003);

console.log('serverhttpberjalanpadaport3003');

UntukmengetestGETdanPOSTbisadilakukanmelaluicurl,browserataumelaluiguiPostman.

GET

URLEncode

23

POST

URLEncode

24

modulequerystringdariNode.jsberfungsiuntukmengubahurlencodedstringmenjadiobjekJavaScript.Contohnya

querystring.parse('nama=lanadelrey&job=singer&album=borntodie&ultraviolence');

//returns

{nama:'lanadelrey',job:'singer',album:['borntodie','ultraviolence']}

URLEncode

25

MultipartDataParsingdataformdanfileyangdiuploadkeserverNode.jsadalahpekerjaanyangcukupsusahkalodikerjakansecaramanualataudariscratchkarenadisampingharusparsingdatabinaryyangberupafilejugaharusparsingdataform.

Protokoluntukmultipart/form-datadidefinisikansecaralengkapdiRFC2388.

MultipartData

26

ModuleNpmnicepeoplematter

npmmerupakanpackagemanageruntukNode.jsdanuntuknamanpmbukanlahsuatusingkatan.Hanyadalamwaktu2tahunsejakdireleasenyaNode.jskepublikjumlahmodulmelesatjauhbahkanhampirmenyamaimoduljavaataupunrubygems.

Grafikdiatasdidapatdarihttp://modulecounts.com.

BanyaknyapushmodulekerepositorinpmdapatdiartikanadanyakepercayaanpublikterhadapplatforminidanuntukkedepannyaNode.jsakanmenjadiplatformyangprospektifuntukberinvestasi.

Modulenpm

27

KonsepnpmmemakaisistemmodulCommonJSyangcukupmudahdalampenggunaanya.Sistemmoduliniakanmeng-exportobjekJavaScriptkevariabelexportsyangbersifatglobaldimodultersebut.

Sebagaicontoh

band.js

'usestrict';

functionBand(){}

Band.prototype.info=function(){

return'NamaBand:'+this.name;

}

Band.prototype.add=function(name){

this.name=name;

}

module.exports=newBand();

Untukpemakaiannyasepertidibawahini

app.js

varband=require('./band.js');

band.add('Dewa19');

console.log(band.info);

require()diatasadalahfungsisinkronyangmeloadpaketataumodullaindarisistemfile.

Konsep

28

PaketnpmSecaradefaultdatapaketnpmdisimpandiregistrynpmjs.org.Sehinggauntukmenginstallpaketnpmtertentuandabisamencaripaketinimelaluicommandnpmataulangsungmelaluiwebsite.

SejakversiNode.js0.6.3commandnpmsudahter-bundledenganinstallerNode.js.Untukmenginstallmodulnpmyangandabutuhkanketikmisalnya

npminstallexpress

perintahdiatasakanmendownloadpaketexpressdarihttp://npmjs.orgdansecaraotomatisakanmembuatdirectorynode_modules.

UntukmemakaimodulexpressinicukupdenganmembuatfileJavaScriptbarudiluardirektorinode_modulesdanloadmoduldengankeywordrequire.

varapp=require('express');

//kodelainnya

MembuatPaketnpmSebelummembuatpaketnpmpastikanfungsionalitasyangandacaritidakadadalamregistrynpm.Caranyayaituandabisamenggunakanperintah

npmsearch

ataudenganmemakaiwebsiteberikutnpmjs.org,node-modules.comataunpmsearch.com

Untukmembuatpaketnpmcaranyacukupmudah.Berikutalurumumuntukmembuatpaketnpmuntukdipublishkeregistrynpmjs.org.

Paketnpm

29

Secaragarisbesarprosespembuatanpaketnpmmenurutalurdiatasakandijelaskansebagaiberikut

Registrasi

Sebelumpublishkeregistrynpmjs.orgkitaharusregistrasidulumelaluiperintahberikut

Paketnpm

30

npmadduser

BuatProject

Untukmembuatprojectbarudarinollangkahpertamaadalahmembuatdirektori

mkdirnpmproject

Kemudianinisialisasiprojecttersebut

npminit

perintahdiatasakanmembuatfilepackage.jsonyangisinyaadalahinfodandependensiproject.Ikutisajatiappertanyaandanisiinformasisesuaidenganpaketyanginginandabuat.

Contohnyapadapaketsvhberikutini

package.json

{

"name":"svh",

"version":"0.0.7-beta",

"author":"EquanPr.",

"description":"Simplefileserverforhtml-javascriptwebclientappdevelopment",

"keywords":[

"process",

"reload",

"watch",

"development",

"restart",

"server",

"monitor",

"auto",

"static",

"nodemon"

],

"homepage":"https://github.com/junwatu/svh",

"bugs":"https://github.com/junwatu/svh/issues",

"main":"./lib/core.js",

"scripts":{

"test":"./node_modules/mocha/bin/mocha"

},

"dependencies":{

"async":"~0.2.9",

"chalk":"0.2.x",

"cheerio":"0.12.x",

Paketnpm

31

"commander":"2.0.x",

"compression":"^1.0.2",

"concat-stream":"1.0.x",

"express":"4.x",

"morgan":"^1.1.1",

"send":"^0.3.0",

"watch":"0.8.x",

"wordgenerator":"0.0.1"

},

"devDependencies":{

"gulp":"^3.5.6",

"gulp-uglifyjs":"^0.3.0",

"gulp-util":"2.2.14",

"mocha":"1.13.x",

"supertest":"0.8.x"

},

"repository":{

"type":"git",

"url":"http://github.com/junwatu/svh.git"

},

"bin":{

"svh":"./bin/svh"

},

"license":"MIT"

}

PublishLokal

Sebelumdipublishpastikanpaketandabisaberjalanataudigunakanpadakomputerlokal.Perintahberikutakanmenginstallpaketandasecaraglobaldikomputer.

npmpublish.-g

ataujikadiinginkanlinksimbolikbisamemakaiperintahnpmberikut

npmlink

PublishPublik

npmpublish

Untuklebihjelasnyasilahkankunjungidokumentasiuntukdevelopernpm.

Paketnpm

32

Paketnpm

33

ExpressJSUntukmemudahkanpembuatanaplikasiwebandabisamenggunakanframeworkExpressJSdaripadaharusmenggunakanmodulehttpbawaanNode.js.Frameworkinimenawarkanbeberapafitursepertirouting,renderingviewdanmendukungmiddlewaredengankatalainandaakanbanyakmenghematwaktudalampengembanganaplikasiNode.js.

ExpressJSmerupakanframeworkminimalyangsangatfleksibel.AndabisamembuatwebserverHTML,serverfilestatik,aplikasichat,searchengine,sosialmedia,layananwebdenganaksesmelaluiRESTAPIatauaplikasihybridyaituselainpenggunamempunyaiaksesmelaluiRESTAPIjugamempunyaiakseskeHTMLpage.

ExpressJS

34

ServerFileSebelumnyabikinprojectkecildenganmengetikkanperintahberikutpadadirektoriproject

$npminit

danberikannamaprojectsebagaiserver-file-statik.Direktoriprojectdariserverfilebisadilihatpadasususantreedibawahini.

Susunanfiledandirektori

server-file-statis

├──package.json

├──app.js

├──node_modules

└──publik

├──index.html

DenganadanyanpminstalasiExpressJSsangatmudah,ketikperintahberikutpadadirektoriproject.

$npminstallexpress--save

Catatan:

PerintahdiatasakanmenginstallExpressJSdenganversiyangpalingterbaru(padasaatbukuiniditulisversiterbaruadalah4.x).Jikamembutuhkanversitertentucukupdenganmenambahkan'@'dannomerversiyangakandiinginkanseperticontohberikut

$npminstallexpress@3--save

UntukkedepannyabahasanmengenaiExpressJSiniakanmemakaiversi4.x.

Kode

ServerFile

35

JikaandaingatserverfileyangmemakaimodulhttppadababsebelumnyaberikutmerupakanversiyangmemakaiExpressJS

app.js

'usestrict';

varexpress=require('express');

varserver=express();

varlogger=require('morgan');

server.use(logger('dev'));

server.use(express.static(__dirname+'/publik'));

server.listen(4000,function(){

console.log('Serverfilesudahberjalanbos!');

});

SepertiyangdijelaskanpadababsebelumnyauntukmemakaimoduleNode.jsdigunakankeywordrequire.

Modulexpressakanmenanganitiaprequestdariuserdankemudianakanmemberikanresponseberupafileyangdiinginkan.Padakodediatasfileyangakandiberikankepenggunadisimpanpadafolderpublik.

ServerFile

36

MiddlewareFungsionalitasyangdiberikanolehExpressJSdibantuolehyangnamanyamiddlewareyaitufungsiasinkronyangbisamengubahrequestdanrespondiserver.

Padakodediatascontohmiddlewareyaitumodulmorgan.Carapemakaianmiddlewareyaitumelaluiapp.use().

Modulemorganmerupakanmoduluntukloggeryangberfungsiuntukpencatatantiaprequestkeserver.Pencatataniniatauistilahnyaloggingakanditunjukkandiconsoleterminal.

Untukmenginstallmoduliniketikperintahberikut

$npminstallmorgan--save

Middlewaremiddlewareinibisaandalihatdilinkberikut

https://github.com/senchalabs/connect#middleware

Middleware

37

AksesServerUntukmenjalankanserverfilestatikini

$nodeapp.js

Andabisameletakkanfileapasajapadadirektori'publik'.Untukmengaksesserveriniketikalamatberikutpadabrowser

http://localhost:4000

dansecaradefaultakanmenampilkanfileindex.htmldibrowser.

UntukmengaksesfileyanglainformatURLadalah

http://localhost:4000/[nama-file]

misalnyauntukmengaksesfileimg.jpg

AksesServer

38

AksesServer

39

ServerRESTFrameworkExpressJSsangatbanyakdigunakansebagaiaplikasiRESTful.Bagiandayangbelumtahu,RESTatauRepresentationalStateTransferadalaharsitekturyangdigunakandalamdesainaplikasinetwork.IdenyayaitudaripadamemakaitekniksepertiCORBA,SOAPatauRPCuntukmembuatWebServicelebihbaikjikamemakaiprotokolHTTPyanglebihmudah.

AplikasiRESTfulmemakairequestHTTPuntukoperasiCreate,Read,UpdatedanDeletedata.RESTitusendiribukanlahsuatustandardjaditidakadayangnamanyaspecdariW3C.Sehinggasangatmudahbagibahasapemrogramanuntukmenggunakanarsitekturini.KeuntunganlainnyayaitudenganarsitekturinibisadibangunberbagaimacamteknologikliensepertiwebataumobileataupundekstopuntukmengaksesaplikasiRESTfultersebut.

UntukcontohaplikasiRESTfulmemakaiExpressJSakandiberikanpadaBabPersonRESTAPI.

ServerREST

40

DatabaseBerkembangpesatnyakomunitasNode.jsmenyebabkandukunganpustakayangsemakincepatjuga.DenganwaktuyangrelatifcepatplatforminisekarangmendukungbanyaktipedatabasesepertiSQLite,MySQL,MongoDB,Redis,CouchDBdll.Dalambukuinihanyaakandibahasa3databasesajayaituSQLite,MySQLdanMongoDB.

Database

41

SQLiteMungkindatabaseinisudahtidakasinglagibagidevelopersepertiandakarenamemangbanyakdigunakandalamaplikasiportabledanembeddable.

KekuatanSQLitesecaragarisbesarsebagaiberikut

Serverless

SQLitetidakmemerlukanproseslainuntukberoperasisepertipadadatabaselainyangumumnyasebagaiserver.LibrarySQLiteakanmengaksesfiledatasecaralangsung.

ZeroConfiguration

Karenasifatnyabukanservermakadatabaseinitidakmemerlukansetuptertentu.Untukmembuatdatabasecukupsepertiketikamembuatfilebaru.

Cross-Platfrom

Semuadatatersimpanpadasatusistemfileyangbersifatcrossplatformdantidakmemerlukanadministrasi.

Self-Contained

LibrarySQLitesudahmencakupsemuasistemdatabasesehinggadenganmudahdapatdiintegrasikankeaplikasihost.

SmallRuntimeFootprint

UkurandefaultdariSQLiteinikurangdari1MBdanmembutuhkanhanyabeberapaMegabytememory.

Transactional

OperasitransaksikompatibeldenganACIDsehinggaamankalauharusmengaksesdatadariprosesbanyakthread.

Untukpenjelasanlebihdetil,silahkankunjungiwebsiteresmihttp://sqlite.org

SQLite

42

nodesqlite3KomunitasnodemenyediakanbanyaksolusiuntukmemakaiSQLitediplatformNode.js.Salahsatuyangpalingpopuleradalahmodulnode-sqlite3.

Instalnodesqlite3merupakanbindingmodulJavaScriptyangasinkronuntukdatabasesqlite3.

Untukpenginstalanmoduliniketikperintahberikut

$npminstallsqlite3--save

CRUDOperasidatabasesepertiCreate,Read,UpdatedanDeleteuntukdatabaseSQLitesangatmudahsepertipadacontohberikut

app.js

/**

*AksesSQLite3

*/

varsqlite=require('sqlite3').verbose();

varfile='film.db';

vardb=newsqlite.Database(file);

varfs=require('fs');

//SampleData

varfilm={

judul:"Keramat",

release:"2009",

imdb:"http://www.imdb.com/title/tt1495818/",

deskripsi:"Filmhorrorpalinghorror!"

}

varfilmUpdate={

id:1,

deskripsi:"BestIndonesianHorrorMovie."

}

//SQLStatement

varCREATE_TABLE="CREATETABLEIFNOTEXISTSfdb(idINTEGERPRIMARYKEYAUTOINCREM

NodeSqlite3

43

ENT,judulTEXTNOTNULL,releaseTEXTNOTNULL,imdbTEXT,deskripsiTEXT)";

varINSERT_DATA="INSERTINTOfdb(judul,release,imdb,deskripsi)VALUES(?,?,?,

?)";

varSELECT_DATA="SELECT*FROMfdb";

varUPDATE_DATA="UPDATEfdbSETdeskripsi=$deskripsiWHEREid=$id";

//RunSQLoneatatime

db.serialize(function(){

//Createtable

db.run(CREATE_TABLE,function(err){

if(err){

console.log(err);

}else{

console.log('CREATETABLE');

}

});

//Insertdatawithsampledata

db.run(INSERT_DATA,[film.judul,film.release,film.imdb,film.deskripsi],functio

n(err){

if(err){

console.log(err);

}else{

console.log('INSERTDATA');

}

});

//Queryalldata

selectAllData();

//Updatedata

db.run(UPDATE_DATA,{$deskripsi:filmUpdate.deskripsi,$id:filmUpdate.id},functi

on(err){

if(err){

console.log(err);

}else{

console.log('UDATEDATA');

}

});

selectAllData();

db.run('DELETEFROMfdbWHEREid=$id',{$id:1},function(err){

if(err){

console.log(err)

}else{

console.log("DELETEDATA");

};

})

});

NodeSqlite3

44

functionselectAllData(){

db.each(SELECT_DATA,function(err,rows){

if(!err){

console.log(rows);

}

})

}

Aplikasidiatasakanmembuattabelfdb,memasukkandatabarukemudiandatatersebutakandiupdatedanterakhirakandihapus.

ContohdiatasmemakaiAPIberikutini

db.serialize()

db.run(operasi_sqlite,callback)

Denganmemakaifungsidb.serialize()makaeksekusisqlakandieksekusisecaraserialatauberurutandanoperasiSQLapasajabisadieksekusiolehnode-sqlite3denganmemakaimetodedb.run()tersebut.

PenjelasanAPIlebihlanjutuntukNodeSQLitebisadilihatpadalinkberikut

https://github.com/mapbox/node-sqlite3/wiki/API.

NodeSqlite3

45

EnkripsiKalauandamemakaiSQLitedanmembutuhkansupayadatabaseiniterenkripsimakaberuntunglahkarenanodesqlite3mendukungpenggunaandatabaseSQLiteyangterenkripsi.

EnkripsiyangdidukungolehSQLiteyaitumelaluiekstensisqlcipher.Sqlciphermerupakanekstensisqliteyangmendukungenkripsi256bitAES.Ekstensiinibisadididownloadmelaluiwebsiteberikut

https://github.com/sqlcipher/sqlcipher

Enkripsi

46

sqlcipherInstalasiekstensiinisangatmudahyaitujikaandaberadadalamlingkunganLinux

$sudoapt-getinstalllibsqlcipher-dev

Kemudiankompilasiulangnode-sqlite3denganperintahberikutini

$npminstallsqlite3--build-from-source--sqlite_libname=sqlcipher\

--sqlite=/usr/

Untukmenggunakanfiturenkripsiiniperuditambahkanbeberapabariskodeberikutpadaaplikasinode-sqlite3.

//encryptdatabase

db.run('PRAGMAkey="passwordmu!"');

PadacontohCRUDnode-sqlite3padabagiansebelumnyakodediatasbisadituliskansebelumoperasiCRUDataupadasaatinisialisasi.

...

//RunSQLoneatatime

db.serialize(function(){

//encryptdatabase

db.run('PRAGMAkey="passwordmu!"');

//Createtable

db.run(CREATE_TABLE,function(err){

if(err){

console.log(err);

}else{

console.log('CREATETABLE');

}

});

...

BandingkanjikaSQLitetidakmemakaienkripsi,andabisamenggunakanSQLiteBrowseratautoolhexdumppadaLinux

Enkripsi

47

danjikaSQLitememakaienkripsibisadilihatdariscreenshotdibawahinibahwaisidaridatabasemenjadi"meaningless".

Enkripsi

48

Enkripsi

49

MySQLKalauandaterbiasadenganbahasapemrogramanPHPmakapastisudahtidakasinglagidengandatabaserelasionalyangpalingbanyakdipakaisaatiniyaituMySQL.

MySQL

50

NodeMySQLImplementasidriverdariMySQLuntukplatformNode.jssudahsangatmaturesepertimodulnode-mysqldihttps://github.com/felixge/node-mysql/

InstalasiSepertibiasauntukmenginstallmodulinidansecaraotomatismenyimpannama&versimoduldifilepackage.jsonyaitudenganmemakaiperintahberikut

$npminstall--savemysql

KoneksiSederhanaUntukmembuatkoneksikedatabaseMySQLsalahsatucaranyaadalahdenganmenggunakanmetodeconnection.connect()

NodeMySQL

51

//app.js

varmysql=require('mysql');

/**

*Settingopsidariconnection,

*lihathttps://github.com/felixge/node-mysql/

*/

varconnection=mysql.createConnection({

host:'localhost',

user:'root',

password:''

});

//MembukakoneksikedatabaseMySQL

connection.connect(function(err){

if(err){

console.log(err);

}else{

console.log('Koneksidenganid'+connection.threadId);

}

});

//Querybisadilakukandisini

//Menutupkoneksi

connection.end(function(err){

if(err){

console.log(err);

}else{

console.log('koneksiditutup!');

}

});

QueryIstilahquerymungkinmenurutmindsetumumartinyaadalahmengambildatadaridatabasetetapidalammodulnode-mysqliniuntukmembuatdatabaseatauschemajugadidefiniskansebagaiquery.Adabeberapabentukfungsiuntukwrapperquery

connection.query(sqlString,callback)

DenganmemakaistatemenSQLkitabisamembuatschemadatabaseebooksepertiberikut

NodeMySQL

52

varcreate_db='CREATEDATABASEIFNOTEXISTSebook'

connection.query(create_db,function(err,result){

if(err){

console.log(err);

}else{

console.log(result);

}

)

connection.query(sqlString,value,callback)

Misalnyauntukmenyimpandatasemuajudulbukupadadatabaseebook

varebook={

id:1,

title:'WiroSablengPendekarKapakMautNagaGeni212:BatuTujuhWarna',

pengarang:'BastianTito'

}

varinsert_sql='INSERTINTOebookSET?';

connection.query(insert_sql,ebook,function(err,result){

err?console.log(err):console.log(result);

})

KalaudibutuhkanescapingvaluesebelumdimasukkankedatabaseMySQLbisamemakaimetode.escape()ataupakeplaceholder?.

varebook={

id:1,

title:'WiroSablengPendekarKapakMautNagaGeni212:BatuTujuhWarna',

pengarang:'BastianTito'

}

varinsert_sql='UPDATEebookSETtitle=?WHEREid=?';

connection.query(insert_sql,[ebook.title,ebook.id],function(err,result){

err?console.log(err):console.log(result);

})

connection.query(options,callback)

Bentukwrapperqueryyangterakhirinicukupringkas.Sesuaikansajamanabentukwrapperyangsesuaidengankebutuhananda.

NodeMySQL

53

varebook={

sql:'INSERTINTOebookSETpengarang=?',

timeout:14000,

values:['PramoedyaAnantaToer']

}

connection.query(ebook,function(err,result,fields){

if(err){

console.log(err);

}else{

console.log(result);

}

})

MetodequeryinisangatfleksibeldanpadaintinyakitabisamemakaistatemenSQLyangbiasadipakaiuntukquerydatadiMySQL.Jadipenggunaandarimodulnpminisebenarnyacukuplahmudah.

UntukpenggunaanmodulnpminilebihlanjutsilahkankunjungiGithubnode-mysqldanuntukcontohaplikasiyangmemanfaatkandatabaseinibisadilihatpadababPengubahGambarPNGKeDataURI.

NodeMySQL

54

MongoDBKalauandasudahterbiasamemakaidatabaserelasionalsepertiMySQLmungkindiperlukansedikitperubahanmindsetuntukmengenaltipedatabaseyangnamanyaNoSQL.Sepertiartidarinamanya,databaseinimerupakandatabaseyangtidakmemakaibahasaSQLquerydatatapibisasecaralangsungmenggunakanbahasapemrogramanclientsebagaicontohadalahMongoDB.

DatabaseNoSQLsepertiMongoDBmenyimpandatasebagaidokumenyangschema-lessyangartinyayaitudatayangdisimpanmempunyaikey-valueyangtidakterikatataubebas.AndabisamembayangkannyasebagaidataJSONyangtersimpandidatabase.

KlienbisaberinteraksilangsungdengandatabaseMongoDBdenganmenggunakanshellJavaScriptmongountukadministrasidanquerydata.Sebagaicontohjikakitainginmembuatsampledatasebanyak100dicollectionsamplemakaperintahdalamshelladalahsepertiberikut,

MongoDB

55

$mongo

MongoDBshellversion:2.6.9

connectingto:test

>usesample;

switchedtodbsample

>for(vari=0;i<100;i++){db.sample.insert({band:"Dewa"+i})};

WriteResult({"nInserted":1})

>db.sample.count()

100

>db.sample.find();

{"_id":ObjectId("55cf520e2dd317a13673d11b"),"band":"Dewa0"}

{"_id":ObjectId("55cf520e2dd317a13673d11c"),"band":"Dewa1"}

{"_id":ObjectId("55cf520e2dd317a13673d11d"),"band":"Dewa2"}

{"_id":ObjectId("55cf520e2dd317a13673d11e"),"band":"Dewa3"}

{"_id":ObjectId("55cf520e2dd317a13673d11f"),"band":"Dewa4"}

{"_id":ObjectId("55cf520e2dd317a13673d120"),"band":"Dewa5"}

{"_id":ObjectId("55cf520e2dd317a13673d121"),"band":"Dewa6"}

{"_id":ObjectId("55cf520e2dd317a13673d122"),"band":"Dewa7"}

{"_id":ObjectId("55cf520e2dd317a13673d123"),"band":"Dewa8"}

{"_id":ObjectId("55cf520e2dd317a13673d124"),"band":"Dewa9"}

{"_id":ObjectId("55cf520e2dd317a13673d125"),"band":"Dewa10"}

{"_id":ObjectId("55cf520e2dd317a13673d126"),"band":"Dewa11"}

{"_id":ObjectId("55cf520e2dd317a13673d127"),"band":"Dewa12"}

{"_id":ObjectId("55cf520e2dd317a13673d128"),"band":"Dewa13"}

{"_id":ObjectId("55cf520e2dd317a13673d129"),"band":"Dewa14"}

{"_id":ObjectId("55cf520e2dd317a13673d12a"),"band":"Dewa15"}

{"_id":ObjectId("55cf520e2dd317a13673d12b"),"band":"Dewa16"}

{"_id":ObjectId("55cf520e2dd317a13673d12c"),"band":"Dewa17"}

{"_id":ObjectId("55cf520e2dd317a13673d12d"),"band":"Dewa18"}

Type"it"formore

>

UntuklebihjelasnyajikaandatertarikuntukbermainmaindenganterminalMongoDBsilahkankunjungidokumentasinya.

SebelumkitalanjutkankekoneksiNode.jsdanMongoDB,perludiingatbeberapakonseppentingdariMongoDB

Collection

Collectionadalahkumpulandaridocument,analoginyawalaupunkurangtepatsebenarnyabisadibilangsepertitabelkalodalamdatabaserelasional.

Document

MongoDB

56

DocumentitusendiriadalahdatayangtersimpandidatabaseNoSQLdandidefinisikanolehyangnamanyaSchema.

Schema

SebelumnyadikatakanbahwaNoSQLmenyimpandatayangschema-less,memangbenartapitetapuntukmenyimpansuatudokumendidatabasebiasanyaaplikasibekerjadenganmodeldatatertentumisalnyauntukdataPersonbisamempunyaikey-valuesepertiberikut

{

nama:"KeboIjo",

email:"[email protected]",

username:"obek_seloso"

}

MongoDB

57

NodeMongoDBMongoDBmenyediakandriverresmiuntukplatformNode.js.Modulenpminitersediadilinkberikuthttps://www.npmjs.com/package/mongodbdandapatdenganmudahdiinstallmelaluinpm

$npminstall--savemongodb

PenulismengasumsikanbahwaMongoDBsudahterinstaldanberjalanpadasistem.Untukmemulaikoneksidapatdenganmudahdilakukansepertiscriptberikutini,

app.js

varMongoClient=require('mongodb').MongoClient;

varMONGODB_URL='mongodb://localhost:27017/sample';

MongoClient.connect(MONGODB_URL,function(err,db){

err?console.log(err):console.log('KoneksikeMongoDBOk!');

db.close();

});

MongoDBakanmembuatdatabasebarujikadatabasetersebuttidakada,sepertihalnyadengandatabasesamplepadakodediataskarenasebelumnyadatabaseinitidakadamakasecaraotomatisMongoDBakanmembuatnya.BentukumumURIuntukkoneksikeMongoDBadalahsepertiberikut

mongodb://<username>:<password>@<localhost>:<port>/<database>

JalankanapikasiditerminaldanjikatidakadamasalahmakaakanmunculpesanpadakonsolbahwakoneksikeMongoDBtelahsukses.

$nodeapp.js

KoneksikeMongoDBOk!

BerikutnyaakankitalakukanoperasidasaruntukMongoDByaituCRUDtapisebelumnyakitabuatschematerlebihdahulu.

person.js

NodeMongoDB

58

functionPersonSchema(data){

this.nama=data.nama;

this.email=data.email;

this.username=data.username;

};

module.exports=PersonSchema;

SchemadiatasmerupakanmodeldatasederhanayangdituliskandalamobjectJavaScriptdantanpabuilt-intypecastingataupunfiturvalidasi.Jikaandamembutuhkanpemodelandatayanglebihhandaldanlebihbaik,makapakailahpustakaODM(Object-DocumentModeler)sepertiMongoose.

DriverNodeMongoDBmenyediakanAPIyanglengkapuntukbekerjadengandatabaseini.SilahkanlihatlinkberikutuntukmelihatlebihlengkaptentangAPIini

http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#insert

InsertUntukmemasukkandokumenbisamemakaimetodeinsertOne()untukmemasukkansatudokumen

app.js

NodeMongoDB

59

/**

*BalajarNode-MongoDB

*

*

*MIT

*EquanPr.2015

*/

varPersonSchema=require('./person.js');

varMongoClient=require('mongodb').MongoClient;

varMONGODB_URL='mongodb://localhost:27017/sample';

varperson=newPersonSchema({

nama:'KeboIjo',

email:'[email protected]',

username:'obek_rebo'

});

MongoClient.connect(MONGODB_URL,function(err,db){

err?console.log(err):console.log('KoneksikeMongoDBOk!');

db.collection('persons').insertOne(person,function(err,result){

if(err){

console.log(err);

}else{

console.log(result);

}

db.close();

})

});

Secaraotomatisoperasiinsertiniakanmenghasilkanprimarykey_idyangunikyaituberupaObjectId.Yangmembedakan_idinidenganidpadadatabaseyanglainadalahdenganObjectIdbisadidapatkankapandatainidimasukkanmelaluipemakaianmetodegetTimestamp().

$mongo

MongoDBshellversion:2.6.9

connectingto:test

>_id=ObjectId()

ObjectId("55d81f48bb934a51424dbd37")

>ObjectId("55d81f48bb934a51424dbd37").getTimestamp();

ISODate("2015-08-22T07:05:44Z")

>

NodeMongoDB

60

Jikaandamempunyaibanyakdokumen,untukmemasukkandokumen-dokumentersebutkecollectionpersonsandabisamemakaimetodeinsertMany().

UpdateOperasiupdatedatajugacukupmudahapalagijikaandasangatpahamntentangMongoDB.Untukmeng-updatedatabisadilakukanmelaluimetodeupdateOne()atauupdateMany().

app.js

varPersonSchema=require('./person.js');

varMongoClient=require('mongodb').MongoClient;

varMONGODB_URL='mongodb://localhost:27017/sample';

varperson=newPersonSchema({

nama:'KeboIjo',

email:'[email protected]',

username:'obek_rebo'

});

MongoClient.connect(MONGODB_URL,function(err,db){

err?console.log(err):console.log('KoneksikeMongoDBOk!');

db.collection('persons').insertOne(person,function(err,result){

if(err){

console.log(err);

}else{

console.log('Simpandatapersonok!');

//updatedata

varpersonUpdate={

nama:'SukatTandika'

}

db.collection('persons').updateOne({nama:person.nama},personUpdate,funct

ion(err,result){

if(err){

console.log(err);

}else{

console.log('Datapersonberhasildimodifikasi!');

}

db.close();

})

}

})

});

MetodeupdateOne()mempunyaibeberapaargumensepertiberikut

NodeMongoDB

61

updateOne(filter,update,options,callback)

Untukdataupdateandabisamemakaioperatorseperti$setcontohnyasepertiberikutini

db.collection('persons').updateOne({nama:person.nama},{$set:{nama:'Angel'}},functi

on(err,result){

if(err){

console.log(err);

}else{

console.log('Datapersonberhasildimodifikasi!');

}

db.close();

})

daribeberapaoptionsyangterpentingadalahkeyupsertyaituupdateinsertdanjikaoptioninidiberikanmakajikadatayangakandi-updatetidakadamakaMongoDBsecaraotomatisakanmembuatdatayangbaru.UntuklebihjelasnyaandabisamelihatdokumentasidariAPIupdateOne().

QueryQuerydatapadadatabaseMongoDBdapatdenganmudahdilakukandenganmemakaimetodefind(),sebagaicontohuntukmenemukandatapersonpadacollectionpersons

NodeMongoDB

62

MongoClient.connect(MONGODB_URL,function(err,db){

if(!err){

findPerson({nama:'MorbidAngel'},db,function(err,doc){

if(!err){

console.log(doc);

}else{

console.log(err);

}

db.close();

});

}else{

console.log(err);

}

});

functionfindPerson(filter,db,callback){

varcursor=db.collection('persons').find(filter);

cursor.each(function(err,docResult){

if(err){

callback(err,null);

}else{

if(docResult!=null){

console.log(docResult);

callback(null,docResult);

}else{

callback(null,'empty!');

}

}

})

}

Denganmetodefind()andabisamemakaioperatorqueryseperti$lt,$gt,operatorkondisiAND,ORdll.UntuklebihlengkapnyasilahkanlihatdokumentasiquerydaridrivernodeMongoDB.

DeleteUntukmenghapusdataandabisamenggunakanfungsideleteOne()ataudeleteMany().Misalnyauntukmenghapussemuadatapadacollectionpersonsandabisamenggunakanemptyobject{}sebagaiquery.

NodeMongoDB

63

functiondeleteAllPerson(db,callback){

db.collection('persons').deleteMany({},function(err,rec){

if(!err){

callback(null,rec.result.n);

}else{

callback(err,null);

}

})

}

ataujikainginmenhapussatudatapadacollectionperson

fucntiondeletePerson(filter,db,callback){

db.collection('persons').deleteOne(filter,function(err,rec){

if(!err){

callback(null,rec.result.n);

}else{

callback(err,null);

}

})

}

DuafungsiinibisaandalihatsecaralengkappadadokumentasiAPIMongoDB.

NodeMongoDB

64

MongooseSepertidikatakansebelumnyauntukmemudahkanpemodelandatadiMongoDBandabisamemakaipustakaMongoose.MeskipunsebenarnyaMongoosememakaiobjekJavaScriptbiasadanmendukungbanyakmetodequerydatatetapiyangpalingmembedakanadalahMongoosemendukungschematypedanvalidasidilevelschema.

data:{type:schemaType}

SchemaTypeAdabeberapaschematypeyangdidukungolehpustakainiyaitu

StringNumberDateBooleanBuffer(untukdatabinarymisalnyagambar,mp3dll.)Mixed(datadengantipeapasaja)ArrayObjectId

PemodelanDataLalubagaimanaMongoosememodeldata?ambilcontohmisalnyadalampengembanganaplikasi,datayangakandipakaisepertiberikut

varlokasiWisataKotaBatu=[{

nama:'BatuNightSquare',

alamat:'Jl.Orooroombo13,Tlekung'

rating:3,

fasilitas:['Wahanabermain','Rollercoaster','Tamanlampion']

}]

DaridatadiatasdenganmemakaiMongoosesangatmudahuntukditerjemahkankeskemadantipedatauntuktiapfield

Mongoose

65

varlokasiWisataKotaBatuSchema=newmongoose.schema({

nama:String,

alamat:String,

rating:Number,

fasilitas:[String]

})

Bagaimanadenganvalidasidata?.Jikaandalihatfielddataratingdiatasdanumumnyamempunyainilaiantara0-5danjikasecaradefaultdiberinilai0makaskemaratingakanmenjadi

rating:{type:Number,'default':0}

Andajugabisamemakaikeyrequireduntukmenandakanbahwafielddatatersebutbersifatwajibadamisalnya

nama:{type:String,required:true}

UntuklebihjelasnyasilahakanlihatdokumentasidariValidatorsMongoose.

Aplikasinode.jsdengandatabaseMongoDByangmemakaipustakaMongoosedapatdikatakanmempunyaihubunganrelasisatu-satuartinyadenganmemakaimodelMongooseberartisecaralangsungaplikasiakanmemakaidatapadaMongoDB.

KalaumisalnyaandabekerjadengandatabaserelasionalMySQLmakaandaakanmemakaibahasaSQLsepertiINSERTINTOtableVALUES(field1=data)untukmemasukkandatasedangkansepertiandaketahuijikadengandatabaseNoSQLuntukmenyimpanmodelobjectcukupdenganmemakaimetodemisalnyamodel.save().DalamMongooseuntukmemakaimodelyangtelahmempunyaischemasepertidiatas

varLokasiModel=mongoose.model('Lokasi',lokasiWisataKotaBatuSchema,'lokasiWisataBa

tu')

KodediatasmemberiartibahwaaplikasiakanmemakaimodelLokasidenganschematypeyangtelahdidefinisikansebelumnyayaitulokasiWisataKotaBatuSchemadanmodeliniakandisimpanpadakoleksidatadiMongoDBdengannamakoleksiadalahlokasiWisataBatu.

UntukmemakaimodelLokasicaranyaadalahdenganmenciptakaninstancedarimodeltersebut

Mongoose

66

varlokasiModel=newLokasiModel()

lokasiModel.nama='MuseumAngkut'

lokasiModel.alamat='Jl.Kembar11'

lokasiModel.rating=5

lokasiModel.fasilitas=['Restoran','Parkirluas']

KemudianuntukmenyimpandatatersebutkedalamdatabaseMongoDB

lokasiModel.save(function(err){

if(!err)console.log('DataDisimpan!')

})

Cukupmudahbukandanmemangpustakainibertujuanuntukmemudahkandeveloperdalampemodelandataterutamajikadatayangakandimodelmempunyaistrukturyangrumitmisalnyanesteddocument.Untuklebihlengkapnyasilahkanandamengunjungisitusresmimongoosejs.com.

ProjekBagiandayangsudahmengertigambaranumumdaripustakaMongooseinisilahkanmengutakatikcontohprojeksederhanayangmemakaiframeworkExpressJSdanMongoose,PersonRESTAPI.

Mongoose

67

TestingPerangkatlunaktidakadayangsempurnadanpastimempunyaibugs.Salahsatucarauntukmenguranginyaataumencegahnyauntukmunculdikemudianhariadalahdenganjalanmelakukanpengetesan.Pengetesanmemberikankeyakinanpadadeveloperbahwaperangkatlunakbekerjasebagaimanamestinyadanjikaadaperubahanmisalnyapenambahanataupenguranganfiturdipastikanbahwapengetesanakanselaluberhasil.

AdabanyaktipepengetesantetapidalampengembanganaplikasiwebadaduatipepengetesanyangpentinguntukdiketahuiyaituUnitTestingdanAcceptanceTesting.

UnitTestingUnitTestingdilakukanlangsungpadalevelkodeaplikasiyaitupadafungsiataumetodedanadaduametodologiyangseringdigunakanyaituTDDdanBDD.DalamJavaScripttersediabanyakpustakayangdibuatuntukpengetesansepertimoduleassertbawaandariNode.js,Nodeunit,Mocha,Jasminedll.

TestDrivenDevelopment(TDD)

IstilahTDDinimenjadisangatkerenbeberapatahunbelakanganini.KonsepTDDadalahmelakukanpengetesanterlebihdahulubarukemudianmenuliskodedariaplikasi,jadimindsetdaripembuatanperangkatlunakdibalikkalodalampengembanganperangkatlunakyangkonvensionalalurnyayaitumenuliskodeterlebihdahulubarukemudianmenuliskodeuntukpengetesansedangkandalamTestDrivenDevelopmentprosesinidibalik.

Kelemahanpengetesaniniadalahpadaprosesawalbiasanyadibutuhkanwaktuyangrelatiflebihlamauntukmengembangkanaplikasikarenaharusmenuliskodepengetesanterlebihdahulutetapikeuntunganjangkapanjangnyayaituaplikasiyangdihasilkanbiasanyamempunyaibugsyanglebihsedikit.

BehaviorDrivenDevelopment(BDD)

PengetesansecaraBDDmemakaibahasaataumetodepengetesanyanglebihramahyaitudenganmelakukanpengetesandengancaraataualursepertiuserstorysehinggapengetesaninirelatiflebihpopulerkarenasecarabahasamenjembataniataumemungkinkankolaborasiantaradeveloperdanklien.AdabanyakpustakapengetesanyangmemakaiBDDsepertiMocha,Jasmin,Vowsdll.SebagaigambarancontohuntukpustakaMochamisalnya

Testing

68

describe('Uploadfile',function(){

it('Harusbisamenerimafilejpeg',function(){

//testingdisini

})

it('Harusbisamenerimafilepng',function(){

//testingdisini

})

it('Harusbisamenerimafilexcf',function(){

//testingdisini

})

})

KalauditerjemahkansecarabahasamanusiamakakodetestingBDDdiatasakanmudahdibacayaituUploadfileharusbisamenerimafilejpeg,pngatauxcf.

AcceptanceTestingUntukpengetesandariantarmukaaplikasiwebbesertafungsinyacontohnyasepertipengetesanpenekananbuttonapakahberfungsidenganbaikatautidakadalahtermasukdariAcceptanceTesting.TestinginibisadilakukandenganmemakaibantuanbrowserdandenganmunculnyaheadlessbrowsersepertiPhantomJSmemungkinkanpengetesandilakukansecaraterintegrasidengankodeback-end.

Catatanpentingdaritestingadalahpengetesandiusahakanberjalansecaraautomatissehinggajikaadakesalahanyangterjadimakadeveloperbisasegeramemperbaikinya,makadariitutestingpastimempunyaiyangnamanyainfolaporanentahituberupafile,outputterminalatauantarmukaberupaweb.Padaprosespengembanganperangkatlunakmodern,automasitestingmerupakanbagianyangsangatpenting.

DalambukuinipengetesanakandibatasipadapengetesanaplikasiwebkhususnyaaplikasiNode.jsdenganRESTdenganmemakaiMocha.

Testing

69

PengetesanRESTKitaambilcontohserveryangdibuatdenganExpressJS.ServerdibawahiniakanmengeluarkandataberupaJSONpadaURL/.

varexpress=require('express')

varapp=express()

app.get('/',function(req,res){

res.json({

message:'hello'

})

})

module.exports=app

LalubagaimanacaramengetestURL/diatas?denganmemakaiMocha,moduleassertdanmodulSupertestyaitumodulnpmyangkhususuntukmengetestserverHTTP.MochatermasukpustakapengetesanyangbisadipakaisecaraBDDataupunTDD.PustakainisecaradefaultmemakaistyleBDDdanmetodeyangyangbiasadipakaiadalahdescribe&it.

varmocha=require('mocha')

varrequest=require('supertest')

varassert=require('assert')

varapp=require('../app')

describe('TestRESTAPI',function(){

describe('GET/',function(){

it('shouldreturnjsonwithkey"message"andvalue"hello"',function(done){

request(app).get('/')

.set('Accept','application/json')

.expect('Content-Type',/json/)

.expect(200)

.expect(function(res){

assert.equal(res.body.message,'hello')

})

.end(function(err,res){

if(err)returndone(err)

done()

})

})

})

})

REST

70

PerluandaingatbahwasupertestakansecaraotomatismenjalankanserverExpressJSdanmengalokasikanportsehinggaandatidakperlumejalankanserversecaralangsung.

request(app)

Kalauditerjemahkandalambahasamanusiakodediatasakanmengetestbeberapakondisiyaitu

expect('Content-Type',/json/)

kodediatasartinyadiharapkanresponsedariGETuntukURL/adalahbertipejson

expect(200)

kodediatasartinyaresponsecodedarirequestHTTPharusmempunyaikode200.

expect(function(res){

assert.equal(res.body.message,'hello')

})

dankodediatasartinyadiharapkanbahwaresponsebodydenganfieldmessagemempunyaivaluehellodantentusajaandabisamenambahkanbanyakkondisiuntukpengetesanyanglebihdetil.

Untukmelakukanpengetesancukupdenganmenginstaldanjalankanmochamakasecaraotomatismochaakanmenjalankanberkasberkaspengetesanyangberadapadafoldertest.

$npminstall-gmocha

REST

71

Sebenarnyabanyakyangbisadijelaskantentangpengetesandansudahbanyakresourceonlinediluarsanayangmembahastentanginitapiyangperluandaingatadalahpastikanpengetesandimasukkandalamalurkerjadalampengembanganperangkatlunakyangsedangandakerjakan.

REST

72

AutomasiDalamprakteknyabiasanyatestinginidilakukansecaraotomatismisalnyajikaandamemakaiGithubdalampengembanganperangkatlunakopensourcemakaandabisamemakaiserverTravisyangdengansettingkonfigurasitertentuakanmenjalankanpengetesanjikaandamenge-pushkodekerepositoryGithubatauandabisamemakaibuildserveryangadadiluaransepertiJenkins,Bamboo,TeamCitydll.

TravisJikaandamemakaiGithubdanmenggunanakanTravismakasecaradefaultservertravisiniakanmenjalankancommand

$npmtest

sehinggadalampackage.jsonyangandapushkeGithubandaperlumenulisscriptuntukpengetesanmisalnyajikamemakaiMocha

"scripts":{

"test":"mocha"

}

.travis.yml

UntuksettingTravisandacukupmembuatfile.travis.ymlpadafolderprojekmisalnyasepertidibawahini(contohsourcecodeprojektestadadisini)

.

├──app.js

├──.travis.yml

├──node_modules

├──package.json

└──test

└──app.test.js

DenganisifiletersebutadalahversidariNode.jsdimanatestakandijalankan.MisalnyauntukmenjalankanpengetesanNode.jsversi4.1dan4.0

Automasi

73

language:node_js

node_js:

-"4.1"

-"4.0"

KemudianandaperlumengaturkonfigurasirepodiGithub(TestingLearn).SupayaGithubbisamemakaiservicedariTravismakaandaperlumeengubahkonfigurasirepo(MasukkemenuSettings)

EnableBuild

SupayabuildselaludijalankanolehTravismakaandaharusmeng-ON-kantombolenableditravis-ci.orgpadarepoyangandainginkan.

TestBuild

Automasi

74

BuildakandijalankanolehTravissetiapadakodebarudiGithubatauandabisamenggunakantombolTestServicepadaSettings->Webhooks&servicesuntukforcebuild.

Notifikasi

NotifikasiTravisbisadiintegrasikandenganSlack,Email,dllataubiasanyadengangambarstatusdiREADMErepo.UntukkonfigurasigambarstatuskliktombolBuild/PassingdisebelahnamarepodiTravisdancopypastekeREADMErepodiGithub.

SehinggajikaandamembukarepodiGithubmakaakanmunculgambarstatusyangapakahbuildsuksesataudalamstatuserror.

Automasi

75

Automasi

76

ToDataURIAplikasiiniadalahaplikasiNode.jsCLIyangmengubahgambarpngkeformatdataURIdankemudianmenyimpandatatersebutkedatabaseMySQL.Prosesinibisadigambarkansepertigambaralurdibawahini.

DataURIBagiyangbelumtahuDataURIadalahsalahsatucarauntukmeng-embeddatasecarainlinealih-alihharusmengambildatatersebutdariresourceluar.DatayangakandiubahkeDataURIdisinibisaberupaimage,filecsvdll.Untukaplikasiinihanyadibatasiuntukgambarberformatpng.

FormatDataURIadalahsepertiberikutini

data:[<MIME-type>][;charset=<encoding>][;base64],<data>

Umumnyauntukgambarjenisencodingyangdipakaiadalahbase64.SebagaicontohnyalihatformatgambarpngyangtelahdirubahkeDataURIberikutini

ToDataURI

77

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAAABGdBTUEAANjr9RwU

qgAAACBjSFJNAABtmAAAc44AAPXqAACD0AAAfHIAAN7iAAAyWgAAI0zujJoVAAADAFBMVEXW1tYxLS6EgYKtrK

y7uro/OzxoZWZMSUrk5OR2c3SfnZ7y8vJaV1iRj5DJyMgjHyD///8RERESEhITExMUFBQVFRUWFhYXFxcYGBgZ

GRkaGhobGxscHBwdHR0eHh4fHx8gICAhISEiIiIjIyMkJCQlJSUmJiYnJycoKCgpKSkqKiorKyssLCwtLS0uLi

4vLy8wMDAxMTEyMjIzMzM0NDQ1NTU2NjY3Nzc4ODg5OTk6Ojo7Ozs8PDw9PT0+Pj4/Pz9AQEBBQUFCQkJDQ0NE

RERFRUVGRkZHR0dISEhJSUlKSkpLS0tMTExNTU1OTk5PT09QUFBRUVFSUlJTU1NUVFRVVVVWVlZXV1dYWFhZWV

laWlpbW1tcXFxdXV1eXl5fX19gYGBhYWFiYmJjY2NkZGRlZWVmZmZnZ2doaGhpaWlqampra2tsbGxtbW1ubm5v

b29wcHBxcXFycnJzc3N0dHR1dXV2dnZ3d3d4eHh5eXl6enp7e3t8fHx9fX1+fn5/f3+AgICBgYGCgoKDg4OEhI

SFhYWGhoaHh4eIiIiJiYmKioqLi4uMjIyNjY2Ojo6Pj4+QkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZma

mpqbm5ucnJydnZ2enp6fn5+goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6vr6

+wsLCxsbGysrKzs7O0tLS1tbW2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr6/v7/AwMDBwcHCwsLDw8PExMTF

xcXGxsbHx8fIyMjJycnKysrLy8vMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXW1tbX19fY2NjZ2dna2t

rb29vc3Nzd3d3e3t7f39/g4ODh4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w

8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7///8EKbe0AAAACXBIWXMAAAsTAA

ALEwEAmpwYAAAB/0lEQVRIx62W2W7sIBBE2Rf3UvX/X5sHYyczyb2eGRnJEjZw6C4KTOD7pQAtXHUKH4AthRab

ldvBIQZmm7GHm8HcOiVTErabweyF6mSJ82bwNjl2Uexe8IiyVxzlVjDnoYEdU9wEFoxVqdut4FFPU9dbwTmdsc

f8Cjj/6BXOwUzP85vxWLXZXwGbSRAnGQZdJZBOuqhzBJLBz34jLvL42xi/wB5T69JTS66uJEZPKXpLSaWntBR1

LdAj5JjlFbCS2BLpC2yJ1C2qxtx6OzwQm9YVqWway4vgolKmq1dxZJVRcx9issmoa0CJW+hnoF7n5eKFRCqzzh

ASTbMya5rBWyssTX+69nt1pfR2j93W4KWAIWq6AAc/8nN/45diVxpPIO6btQMPBpWGvZjMvRKNtojl96H/9K7A

3jcDwM+WhGhmZuYJmGaWgHKAKVblAtwQhWRFBTg8kKS4U890dTlYYfatwbALcInIpAMZoO0MB9jQ3d3/CeaVFG

6opEL9ERzqrmz6GDywP09gMrh7BvxTMCfaROcvMElygZM2eQDnZyP/AQ4AkBe4urtvAIO7uxvgEzGXiB6RDTW7

u7so4iWYCtQ9TNFlXvCopVNtdDnbvdX6X3BwIcWL7B4j5THifV8Gd/eJGMixPouPd84K3/dEfc5zv1yYyac3IV

uZtvfPqi9DYJrPLQNESAAAAABJRU5ErkJggg==

CukuppanjangmemangkalodijadikankeformatDataURI.Cobakopicontohdatauridiatasdanpastekebrowserurldanjanganlupatekanenter.

ToDataURI

78

PenggunaanUntukkodesumberselengkapnyabisadilihatdiGithubpadabranchtodatauri-mysqlatauketikperintahberikutpadacommandline.

$gitclone-btodatauri-mysqlhttps://github.com/junwatu/todatauritodatauri-mysql

$cdtodatauri-mysql

$npminstall

Adatigapenggunaanaplikasiiniyaitu

DownloadPNG&SimpanKeMySQL

$node./tdi.js<url_image_png_dari_internet>

Aplikasiiniakanmendownloadfileimagepngdariinternetdanmendukungprotokolhttpataupunhttps.

Demo

$node./tdi.js

JikadijalankantanpaargumenmakahasilnyaadalahkeluarandemoyangtidaklainadalahcontohformatDataURI.

TampilkanDataMySQL

$node./tdi.jsshow

JikaadaargumenshowmakadatadalamMySQLakanditampilkansemua.Hatihatijikaandabanyakmengkonversibanyakdatagambarpng.

Penggunaan

79

Penggunaan

80

KonverterPNGKeDataURIMarikitalihatisidarifiletodatauri.js.Padadasarnyafileinimerupakanpustakasederhanayangakanmendownloadimageberformat.pngdiinternetdankemudianmengubahnyamenjadiformatDataURI.

Jadiandabisamenggunakanpustakainisepertihalnyamodulnpmdenganbantuanrequire.

/**

*todatauri.js

*DownloadimageandconvertittoDataURI

*

*WTFPL

*(c)2015,EquanPr.

*/

varfs=require('fs');

varhttp=require('http');

varhttps=require('https');

varurl=require('url');

functionUtil(){

vardefaultImage='https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Node

.js_logo.svg/320px-Node.js_logo.svg.png';

varimageDest='download.png';

varfile;

return{

toDataURI:function(urlArg,cb){

varimageURL;

urlArg?imageURL=urlArg:imageURL=defaultImage;

if(url.parse(imageURL).protocol==='https:'){

Util().httpsGetImage(imageURL,function(err,data){

err?cb(err,null):cb(null,data);

});

}else{

Util().httpGetImage(imageURL,function(err,data){

err?cb(err,null):cb(null,data);

});

}

},

httpGetImage:function(url,cb){

file=fs.createWriteStream(imageDest);

http.get(url,function(response){

response.pipe(file);

todatauri.js

81

console.log('Downloadnode.jsdefaultImagefrom',defaultImage);

console.log('\n');

file.on('finish',function(){

file.close();

Util().imageToDataUri(imageDest,function(err,data){

err?cb(err,null):cb(null,data);

});

});

})

},

httpsGetImage:function(url,cb){

file=fs.createWriteStream(imageDest);

https.get(url,function(response){

response.pipe(file);

console.log('Downloadnode.jsdefaultImagefrom',defaultImage);

console.log('\n');

file.on('finish',function(){

file.close();

Util().imageToDataUri(imageDest,function(err,data){

err?cb(err,null):cb(null,data);

});

});

})

},

imageToDataUri:function(imageName,cb){

//TODO:lookupmimetypehere

varmime='image/png';

varencoding='base64';

varuri='data:'+mime+';'+encoding+',';

fs.readFile(imageName,function(err,buf){

if(err)returncb(err,null);

cb(null,uri+buf.toString(encoding));

})

}

}

}

module.exports=Util();

Fungsiutamapadafiletodatauri.jsyaitufungsiimageToDataUri()danjikadilihatisinyamakasebenarnyasangatsederhanauntukmengubahgambarpngkeencodingbase64yangakandigunakandiDataURI.

todatauri.js

82

buf.toString(encoding))

fs.readFile(filename[,options],callback)merupakanfungsiuntukmembacafilesecaraasinkrondanjikaencodingtidakdiberikanmakahasilpembacaanfileakanmengembalikandataberupabuffer.Sehinggauntukmengubahdatabufferinikebase64cukupdenganmemakaimetode.toString(encoding).

todatauri.js

83

KoneksiMySQLUntukkoneksikedatabaseMySQLcukupmudahsepertiyangtelahdibahaspadababsebelumnyatentangNodedanDatabaseMySQL.

/**

*todatauri.jsdenganMysql

*

*

*WTFPL

*EquanPr.

*2015

*/

varUtil=require('./todatauri.js');

varmysql=require('mysql');

varconnection=mysql.createConnection({

host:'localhost',

user:'root',

password:''

});

connection.connect(function(err){

if(err){

console.log(err);

}else{

console.log('KoneksiMySQLdenganid'+connection.threadId);

}

});

varcreate_database='CREATEDATABASEIFNOTEXISTSdata_uri';

varcreate_table="CREATETABLEIFNOTEXISTSdata_uri.images(idINTAUTO_INCREMENT

PRIMARYKEY,dataTEXT)";

connection.query(create_database,function(err,res){

if(err){

console.log(err);

}else{

console.log('Createdatabasedata_uri[ok]');

connection.query(create_table,function(err,result){

if(err){

console.error(err);

}else{

console.log('Createtableimages[ok]');

main(process);

}

})

}

KoneksiMySQL

84

});

functioninsertData(data,connection){

connection.query('INSERTINTOdata_uri.imagesSET?',{data:data},function(er

r,res){

if(err){

console.log(err);

}else{

console.log(res);

closeConnection();

}

})

};

functionshowData(connection){

connection.query('SELECT*FROMdata_uri.images',function(err,result){

if(err){

console.log(err);

}else{

console.log('\n\nMySQLDatabase\n============================\n');

result.forEach(function(element,index){

console.log('id\u2192',element.id);

console.log('image\u2192',element.data);

});

closeConnection();

}

})

}

functionmain(process){

if(process.argv[2]){

if(process.argv[2].toLowerCase().trim()=='show'){

showData(connection);

}else{

Util.toDataURI(process.argv[2],function(err,data){

if(!err){

insertData(data,connection);

}

})

}

}else{

Util.toDataURI(null,function(err,data){

if(!err){

console.log(data);

closeConnection();

}

});

}

}

functioncloseConnection(){

KoneksiMySQL

85

connection.end(function(err){

if(err){

console.log(err);

}

});

}

ParsingargumendilakukanolehprocessyangmerupakanobjectglobaldiNode.js.PerludiingatbahwaJavaScriptmemulaiarraydenganindex0sehinggaargumenyangdiperlukanberadapadaindex2

process.argv[2]

JikaandaberadapadaplatformUNIXatauGNULINUXscriptdiatasbisadirubahuntukselfexecutabledenganmenambahkanscriptberikutpadaline1difiletdi.js

#!/usr/bin/envnode

Kemudianuntukmenjalankannyacukupmudahsepertiberikut

$./tdi.js

Scripttdi.jssebenarnyacukupsederhanadancukupuntukmelakukantugasyangdiinginkan.Mungkinkelihatanagakrumitkarenabanyakcallbackdisanasinitetapikalauandasudahterbiasamemprogramsecaraasinkronpastimudahsekaliuntukmelihatkodediatas.

PenulissarankanuntukkedepannyajikadiinginkanpenulisantanpacallbackbisadipelajaritentangPromisesJavaScriptES6.

KoneksiMySQL

86

PersonRESTAPIAplikasiinimerupakancontohsederhanalayananservermelaluiREST(RepresentationalStateTransfer)yaitumekanismeuntukkomunikasidenganservermelaluiprotokolHTTPyangmudahuntukdigunakandaripadamemakaimekanismeprotokollamasepetiCORBA,SOAPataupunRPC.

AplikasiRESTmemakaimetodeHTTPsepertiPOST,PUT,GETdanDELETEuntukmenambah,mengubah,mengambilataupunmenghapusresourceyangadapadaserver.SehinggadalampengembanganaplikasikhususnyadenganmemakaiNode.js,mekanismeRESTsangatmudahuntukdigunakan.

SusunanAPIBerikutsusunanAPIyangakandigunakandalamcontohaplikasiini.ContohAPIinimungkintidakmengikutibestpracticetapipenulisrasasudahcukupuntukmenggambarkanbagaimanamenyusunaplikasiRESTsecarasederhana.

PersonRESTAPI

87

CaraKerjaCarakerjadariaplikasiPersonRESTinicukupmudah.HanyasajauntukantarmukadenganpenggunatidakmelaluiantarmukawebsepertihalnyakitamengakseshalamanFacebookmisalnya,karenasecaraumumaplikasiRESTfungsinyalebihkememberikanlayanandatamentahsehinggadeveloperbertanggungjawabpenuhuntukapadata-datatersebutdigunakan,apakahakanditampilkankebrowserwebataudigunakanpadaaplikasimobileandroidatauakandigunakanuntukmengaktifkandeviceelektroniksepertiArduino,RaspberryPidll.

UntukaplikasiPersonRESTini,datadisimpandidatabaseMongoDBmelaluioperasiCRUD(Create,Read,Update,Delete)yangdapatdiaksesmelaluirequestAPIyangtelahdisebutkansebelumnya.

DiagramkerjaaplikasiPersonRESTdigambarkanpadadiagramdibawahini

PengetesanoperasiCRUDuntukaplikasibisadenganmenggunakanbantuanalatCommandLineInterfaceseperticURLatauHTTPieataupuntoolyanglebihramahsepertiPostmandanbisajugaandamembangunantarmukawebdenganmemakaiAPIyangdisediakanolehserverPersonini.ContohpengetesanuntukaplikasiPersonRESTinibisalihatpadasub-babPengetesan.

CaraKerja

88

ServerKodesumberdariaplikasiinidapatdidownloaddilinkberikut

https://github.com/junwatu/rest-node-mongoose-mongodb

InstalasiClonekodesumbermelaluigitdaninstaldepedensipaketmelaluinpm

$gitclonehttps://github.com/junwatu/rest-node-mongoose-mongodb.git

$cdrest-node-mongoose-mongodb

$npminstall

PastikandatabaseMongoDBsudahberjalanpadasistemandajikamenggunakanservicesystemdpadalinuxandabisamenjalankannyadenganperintahberikut

$sudoservicemongodstart

KodeUntuklingkunganproduksiadabaiknyauntukmemecahfilekodekebagianyanglebihkecilsupayalebihmudahdalamhalmaintenance.Untukkemudahandankesederhanaanaplikasiinimakakodeutamaditulisdalamsatufile.

app.js

/*

*KoneksiNodejsdenganMongoDBmenggunakanMongoose

*

*AuthorByEquanPr.

*http://equan.me

*

*License:Whateveryouwant!:D

*/

varexpress=require("express"),

app=express(),

mongoose=require('mongoose'),

Server

89

path=require('path'),

engines=require('consolidate');

app.configure(function(){

app.use(express.logger());

app.use(function(req,res,next){

res.header('Access-Control-Allow-Origin','*');

res.header('Access-Control-Allow-Methods','GET,PUT,POST,DELETE');

res.header('Access-Control-Allow-Headers','Content-Type')

if('OPTIONS'==req.method){

res.send(200);

}

else{

next();

}

})

app.use(express.bodyParser());

app.use(express.methodOverride());

app.use(express.static(__dirname+'/public'));

app.engine('html',engines.handlebars);

app.set('views',__dirname+'/views');

app.set('viewengine','html');

app.set('PORT',process.env.PORT||5000);

app.set('MONGODB_URI',process.env.MONGOLAB_URI||

process.env.MONGOHQ_URL||'mongodb://localhost/persons');

});

/**

*MongoDBconnection

*/

vardb=mongoose.createConnection(app.get('MONGODB_URI'));

db.on('connected',function(){

console.log('ConnectedtoMongoDB.');

});

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

console.error.bind(console,'ConnectiontoMongoDBerror!.');

});

db.on('close',function(){

console.log('ConnectiontoMongoDBclosed.');

});

//Schema

varPersonsSchema=newmongoose.Schema({

Server

90

name:'string',

username:'string',

website:'string',

createdAt:'date',

updatedAt:'date'

}),

Persons=db.model('Persons',PersonsSchema);

//Routes

app.get("/",function(req,res){

res.render('index',{

data:'SillyRESTfulsampleappbuiltwithNode.js,Express,MongooseandMong

oDB.'+

'Maybeit\'susefulforbeginners;)'

});

});

//GET/persons

app.get("/persons",function(req,res){

//FindAll

Persons.find(function(err,persons){

if(err)res.json({error:err})

if(persons)

res.json({persons:persons});

})

});

//POST/persons

app.post("/persons",function(req,res){

/**

*Getdatafrompost

*@type{Persons}

*/

varperson=newPersons({

name:req.body.name,

username:req.body.username,

website:req.body.website,

createdAt:newDate(),

updatedAt:newDate()

});

person.save(function(err,person){

if(err){

res.send({error:err});

}else{

console.log('Savedata:'+person);

res.json({message:'saveok'});

}

})

});

Server

91

//GET/persons/:username

app.get('/persons/:username',function(req,res){

varparam_username=req.params.username;

Persons.find({username:param_username},function(err,person){

if(err){

res.json({

data:"Errorfindingperson."

});

}else{

res.json({

person:person

});

}

})

});

//PUT/persons/:username

app.put('/persons/:username',function(req,res){

varquery={username:req.params.username},

data_update={

name:req.body.name,

username:req.params.username,

website:req.body.website,

updatedAt:newDate()

}

Persons.update(query,data_update,{multi:false},function(err,numberAffected,ra

wResponse){

if(err){

res.json({

error:err

})

}else{

res.json({

numberAffected:numberAffected,

rawResponse:rawResponse

});

}

});

});

//DELETE/persons/:username

app.delete('/persons/:username',function(req,res){

varparam_username_del=req.params.username;

Persons.remove({username:param_username_del},function(err){

if(err){res.json({

error:err

})

}else{

res.json({message:"deleteok"});

Server

92

}

});

});

app.listen(app.get('PORT'));

console.log("ServerPort:"+app.get('PORT'));

Server

93

PengetesanUntukmenggunakanataupengetesanAPIinicaratermudahadalahdenganmemakaiPostmanataujikaandasudahterbiasamemakaitoolterminalseperticURLatauHttpiesilahkansaja.

Demoaplikasiberadapadalinkberikut,

persons-api.herokuapp.com

POST/personsUntukmembuatdataPersonbarumelaluiapi/personscukupdenganmemakairequestPOST.

GET/persons

Pengetesan

94

MengambildatadenganmengaksesAPI/personsyangakanmengembalikansemuadatayangtelahtersimpansebelumnya.

PUT/persons/:usernameUpdatedatabisadilakukandenganmudahdenganmemakaiPUT.

Pengetesan

95

DELETE/persons/:usernameOperasipenghapusandatahanyabisadilakukansatupersatumelaluikey:username.

Pengetesan

96

Pengetesan

97

ImageUploader

Aplikasiinisangatsederhana,carakerjanyayaitugambardiuploadkeserverdankemudianditampilkankembalikebrowser.

ServerPadasisiserveraplikasiinimemakaiframeworkExpressJSuntukmenanganirequestHTTPdanpaketformidableuntukmenanganifileyangdiupload.

Catatan:AplikasiinimemakaisedikitfiturES6sepertilet,const,arrowfunctionsehinggaandaperlumenginstallsetidaknyaNode.jsv4.2.1LTS

ImageUploader

98

server.post('/upload',(req,res)=>{

letform=newformidable.IncomingForm()

form.uploadDir=path.join(__dirname,'uploads')

form.hash=true

form.multiples=false

form.keepExtensions=true

form.parse(req,(err,fields,files)=>{

if(!err){

console.log(files.file.name)

console.log(files.file.path)

console.log(files.file.type)

}

res.end()

})

})

Kodediatasakanmenanganifileyangakandiuploaddanmenyimpanhasiluploadpadadirektoriuploads.Formidabledapatdenganmudahdikonfigurasi,lihatGithubFormidable.

UploaderPadasisiklienuploderdibangundenganmemakaipustakaDropzoneJSyangmendukungdragndropdanpreviewthumbnail.Pustakainisangatmudahuntukdigunakandandikustomisasi

Dropzone.options.mydropzone={

init:function(){

this.on("complete",function(file){

updateImage()

})

},

maxFileSize:2,

acceptedFiles:'image/*'

}

Konfigurasidiatasmengakibatkanuploaderhanyamenerimafilebertipegambardanukuranfiletidaklebihdari2MBdanyangperludicatatyaituketikafileselesaidiuploadyaitudenganmendengarkaneventcompletemakalistviewgambaryangdibuatdenganKendoUIharusdiupdatedenganmemanggilmetodeupdateImage().

ImageList

ImageUploader

99

Kendoakanmengambildatadariserverkemudiansecaraotomatisakanmengupdate#listViewsesuaidenganbanyaknyagambaryangtelahterupload.

varupdateImage=function(){

vardataSource=newkendo.data.DataSource({

transport:{

read:{

url:document.location.href+'service/images',

dataType:'json'

}

},

pageSize:21

})

$('#pager').kendoPager({

dataSource:dataSource

})

$('#listView').kendoListView({

dataSource:dataSource,

template:kendo.template($('#template').html())

})

}

KomponenKendoUIyangdipakaiadalahListViewdanframeworkUIinisepertiframeworkkebanyakanlainnyajugamemakaitemplateuntukmenghasilkanUIsecaradinamik.

<divclass="demo-sectionk-contentwide">

<divid="listView"></div>

<divid="pager"class="k-pager-wrap"></div>

</div>

<scripttype="text/x-kendo-template"id="template">

<divclass="product">

<imgsrc="/#=ImageName#"/>

<h3>#:ImageId#</h3>

</div>

</script>

DatayangakandiambildariserveryaitudatagambarharusmempunyaifieldImageNamedanImageId.

KomponenyangdisediakanolehKendocukuplengkapdanjikaandatertarikdenganKendoUIlebihlanjutsilahkanberkunjungkewebsiteresmiTelerik.

UntukmengambildaftargambaryangtelahdiuploadklienakanmengaksesURLberikut

ImageUploader

100

http://localhost:5005/service/images

Serverhanyaakanmemfilterfiledengantipejpgdanpngpadafolderuploads.Filterdilakukandenganmengecektipefilemelaluipaketmimetepatnyamelaluimetodemime.lookup(image).

server.get('/service/images',(req,res)=>{

letimages=[]

fs.readdir(upload_dir,(err,files)=>{

if(!err){

for(letimageIndex=0;imageIndex<files.length;imageIndex++){

letftype=mime.lookup(path.join(upload_dir,files[imageIndex]))

if(filetypes.indexOf(ftype)!==-1){

letdata={}

data.ImageId=imageIndex

data.ImageName=files[imageIndex]

images.push(data)

}

}

res.json(images)

}else{

res.end()

}

})

})

DatayangdikembalikankeklienadalahdataJSONdenganformatsepertiberikut

[{

ImageId:1,

ImageName:'file.jpg'

}]

KodesumberdariaplikasiinibisaandadapatkanpadarepoGithubImageUploader.

ImageUploader

101

ECMAScript2015atauES6MeskipunECMAScript2015atauyanglebihdikenalES6sudahdiresmikantetapiimplementasidibrowserdanplatformNode.jsmasihterjadisecaragradual.PadasaatbukuiniditulispadaplatformNode.js4.x(LTS)dan5.xsudahbanyakfitur-fiturES6yangbuilt-indanuntukfituryangbelumdidukungandabisamemakaitoolyangbernamaBabel.

UntukmempelajariES6banyaksekaliresourceonlineyangmenyediakanjadisilahkanandabereksplorasidanmencobakonsep-konsepbaruyangditawarkanolehJavaScript.AndabisamemulaidaribukuonlinegratistentangES6

ExploringJSES6

PadadasarnyaBabelberfungsiuntukmengkompilasiES6(atauES7,8)menjadiES5sehinggabisadijalankanpadabrowseratauplatformNode.jsyangbelummendukungES6.SalahsatupertanyaanterbesarkalauandamemakaiBabeladalahbagaimanamengkompilasihanyabeberapafiturES6sajajikalaubrowserataupunNode.jsyangakankitapakaisecaranativesudahmendukungsebagianataubeberapafiturES6?

BabelonTheFly

SejakdireleaseBabel6,toolinimendukungkompilasiES6berdasarkanpluginartinyaandabisamemilihfiturmanayangakandikompilasikeES5jikamisalnyapadaplatformNode.jssudahmendukungbanyakfiturES6.

Kalauandapernahmemakaibabel-nodemakesekarangdigantidenganpaketbabel-cli

$npminstall-gbabel-cli

Apakegunaannya?babel-clisangatbergunauntukmengkompilasiES6secaraontheflymisalnyabegini

lib/module.js

'usestrict'

exportfunctiontest(){

console.log('test')

}

main.js

MemakaiES6

102

'usestrict'

import{test}from'./lib/module'

test()

Makauntukmengeksekusifilemain.jsbisamelaluibabel-node

$babel-nodemain.js

JikaandamemakaiprojectdenganES6selainbabel-climakapaketberikutjugaperludiinstall

$npminstall--save-devbabelbabel-corebabel-loader

danjugasettingfile.babelrc(meskipunsebenarnyaadabeberapaalternatiflaindimanaharusmenuliskansettingbabeltapicarainilebihUNIX,tergantungseleramasingmasing)

.babelrc

{

"plugins":["transform-es2015-modules-commonjs"]

}

Bisaandalihatbahwapluginsyangakankitagunakanadalahtransform-es2015-modules-commonjsjadiharusdiinstalljuga

$npminstall--save-devbabel-plugin-transform-es2015-modules-commonjs

Lalubagaimanakalaukitamenginginkansemuapluginkitapakai?jawabannyaadalahdenganmemakaipresets

$npminstall--save-devbabel-preset-es2015

dan.babelrcperluandarubahmenjadisepertidibawahini

{

"presets":["es2015"]

}

CatatanPenjelasandiatasberlakuuntukJavaScriptyangdigunakanpadasisiserveruntukclientatauJavaScriptyangberjalanpadabrowserandabisamemakaiwebpackbersamadenganbabel.

MemakaiES6

103

MemakaiES6

104

TentangPengarangEquanPr.adalahdeveloperNodeJSdanpeminumkopikelasberat.SelalusibukdenganyangberbauJavaScriptketikadidepankomputer,penggilafilm&musikmetaldankadang-kadangnge-twitdi@junwatuataunge-Github.

TentangPengarang

105