You are on page 1of 5

Personal

Open source

Business

Explore

Pricing

Blog

Support

This repository

martijnboland / appointsapinode

Code

Issues 0

Watch

Pullrequests 0

Pulse

Sign in

Search

Star

Sign up

Fork

15

Graphs

AppointmentSchedulerRestAPIbuildwithNode.js.https://appointsapi.azurewebsites.net

56commits

Branch:master

1branch

Newpullrequest

Newfile

0releases

Findfile

HTTPS

1contributor

https://github.com/martijnboland/appointsapinode.git
DownloadZIP

martijnbolandUpdateREADME.md

875f005onAug24,2014

Latestcommit

config

EnabledCORS

2yearsago

infrastructure

Smallrefactorings

2yearsago

models

Propertestingofdates

2yearsago

routehandlers

RenamedembeddedAppointmentsfrom'appointments'to'appointment'to

2yearsago

test

Propertestingofdates

2yearsago

.gitattributes

Added.gitattributesfile.

2yearsago

.gitignore

Configchangesandtestingonwindows.

2yearsago

.jshintrc

Createanewappointment.

2yearsago

README.md

UpdateREADME.md

2yearsago

index.js

Refactoredconfigurationsystem

2yearsago

package.json

Propertestingofdates

2yearsago

passportconfig.js

RequestprofileinfofromGoogle

2yearsago

routes.js

Sanitizerequestbodieswithmiddleware.

2yearsago

runtests.bat

Changedbatchfiletoruntests.

2yearsago

server.js

EnabledCORS

2yearsago

README.md

AppointsApi
AppointsApiisasimpleexampleappointmentschedulerRESTAPIbuiltwithNode.js,Express4andMongodb.Aclientthat
consumesthisAPIcanbefoundathttps://github.com/martijnboland/appointsclient.

