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 🙏

© 2025 – Pkg Stats / Ryan Hefner

hapi-view-models

v1.1.0

Published

Role-filtered view model support for Hapi

Downloads

5

Readme

hapi View Models

JavaScript Style Guide Build Status Vendigo Open Source

A plugin to provide a concept of 'view-models' to hapi.

Purpose

When rendering payloads from an API, we often want to provide different subsets (or supersets) of data to users with different roles, or scopes. By leveraging lodash.omit and server.decorate, we can contain the complexity of doing this as a fairly neat abstraction, reducing the overall complexity of payload modification and filtering, and keeping our controllers clean.

With hapi-view-models it is possible to render a variety of different versions of a single entity (payload) from a single endpoint.

Installing

Installing is done in the usual way

npm install hapi-view-models

Usage

The vm reply helper allows you to render views of your data:

const KeyPairViewModel = require('...')
server.route({
  ...
  handler (request, reply) {
    reply.vm(KeyPairViewModel, response)
  }
})

Where vm takes the KeyPairViewModel and filters the data inside response according to the user's scope. Response can be a single entity or an array of entities.

If you're returning a response envelope you can provide a path to the entity as a third argument to the vm reply helper.

const KeyPairViewModel = require('...')
server.route({
  ...
  handler (request, reply) {
    reply.vm(KeyPairViewModel, {
      nested: {
        data: [...]
      }
    }, 'nested.data')
  }
})

Real World Example

Suppose we owned a cryptocurrency exchange. That's very 'of the moment', isn't it?

Lets say our wallet owner has a role of 'owner', and a visitor doesn't have this role.

We'd want to build a view model to render a wallet's data in a secure way. We declare a set of properties that are included in the payload for each role. Including a property in a role automatically excludes that data from all other roles.

Roles are defined by hapi in request.auth.credentials.scope and are controlled by your auth mechanism.

Our view model extends ViewModel and looks like this:

const { ViewModel } = require('hapi-view-models')

class KeyPairViewModel extends ViewModel {
    get includes () {
      return {
        owner: ['private']
      }
    }
}

And our handler would look something like this:

const { plugin } = require('hapi-view-models')
const KeyPairViewModel = require('path/to/key-pair-view-model')

// Register the plugin with hapi
server.register(plugin, err => {
  assert.ifError(err)
})

function getWalletKeys (address) {
  // Fetched from a database, more than likley.
  const keyPair = {
    private: '0x000',
    public: '0xaaa'
  }
}

server.route({
  method: 'GET',
  path: '/wallet/{address}',
  handler: function (request, reply) {
    const keyPair = getWalletKeys(request.params.address)
    reply.vm(KeyPairViewModel, keyPair)
  }
})

The rendered data would look something like this:

// If the owner requests the wallet
{
  private: '0x000',
  public: '0xaaa'
}

// But if a visitor requests the wallet
{
  public: '0xaaa'
}

Understanding get includes()

The getter method get includes() or just .includes as a property on your view model is a mapping of scope to an array of properties that are (deep) filtered from the resultant payload. This means we also support nesting!

This means you can do:

const data = {
  a: {
    foo: 'bar'
  },
  b: {
    e: 'stuff here',
    c: {
      d: 'some-data'
    }
  }
}

class SomeViewModel extends ViewModel {
    get includes () {
      return {
        role1: ['a'], // Role 1 can see property 'a'
        role2: ['a', 'b'] // Role 2 can see property 'a' and 'b'
        role3: ['b.c.d'] // Without this role, you can see b and b.e, but the contents of b.c will be '{}' as 'b.c.d' is hidden.
      }
    }
}

You'll notice something going on here with includes. Includes is an 'exclusive' mapping. This means that if you declare a property visible by a role, it is hidden for all other roles. This means that you can minimally hide role-sensitive data, without having to re-iterate yourself or risk the leak of private properties leaking through.

TL;DR: Once a property is declared as visible to a role, it is automatically invisible to all other roles.

Ideally your user should have all the roles it needs to see all the data it needs, but if you like, you can 're-declare' visibility as we have done in the ase of role2 above. A user wanting to see a can have roles role1, role2, or both. a (and as a result a.foo) isn't visible to any other roles.

Structure

The plugin exports two modules:

  • plugin which is the hapi plugin providing reply.vm()
  • ViewModel which is the base class your view models should extend.

The plugin uses a slightly stricter extension of standard-style

Contributing

To contribute to the plugin, fork it to your own github account, create a branch, make your changes, and submit a PR.

Note that Vendigo Finance Ltd requires that you include tests to cover your new code, along with your PR in order to get it merged.

To run our test suite:

npm install
npm test
npm run lint