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

supersave

v0.14.2

Published

A package to create a simple datastore with a generic API for side projects.

Downloads

51

Readme

SuperSave

Installation

Sqlite

npm i --save supersave sqlite3 sqlite

Example connection string: sqlite://:memory:

Mysql

npm i --save supersave mysql

Example connection string: mysql://examplename:somepassword@examplehost:3306/dbname

Usage

You can use addEntity to create a database-only entity, use addCollection to add an entity that will automatically become available via the API.

Use await superSave.getRouter() to register the API at your express application. For example: app.use('/api', await superSave.getRouter());.

Entity

const planetEntity = {
    name: 'planet',
    template: {
        name: '',
    },
    relations: []
}

const moonEntity = {
    name: 'moon',
    template: {
        name: '',
    },
    relations: [{
        name: 'planet',
        field: 'planet',
        multiple: false,
    }],
}

const superSave = await SuperSave.create(connectionString);
await superSave.addEntity(planetEntity);
await superSave.addEntity(moonEntity);

Collection

const planetCollection = {
    name: 'planet',
    template: {
        name: '',
    },
    relations: []
}

const moonCollection = {
    name: 'moon',
    template: {
        name: '',
    },
    relations: [{
        name: 'planet',
        field: 'planet',
        multiple: false,
    }],
}

const superSave = await SuperSave.create(connectionString);
await superSave.addCollection(planetCollection);
await superSave.addCollection(moonCollection);

Close connection

You can use await superSave.close() to close the connection with the underlying storage. For sqlite this means that the connection is closed and the superSave instance can no longer be used. When using mysql this will close all active connections in the pool.

Hooks

There are several hooks available that can be used to manipulate the behavior in the HTTP endpoints:

export type Hooks = {
  get?: (collection: Collection, req: Request, res: Response) => Promise<void> | void,
  getById?: <T>(collection: Collection, req: Request, res: Response, entity: T | null) => Promise<T> | T,
  entityTransform?: <IN, OUT>(collection: Collection, req: Request, res: Response, entity: IN) => Promise<OUT> | OUT,
  updateBefore?: <IN, OUT>(collection: Collection, req: Request, res: Response, entity: Partial<IN>) => Promise<OUT> | OUT,
  createBefore?: <IN, OUT>(collection: Collection, req: Request, res: Response, entity: Omit<IN, 'id'>) => Promise<OUT> | OUT,
  deleteBefore?: <T>(collection: Collection, req: Request, res: Response, item: Omit<T, 'id'> | null) => Promise<void> | void,
};

A hook can be set when registering a collection, by providing at least one of the functions described above.

const planetCollection = {
  name: 'planet',
  template: {
      name: '',
  },
  relations: [],
  hooks: {
    ...
  }
}

| Hook | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | get | Manipulate the filters/get parameters of the request before data is actually being requested. The endpoint is /planet for example. | | getById | Perform an action on the retrieved entity before it is transformed and then returned via the API. Entity value can be null if its not found. | | entityTransform | Used at every location where an entity is returned in the API (create, update, get, getById). The entity as its retrieved from the database can be changed. For example a field that should not be publicly displayed can be removed from the payload. | | updateBefore | This hook is invoked just before the updated statement is executed towards the database. The entity argument in the payload is the entire object, not just the provided fields in the request. | | createBefore | Invoked before an item is created, the function will receive the item as it will be saved. The id field will not be available, unless explicitly specified in the API request. | | deleteBefore | Invoked before an item is deleted. |

Errors

Any error that is thrown from within a hook is output directly towards the requester in the API. So be careful that no unwanted information is leaked via the Error. A HookError can be imported import { HookError } from 'supersave';. An exception with a defined HTTP status code can be initialized via is contructor: throw new HookError('msg', 401). This will result in a HTTP 401 error in the API out, with the JSON payload: { "message": "msg" }.

Access the native connection

The SuperSave class offers a function getConnection()that returns the underlying connection. This can be used to perform custom queries.

| Connection | Description | | ---------- | -------------------------------------------------------------------------------------------- | | sqlite | The sqlite connection is a sqlite Database object. | | mysql | The mysql connection is a mysql Pool object. |

In typescript, the getConnection<T>() provides a generic type, which you can use to set the expected return type from the function. You are yourself responsible for the correct type.

Development

This module currently supports both sqlite and mysql. Development for sqlite is pretty straight-forward, you can use :memory: as the filename to set up a memory-only database.

Mysql is a bit more cumbersome, as it requires a running mysql server. For this purpose a docker-compose.yml file is present in the repository. Run the following command to start a jest watch command for the unit tests.

docker-compose up

This command uses the --runInBand to run all tests synchronously, to prevent the tests from interfering with each other. Each test will drop all tables in the database before running, so that they do not interfere with each other.