Features
Create,view,updateanddeleteappointments
Tokenbasedauthenticationwith3rdpartyproviders(Facebook&Google)
HypermediaAPI(HAL,seehttp://stateless.co/hal_specification.html)
Fullintegrationtestsuite

Gettingstarted
1. Node.jsneedstobeinstalledonyourmachine
2. MakesureyouhaveaccesstoaMongoDBinstance,eitheronyourlocalmachineorsomewhereinthecloud.Thetest
suiteisconfiguredbydefaulttousealocaldatabasewiththename'appointstest'

3. Clonetherepository:

gitclonehttps://github.com/martijnboland/appointsapinode.git

4. Installpackages:

npminstall

5. Createaconfigfileforthedevelopmentenvironmentbycopying/config/example.jsto/config/development.js.

6. Runtheserver(defaultonhttp://localhost:3000/):

nodeindex.js

Youcanruntheintegrationtestsuitewith:

npmtest(*nix,MacOSX)
runtests.bat(Windows)

Usage
Whentheserverisrunninglocally,youcantrytheapiwithabrowser,curloranAPItestingtoollikePostman.Ifyoudon't
havealocalserverrunning,youcantrytheAPIathttps://appointsapi.azurewebsites.net/insteadofhttp://localhost:3000/.

Thedefaultresponsecontenttypeisapplication/hal+json.It'salsopossibletorequestapplication/jsonbyaddingthe

Accept:application/jsonheadertotherequest.POST,PUTandPATCHrequestsneedtohavetheircontenttypesetto
application/json.
StartwithGEThttp://localhost:3000/:

{
"message":"AppointsserviceAPI",
"details":"ThisisaRESTapiwhereyoucanscheduleappointmentsfor<insertbusinesshere>",
"_links":{
"self":{"href":"/"},
"me":{"href":"/me"},
"appointments":{"href":"/appointments"}
}
}

Followingthelinks,youcansee2otherresources:/meand/appointments.Let'sgoto/meandseewhathappens:

{
"message":"Accessto/meisnotallowed.",
"details":"NoAuthorizationheaderwasfound.FormatisAuthorization:Bearer[token]",
"_links":{
"auth_facebook":{"href":"/auth/facebook"},
"auth_google":{"href":"/auth/google"}
}
}

Alright,sowe'renotsupposedtoviewtheresourceunauthenticatedandweneedtosupplyanauthorizationtoken.Howcan
wegetatoken?Perhapsfollowoneofthelinks?We'regoingtotrytheGoogleroute:http://localhost:3000/auth/google.At
thispointtherearetwooptions:

1. DoaGETrequesttohttp://localhost:3000/auth/googlethatwillredirecttotheGoogleauthentication/authorization
pagethatredirectsbacktoourAPIaftersuccessfulauthorization.Afterthis,wegenerateourownJWTtokenand
redirectagainwiththeaccess_tokeninthehash:http://localhost/auth/loggedin#access_token=eyJ0eXAiOiJK....From
here,thingsareunfortunatelyabithackybecausethisistheonlyplacewheretheAPIdoesn'treturnaniceJSON
responsebutHTMLwithascriptthatpoststheaccess_tokenbacktotheopenerwindow:

if(window.opener){window.opener.postMessage(window.location.hash.replace("#access_token=",""),"*");}

Thistofacilitatebrowserclientsfromotherdomainsthatuseapopupbrowserwindowfortheauthenticationflowbutcan
notaccesstheaccess_tokenhashduetocrossdomainrestrictions.WithpostMessage()it'spossibletosendvalues
betweenbrowserwindowsanddifferentdomains.

2. DoaPOSTrequesttohttp://localhost:3000/auth/googlewithanalreadyobtainedtokenviasomeclientAPI.This
generatesourauthorizationtoken:

{
"message":"Authenticationsuccessful",
"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjUzN2Y2Yjk4MzFhNTEyYWJjY2Q1OGE5OCIsImVtYWlsIjoibWFydGlqbmJvbGF
"_links":{
"self":{"href":"/"},
"me":{"href":"/me"},
"appointments":{"href":"/appointments"}
}
}

Ifthingswentalright,wenowhavetheauthorizationtoken.Trythe/melinkagainbutnowwiththeauthorizationheader
HTTPHEADERset:

Authorization:BearereyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjUzN2Y2Yjk4MzFhNTEyYWJjY2Q1OGE5OCIsImVtYWlsIjoibWFydGlqbmJ

whichreturns:

{
"_links":{
"self":{"href":"/users/537f6b9831a512abccd58a98"}
},
"userId":"89928749324",
"provider":"google",
"email":"someone@gmail.com",
"displayName":"Agoogleuser",
"roles":["customer"]
}

Lookinggood!Wherecanwegonext?Westillhaveonelinklefttotry:/appointments.Herewecanmanageour
appointments(create,view,updateanddeletewiththestandardGET,POST,PUT,PATCHandDELETEHTTPverbs).Let's
createanewappointmentbyPOSTingto/appointments(withtheauthorizationheadersetproperly).Thedataforthe
appointmentis:

{
"title":"Freshhaircut",
"dateAndTime":"20140601T14:45:00.000Z",
"endDateAndTime":"20140601T15:15:00.000Z",
"duration":30,
"remarks":"Sameaslasttime"
}

TheresponsehasHTTPstatuscode201(created)andthebodycontainsthenewlycreatedappointment:

{
"_links":{
"self":{"href":"/appointments/53838715fd51be21ee42b7d4"},
"user":{
"href":"/users/537f6b9831a512abccd58a98",
"title":"Agoogleuser"
}
},
"id":"53838715fd51be21ee42b7d4",
"title":"Freshhaircut",

"dateAndTime":"20140601T14:45:00.000Z",
"endDateAndTime":"20140601T15:15:00.000Z",
"duration":30,
"remarks":"Sameaslasttime"
}

TheappointmentisstoredinthedatabaseandaGETrequestfor/appointmentsreturnsallourappointments:

{
"_links":{
"self":{"href":"/appointments"}
},
"_embedded":{
"appointment":[{
"_links":{
"self":{"href":"/appointments/53838715fd51be21ee42b7d4"},
"user":{
"href":"/users/537f6b9831a512abccd58a98",
"title":"Agoogleuser"
}
},
"id":"53838715fd51be21ee42b7d4",
"title":"Freshhaircut",
"dateAndTime":"20140601T14:45:00.000Z",
"endDateAndTime":"20140601T15:15:00.000Z",
"duration":30,
"remarks":"Sameaslasttime"
}]
},
"count":1
}

Existingappointmentscanbemodifiedordeletedat/appointments/:idwiththePUT,PATCHandDELETEverbs.Let'ssay
wewanttorescheduletheappointment.ThiscanbedonebysendingaPATCHrequestto

/appointments/53838715fd51be21ee42b7d4withthenewdateandtime:

{
"dateAndTime":"20140601T17:15:00.000Z",
"endDateAndTime":"20140601T17:45:00.000Z",
"remarks":"Sameaslasttime(rescheduledfrom14:45to17:15)"
}

ThisreturnsthemodifiedresourceasresponsewithHTTPstatuscode200:

{
"_links":{
"self":{"href":"/appointments/53838715fd51be21ee42b7d4"},
"user":{
"href":"/users/537f6b9831a512abccd58a98",
"title":"Agoogleuser"
}
},
"id":"53838715fd51be21ee42b7d4",
"title":"Freshhaircut",
"dateAndTime":"20140601T17:15:00.000Z",
"endDateAndTime":"20140601T17:45:00.000Z",
"duration":30,
"remarks":"Sameaslasttime(rescheduledfrom14:45to17:15)"
}

Securitywarning
Theauthorizationheadercontainssensitiveinformation.AlwaysuseSSLwhendeployinganAPIlikethisinaproduction
environment.

2016GitHub,Inc.

Terms

Privacy

Security

Contact

Help

Status

API

Training

Shop

Blog

About

You might also like