{"id":13467928,"url":"https://github.com/dwyl/learn-hapi","last_synced_at":"2025-12-17T20:05:05.007Z","repository":{"id":17549631,"uuid":"20352444","full_name":"dwyl/learn-hapi","owner":"dwyl","description":":sunny: Learn to use Hapi.js (Node.js) web framework to build scalable apps in less time","archived":false,"fork":false,"pushed_at":"2021-12-27T22:31:06.000Z","size":268,"stargazers_count":791,"open_issues_count":1,"forks_count":66,"subscribers_count":116,"default_branch":"master","last_synced_at":"2025-08-21T23:01:52.840Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwyl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-05-31T08:50:14.000Z","updated_at":"2025-06-30T03:51:46.000Z","dependencies_parsed_at":"2022-08-25T16:41:36.563Z","dependency_job_id":null,"html_url":"https://github.com/dwyl/learn-hapi","commit_stats":null,"previous_names":["nelsonic/learn-hapi"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dwyl/learn-hapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Flearn-hapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Flearn-hapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Flearn-hapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Flearn-hapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwyl","download_url":"https://codeload.github.com/dwyl/learn-hapi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Flearn-hapi/sbom","scorecard":{"id":361732,"data":{"date":"2025-08-11","repo":{"name":"github.com/dwyl/learn-hapi","commit":"a187a4f0f74ad5af9d1a1575d8da2aba194a19eb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.5,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T11:00:08.191Z","repository_id":17549631,"created_at":"2025-08-18T11:00:08.191Z","updated_at":"2025-08-18T11:00:08.191Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27786162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-17T02:00:08.291Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T15:01:02.704Z","updated_at":"2025-12-17T20:05:04.970Z","avatar_url":"https://github.com/dwyl.png","language":"HTML","funding_links":[],"categories":["HTML","Resources"],"sub_categories":["Tutorials"],"readme":"![Happiness Is...](https://i.imgur.com/Df5Z18T.jpg)\n\n[![Build Status](https://travis-ci.org/dwyl/learn-hapi.png?branch=master)](https://travis-ci.org/dwyl/learn-hapi)\n[![codecov.io test coverage](https://codecov.io/github/dwyl/learn-hapi/coverage.svg?branch=master)](https://codecov.io/github/dwyl/learn-hapi?branch=master)\n[![Code Climate](https://codeclimate.com/github/dwyl/learn-hapi.png)](https://codeclimate.com/github/dwyl/learn-hapi)\n[![Dependencies](https://david-dm.org/dwyl/learn-hapi.png?theme=shields.io)](https://david-dm.org/dwyl/learn-hapi)\n[![devDependencies Status](https://david-dm.org/dwyl/learn-hapi/dev-status.svg)](https://david-dm.org/dwyl/learn-hapi?type=dev)\n[![NPM Version][npm-image]][npm-url]\n[![HitCount](https://hits.dwyl.com/dwyl/learn-hapi.svg?style=flat-square)](https://hits.dwyl.com/dwyl/learn-hapi)\n\n# Learn Hapi\n\nHappiness is learning how to use the [**Hapi.js**](https://hapijs.com/) (Node.js) web framework to\n_**build reliable/scalable apps faster**_.\n\n## What is Hapi?\n\nHapi is *the* framework for rapidly building RESTful \u0026 Real-Time web applications and services with Node.js. \u003cbr /\u003e\nWhether you are building a very simple API\nfor your website/mobile app or a large scale, cache heavy,\nsecure e-commerce website, hapi has you covered.\nHapi will help get your server developed quickly with its wide range\nof configurable options.\n\n### *Watch* this intro/background to Hapi video:\n\n[![What is Hapi?](https://i.imgur.com/sZRoxdD.png)](https://youtu.be/BsyvnVOhp4U?t=3m50s \"What is Hapi.js - Click to Watch!\")\n\n*Most* people/teams that have _tried_ Hapi have _embraced_ Hapi to build *complete* web applications. But if you are only building a REST API (_e.g. for a mobile app_)\nplease read:\nhttps://github.com/dwyl/learn-api-design\n\n## _Why_ Hapi instead of XYZ framework?\n\n**Q**: I already know how to build REST APIs in `{framework-xyz}` why learn a *new* framework?  \u003cbr /\u003e\n**A**: If you are *happy* with your existing system \u0026 level of team productivity,\nstick with what you know. If not, learn [how to be] Hapi.\n(We have built Sites/APIs with both Express, Restify, Sails \u0026 Meteor and find Hapi has solved more\n\"real world\" problems and thus we end up writing less code. YMMV. See benefits below)\n\n**Q**: Hapi looks like quite a steep learning curve,\nhow long will it take me to learn? \u003cbr /\u003e\n**A**: You can get started *immediately* with the examples below,\nit will take _approximately **60 mins** to complete_ them all (after that add a couple of hours to read/learn further).\nThe most important part is to ***try Hapi*** on a simple project to gain experience/confidence.\n\n### Key Benefits\n\n- ***Performance*** - WalmartLabs are the guys who found/solved the\n[Node.js *CORE* Memory Leak](https://www.joyent.com/blog/walmart-node-js-memory-leak);\nthey have developed Hapi following\n[Benchmark Driven Development](https://github.com/felixge/faster-than-c)\nand the result is a high-performance framework\n+ ***Security*** - The *Lead* Developer of Hapi is [**Eran Hammer**](https://github.com/hueniverse) who was one of the original authors\nof the OAuth (Secure Authentication) Spec. He has built a security-focussed\nmindset into Hapi and reviews all code included in Hapi. Several members of the [Node Security Project](https://nodesecurity.io) are *core* contributors to\nHapi which means there are many security-minded eyes on the code.\n- ***Scalability*** - they have focussed on *horizontal-scalability*\nand battle-tested the framework during [Black Friday](https://nodeup.com/fiftysix)\n(*holiday shopping busy day*) without incident.\n- **Mobile Optimised** (lightweight - built for mobile e-commerce)\n- **Plugin Architecture** - extend/add your own modules (good ecosystem)\n- ***DevOps Friendly*** - configuration based deployment and great stats/logging see: [#logging with good](https://github.com/dwyl/learn-hapi#logging-with-good) section below!\n- Built-in ***Caching*** (Redis, MongoDB or Memcached)\n- ***100% Test/Code Coverage*** (for the core) - *disciplined approach to code quality*\n+ ***Testability*** - End-to-End testing is ***built-in*** to Hapi because\nit *includes* [**shot**](https://github.com/hapijs/shot)\n- **Key Functionality** is **Built-in** and there are *many* plugins for other\nfeatures: https://hapijs.com/plugins\n\n### _In-depth Comparison_ to Express.js\n\n@ethanmick wrote a detailed post on why _he_ prefers Hapi to Express:\nhttps://www.ethanmick.com/why-i-like-hapi-more-than-express/ --its worth a read.\n[PDF](https://github.com/dwyl/learn-hapi/files/502449/Why-I-like-Hapi-more-than-Express.pdf)\n\n### _Beginner Friendly_ Examples/Apps to Learn From/With\n\nWe have a few \"_beginner_\" example apps (with documentation \u0026 tests!)\nthat will help you get started with something a bit more \"real world\":\n\n+ Registration \u0026 Login (Basics): https://github.com/dwyl/hapi-login-example-postgres\n+ Chat using Hapi, Redis \u0026 Socket.io: https://github.com/dwyl/hapi-socketio-redis-chat-example\n\nFor a _list_ of examples see: https://github.com/dwyl?\u0026query=example\n\n\n## Who (_is using Hapi_) ? [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/learn-hapi/issues)\n\nThe list of teams using Hapi.js to build their node.js apps grows every day!\nSee: https://hapijs.com/community\n\n\u003e While you should _not_ make your decisions to use a given technology\nbased on who _else_ is using it, you should be _aware_ that\nand if you need to answer the **question**:\n\"***Who is already using this in Production?***\"\nit's _really_ useful to have a good list.\n\n\n## _How?!_ (_Dive In_!)\n\n## Requirements\n\n- [x] A **computer** that can run [**Node.js**](https://nodejs.org/download/)  Mac/Windows/Linux/Chromebook\n- [x] Access to the Internet (only required for installation)\n- [x] 60 minutes of time +/-\n\n### Optional\n\n(_Not essential before you start, however_) You will _benefit_ from having:\n\n+ [x] Basic JavaScript knowledge\n+ [x] Basic experience of using node.js's `http` module.\n\n## Make Me Hapi (\"_Official_\" _Beginner Workshop_)\n\nFirst thing you should do to get familiar with Hapi is work through the\n[makemehapi](https://nodeschool.io/#makemehapi) workshop. \u003cbr /\u003e\n(_assumes some [node.js](https://nodeschool.io/#learn-you-node) prior\nknowledge but otherwise a gentle self-paced introduction_)\n\n_Note: makemehapi currently uses Hapi v16. Some major changes were introduced to Hapi in v17. [Differences between v16 and v17](#hapi-v16)_\n\nCreate a new folder on your local machine for your answers to **makemehapi**:\n\n```\nmkdir makemehapi \u0026\u0026 cd makemehapi\n```\n\nInstall the workshop:\n\n```\nnpm install -g makemehapi@latest\n```\n( if it fails to install see: https://stackoverflow.com/questions/16151018/npm-throws-error-without-sudo )\n\nOnce its installed, start the tutorial with the following command:\n```\nmakemehapi\n```\n\n_Try_ to complete all challenges.\n\n![makemehapi complete](https://i.imgur.com/luXMDmg.png)\n\nIf you get *stuck*, you can either _google_ for the specific error you are\nseeing or if you are not \"getting\" it, you can always look at my answers in the /**makemehapi** directory of this repository ***or***  \n_the_ \"official\" solutions\nin the **/makemehapi/exercises/{exercise-name}/solution** directory  \ne.g: https://github.com/hapijs/makemehapi/tree/master/exercises/hello_hapi/solution  \n\nor if you still don't get it, _**ask us**_:\nhttps://github.com/dwyl/learn-hapi/issues\n\n\u003chr /\u003e\n\n## _Extended_ Examples\n\nFor the rest of the tutorial we will cover the various\nplugins and features we have used in the Hapi.js ecosystem\nthat will help you getting up-and-running with Hapi!\n\n### Recap: Hello World in Hapi\n\nOnce you have completed the **makemehapi** workshop,\non your computer, create a new directory called \"**hapiapp**\". e.g:\n\n```sh\nmkdir hapiapp \u0026\u0026 cd hapiapp\n```\n\nType out (or copy-paste) this code into a file called **index.js**\n\n```js\nconst Hapi = require('hapi');\nconst server = new Hapi.Server({port: 3000}); // tell hapi which TCP Port to \"listen\" on\n\nserver.route({\n\tmethod: 'GET',        // define the method this route will handle\n\tpath: '/{yourname*}', // this is how you capture route parameters in Hapi\n\thandler: function(req, h) { // request handler method\n\t\treturn 'Hello ' + req.params.yourname + '!'; // reply with text.\n\t}\n});\n\nasync function startServer() {\n  await server.start() // start the Hapi server on your localhost\n  console.log('Now Visit: http://localhost:' + server.info.port + '/YOURNAME');\n}\n\nstartServer();\n\nmodule.exports = server;\n```\nInstall Hapi:\n```\nnpm init -y \u0026\u0026 npm install hapi --save\n```\nRun:\n```\nnode .\n```\n\nVisit: http://localhost:3000/YOURNAME (in your browser)\nyou should see something like:\n\n![hello world in hapi](https://i.imgur.com/m9qcs17.png)\n\n\n### Validation with Joi\n\n**Validation** is a fancy way of saying \"checking\" a value is\nthe **type** / **format** and **length** you expect it to be.\n\ne.g. imagine you ask people to input their phone number\nand some joker enters letters instead of numbers. The validation\nwill display a message to the person informing the data is incorrect.\n\n[**Joi**](https://github.com/hapijs/joi) is the validation library built by\nthe same team as Hapi.\nMost people use Joi with Hapi, but given that it is a separate\nmodule, plenty of people use Joi independently;\nits well worth checking it out!\n\nAn example:\nType out (or copy-paste) this code into a file called **hellovalidate.js**\n\n```js\n// Start this app from your command line with: node hellovalidate.js\n// then visit: http://localhost:3000/YOURNAME\n\nconst Hapi = require('hapi'),\n    Joi  = require('joi');\n\nconst server = new Hapi.Server({ port: 3000 });\n\nserver.route({\n\tmethod: 'GET',\n\tpath: '/{yourname*}',\n\tconfig: {  // validate will ensure YOURNAME is valid before replying to your request\n\t\tvalidate: {\n\t\t\tparams: {\n\t\t\t\tyourname: Joi.string().min(2).max(40).alphanum().required()\n\t\t\t}\n\t\t},\n\t\thandler: function (req, h) {\n\t\t\treturn 'Hello '+ req.params.yourname + '!';\n\t\t}\n\t}\n});\n\nasync function startServer() {\n  await server.start(); // start the Hapi server on your localhost\n  console.log('Now Visit: http://localhost:' + server.info.port + '/YOURNAME');\n}\n\nstartServer();\n```\n\nNow try entering an _invalid_ name: http://localhost:3000/T  \nYou should see a **Validation Error**:\n\n![Hapi Joi validation error](https://i.imgur.com/Dyhel2V.png)\n\nThis might not _look_ like a \"Friendly\" Error message.\nBut as we will see later, it provides all the information we need\nin our Client/App and we can display a more user-friendly error to people.\n\n[Joi](https://github.com/hapijs/joi) has many more useful validation methods.\nWe will use a few of them later on when we build our example app.\n\n+ Detailed example: https://github.com/hapijs/joi#example\nand https://vawks.com/blog/2014/03/22/the-joi-of-validation/\n+ Want _friendly_ error messages in your web app?\nsee: [https://github.com/dwyl/**hapi-error**](https://github.com/dwyl/hapi-error)\n\n### Testing with Lab\n\nIf you're _new_ to Test Driven Development (**TDD**) read\nour ***Beginners' TTD Tutorial***:\n[https://github.com/dwyl/**learn-tdd**](https://github.com/dwyl/learn-tdd) (_first_)  \nand then come _back_ to this tutorial!\n\nIf you've done functional or unit testing in previous\nprogramming projects you will be at home with Lab.\n\nLab borrows *heavily* from [Mocha](https://github.com/mochajs/mocha),\nso if you followed our\n[learn-mocha](https://github.com/dwyl/learn-mocha) tutorial this should all be familiar.\n\n(Using the code we wrote above in the **Validation with Joi** section with a minor addition)\nAn example of testing with Lab:\n\n```js\nconst Lab = require(\"lab\");           // load Lab module\nconst lab = exports.lab = Lab.script(); //export test script\nconst Code = require(\"code\");\t\t //assertion library\nconst server = require(\"../examples/hellovalidate.js\");\n\nlab.experiment(\"Basic HTTP Tests\", function() {\n\t// tests\n\tlab.test(\"GET /{yourname*} (endpoint test)\", async function() {\n\t\tconst options = {\n\t\t\tmethod: \"GET\",\n\t\t\turl: \"/Timmy\"\n\t\t};\n\t\t// server.inject lets you simulate an http request\n\t\tconst response = await server.inject(options);\n\t\tCode.expect(response.statusCode).to.equal(200);  //  Expect http response status code to be 200 (\"Ok\")\n\t\tCode.expect(response.result).to.have.length(12); // Expect result to be \"Hello Timmy!\" (12 chars long)\n\t\tawait server.stop();\n\t});\n});\n```\nFirst we create a *test suite* for our test **Lab.experiment**\n(the _first argument_ is the name of the test suite \"Basic HTTP Tests\")\n\nNext is a basic test that calls the only route we have `/{yourname}`\nin this case **GET /Timmy**.  \nWe expect to receive a **200** http status code and the response body to be\nthe text \"Hello Timmy!\".\n\n1. Create a **new directory** in your project called **test**\n2. Create a **new file** called **test.js** in the **./test** dir\n3. Type out or copy-paste the above code into **test.js**\n4. Open your package.json file\n5. Add a **scripts** section to the package.json file with the following:\n```\n  \"scripts\": {\n    \"test\": \"lab -c\"\n  }\n```\n6. Save the package.json file\n7. run the **npm test** script from your command line to execute the tests\n\nThe result should look something like this:\n\n\u003cimg width=\"287\" alt=\"Hapi testing with Lab 100% coverage\" src=\"https://cloud.githubusercontent.com/assets/4185328/10119715/6232f530-6495-11e5-86ef-17d2bd61795a.png\"\u003e\n\nNote how the test script has a `-c` (_coverage_) flag above\nthis give us the **code coverage**.\n\nWe have **100% code coverage** so we can move on to our next test/feature!\n\n\u003e How do you think we would write a test for an error?\n\u003e (hint: have a look inside ./test/test.js and see the second test :)\n\n### Note on Testing: Tape is _Simpler_ than Lab+Code\n\n\u003e *While* ***Lab*** *is really* ***Good*** *and is the \"official\" testing\nframework used by Hapi*, *we* ***prefer***  \n*the* ***simplicity***\n\u003e *of* [***tape***](https://github.com/substack/tape);\n\u003e we find our tests are simpler to write/read/understand. #YMMV\n\u003e Also we *prefer* to use a *separate* \u0026 *specialised* module for tracking\ntest coverage: [istanbul](https://github.com/dwyl/learn-istanbul)\nwhich we find does a [better job](https://github.com/hapijs/lab/issues/401) at tracking coverage...\n\nThe preceding `Lab` test can be re-written (*simplified*) in `Tape` as:\n\n```js\nconst test   = require('tape');\nconst server = require(\"../index.js\"); // our index.js from above\n\ntest(\"Basic HTTP Tests - GET /{yourname*}\", async function(t) { // t\n\tconst options = {\n\t\tmethod: \"GET\",\n\t\turl: \"/Timmy\"\n\t};\n\t// server.inject lets you similate an http request\n\tconst response = await server.inject(options);\n\tt.equal(response.statusCode, 200);  //  Expect http response status code to be 200 (\"Ok\")\n\tt.equal(response.result.length, 12); // Expect result to be \"Hello Timmy!\" (12 chars long)\n\tawait server.stop();\n\tt.end();  // t.end() is required to end the test in tape\n});\n```\nThese tests are *functionally equivalent* in that they test *exactly* the\nsame *outcome*. Decide for yourself which one you prefer for readability\nand maintainability in your projects.  \nFor our **Tape Tutorial** see: https://github.com/dwyl/learn-tape\n\n\n#### Related Links\n\n- Lab github module: https://github.com/hapijs/lab\n- Is TDD Dead? https://www.youtube.com/watch?v=z9quxZsLcfo (hint: no!)\n- Getting Started with HapiJS and Testing: https://blog.abcedmindedness.com/2014/10/getting-started-with-hapijs-and-testing.html (on hapi v8.0)\n\n## Continuous Integration\n\nMaking sure your code is working as you expect it to (over time).\n\n### Integrating Hapi with Travis CI\n\nIf you are new to Travis-CI or need a refresher see:  https://github.com/dwyl/learn-travis  \n\nWe have Travis-CI enabled for all our hapi.js based projects:\n+ https://github.com/dwyl/hapi-socketio-redis-chat-example\n+ https://github.com/dwyl/hapi-auth-jwt2\n+ https://github.com/dwyl/time\n+ https://github.com/dwyl/api\n\nSo if you need an example to follow, check out our repos!  \nAnd as always, if you have _any questions, **ask**_!\n\n### Error Handling with Boom\n\n[Boom](https://github.com/hapijs/boom) makes custom errors easier in Hapi.\nImagine you have a page or item of content (photo, message, etc.) that\nyou want to protect from public view (only show to someone who is logged in).\n\nFirst **install boom**:\n\n`npm install boom --save`\n\nNext write another test in ./test/**test.js**\n(If you aren't used to \"Test First\" - ***trust*** the process...)\n\n```js\nlab.experiment(\"Authentication Required to View Photo\", function() {\n    // tests\n    lab.test(\"Deny view of photo if unauthenticated /photo/{id*} \", async function() {\n\t    const options = {\n\t        method: \"GET\",\n\t        url: \"/photo/8795\"\n\t    };\n\t \t// server.inject lets you simulate an http request\n\t    const response = await server.inject(options);\n      Code.expect(response.statusCode).to.equal(401);  //  Expect http response status code to be 200 (\"Ok\")\n      Code.expect(response.result.message).to.equal(\"Please log-in to see that\"); // (Don't hard-code error messages)\n\t});\n});\n```\n\nWhen you run `npm test` you will see a fail:\n\n\u003cimg width=\"809\" alt=\"Hapi auth test fail\" src=\"https://cloud.githubusercontent.com/assets/4185328/10119711/21c84fc2-6495-11e5-8d51-c6df5181e4f4.png\"\u003e\n\nNext we want to make this test pass and we'll use Boom to get our custom error message.\n\nThe wrong way of doing this is to explicitly hard-code the response for this route.\nThe right way is to create a generic route which responds to any request for a photo with any id.\nAnd since we don't currently have any authentication set up, we ***mock*** (fake) it.\n(Don't worry we will get to the authentication in the next step...)\n\n```js\nconst Boom = require('boom');\nserver.route({\n  method: 'GET',\n  path: '/photo/{id*}',\n  config: {  // validate will ensure `id` is valid before replying to your request\n    validate: { params: { id: Joi.string().max(40).min(2).alphanum() } },\n    handler: function (req, h) {\n        // until we implement authentication we are simply returning a 401:\n        return Boom.unauthorized('Please log-in to see that');\n        // the key here is our use of the Boom.unauthorised method\n    }\n  }\n});\n```\n\nOur test passes but the point was to show that returning errors\nwith specific messages is *easy* with **Boom**.\n\n\u003cimg width=\"504\" alt=\"learn-hapi-clearer-boom-message\" src=\"https://cloud.githubusercontent.com/assets/4185328/10119795/474a8504-6499-11e5-9833-76a4f0fb3818.png\"\u003e\n\nHave a look at https://github.com/hapijs/boom for more error response options.\nWe will be using these later as we build our app.\nLet's move on to authentication.\n\n\u003e For a more _user-friendly_ approach to error-handling see: https://github.com/dwyl/hapi-error\n\n\n### Logging with `good`\n\nApplication logging can often be an _afterthought_ developers only implement\n_after_ they have a production bug which is crashing their API/App and\nthey are scrambling to try and \"debug\" it.\n\nThankfully, it Hapi has first-class support for logging with the `good` module.\n\nWe have written a little example you can use to get started:\n[examples/hellogood.js](https://github.com/dwyl/learn-hapi/blob/master/examples/hellogood.js)\n\nRun it locally with `node examples/hellogood.js` then visit http://localhost:3000/hello/yourname in your browser.\n\n_Note: Good is not yet compatible with Hapi 17, so this code will only run if you are using v16. [See here for more details](#hapi-v16)_\n\nYou should expect to see something like this:\n![learn-hapi-good-log-two-ops](https://cloud.githubusercontent.com/assets/194400/18990153/051440e8-8708-11e6-9337-bcc2ab067853.png)\n\nThere are good examples including logging use http (e.g. to a 3rd party logging tool)\nin the Good repo: https://github.com/hapijs/good/tree/master/examples\n\nAgain, if you have _any_ questions, [_ask_](https://github.com/dwyl/learn-hapi/issues)\n\n\n### Authentication\n\nAuthentication is the process of determining whether someone\nor something is, in fact, who or what it is declared to be.\n\nAuthentication (or \"Auth\") is something many *novice* (*naive*?)\ndevelopers attempt to write themselves. (I was once that kid...\ntrust me, we have *bigger fish to fry*, use a well-written/tested library!)\n\nWe have 4 options:\n\n1. Google - If you are building an \"enterprise\" or \"education\" app\nwhich you know will be used in Google-enabled companies/schools we\nwrote a Hapi plugin: https://github.com/dwyl/hapi-auth-google which\nlets you include Google Login in your app in a few clear steps. The plugin uses the [***Official Google Node.js API Client***](https://github.com/google/google-api-nodejs-client) and is\nwritten to be as readable as possible for complete beginners.\n2. EveryAuth - Specific to Connect/Express apps: https://github.com/bnoguchi/everyauth\n3. [Passport.js](https://github.com/jaredhanson/passport) - ***100% Code Coverage*** and *many* excellent integrations https://passportjs.org/guide/providers/\n4. Bell - the 3rd party Login Hapi.js Plugin is *good* however we found it\n*lacking in documentation/usage examples*, which is why we wrote\nour own (*simpler*) Auth Plugin *specific* to our projects.\nsee: [https://github.com/**dwyl**?query=**auth**](https://github.com/dwyl?utf8=%E2%9C%93\u0026query=auth)\n\n\n#### Bell\n\nThe go-to solution for 3rd party authentication in hapi is bell: https://github.com/hapijs/bell.\nThere are a few good examples in the repo: https://github.com/hapijs/bell/tree/master/examples.\n\n\n### Caching with Catbox\n\nMost apps don't _need_ caching from \"Day 1\"\n(_because you don't **know** upfront where your app's bottlenecks are going to be..._).\n\nBut, once again, the team that brought you Hapi.js have _solved_ the problem of caching,\nsee: https://github.com/hapijs/catbox/ and https://hapijs.com/tutorials/caching\n\u003e We use Redis for blazing fast application and data caching.\nHapi.js Catbox makes this very easy!\n\n\n\n### Using Socket.io with Hapi for Real-Time Apps\n\nUsing Socket.io with Hapi.js could _not_ be easier!\nTo help you get started we've built a fully-functional chat application with tests (now [featured on the hapijs.com Resources page](https://hapijs.com/resources#Tutorials)),\nwhich demonstrates the power of Real-Time data-synching in your apps.\n\n\u003e https://github.com/dwyl/hapi-socketio-redis-chat-example\n\n## Hapi v16\nThere were some major changes introduced to Hapi when version 17 was released. For a full list, see [the version 17 release notes](https://github.com/hapijs/hapi/issues/3658), but here are the major differences relevant to this guide:\n* Callbacks replaced with `async` functions. This means that instead of passing a callback to the function, and having that called when the function is finished, hapi functions return a promise that can either be resolved, or called synchronously with `await`. For more on `async` functions, see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) and [this guide](https://ponyfoo.com/articles/understanding-javascript-async-await)\n* `server.connection()` replaced with options passed directly into the server when it's created. A small change, but important to update. Before, we created our server with:\n```js\n\tvar server = new Hapi.server();\n```\nand passed our options to:\n```js\n\tserver.connection({port: 8000});\n```\nNow, we just pass our options straight away, and no longer need to call the connection method:\n```js\n\tconst server = new Hapi.server({port: 8000});\n```\n* `reply()` interface replaced with a new lifecycle methods interface. You no longer have to call reply when sending a response from a handler. You can now just:\n```js\n\treturn \"your reply\";\n```\nAnd the reply parameter to your handler has been replaced with a response toolkit (h) containing helpers from hapi core and your plugins.\n\nNot all of the hapi plugins have been updated to work with v17 yet (For example [Bell](https://github.com/hapijs/bell/issues/330), and [Good](https://github.com/hapijs/good/issues/568)), so be careful if you decide to upgrade an existing project.\n\nThe previous version of this tutorial and code examples for Hapi 16 can be found here: https://github.com/dwyl/learn-hapi/tree/b58495ea002a9f3f8af8d183f6004d2b483f4591\n\n## Please Suggest Improvements! [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/learn-hapi/issues)\n\nIf you want to extend this tutorial or simply request additional sections,\nopen an issue on GitHub: https://github.com/dwyl/learn-hapi/issues\n\n\n## Background Reading / Watching\n\n- GitHub Repo: https://github.com/hapijs/hapi (has documentation)\n- An ecosystem of tools and best practices for the working hapijs developer (up-to-date with last version of hapi): https://hapipal.com/ https://hapipal.com/getting-started\n\n- Restify vs Express performance: https://stackoverflow.com/questions/17589178/why-should-i-use-restify\n- REST API in Express: https://pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose\n- Hapi API Reference: https://github.com/hapijs/hapi/blob/master/API.md\n\n\n### Video Intro\n\n- Hapi.js and why it's awesome: https://www.youtube.com/watch?v=ZI9wXL-add0\u0026t=2m5s\n- Hapi overview: https://www.youtube.com/watch?v=Recv7vR8ZlA (old version but concepts still relevant)\n\n### Tutorials\n\n- Hapi Boilerplate app: https://github.com/poeticninja/hapi-ninja [updated for hapi 8.0]\n- Building APIs with Hapi and MongoDB: https://speakerdeck.com/donnfelker/building-web-apis-with-hapi-dot-js-and-mongodb-mongoose\n- Repo for the above speakerdeck: https://github.com/donnfelker/hapi-mongodb-example\n- Micro-tutorial: https://github.com/hapijs/makemehapi\n- https://stackoverflow.com/questions/21455076/hapi-and-node-js-to-create-a-rest-api-server\n- Hapi + Twilio (sms): https://code.tutsplus.com/tutorials/creating-a-node-web-app-with-hapi-and-twilio-integration--cms-20769\n- Authentication: https://github.com/hapijs/hapi-auth-cookie\n- A few examples: https://github.com/andyroyle/hapi-examples\n- More examples: https://github.com/wpreul/hapikc (*very old* version of Hapi!)\n- BDD with Hapi and Lab: https://gist.github.com/thebillkidy/10a11fed1bf61d04c3c5 (*old* version of Hapi!)\n+ If you like React.js checkout Mullet.js (Hapi.js + React.js):\nhttps://mullet.io/ + https://github.com/lynnaloo/mullet\n+ If you have an *existing* ***Express*** App and are thinking of\nmigrating to Hapi, read: https://matt-harrison.com/moving-from-express-to-hapi-js/\n\nSelected StackOverflow Questions \u0026 Answers:\n- https://stackoverflow.com/questions/22934340/hapi-js-api-authentication  \nsee: https://stackoverflow.com/a/33877047/1148249 (*answer*)\n- https://stackoverflow.com/questions/22985392/how-do-you-make-a-hapi-js-plugin-module\nsee: https://stackoverflow.com/a/25135343/1148249 (*answer*)\n- https://stackoverflow.com/questions/18343509/hapi-js-with-socket-io-where-is-socket-io-js\nsee: https://stackoverflow.com/a/33876615/1148249 (*answer*\n\n\n[npm-image]: https://img.shields.io/npm/v/hapi.svg?style=flat\n[npm-url]: https://npmjs.org/package/learn-hapi\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Flearn-hapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwyl%2Flearn-hapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Flearn-hapi/lists"}