Book Image

Getting Started with hapi.js

Book Image

Getting Started with hapi.js

Overview of this book

This book will introduce hapi.js and walk you through the creation of your first working application using the out-of-the-box features hapi.js provides. Packed with real-world problems and examples, this book introduces some of the basic concepts of hapi.js and Node.js and takes you through the typical journey you'll face when developing an application. Starting with easier concepts such as routing requests, building APIs serving JSON, using templates to build websites and applications, and connecting databases, we then move on to more complex problems such as authentication, model validation, caching, and techniques for structuring your codebase to scale gracefully. You will also develop skills to ensure your application's reliability through testing, code coverage, and logging. By the end of this book, you'll be equipped with all the skills you need to build your first fully featured application. This book will be invaluable if you are investigating Node.js frameworks or planning on using hapi.js in your next project.
Table of Contents (15 chapters)
Getting Started with hapi.js
Credits
Foreword
About the Author
About the Reviewer
www.PacktPub.com
Preface
5
Securing Applications with Authentication and Authorization
Index

The hapi philosophy versus other frameworks


Throughout Walmart's journey of rebuilding their mobile services tier and working with other frameworks, they realized that certain values were vital to building applications in Node for keeping the code intuitive, maintainable, and scalable. As a result, hapi centered around the following concepts:

  • Time and effort should be spent on delivering value, not on rebuilding application infrastructure

  • Configuration is better than code

  • Business logic must be isolated from the transport layer

  • Open Source and community-centric from day one

Let me elaborate more on hapi and these points, the thought process behind them, and how they compare to other frameworks in Node.

Building value, not infrastructure

As I mentioned at the start of this chapter, hapi aimed to be a solution for the rapid development of RESTful APIs, but has since grown to be a framework for handling other aspects of a fullstack web application like templating, caching, and more. To describe it best, hapi is a unified framework that aims to solve the common problems with building web applications with out-of-the-box functionality so that you don't have to.

There are competing strategies' frameworks use to provide a boost in productivity. Some take the "all-in" and highly opinionated approach by providing large amounts of frameworks features, APIs, and code-generation tooling. Loopback (http://loopback.io/) by Strongloop is an example of this in the Node world, or Rails (http://rubyonrails.org/) if you are familiar with Ruby. While this offers an initial productivity gain, I'm not a fan of this approach, as these frameworks usually have quite a large API surface area, which means that you spend a lot of time learning the framework API instead of learning JavaScript or whatever the underlying technology is. It also means that if the framework doesn't account for your particular needs, you have to hack against it, and when something goes wrong, you're left debugging generated or framework code which you're unfamiliar with. Experts in these types of frameworks can be very productive, but I generally prefer approaches that involve more exposure to application code and structure, as I find it's one of the best foundations in which to grow as a developer.

