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 🙏

© 2026 – Pkg Stats / Ryan Hefner

pylor

v1.1.0

Published

A library for creating and managing REST APIs and their permissions in Node.js and the browser

Downloads

41

Readme

Pylor

Table of Contents

Introduction

Pylor is an API helper library for Express.js. It lets you create and manage REST API routes with a simpler wrapper on top of Express, and adds a powerful permission system and support for dogfooding your endpoints.

Pylor has no dependencies, apart from the implicit dependency on Express, and works equally well in both Node.js and the browser.

Pylor was initially developed for internal use at ITSI.

Build Status Coverage Status

Installation

npm install --save pylor

Why do I want this? DO I want this?

Pylor aims to make life easier when your backend is more or less just Express. Pylor is not a replacement for Express, nor is it trying to be a fundamental rework of how Express does things, nor an extremely clever DSL. It is just a wrapper designed to be obvious and simple.

Use Pylor if you want some or all of these:

  • A REST system that works with Express instead of replacing it.
  • A logical convention for organising your endpoint controllers.
  • A fire-and-forget system for adding new endpoints.
  • A mechanism for versioning your API easily.
  • A powerful permission system, integrated directly into your endpoints, and extensible to the rest of your backend (and front-end if needed).
  • A standard for consuming controllers internally, aka. dogfooding.
  • It's been 3 months so it's time to rewrite your codebase with something new.

Don't use Pylor if:

  • You don't want to.
  • You distrust people who bothered to aim for 100% coverage. They're probably counterproductively obsessive.
  • You looked up the name and now you're imagining sphincters and it's grossing you out a litte.

Basic Walkthrough

Boilerplate

First, you need to initialise Pylor once, with whatever options you need. Typically, this will occur directly after the Express instance is instantiated:

  pylor.init({ server: express });

The server property is the only required property, but there are several others. The permission system will be inactive without some of them. You can read the full list in the documentation.

Then, you need to set up some system for loading the endpoints. The convention is to define each section of the API in it's own file, which is then stored in a folder. You can then use a module like walk to iterate all the API pieces and load them. Here's an example of that:

  const path = require("path");
  const walk = require("walk");

  const apiFolder = "/some/path";

  const walker = walk.walk(apiFolder, {});

  walker.on("file", (root, fileStats, next) => {
    if(path.extname(fileStats.name) !== ".js")
      return next();

    try {
      const mod = require(path.join(root, fileStats.name));
      if(typeof mod.setup === "function")
        mod.setup();
    }
    catch(e) {
      console.error("Error loading API definition from " + fileStats.name);
    }

    next();
  });

  walker.on("end", () => {
    console.log("Finished loading API");
  });

Alternatives include iterating a fixed list of known files, or using some form of module self-registration.

The loading is done - again, by convention - by exporting a method named setup() from each file. This method should perform all the logic required to configure that particular set of endpoints. Typically, this is just a call to pylor.active() with the object defining the structure.

Once that's done, you're set to begin defining endpoints.

Endpoints

Here is an annotated sample endpoint file, which exports a setup() method as described in the previous section.

const pylor = require("pylor");

exports.setup = () => {

  const restStructure = {

    // This entire object will be exposed at /api/1.0/items
    items: {
      // Apply middleware at any level to have it apply to all sibling and child nodes
      _middleware: [pylor.sslOn],

      // Use the standard verbs to define endpoints
      get: exports.getAllItems,

      // Endpoints will automatically be decorated with parameters if the verb needs it
      put: exports.updateItem,

      // Define custom parameters wherever you like
      ":id": {

        // Add specialised middlwares for endpoints
        get: [someMiddlewareThing, exports.getSpecificItem],

      },
    },
  };

  // Add these endpoints to
  pylor.activate(restStructure);

};

// Endpoints can use callbacks...
exports.getSpecificItem = function(options, callback) {

  // Any other endpoint defined in the entire application can be invoked.
  // Pylor converts seamlessly between promises and callbacks
  // so you can use the syntax that makes sense for the calling site.

  return pylor.api.latest.items.get({ uid: options.id })

};

// ...or promises
exports.getAllItems = function(options) {

  // Endpoints are implicitly scoped to the Express `req` object
  const user = this.session.user;

  // Perform custom permission checks whenever you need.
  if(!pylor.hasAccess(pylor.p.some.permission, user))
    throw new Error("No access");

  // The "options" object includes some metadata about the endpoint,
  // such as whether or not it was called with multiple parameters,
  // and exposed Express path parameters.

  return Promise.resolve(options.multiID ? [1, 2, 3] : 1)
};


// If you don't need the options for a promise-based endpoint, it can be omitted
exports.updateItem = async function() {

  // All endpoints are given implicit permissions that can be used for access control
  // The permission system supports roles, wildcards, default permissions, and placeholder values
  if(!pylor.hasAccess(pylor.p.api.anotherSection._.values, this.session.user))
    throw new Error("Nope");

  const item = await db.saveThing(this);

  // Configure the response as needed.
  const resultObject = { result: item, code: 202, headers: { "Content-Type": "application/json" } };

  // You can also use a simple fluent interface.
  return rester
    .response(item)
    .status(202)
    .addHeaders({
      "Content-Type": "application/json",
    });
};

Documentation

Documentation