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
- Run
npm install pinniped-cli -g
- 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