In contrast to the all-in framework approach, is the minimalist framework approach, which usually aims to act more as a user-friendly API. express on the server side or Zepto.js (http://zeptojs.com/, a lightweight jQuery alternative) are examples of these. There are lots of advantages of these kind of frameworks, as they remove a lot of boilerplate such as accounting for different runtime environments or browser differences, and usually have less performance overhead as the library size is small.

The downside to these frameworks is that, for larger apps, it is generally up to the developer to create their own framework on top of these to break up the business logic of the application into smaller, more loosely coupled, and manageable parts. This is one of the more complicated parts of building non-trivial applications. It also leads to spending time rebuilding common features of an application which generally don't add much business value, for example authentication. It was this desire to spend time on business value instead of building application infrastructure and common application features that made me research the available Node frameworks, and thankfully, I found hapi.

hapi treads the line quite nicely between being minimalist and providing too much tooling. Some of the out-of-the-box functionality I mentioned earlier covers areas like authentication, caching, validation, and of course, plugins. The hapi plugin system also provides the perfect abstraction for divvying up your business logic into manageable pieces. It also makes it trivial to avail of plugins from the hapi core team and the wider ecosystem to provide the building blocks you need. It encourages a great level at which to encapsulate the code in a structured and obvious manner. This makes hapi code bases usually much easier to maintain and comprehend due to their plugin-centric approach. I will cover plugins and the out-of-the-box functionality that hapi offers in detail in later chapters.

Configuration is better than code

One of hapi's code design principles is centered around the idea that configuration is better than code. This is one of the concepts I disagreed with before using hapi.js. I remember the nightmare of configuring development environments in Java and PHP, and I thought this is where Node.js excels—avoiding config problems! However, I've found application/framework configuration done well as an excellent approach for a number of reasons.

For one, when done with smart, secure defaults, it helps developers avoid making unnecessary mistakes. You shouldn't be punished for not knowing some configuration setting, or for some non-obvious convention. The thought process behind this is that it is easier to detect missing/wrong configuration than missing code. This gives hapi.js a huge advantage in that it provides much more detailed error messages for anything that is misconfigured.

You'll find so many more errors with detailed error messages on startup instead of at runtime. This speeds up the feedback loop on the effect your added/changed code has, which makes developing applications much more enjoyable and satisfying. A great example of this is in-route configuration. Any mistake here will be listed on server start, with line numbers and a detailed explanation of what is wrong, instead of trying to debug why handlers are not being called, or being called in the wrong order. These types of runtime errors often throw no error at all, and are quite difficult to debug.

Another example of this is limiting the file upload size by default. This means when you release your application to production, file uploads won't buffer an entire file into memory crashing your server. With hapi, by default, the max upload size is limited, and an error message will be returned to the user, making it clear that the file size is too large. You, the developer, then have the choice to increase this and learn about the pitfalls of buffering files into memory, while your application is up returning error messages, and not while your application is in production, being repeatedly brought down by large file uploads.

Separation of business logic from the transport layer

The hapi team encountered a number of issues in their early attempts to build their mobile tier at Walmart with other frameworks, a number of which stemmed from a mix of business logic with a transport layer. These issues ranged from trivial to complex.

In the preceding Node.js example, we've seen that we have to set headers, status codes, and specify the end of the response. While this is easy to follow in the preceding example, this quickly leads to unnecessarily complex code when returning more complicated structures. For example, if you swap out 'hello world' with a normal JavaScript object, the server now fails to return anything, and even worse, you won't discover this until you test how the server is responding to requests when running.

A more complicated example of this is routing collisions. In express and most other Node.js frameworks, routing is non-deterministic, which means that you can have two routes that will both be called for a particular URL path. This, like the preceding issue, is a more prevalent issue in larger applications where there may be different teams working on different parts of a bigger application. This can be quite hard to test for it is not an error, and can usually only be tested for at request time, much like the aforementioned issue. Furthermore, the order in which they are called is decided by the order in which they are registered.

This means that developers have to be careful when registering routes, or they may accidentally register a handler that is called twice, or expose a certain route before an authentication handler, for example, causing security issues. Routing conflicts may seem easy to spot, but when there are a large number of routes, and they can be defined using regular expressions as the routing patterns, this can happen more easily than you think.

To combat this, the hapi team designed a deterministic routing algorithm ensuring that there can be no conflicting routes. To aid productivity and developer happiness here, conflicting routes will fail on server start with the initial run, providing a detailed error message and thus making it simple to debug and fix.

They also created a reply interface that accepts objects, node streams, strings, promises, and buffers. This interface detects the data type, and acts accordingly so that you don't have to do extra work for the common, yet different response types.

Eliminating the possibility of conflicting routes, and reducing areas where you can have no error with no response via the reply interface are great examples of where hapi does its best to increase developer productivity by making it easier to catch common errors that can normally be difficult to spot.

Open source and community-centric

With hapi, you don't just get a framework, you also join a welcoming and inclusive community that wants to help you learn and succeed. You will find that most questions are asked through GitHub instead of the usual Stack Overflow, and are answered quite frequently by other members of the community as well as the core team. The entire time I have been working with hapi, I have been continually impressed by the community—from how it deals with and communicates breaking changes, encourages newcomers, and in how people respond to issues on GitHub. If you are working with hapi, don't neglect this as a resource.

Like most frameworks these days, hapi has been open source from day one, with a full-time team supporting it. This makes it a great learning resource, and I encourage you to read through the source code at times. Its style guide and talented contributors have created a code base with great examples of clean and well-structured code.

As a result of such great community support and involvement, the open source focus and hapi's excellent plugin system, a large ecosystem of quality plugins has been created for hapi too. A website has also been created to navigate these at https://hapi-plugins.com/.

The encouraging and inclusive nature of the community meant that my first npm module was a hapi module, my first conference talks were about hapi, I eventually become a lead maintainer of one of the core library modules, and am now writing a book (this!) to share the positive experiences that I've encountered in developing with hapi and being a part of its community.

Ecosystem

The hapi core ecosystem was the first of its kind (that I'm aware of) to only use modules that had 100% code coverage, strictly adhered to SemVer (http://semver.org/), a specification for communicating breaking changes, and only depended on modules that adhered to the same criteria.

If you're unfamiliar with SemVer, and it's something I also encourage you to read about, it stands for semantic versioning. It's a method of versioning modules that is easier for humans to read and understand (as opposed to machines), which communicates clearly breaking changes. When done properly, this means you can update the modules that your application depends on, availing of new features, performance improvements, bug fixes, and so on, without worrying about breaking your application.

In contrast, most modules/plugins for other frameworks are created by third-party users, and hugely vary in quality. Many have poor (or no) test coverage, and may cover only one particular use case. This leads to module discovery being a problem where the productivity increase of availing the existing modules is offset by having to take time to search, read the source, write your own tests, and debug issues of third-party modules.

Thankfully, the core hapi modules not only cover a wide range of functionality, but also have set a high standard for third-party modules; this has lead to an ecosystem of high-quality plugins for hapi. You can view the list of core modules within the hapi GitHub organization at https://github.com/hapijs.

Small modules

I often hear the argument of using only small modules to build applications in the Node community. With modules that do only one thing and do that one thing well, it makes it quite easy to use many of these to create full-featured applications easily. While this is appealing over learning the full API of a framework, it leads to the problem where the onus is now on the developer to decide on an application structure, write a lot of unnecessary code and infrastructure, and make a lot of unnecessary decisions.

The structure hapi provides, along with the use of configuration over code, leads to features and clever tools not possible with more minimal frameworks, or with the module-only approach. Examples of these are documentation generation tools like lout from the core team and hapi-swagger from Glenn Jones, which we will take a look at in later chapters.

Summarizing hapi

To summarize this section, hapi is a unified framework that aims to solve most problems that you encounter when building web applications as opposed to more minimalist frameworks that focus on providing more user-friendly APIs and utilities. It is building a lot of momentum and adoption in recent times, and you can check out the many companies using hapi on the hapijs.com community webpage at http://hapijs.com/community. But enough of the background—let's take a look at what a sample hapi.js server looks like in the next section.