npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@hapipal/schmervice

v3.0.0

Published

A service layer for hapi

Downloads

6,829

Readme

schmervice

A service layer for hapi

Build Status Coverage Status

Lead Maintainer - Devin Ivy

Installation

npm install @hapipal/schmervice

Usage

See also the API Reference

Schmervice is intended for use with hapi v20+ and nodejs v16+ (see v2 for lower support).

Services are a nice way to organize related business logic or transactions into classes. Schmervice is a service layer designed to integrate nicely with hapi. It consists of two parts that can be used together or separately:

  1. a base Service class that integrates your service with hapi by:

    • giving it access to the relevant server and plugin options.
    • allowing you to implement async initialize() and async teardown() methods that should run when the server initializes and stops.
    • allowing you to configure certain methods as being cacheable, with all the server method configuration that you're accustomed to.
  2. a hapi plugin that allows you to register services and access them where it is most convenient, such as in route handlers. This registry respects plugin boundaries and is hierarchical, so unrelated plugins can safely register their own services without affecting each other.

const Schmervice = require('@hapipal/schmervice');
const Hapi = require('@hapi/hapi');

(async () => {

    const server = Hapi.server();

    await server.register(Schmervice);

    server.registerService(
        class MathService extends Schmervice.Service {

            add(x, y) {

                this.server.log(['math-service'], 'Adding');

                return Number(x) + Number(y);
            }

            multiply(x, y) {

                this.server.log(['math-service'], 'Multiplying');

                return Number(x) * Number(y);
            }
        }
    );

    server.route({
        method: 'get',
        path: '/add/{a}/{b}',
        handler: (request) => {

            const { a, b } = request.params;
            const { mathService } = request.services();

            return mathService.add(a, b);
        }
    });

    await server.start();

    console.log(`Start adding at ${server.info.uri}`);
})();

Functional style

Schmervice allows you to write services in a functional style in additional to the class-oriented approach shown above. server.registerService() can be passed a plain object or a factory function. Just make sure to name your service using the Schmervice.name symbol or a name property. Here's a functional adaptation of the example above:

const Schmervice = require('@hapipal/schmervice');
const Hapi = require('@hapi/hapi');

(async () => {

    const server = Hapi.server();

    await server.register(Schmervice);

    server.registerService(
        (srv) => ({
            [Schmervice.name]: 'mathService',
            add: (x, y) => {

                srv.log(['math-service'], 'Adding');

                return Number(x) + Number(y);
            },
            multiply: (x, y) => {

                srv.log(['math-service'], 'Multiplying');

                return Number(x) * Number(y);
            }
        })
    );

    server.route({
        method: 'get',
        path: '/add/{a}/{b}',
        handler: (request) => {

            const { a, b } = request.params;
            const { mathService } = request.services();

            return mathService.add(a, b);
        }
    });

    await server.start();

    console.log(`Start adding at ${server.info.uri}`);
})();

Using existing libraries as services

It's also possible to use existing libraries as services in your application. Here's an example of how we might utilize Nodemailer as a service for sending emails. This example features Schmervice.withName(), which is a convenient way to name your service using the Schmervice.name symbol, similarly to the example above:

const Schmervice = require('@hapipal/schmervice');
const Nodemailer = require('nodemailer');
const Hapi = require('@hapi/hapi');

(async () => {

    const server = Hapi.server();

    await server.register(Schmervice);

    server.registerService(
        Schmervice.withName('emailService', () => {

            // Sendmail is a simple transport to configure for testing, but if you're
            // not seeing the sent emails then make sure to check your spam folder.

            return Nodemailer.createTransport({
                sendmail: true
            });
        })
    );

    server.route({
        method: 'get',
        path: '/email/{toAddress}/{message*}',
        handler: async (request) => {

            const { toAddress, message } = request.params;
            const { emailService } = request.services();

            await emailService.sendMail({
                from: '[email protected]',
                to: toAddress,
                subject: 'A message for you',
                text: message
            });

            return { success: true };
        }
    });

    await server.start();

    console.log(`Start emailing at ${server.info.uri}`);
})();

Extras

What is a service layer?

"Service layer" is a very imprecise term because it is utilized in all sorts of different ways by various developers and applications. Our goal here is not to be prescriptive about how you use services. But speaking generally, one might write code in a "service" as a way to group related business logic, data transactions (e.g. saving records to a database), or calls to external APIs. Sometimes the service layer denotes a "headless" interface to all the actions you can take in an application, independent of any transport (such as HTTP), and hiding the app's data layer (or model) from its consumers.

In our case services make up a general-purpose "layer" or part of your codebase– concretely, they're just classes that are instanced once per server. You can use them however you see fit!

hapi actually has a feature deeply related to this concept of services: server methods. We love server methods, but also think they work better as a low-level API than being used directly in medium- and large-sized projects. If you're already familiar with hapi server methods, you can think of schmervice as a tool to ergonomically create and use server methods (plus some other bells and whistles).