<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by hapi pal on Medium]]></title>
        <description><![CDATA[Stories by hapi pal on Medium]]></description>
        <link>https://medium.com/@hapipal?source=rss-9c3858d246bd------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*k93BZ5aleflEwdWZ1QXc7A.jpeg</url>
            <title>Stories by hapi pal on Medium</title>
            <link>https://medium.com/@hapipal?source=rss-9c3858d246bd------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 03 Apr 2026 23:30:57 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@hapipal/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Building real-world APIs with hapi pal]]></title>
            <link>https://medium.com/@hapipal/building-real-world-apis-with-hapi-pal-c0303fcef1c6?source=rss-9c3858d246bd------2</link>
            <guid isPermaLink="false">https://medium.com/p/c0303fcef1c6</guid>
            <category><![CDATA[hapijs]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[app-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[hapi pal]]></dc:creator>
            <pubDate>Fri, 01 Feb 2019 13:30:18 GMT</pubDate>
            <atom:updated>2019-02-01T13:30:18.941Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sd1Vr2UwSjvs6gyUmtVnlg.png" /></figure><p>The <a href="https://realworld.io">RealWorld project</a> is an ambitious effort to gather practical examples of a single web application, written in a wide variety of languages and frameworks. The application is called Conduit, which is essentially a clone of some of the core features of this very application (Medium!). The idea is this: RealWorld provides an interface spec and an API spec (i.e. frontend and backend), developers implement one spec without worrying about the implementation of the other, and then we find ourselves with several hundred unique “stacks” by pairing any frontend example with any backend example. Ever curious to see a <a href="https://github.com/gothinkster/vue-realworld-example-app">Vue frontend</a> talk to a <a href="https://github.com/gothinkster/rust-realworld-example-app">Rust backend</a>? <a href="https://github.com/gothinkster/aurelia-realworld-example-app">Aurelia</a> and <a href="https://github.com/gothinkster/rails-realworld-example-app">Rails</a>? <a href="https://github.com/gothinkster/dojo2-realworld-example-app">Dojo</a> and <a href="https://github.com/gothinkster/golang-gin-realworld-example-app">Go</a>? You get the picture: there are a ton of rich examples to explore.</p><p>We’ve recently added a new such example to the list by implementing the <a href="https://github.com/devinivy/hapipal-realworld-example-app">RealWorld API with hapi pal</a>: a combination of nodejs, the hapi web framework, Objection ORM, and SQLite, all under hapi pal’s methodology/toolset for building APIs. This article takes an in-depth look at the stack and the codebase used to implement the <a href="https://github.com/gothinkster/realworld/tree/master/api">RealWorld API spec</a>, which includes features such as:</p><ul><li>Users can sign-up and authenticate with JSON Web Tokens (JWTs).</li><li>Articles can be created by users, and later edited or deleted only by their owning user.</li><li>Articles may be favorited by users.</li><li>Tags may be attributed to articles, which can later be used in paginated article searches. These searches can additionally filter by articles favorited by or written by a given user.</li><li>Users may follow other users, and each user has a paginated feed of articles by their follows.</li><li>Users may comment on articles.</li><li>Articles must have unique slugs generated from their titles, while titles need not be unique.</li><li>There are particular requirements around response payload formatting and errors.</li></ul><p>And a closer look at the stack:</p><ul><li><a href="http://hapijs.com">hapi</a>, our nodejs web framework of choice for its stability, high-quality ecosystem and architecture, bright community, and security-mindedness.</li><li><a href="http://vincit.github.io/objection.js/">Objection</a>, our favorite ORM for its SQL-savviness, emphasis on queries over records, flexibility, top-notch maintenance, and helpful community.</li><li><a href="https://github.com/mapbox/node-sqlite3">SQLite</a>, an easy-to-install (via npm!), transactional, and relational SQL database—perfect for a widely-shareable project like this.</li><li>And of course <a href="https://hapipal.com/">hapi pal</a>, an ecosystem of tools and best practices for the hapi web framework. Pal primarily provides us our project structure and application architecture while letting hapi, Objection, and SQLite do what they do best. Beyond that it also gives us some great <a href="https://github.com/hapipal/hpal">scaffolding</a> and <a href="https://github.com/hapipal/hpal-debug">debugging tools</a>.</li></ul><h4>Let’s begin!</h4><p>With all that said, let’s take a look under the hood. We suggest following along with the codebase, which you can find right about… <a href="https://github.com/devinivy/hapipal-realworld-example-app">here</a>. You can also find it deployed as a tweakable, hackable live <a href="https://glitch.com/~hapipal-realworld">demo hosted on Glitch</a>. If you’re looking for a lighter, more guided introduction to hapi and hapi pal, please feel free to hop on over to <a href="https://hapipal.com/getting-started">our Getting Started tutorial</a>, which utilizes the exact same stack as this project.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fglitch.com%2Fembed%2F%23%21%2Fembed%2Fhapipal-realworld%3Fpath%3Dlib%2Froutes%2Fusers-login.js%26previewSize%3D0&amp;dntp=1&amp;url=https%3A%2F%2Fglitch.com%2Fembed%2F%23%21%2Fembed%2Fhapipal-realworld&amp;image=https%3A%2F%2Fglitch.com%2Fedit%2Fimages%2Flogos%2Fglitch%2Fsocial-card%402x.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=glitch" width="600" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/80acfcf3cb632158edc64db206d4420d/href">https://medium.com/media/80acfcf3cb632158edc64db206d4420d/href</a></iframe><h3>Directory structure</h3><p>The codebase is based upon the <a href="https://github.com/hapipal/boilerplate">pal boilerplate</a>, which splits-up projects into two top-level directories: <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib">lib/</a> and <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/server">server/</a>.</p><p>The lib/ directory contains all the core functionality of the application. Strictly speaking, it constitutes a <a href="https://hapijs.com/tutorials/plugins">hapi plugin</a>, which is a portable and well-encapsulated way to articulate a web service. The sub-directories under lib/ each define some pieces of the application: routes, models, services, other hapi plugins, etc. Most of the contents of lib/are picked-up automatically by pal&#39;s file-based hapi plugin composer <a href="https://github.com/hapipal/haute-couture">haute-couture</a>, and were scaffolded using the <a href="https://github.com/hapipal/hpal">hpal CLI</a>. Without haute-couture we would instead make many imperative calls to the hapi server interface; for example, we would call <a href="https://github.com/hapijs/hapi/tree/master/API.md#server.route()">server.route()</a> rather than creating a file in lib/routes/, <a href="https://github.com/hapijs/hapi/tree/master/API.md#server.auth.strategy()">server.auth.strategy()</a> rather than a file in lib/auth/strategies/, <a href="https://github.com/hapijs/hapi/tree/master/API.md#server.register()">server.register()</a> rather than a file in lib/plugins/, etc.</p><p>The server/ directory contains all configuration and code required to deploy the application. Given that lib/ exports a hapi plugin, server/ is primarily responsible to create a hapi server and register the app&#39;s plugin with some configuration provided by server/.env.</p><p>The reasoning behind this separation of lib/ and server/ is explained in detail in an article: <a href="https://hapipal.com/best-practices/server-plugin-separation">The joys of server / plugin separation</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f62cf17e2a6a467832132f5decba3e86/href">https://medium.com/media/f62cf17e2a6a467832132f5decba3e86/href</a></iframe><h3>The model layer</h3><p>This application’s model is based upon <a href="https://github.com/Vincit/objection.js">Objection ORM</a>, which is integrated into hapi using the <a href="https://github.com/hapipal/schwifty">schwifty</a> plugin. Each model lives in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/models">lib/models/</a> and corresponds to a particular table in the SQLite database: Users, Articles, Comments, and Tags.</p><p>Our model layer is very light. It represents a thin mapping between the application and the database, and enforces some basic rules related to data integrity: setting createdAt and updatedAt fields, computing an article&#39;s slug from its title, and validating column values when they are persisted to the database. The models are used to interface with the database via Objection&#39;s wonderfully expressive SQL query builder which extends <a href="http://knexjs.org/">knexjs</a>.</p><p>You will find that models are used exclusively within the service layer, which is detailed below.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1052fdf5fc2c43643bdb3c3e3f586843/href">https://medium.com/media/1052fdf5fc2c43643bdb3c3e3f586843/href</a></iframe><h3>The service layer</h3><p>The service layer represents a sort of “headless” interface to all the actions and means of fetching data within the application. You’ll find a service method that causes one user to follow another, another to fetch a user’s articles feed, etc. In this way our route handlers/controllers have a means of sharing common logic (“how do I get an article by its id?”) while hiding away the implementation details (e.g. details of the model) in a common library. The service layer is actually generic enough that it could also be re-used to write a different interface to the exact same data and actions, such as a CLI.</p><p>We endow our application with a service layer using the <a href="https://github.com/hapipal/schmervice">schmervice</a> hapi plugin. Alongside the plugin, schmervice also ships with a base service class that provides some useful and convenient functionality, such as access to the hapi server and application configuration (plugin options), integration with the server’s start/stop lifecycle, and the ability to leverage hapi’s robust system for persistent caching.</p><p>This application has three services: the <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/article.js">ArticleService</a>, the <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/user.js">UserService</a>, and the <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/display.js">DisplayService</a>, all in the <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services">lib/services/</a> directory. Each service is a class that extends schmervice&#39;s base class. The ArticleService comes with methods such as create(), findBySlug(), and addComment(); it provides an interface to articles, comments, tags, and favorites. The UserService comes with methods such as signup(), findByUsername(), and login(); it provides an interface to users, following, and authentication.</p><p>Lastly, the DisplayService is responsible for enriching and transforming user, article, comment, and tag models into objects transferred by the API endpoints. This allows us to defer to the ArticleService and UserService to worry about the details of fetching/searching articles and users in various ways—which are complex in their own right—without having to also be concerned with composing the data in these equally complex API responses. For example, the articles list (GET /articles) must be able to paginate while filtering by tag, author, or favorited status; then the API response must additionally include specially-formatted information about whether the author of each article is followed by the current user (if there&#39;s a logged-in user), and whether each article is favorited by the current user. In the RealWorld specification there are also multiple representations of some models; for example, a user presents differently when the current user is acting on or asking for information about themselves, versus a separate user (a.k.a. a &quot;profile&quot;). That&#39;s a lot of responsibility, so we decided to decouple fetching from enriching/formatting! Luckily, as you will see in the DisplayService, Objection&#39;s <a href="http://vincit.github.io/objection.js/#loadrelated">loadRelated()</a> feature is especially well-suited to this approach.</p><p>The final point of interest in the service layer is its convention for transactions. Objection’s handling of SQL transactions is very ergonomic. We take advantage of the fact that you may optionally specify a knex transaction object at query-time to any Objection query. By convention, each of our database-backed service methods take a transaction object as an optional final argument. That transaction object is simply passed down to any queries inside the method, and commits/rollbacks of a transaction are handled by the caller of the service method. In this way <em>any </em>database-backed service method may be composed into arbitrary transactions with other service methods without the caller having to understand the underlying queries being made. More on this in the next section on routes!</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd2df8ef395ecfec2408c0748d97c88c/href">https://medium.com/media/bd2df8ef395ecfec2408c0748d97c88c/href</a></iframe><h3>Routes</h3><p>At the end of the day, we do all this work so that we can create some routes, or API endpoints. Each route consists of a <a href="https://github.com/hapijs/hapi/blob/master/API.md#server.route()">hapi route configuration</a> placed as a file in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/routes">lib/routes/</a>. These configurations provide information about the matching HTTP method and path; validation of incoming query, path, and payload parameters; authentication; and a handler or controller implementing the logic behind the route.</p><p>Validation is specified using hapi’s robust <a href="https://github.com/hapijs/joi">joi</a> validation library, which is the same means of validation used by our model layer. Since the routes and models use the same means of validation, routes are able to refer to the model’s validation. For example, when a user logs-in the payload contains an email parameter that must be a valid email; in the route configuration we defer to the User model&#39;s definition of a valid email and mark it as a required field: <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/routes/users/signup.js#L16">User.field(&#39;email&#39;).required()</a>.</p><p>The route handlers themselves are relatively light. They generally compose payload, query, and path parameters, and the user’s authentication status into one or many calls into the service layer, then return a response. Handlers are also responsible for the transactional integrity of their calls into the service layer. For example, if a user makes requests in quick succession to favorite then unfavorite an article, each of those requests must come back reflecting the proper state: there should be no way for the request to unfavorite the article sneak its way in so that the request to favorite the article responds with favorited: false, or vice-versa. So, handlers will often generate a transaction object using a thin helper around <a href="http://vincit.github.io/objection.js/#transaction%5D">Objection.transaction()</a> (defined in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/bind.js">lib/bind.js</a>), then pass that transaction to the various service methods that it uses. As mentioned in the previous section, handlers typically end with a call to the DisplayService, whose sole purpose is to format and enrich information about the model (users, articles, comments, and tags) for API responses.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3b2dc1ed087105b9f52c45bf374d1bd3/href">https://medium.com/media/3b2dc1ed087105b9f52c45bf374d1bd3/href</a></iframe><h3>Authentication</h3><p>Per the RealWorld API spec, authentication occurs via signed JSON Web Tokens (JWTs). There are essentially two sides to this form of authentication:</p><ul><li>The application must hand-out a JWT to a user when that user provides a matching email and password.</li><li>The application must verify the authenticity and contents of the JWT when it is passed with future requests.</li></ul><p>In order to hand-out a JWT, we have a <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/routes/users/login.js">login endpoint</a> that performs the process described above by calling into the service layer. In particular, the <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/user.js">UserService</a> has a <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/user.js#L96">login()</a> method to lookup a user by their email and password, and a <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/services/user.js#L116">createToken()</a> method to create a JWT containing the user&#39;s id. Aside from the user id, createToken() also needs a &quot;secret key&quot; in order to sign the token. In our case, we obtain the secret from our application&#39;s plugin options (this.options.jwtKey), which the UserService has access to because it extends the <a href="https://github.com/hapipal/schmervice">schmervice</a> base class. The jwtKey plugin option is set using the APP_SECRET environment variable inside our app&#39;s deployment, configured within <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/server">server/</a>.</p><p>In order to verify the authenticity and contents of the JWTs passed with future requests, we utilize the <a href="https://github.com/dwyl/hapi-auth-jwt2">hapi-auth-jwt2</a> (registered via <a href="https://github.com/hapipal/haute-couture">haute-couture</a> in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/plugins">lib/plugins</a>). This plugin creates an &quot;auth scheme&quot; for JWTs which we configure into an auth strategy in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/auth/strategies/jwt.js">lib/auth/strategies/jwt.js</a>. The auth strategy determines the details underlying our JWT auth: tokens should be signed using a certain hashing algorithm, with a certain secret key (as described above); the token is further validated by looking-up the user whose id is stored on the token; etc. One the auth strategy is created in this way, it&#39;s trivial to protect an API endpoint with JWT authentication using hapi&#39;s <a href="https://github.com/hapijs/hapi/blob/master/API.md#route.options.auth">auth route configuration</a>, as can be seen on <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/routes/articles/delete.js#L16">the route for article deletion</a>.</p><h3>Error handling</h3><p>The RealWorld API Spec <a href="https://github.com/gothinkster/realworld/tree/master/api#errors-and-status-codes">is particular</a> about the format and HTTP codes that our application responds with. In order to meet those requirements we wrote a centralized hapi <a href="https://github.com/hapijs/hapi/blob/master/API.md#server.ext.args()">request extension</a>, which can be found in <a href="https://github.com/devinivy/hapipal-realworld-example-app/blob/master/lib/extensions/error.js">lib/extensions/error.js</a>. This request extension is a hook into hapi&#39;s <a href="https://github.com/hapijs/hapi/blob/master/API.md#request-lifecycle">request lifecycle</a> to process all responses right before the server responds. In hapi parlance this request extension point is called &quot;onPreResponse&quot;.</p><p>There are a few different types of errors that are encountered in the app and pass through this request extension. Whenever a route needs to express a standard HTTP error, its handler will throw a <a href="https://github.com/hapijs/boom">boom</a> error, which is standard in the hapi ecosystem. Other errors also may come from within the model layer (e.g. when a record is not found) or from a route’s request validation (these are already considered “400 Bad Request” boom errors). We interpret errors from the model with help from Objection’s <a href="https://github.com/Vincit/objection-db-errors">objection-db-errors</a> plugin — which normalizes database errors across the various flavors of SQL — and <a href="https://github.com/PixulHQ/avocat">avocat</a> which further transforms them into hapi’s preferred boom HTTP error objects; for example, a uniqueness violation may be transformed into a “409 Conflict” boom error. Once the error is interpreted as an HTTP error, the final step is to simply format them into the shape preferred by the RealWorld specification.</p><h3>Conclusion</h3><p>Well, that’s just about all we have for you for now! We hope that the deep-dive proved useful, and that you have some idea what a <a href="https://hapipal.com">hapi pal</a> codebase might look like out in the wild. We work on APIs like this every day, which is why we insist that pal is for the practicing, working developer looking for practical tooling. If you’re looking for more info, there’s a community of us who would love to chat with you! Please join us in the #hapipal channel in the <a href="https://join.slack.com/t/hapihour/shared_invite/enQtMjM5Njk1NDgzNTY5LThmODkxODIzOTg5NjJjODFiYjcxZDMxMTAyMzBkZDk3MWY4MTFlNDAyMTU3MmUwMmM0Y2UwMjU3YjAwYjRkN2E">hapi hour slack</a> and say hello 👋</p><p>Your pals,</p><p>Devin (<a href="https://github.com/devinivy">@devinivy</a>) and the <a href="https://github.com/orgs/hapipal/people">pal team</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wRwbkC0Xts9Wk6jczbkxJw.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c0303fcef1c6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A stand against the “easy” nodejs backend]]></title>
            <link>https://medium.com/@hapipal/a-stand-against-the-easy-nodejs-backend-f67f4c25e04c?source=rss-9c3858d246bd------2</link>
            <guid isPermaLink="false">https://medium.com/p/f67f4c25e04c</guid>
            <category><![CDATA[culture]]></category>
            <category><![CDATA[hapi]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[backend]]></category>
            <dc:creator><![CDATA[hapi pal]]></dc:creator>
            <pubDate>Tue, 19 Jun 2018 16:18:23 GMT</pubDate>
            <atom:updated>2018-06-19T18:00:24.148Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9d04QHrGQm1-yMtx" /><figcaption>☝Above, a pal takes a stand. Photo by <a href="https://unsplash.com/@paul_nic?utm_source=medium&amp;utm_medium=referral">Paolo Nicolello</a></figcaption></figure><p>On the homepage of <a href="https://hapipal.com">hapipal.com</a> we try to make a memorable first impression when we actively broach the topic we know you’re wondering about: “<em>is this tool going to make things easy for me?”</em></p><blockquote>Writing scalable web services is hard work, and we don’t think there’s any way around that.</blockquote><p>“Easy” isn’t exactly what we’re going for.</p><p>Now, that’s not to say that we think pal is hard to learn—quite the opposite! (As far as we can tell, folks find it delightful.) We just acknowledge that we have <em>probably</em> not solved all the hardest problems that you have in front of you as you embark on authoring your next web service. You’re going to have to deal with countless issues—sometimes massive cross-cutting concerns of your entire application—that the JS framework/tooling scene has a habit of casually grouping into broad terms: “performance” “security” “realtime” “business requirements”. And we take issue with calling any of this stuff easy.</p><p>In fact, we designed hapi pal in such a way that acknowledges the difficulties that lay ahead for you: basically, pal is prepared to get out of your way when it comes time for heavy-lifting. Let us explain!</p><h3>“Build a production-ready realtime backend in days”</h3><p>We’re used to seeing claims like this (👆) as theses for blog posts and tutorials, as hooks on framework homepages, in Twitter posts, in contentious Hacker News comments, etc. Whether intentionally or not, it is designed to play into our anxieties about choosing the right tool for the job, as we swirl around in a sometimes dizzying sea of JS tooling and frameworks.</p><blockquote>If I try this framework out and it’s taking me over a week to build my application, is it because I’m doing something wrong? <em>😓</em></blockquote><p>Rest assured, if your application is taking more than days to complete then it doesn’t necessarily mean there’s anything wrong with your technology choices or your approach. Depending on the inherent complexities of the problem you’re servicing, data consistency guarantees, security requirements of your business/country/industry, service-level agreements your API must uphold in the way of availability and responsiveness, your Ops budget, the number of anticipated concurrent users and magnitude of traffic influxes, the size of your team, etc. it would not be at all weird for a project to take months or longer <em>under any framework</em>. And if the term “production-ready” doesn’t encapsulate the laundry list of concerns above, what are we even talking about?</p><p>Now, if we all agree that your production-ready application oughtn’t be assumed to be easy to build, what are the technical ramifications for framework authors and tool builders? It all comes down to the abstractions that these libraries choose to introduce their users.</p><h3>Abstraction leaks</h3><p>The only thing worse than memory leaks are abstraction leaks. You know that feeling when you feel cornered by a “missing feature” of one of your project’s dependencies? When some library or reusable module feels like it carries existential weight with it wherever it shows-up? When the code that you write using a library seems so distant from its performance characteristics? That’s what a bad leaky abstraction feels like.</p><p>A common definition for this you’ll find <a href="https://en.wikipedia.org/wiki/Leaky_abstraction">on wikipedia</a>: “a leaky abstraction is an abstraction that exposes details and limitations of its underlying implementation to its users that should ideally be hidden away.” I would opt to amend that, because I think these leaks go both directions. If a library requires changes to its implementation in order for application code to circumvent its limitations, that’s a leak too. For example, how many times have you tried to use some particular MySQL or PostgreSQL feature only to find that you were completely barred from it until it’s implemented in your ORM of choice? That’s not an ORM with missing features—it’s an abstraction leak.</p><p>Yes, abstractions are beautiful. They’re also bold and sometimes fraught with hubris. After all, creating an abstraction is an assertion that you can solve multiple different problems at the same time. In a world of development where we all share problems that are similar but not quite identical, what does a healthy abstraction look like? Just how far ought framework and library authors go in attempting to solve others people’s problems?</p><p>There’s a balance to be struck here between having a tight, minimally-leaky abstraction that doesn’t offer much value, and a massive-but-leaky abstraction that provides great value only within its limits. Libraries that claim to make your life “easy” or “quick” have a tendency to end in one of these “massive-but-leaky” abstractions. And the reasoning there isn’t so controversial: optimizing for easiness implies feature richness over the ability to customize and fine-tune. Needless to say, we’ve sort of obsessed over this balancing act in authoring hapi pal, and we’ve learned a thing or two along the way.</p><h3>How pal takes a stand</h3><p>We designed hapi pal with a hyper-sensitivity to all that we acknowledge as “hard.” Sensitivity to all the problems that we could never anticipate, that <em>you</em> are inevitably going to encounter. As such, we have spent a massive amount of effort to ensure pal can get out of your way when the going gets tough. In the end it’s a bunch of small design decisions that we hope add-up to a flexible, minimally leaky experience. Here are a few examples of the many “escape hatches” built into pal,</p><ul><li><a href="https://github.com/hapipal/haute-couture"><strong>haute-couture</strong></a><strong> isn’t a hapi plugin itself.</strong> We want you to be able to use haute-couture to compose your hapi plugins from files, but also want you to feel free to write and run any other plugin code you may need—for example, if hapi introduces a new feature that haute-couture doesn’t immediately support. So we made sure that haute-couture can be used as a sub-routine of <em>any</em> plugin, and you can write all the custom code you need!</li><li><a href="https://github.com/Vincit/objection.js"><strong>Objection ORM</strong></a><strong> is really just a glorious SQL query builder.</strong> When deciding on a data access layer for ourselves, we wanted to break away from the massive abstractions ORMs traditionally introduce. Objection strikes a wonderful balance, where it focuses on top notch support for features of SQL data stores specifically (composing transparently with the <a href="http://knexjs.org/">knex</a> query builder) rather than attempting to create a generic interface for working with “records.” This allows us to drop down to the metal and work closely with all the <em>hard</em> stuff that we need like transactions, column types with specialized indexes (e.g. tsvectors, JSONB, and geospatial types), aggregations, join semantics, etc. Objection makes working with model/table relationships and these critical features of our RDBMS feel organic.</li><li><a href="https://github.com/hapipal/schwifty"><strong>schwifty</strong></a><strong> hooks-up models to db connections for you… unless you do it first.</strong> Schwifty is a hapi plugin integrating Objection ORM as a model layer for your application. It respects hapi plugin boundaries by letting you configure different db connections for your models on a per-plugin basis, e.g. to handle the case that you’re composing multiple pluginized hapi apps into a single deployment. We find this to be very flexible, but we also acknowledge that there may be more complex use-cases out there: what if someone has a single legacy users table that lives in a different db from all their other models? In that case, no sweat—schwifty is designed to get out of your way if it sees you manually hooking-up db connections to a given model.</li><li><strong>the contents of scaffolding files made by the </strong><a href="https://github.com/hapipal/hpal"><strong>hpal CLI</strong></a><strong> is totally customizable.</strong> The hpal CLI has a command hpal make that allows you to scaffold files for new routes, models, services, auth strategies, etc. We have reasonable default contents of those files, but perhaps you’d like to add a little flair or make it match your coding style more closely. The file contents can be customized by simply creating a haute-couture amendment in your .hc.js file with an example property, which means you’re free to make hpal make behave however you like! In a similar vein, you can use haute-couture amendments to change the meanings of directories in your project: perhaps you want models/ to contain Mongoose models rather than Objection models, for example. We’re happy to get out of your way so that you can work within your own requirements.</li></ul><p>There are a bunch more examples of this in the pal universe, but these are a few important ones that jump to mind. Several modules/libraries are mentioned above. All these libraries are also designed to function and “make sense” entirely on their own, but if you’re looking for a more integrated experience, we highly suggest checking out <a href="https://github.com/hapipal/boilerplate">the pal boilerplate</a>, which comes setup to author hapi applications with <a href="https://github.com/hapipal/hpal-debug">top notch hapi debugging tools</a>, a test suite and linter, a configuration and deployment strategy, and a bunch of “flavors” e.g. to integrate schwifty and Objection ORM, Swagger UI, templated views, and more. Not to mention a very welcoming community on the <a href="https://join.slack.com/t/hapihour/shared_invite/enQtMjM5Njk1NDgzNTY5LThmODkxODIzOTg5NjJjODFiYjcxZDMxMTAyMzBkZDk3MWY4MTFlNDAyMTU3MmUwMmM0Y2UwMjU3YjAwYjRkN2E">hapi hour slack</a> (find us in #hapipal!). We’d love to have you.</p><p>Your pals,</p><p>Devin (<a href="https://github.com/devinivy">@devinivy</a>) and the <a href="https://github.com/orgs/hapipal/people">pal team</a></p><blockquote><strong>Important P.S.!</strong></blockquote><blockquote>We want to make it clear that hapi pal is not here to point fingers! No content of this post should be used to shame open-source maintainers for their “leaky abstractions” or shameless promotion of “the quick and easy.” It’s always your responsibility to understand the abstractions, approaches, limitations of the dependencies you introduce into your application. The greater JavaScript community has a long tradition of touting libraries as quick/easy solutions to complex problems, and we are simultaneously subject to hype, tooling anxieties, “JS fatigue” as much as we are instruments of it. We’re just hoping to acknowledge these issues, counter them systemically, and write some delightful hapijs tooling while we’re at it. <em>💞</em></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f67f4c25e04c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The hapi pal roadmap: build a healthy community around building healthy nodejs web services]]></title>
            <link>https://medium.com/@hapipal/the-hapi-pal-roadmap-build-a-healthy-community-around-building-healthy-nodejs-web-services-820c66d978c0?source=rss-9c3858d246bd------2</link>
            <guid isPermaLink="false">https://medium.com/p/820c66d978c0</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[hapijs]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[hapi pal]]></dc:creator>
            <pubDate>Mon, 30 Apr 2018 15:16:37 GMT</pubDate>
            <atom:updated>2018-04-30T15:16:37.896Z</atom:updated>
            <content:encoded><![CDATA[<p>First, we wanted to say a big <strong>thanks</strong> to all of you who supported us in the initial launch of hapi pal. The response from the hapijs community was incredibly positive, and that felt really encouraging to both the team and our sponsor, <a href="https://www.bigroomstudios.com/">Big Room Studios</a>. Since then we’ve kicked some plans in motion as part of our mission to build a healthy community around cutting-edge hapijs tools and methodologies for building nodejs web services. We’re excited to share some pal news and important bullet points on our short/medium-term roadmap.</p><p>Below you’ll find,</p><ul><li><strong>We spoke at the CBBHacks hackathon.</strong> The students were very smart.</li><li><strong>We launched a “</strong><a href="https://hapipal.com/getting-started"><strong>getting started</strong></a><strong>” tutorial.</strong> Finally, right? Also, a simple pal deploy via Glitch.</li><li><strong>We’re releasing a tool that generates knex migrations for you.</strong></li><li><strong>In progress: three novel CLI debugging tools for hapijs.</strong> One of them just might be a server- and route-aware cURL command.</li><li><strong>We’re designing “overridable” hapi plugins,</strong> paving the way for some big plans.</li><li><strong>A new, concise way to write hapi route pre-handlers.</strong></li><li><strong>The greater pal community has been busy too!</strong> Over the past couple weeks we’ve seen the community write pal-oriented tools for schwifty and Objection ORM, and produce a very curious example project.</li></ul><h4>We spoke at the CBBHacks hackathon</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kVfOas8Se-FuEzhv00SkyA.jpeg" /><figcaption>Devin presents on web services, nodejs, hapijs, and demos pal</figcaption></figure><p><a href="http://hackcolby.com/">CBBHacks</a> was a 36 hour hackathon hosted by Colby College open to university students across Maine (USA), and attended by nearly 90 students. We visited and had an awesome time. It was heartening to be reminded how much awareness has increased in academia surrounding web technologies, even over the past five years. At 11:30pm on the first night, about 50–60 students and organizers gathered to hear <a href="https://twitter.com/devinivy">@devinivy</a> give his spiel on building web services; how nodejs relates to the web; why he reached for hapijs; the impetus for starting pal; and finally a demo of hapi pal tooling to build an API that serves a random riddle. One team actually utilized pal for their project (so cool!), and we hope that one of them will join Big Room Studios for an internship this summer.</p><h4>We launched a “getting started” tutorial</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fglitch.com%2Fembed%2F%23%21%2Fembed%2Fthe-pal-boilerplate%3Fpath%3Dlib%2Froutes%2Fmy-first-route.js%26previewSize%3D20%26attributionHidden%3Dtrue%26previewFirst%3Dtrue%26sidebarCollapsed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fglitch.com%2Fembed%2F%23%21%2Fembed%2Fthe-pal-boilerplate&amp;image=https%3A%2F%2Fglitch.com%2Fedit%2Fimages%2Flogos%2Fglitch%2Fsocial-card%402x.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=glitch" width="600" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/4dd5f5ebb87f125bf360f5de27d0356e/href">https://medium.com/media/4dd5f5ebb87f125bf360f5de27d0356e/href</a></iframe><p>One thing that, in retrospect, we wished we had completed prior to the pal release was a proper “getting started” tutorial. And now, with huge thanks to <a href="https://github.com/zemccartney">@zemccartney</a>, anyone should be able to <a href="https://hapipal.com/getting-started"><strong>get started</strong></a> in short order! By its nature, hapi pal defers to other packages and ecosystems that are great at what they do — for example, <a href="https://github.com/Vincit/objection.js">Objection ORM</a>, <a href="http://knexjs.org/">knex</a>, and <a href="http://hapijs.com/">hapi</a> itself. Having a high-level overview of how all of these interface with pal (and each other) is really critical context that this tutorial provides. Plus, the tutorial is fun: you help your pal, Paldo, share riddles with their friends by incrementally building a database-backed API. The completed example can be found in the new <a href="https://github.com/hapipal/examples">hapipal/examples</a> repo. Relatedly, we have a new vanilla pal deployment all setup to be remixed on Glitch—just look up and to your left!</p><h4>We’re releasing a tool that generates knex migrations for you</h4><pre># Soon!<br><strong>$</strong> hpal run schwifty:migrate:diff migration-name</pre><p>As we work on web APIs, we find ourselves spending a fair amount of time combing through new knex migration files to ensure they match-up with our model definitions. It’s tedious, often repetitive, and prone to error. In short, it sounded like a nice thing to automate for ourselves, so we did! (With tons of effort especially from <a href="https://github.com/wswoodruff">@wswoodruff</a>!) This plays nicely with the <a href="https://github.com/hapipal/hpal">hpal CLI</a>’s ability to run commands defined directly on your hapi server: we’re adding a command to <a href="https://github.com/hapipal/schwifty">schwifty</a>, our model layer for hapi, that performs a diff between your model definitions and the current state of the database, then outputs a base migration file that you can use as a safe starting point to add indexes, foreign keys and other constraints, specifics about column nullability, etc. We’re not trying to cover every possible case or make any assumptions about your application—we just want want to remove the most repetitive tasks associated with writing migration files. We’re aiming to release schwifty:migrate:diff this week!</p><h4>In progress: three novel CLI debugging tools for hapijs</h4><p>We have begun development on some new debugging tools for hapijs development, provided as an extension to the <a href="https://github.com/hapipal/hpal">hpal CLI</a>, which already allows you to search documentation, create new projects, and scaffold existing projects. We’re working on a REPL for interacting with your server, a panel describing your deployment (routes, services, models, etc.), and a cURL-like command that exposes your routes and their validated parameters over the command line. We’re especially excited about this last one because it potentially removes the common development flow of restarting the server then making requests to it repetitively. Instead, requests can be made easily as one-offs, even when a server isn’t running. Looks for these debug tools released as <strong>hpal-debug</strong> some time in the next few weeks! Below is a demo of what we currently <em>do</em> have working.</p><pre># Just a demo for now, but planning to release soon!</pre><pre><strong>$</strong> hpal run debug:curl post /users --name Paldo -v</pre><pre>post /users (41ms)</pre><pre>payload<br>───────────────────────────────────────<br> name   Paldo</pre><pre>request headers<br>───────────────────────────────────────<br> user-agent        shot<br> host              my-computer.local:0<br> content-type      application/json<br> content-length    16</pre><pre>response headers<br>───────────────────────────────────────<br> content-type      application/json; charset=utf-8<br> cache-control     no-cache<br> content-length    24</pre><pre>result<br>───────────────────────────────────────<br>{ id: 10, name: &#39;Paldo&#39; }</pre><h4>We’re designing “overridable” hapi plugins</h4><p>We’ve dreamt of highly reusable application plugins for almost as long as we’ve been deploying hapi servers. Imagine the ability to pull-in a community- (or internally-) maintained “users” plugin into your project as a simple dependency (users CRUD, login and logout, password reset, etc.), without losing the ability to customize all of its internals for the purpose of <em>your</em> application. It’s a tricky problem that we’ve been chewing on for a while, and over time we’ve gradually built-up the machinery that we think is necessary to make this a reality. In particular,</p><ul><li>We now have a class-based service layer via <a href="https://github.com/hapipal/schmervice">schmervice</a>—including the concept of “plugin ownership” of services—that can be used to implement and extend the business logic used in route handlers or elsewhere.</li><li>A nice result of composing hapi plugins from files via <a href="https://github.com/hapipal/haute-couture">haute-couture</a> is that we have a natural <em>identifier</em> for each call your plugin makes to hapi’s API—say, to add a route with server.route() or a model with server.schwifty(). The identifier is just the filename itself, e.g. &quot;routes/user-create.js” or &quot;models/comment.js”! So if we want to allow skipping or altering any calls like this (which we do!), we have a natural way to reference them.</li></ul><p><a href="https://twitter.com/devinivy">@devinivy</a> threw together a proof of concept of one plugin overriding another plugin’s routes with relatively minimal changes to haute-couture, and <a href="https://github.com/mattboutet">@mattboutet</a> is refining it to more gracefully handle other hard problems that come with this new ability. We don’t know precisely when we’ll be releasing this haute-couture feature or if it necessitates any breaking changes, but be on the lookout!</p><pre>// Just an experiment... for now</pre><pre>const HauteCouture = require(&#39;haute-couture&#39;);</pre><pre>// my-user-plugin overrides a base-user-plugin<br>module.exports = {<br>    name: &#39;my-user-plugin&#39;,<br>    register: HauteCouture.using({<br>        overrides: require(&#39;base-user-plugin&#39;)<br>    })<br>};</pre><h4>A new, concise way to write hapi route pre-handlers</h4><p>We love <a href="https://github.com/hapijs/hapi/blob/master/API.md#route.options.pre">route pre-handlers</a>. They’re a great place to write route-level authorization rules, fetch data to use in the handler, or any routines that are otherwise incidental to the “guts” of your handler. We love them, but have always wished that they were just a <em>little</em> pithier—mostly because writing an object that looks like { assign: &#39;user&#39;, method: getUser } just <em>feels</em> like it should be able to be written { user: getUser }. And that’s all we’ve done! The new <a href="https://hapipal.com/docs/toys#toyspreprereqs">Toys.pre()</a> method transforms concise pre-handler configs into hapi’s longer configs. The <a href="https://github.com/hapipal/toys">toys</a> package contains all sorts of other goodies like this, such as concise request extensions, and helpers to work with events and streams inside async handlers.</p><h4>The greater pal community has been busy too!</h4><p>Over the past couple weeks we’ve seen some really sweet work by community members.</p><p><a href="https://github.com/PixulHQ/avocat"><strong>Avocat</strong></a> is a much-needed tool by <a href="https://github.com/optii">@optii</a> to help transform errors from <a href="https://github.com/Vincit/objection.js">Objection ORM</a> into relevant <a href="https://github.com/hapijs/boom">boom</a> HTTP errors. Its API is inspired by hapi’s own <a href="https://github.com/hapijs/bounce">bounce</a> module, which is pretty neat.</p><p>The <a href="https://github.com/PixulHQ/schwifty-i18n"><strong>schwifty-i18n</strong></a> module, also by <a href="https://github.com/optii">@optii</a>, makes schwifty models <em>translatable.</em> It’s labeled as a work in progress, but seems to be coming along really nicely. We can see that it’s designed to work especially well with haute-couture, which warms our little developer hearts.</p><p>Finally, <a href="https://github.com/jeff-kilbride">@jeff-kilbride</a> shared <a href="https://github.com/jeff-kilbride/haute-couture-example"><strong>a really interesting example</strong></a> of a minimal haute-couture project organized using multiple plugins. Jeff decided to drop some dependencies of <a href="https://github.com/hapipal/boilerplate">the pal boilerplate</a> so that it could run lighter on AWS Lambda. Slick stuff!</p><h4>That’s all!</h4><p>Well, that’s all for now. We clearly have a lot of gratifying work ahead of us, and better yet, many new conveniences on the horizon. Again, big thanks to those mentioned in this update for their contributions. If you’re looking to get involved, feel free to pick up <a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+user%3Ahapipal+label%3A%22help+wanted%22+">any issue labeled </a><a href="https://github.com/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+user%3Ahapipal+label%3A%22help+wanted%22+">help wanted</a> or simply join us in the #hapipal channel of the official <a href="https://join.slack.com/t/hapihour/shared_invite/enQtMjM5Njk1NDgzNTY5LThmODkxODIzOTg5NjJjODFiYjcxZDMxMTAyMzBkZDk3MWY4MTFlNDAyMTU3MmUwMmM0Y2UwMjU3YjAwYjRkN2E">hapi hour Slack</a>.</p><p>Your pals,</p><p>Devin (<a href="https://github.com/devinivy">@devinivy</a>) and the <a href="https://github.com/orgs/hapipal/people">pal team</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=820c66d978c0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing hapi pal]]></title>
            <link>https://medium.com/@hapipal/introducing-hapi-pal-550c13f30c5b?source=rss-9c3858d246bd------2</link>
            <guid isPermaLink="false">https://medium.com/p/550c13f30c5b</guid>
            <category><![CDATA[node]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[hapijs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[hapi pal]]></dc:creator>
            <pubDate>Wed, 11 Apr 2018 15:38:17 GMT</pubDate>
            <atom:updated>2018-04-11T15:38:17.560Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9JSw-lw6qgxaTs-WBy5cWA.jpeg" /></figure><p><strong>Today we are overjoyed to announce </strong><a href="https://hapipal.com/"><strong>hapi pal</strong></a><strong>, a suite of tools and best practices for the working hapijs developer. We have invested years of energy into what we have found to be a delightful, sustainable approach to developing web services using nodejs, and it’s finally time to open up.</strong></p><h3>What is hapi?</h3><p>If you’re not familiar with <a href="http://hapijs.com/">hapijs</a>, it’s an open-source web framework for nodejs that got off the ground with a 2+ million dollar investment from Walmart Labs and now operates entirely through an open governance model. (Sometimes we joke that we’re driving the Rolls Royce of web frameworks.) The framework is extensive, offering a uniquely rich plugin architecture and ecosystem, integrated concepts of caching, authentication and authorization, a strong community, and an emphasis on stability and security. It touts itself as “enabling developers to focus on writing reusable application logic instead of spending time building infrastructure.” And it really does — it’s a beautiful thing. We’re familiar with the alternative frameworks, those both well-established and new, and we find ourselves regularly renewing our vows with hapi.</p><h3>Creating efficiency</h3><p>But it’s important to note that the core hapi organization has intentionally remained unopinionated about <em>how</em> to author applications using hapi. The framework only offers a programming interface: there’s no directory structure, concrete deployment strategy, model or service/business layer, and generally no suggested architecture whatsoever. To be honest, we think that this is totally fair on hapi’s part. For one, there are only so many resources to spend on hapi development, so <em>not</em> focusing on additional tooling means that the rest of the framework is more stable and fully-featured. And secondly, we’re glad they’ve made room for the community to experiment.</p><p>For this reason, we found it invaluable to create for ourselves common conventions, tooling, and patterns that jive with hapi. <a href="https://www.bigroomstudios.com/">Big Room Studios</a> takes on a lot of projects, and there are incentives — for our business and those of our customers — to avoid solving the same problem or making the same decision too many times, which our development team understands acutely. It’s important for us to share a common knowledge base, to be able to onboard new developers easily, to have consistency across projects that enables us to effectively perform long-term maintenance. These issues are probably familiar to you too. Our solutions to these issues, refined over the course of nearly four years, constitute hapi pal.</p><h3>Building a set of tools</h3><p>You can think of hapi pal as a methodology for building hapi apps, backed both by software and written articles to guide you. While consulting on others’ hapi deployments, we find certain hapi features to be underutilized, often simply because they are covered without much ceremony in the hapi docs, or for other superficial reasons: sometimes it can be hard enough to decide <em>where</em> to put some code that you fall back to familiar techniques. We think that this is a very real, valid conceptual gap, and that it’s worth closing for the betterment of the community. We want to make it simpler to learn the framework, and to bring the most powerful features of hapi into reach. No single aspect of pal is required (we’ve incrementally adopted these tools ourselves) but here are some things pal has to offer:</p><ul><li><a href="https://github.com/hapipal/hpal"><strong>hpal</strong></a><strong> — </strong>A CLI tool for searching hapi-related documentation, starting new projects, scaffolding existing projects, and running custom commands in the context of your project.</li><li><a href="https://github.com/hapipal/boilerplate"><strong>the pal boilerplate</strong></a><strong> —</strong> A generic boilerplate setup for testing, linting, development, and deployment while enforcing <a href="https://12factor.net/">12factor</a> practices and pluginization of your app. The boilerplate can be customized using “flavors,” which add common functionality, e.g. database integration, templated views, Swagger API documentation, and more.</li><li><a href="https://github.com/hapipal/haute-couture"><strong>haute-couture</strong></a><strong> — </strong>A plugin composer that wires-up your plugin for you based upon a (customizable) directory structure. Rather than calling server.route(), place route configurations in the routes/ folder; implement your auth strategies in auth/strategies/; register additional plugins by configuring them in plugins/; define models in models/; services in services/, etc.</li><li><a href="https://github.com/hapipal/schwifty"><strong>schwifty</strong></a><strong> — </strong>A model layer for dialects of SQL based on the inimitable <a href="https://github.com/Vincit/objection.js">Objection ORM</a>.</li><li><a href="https://github.com/hapipal/schmervice"><strong>schmervice</strong></a><strong> — </strong>A service layer that integrates with hapi caching.</li><li><a href="https://github.com/hapipal/hecks"><strong>hecks</strong></a><strong> —</strong> Interoperability with express applications.</li><li><a href="https://github.com/hapipal/underdog"><strong>underdog</strong></a><strong> —</strong> A plugin adding support for HTTP/2 server-push.</li><li><a href="https://github.com/hapipal/tandy"><strong>tandy</strong></a><strong> — </strong>A plugin that helps auto-generate CRUD routes for your models.</li><li><a href="https://github.com/hapipal/toys"><strong>toys</strong></a><strong> — </strong>A collection of hapi-oriented helpers.</li><li><a href="https://hapipal.com/best-practices"><strong>best practices</strong></a><strong> — </strong>A growing list of articles on idiomatic hapijs app development.</li><li>More, including a package that helps resolve hairy inter-plugin dependencies (<a href="https://github.com/hapipal/hodgepodge">hodgepodge</a>).</li></ul><h3>A hapi pal when you need it</h3><p>Now, I’m specifically not here to claim that hapijs or hapi pal are going to allow to you to ship complex features with tight SLAs to millions of users in a matter of minutes, hours, or days: one of our founding design principles is acknowledging that <em>building scalable web services is hard work</em>. We know this because we’ve built tons of them, and in plenty of different languages and frameworks: there’s no silver bullet. So we’re not interested in any platform that gets in our way when it comes time for heavy lifting. When that time comes, wouldn’t you rather just have a pal to cheer you on, offer you a hand and moral support, and leave you to your own devices if you don’t really need the extra help? That’s what hapi pal is all about.</p><h3>Sponsored and maintained</h3><p>And did we mention hapi pal is sponsored?! <a href="https://www.bigroomstudios.com/">Big Room Studios</a> has demonstrated a commitment to open-source software through development and design of <a href="https://hapipal.com/">hapipal.com</a>, development of <a href="https://github.com/hapipal/schwifty">schwifty</a> and <a href="https://github.com/hapipal/tandy">tandy</a>, lots of <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">dogfooding</a>, and the ongoing maintenance of numerous pal and hapijs modules. The hapi pal organization is openly governed, but Big Room has helped make this initial release a reality, and plans to support several of us in our maintainership of hapi pal going forward. We’re thankful for that because we love using and writing well-maintained software. Now, don’t get us wrong — there’s still a lot of work to do and we could use your help too.</p><h3>The community</h3><p>So, come check it out. Join us — we’d love to have you! If you’re already a part of the hapi community, we hope you’ll be pleased to learn that hapi pal is designed to be entirely compatible with the <a href="https://github.com/hapijs">hapi organization</a>. We have the same code style, identical issue labels, the same standard of 100% code coverage, and a consistent <a href="https://github.com/hapipal/contrib/blob/master/code-of-conduct.md">code of conduct</a>, all of which we are quite familiar with, as some of us are also core and community hapi collaborators. We will continue to move in step with the hapi organization, and we earnestly look forward to seeing where we end up. Also, be on the lookout for some big new pal capabilities on the horizon. We have some fun plans — come chat with us about them in our #hapipal channel in the <a href="https://join.slack.com/t/hapihour/shared_invite/enQtMjM5Njk1NDgzNTY5LThmODkxODIzOTg5NjJjODFiYjcxZDMxMTAyMzBkZDk3MWY4MTFlNDAyMTU3MmUwMmM0Y2UwMjU3YjAwYjRkN2E">hapi hour Slack</a>. Hope to see you there.</p><p>Your pals,</p><p>Devin (<a href="https://github.com/devinivy">@devinivy</a>) and the <a href="https://github.com/orgs/hapipal/people">pal team</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=550c13f30c5b" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>