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

pinniped

v0.5.5

Published

Easy-to-deploy Node backend server.

Downloads

13

Readme

Summary

Pinniped is an open-source, JavaScript backend-as-a-service application that offers:

  • An embedded SQLite3 Database,
  • An admin dashboard,
  • Autogenerated RESTish APIs,
  • Custom events and extensible routes.

Pinniped is comprised of several tools, check out their READMEs and the overview README for more information:

Table of Contents

How to Use

Install the dependency

npm install pinniped

Import and create a Pinniped instance

// CommonJS
const { pnpd } = require('pinniped');
const app = pnpd();
app.start(serverConfig);

// Or ECMAScript
import { pnpd } from 'pinniped'
const app = pnpd();
app.start(serverConfig);

Or install the CLI and create a project

  1. Run npm install pinniped-cli -g
  2. Run pinniped create

Documentation

Server Config

If your Pinniped project was built with pinniped-cli, then the project's .env file will contain supported configuration settings.

start (serverConfig)

The Pinniped instance accepts an object that contains the server configuration. There are configurations to change how the Express server runs. By default, these values are expected to be in a .env file. However, you can change the source for the server-specific configurations by passing in your own object when start is invoked.

let serverConfig = {
  port: process.env.SERVER_PORT,
  domain: process.env.SERVER_DOMAIN,
  altNames: process.env.SERVER_ALTNAMES,
  directory: process.env.SERVER_DIRECTORY,
};

app.start(serverConfig);

SERVER_DOMAIN

The base domain name for requesting a TLS certificate from Let's Encrypt. If SERVER_DOMAIN has a value, it will attempt to auto-cert the domain. If this is undefined the server will run on SERVER_PORT. If SERVER_DOMAIN is present and the auto-cert runs, the SERVER_PORT value is ignored and the server will run on port 443 with a redirect server (running on port 80) that points to port 443.

SERVER_DOMAIN=example.com

SERVER_ALTNAMES

Holds any alternative names you'd like on the certificate. If left undefined, it'll automatically add the www version of SERVER_DOMAIN. If you have multiple domain names that point to the same site you can add them here. The format is the same as SERVER_DOMAIN but commas separate each name.

SERVER_ALTNAMES=www.example.com,www.example.net

SERVER_DIRECTORY

This specifies whether to try to obtain a staging certificate or a production certificate from Let's Encrypt. By default, it will get a staging certificate. Once you verify that you can get the staging certificate you can change this value to production.

SERVER_DIRECTORY=production

For more information about this process, check out LetsEncrypt.

SERVER_PORT

The port that the server runs on. This is only used if SERVER_DOMAIN is undefined and the server is running on HTTP. It defaults to 3000 if undefined.

CORS_WHITELIST

Can be regex or plaintext, if not provided, defaults to allowing all CORS traffic. Commas separate each domain that CORS should allow.

CORS_WHITELIST=www.example.com,/regexvalue/,www.example2.com

SESSION_SECRET

Used by the server to encrypt session information. If left blank the server will automatically generate one and save it here.

Custom Routes and Event Handlers

To extend Pinniped's base functionality, you can add custom routes or use Pinniped's custom events to add an event listener and run a callback. This extension code is added to the pinniped app instance before app.start() is called.

import { pnpd } from 'pinniped'
const app = pnpd();

app.addRoute("GET", "/store", () => {
  console.log("GET request received at /store");
});

app.start(serverConfig);

addRoute (method, path, handler)

addRoute mounts the parameter, path, onto the host's path. Once it receives the specified HTTP request method at that path, it'll invoke the handler passed in.

app.addRoute("GET", "/store", () => {
  console.log("GET request received at /store");
});

// The route can accept parameters
app.addRoute("GET", "/custom/:msg", (c) => {
  const msg = c.pathParam('msg');
  return c.json(200, { message: `My custom endpoint ${msg}` });
});

addListener (handler, tables)

addListener takes a handler function that executes when Pinniped's custom events are triggered. As the second argument, You can specify an array of table names for which you'd like to apply the callback to. If no tables are passed, the callback will be invoked for every table.

The handler function has access to the Express req and res objects, as well as an additonal data object that has information relevant to the event. These are passed to the callback as a single object that can be destructured for convenience as in the examples below. Depending on what event is trigger, the data object will contain different properties.

The route that triggered the event will not return a response to the client until all of the event callbacks have run, this allows you to enrich the data object, or early return the response from the callback. The route will not attempt to return a response if the response has been sent from a callback.

An example of the data object for onGetAllRows:

{
  table: Table {
    id: 'c45524681238c0',
    type: 'base',
    name: 'seals',
    columns: [ [Column] ],
    getAllRule: 'admin',
    getOneRule: 'admin',
    createRule: 'admin',
    deleteRule: 'admin',
    updateRule: 'admin',
    options: {}
  },
  rows: [
    {
      id: 'da0157aebacc9b',
      created_at: '2024-04-22 17:50:36',
      updated_at: '2024-04-22 17:50:36',
      name: 'Gary'
    }
  ]
}

An example that will only run the callback if the table name is seals:

// Adds a listener on the event: "getOneRow".
// The handler is executed when the event, "getOneRow", is triggered on table "seals".
app.onGetOneRow.addListener(({req, res, data}) => {
  console.log("Triggered Event: getOneRow");
}, ["seals"]);

You can add several tables, or omit the tables array to run anytime the event is triggered.

// Adds a listener on the event: "createOneRow".
// The handler is executed when the event, "createOneRow", is triggered on any table.
app.onCreateOneRow.addListener(({req, res, data}) => {
  console.log("Triggered Event: createOneRow");
});

// Or the handler can be executed on specific tables.
app.onCreateOneRow.addListener(({req, res, data}) => {
  console.log("Triggered Event: createOneRow");
}, ["seals", "pinnipeds", "users"]);

addListener can work asynchronously. Note: The route that triggered the event will not return a http response to the client until all callbacks return. This means if you await something in a async callback the response will be delayed. This allows you to use the async callback to enrich the response object, but could unintentionally delay a response to the client.

// Adds a listener on the event: "loginUser".
app.onLoginUser.addListener(async ({req, res, data}) => {
  await setTimeout(() => {
    sendUserWelcomeEmail();
  }, 3000);
});

Here are all the the events that Pinniped fires that you can add listeners for:

CRUD Operation Events (these take the optional table argument)

  • onGetAllRows
  • onGetOneRow
  • onCreateOneRow
  • onUpdateOneRow
  • onDeleteOneRow

Managing Users and Database Events

  • onBackupDatabase
  • onRegisterUser
  • onRegisterAdmin
  • onLoginUser
  • onLoginAdmin
  • onLogout

Custom Route Event

  • onCustomRoute

DDL Operation Events

  • onGetTableMeta
  • onCreateTable
  • onUpdateTable
  • onDropTable