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

@db-essentials/base

v2.0.2

Published

Database abstraction layer for nodejs

Downloads

13

Readme

Description

Lightweight and fast local json database manager

  • supports all CRUD operations
  • comprehensive querying syntax
  • built-in seeder to quickly populate your database up to thousands of records
  • a command line tool to run dabase queries from terminal
  • 'persist' or 'no_persist' mode if you want to block write operations
  • base package comes with no dependencies
  • easily extensible, dedicated database modules are under way

Installation


    npm install @db-essentials/base --save

    or

    yarn add @db-essentials/base

HTTP methods

| Method | Http Verb | Description | | ----------- | --------- | ---------------------------------------------------------- | | find | GET | Returns all matching records | | find_one | GET | Returns the first matching record | | count | GET | Returns the number of matching records | | delete_one | DELETE | Deletes matching record filters | | delete_many | DELETE | Deletes multiple records, returns an array of deleted id's | | save_one | POST | Saves a record | | save_many | POST | Saves multiple records | | update_one | PUT | Updates the first matching record | | update_many | PUT | Updates all matching records |

Client

Example with fetch API

// with query parameters

await fetch("somedomain/find/items?_only=price,name,updated_at&_limit=10");

// with body

await fetch("somdedomain/update_one/items", {
  method: "PUT",
  body: JSON.stringify({
    _id: 12,
    _set: {
      price: 169.99,
    },
  }),
});

Server

// Basic setup with express.js

const app = require("express")();

const { Connection, Query } = require("@db-essentials/base");

app.get("/", async (req, res) => {
  const conn = await Connection.create({
    database: "public/db",
    label: "default",
    mode: "no_persist",
  });

  const query = await Query.create({
    connection: conn,
    url: "find/test_data",
    body: null,
  });

  const data = await query.run();

  res.status(200).send(data);
});

app.listen(4000, () => console.log("listening on port 4000"));

This is just a most basic example. The key parts however are:

Connection

| Argument | Accepts | Description | | -------- | ------------------- | ----------------------------------------------------------------------------------------------------- | | label | any unique string | An identifier for this particular connection, since you can have many of them running simultaneously. | | database | string | A valid path to your local json files directory. | | mode | persist, no_persist | It tells whether you want to persist write / delete / update operations or not. |

Query

| Argument | Accepts | Description | | ---------- | ------- | -------------------------------------------------------------------------------------------------------------------------- | | connection | object | An instance of active connection. | | url | string | A url that includes a valid http method (see above), name of the collection you wish to address and optional search query. | | body | string | A stringified body object when performing write / update operations. |

Query builidng

General

Query operators always start with underscore

// _exists, _type, _lte, etc...

so they can later be separated and parsed. In general regular field names should not start with underscores.

Nested properties can be accessed using dot notation:

const q = "?field1.nested.value._gte=10";

Array values should be comma separated. Example of selecting fields to return:

const q = "?_only=field1,field2,field3,nested.field1,nested.nested.field3"; // etc...

Some operators precede the field name and some come after:




const q = "?_nor.field=value"

// vs

const q = "?field.someNestedField._in=1,2,3,4"

/*

Search params use a regular '?' and '&' syntax. Some operators allow multiple conditions:

const q =
  "somedomain/find/items?_nor.itemName._in=name1,name2,name3&_nor.someValue._gte=100&optonalField._exists=true";

Same example but using request body object

const body = JSON.stringify({
  _nor: {
    itemName: {
      _in: ["name1", "name2", "name3"],
    },
    someValue: {
      _gte: 100,
    },
  },
  optionalField: {
    _exists: true,
  },
});

Request filters

These are treated as reserved keywords and you should avoid using them as database field names. Filters can access deep nested fields as well

field.nested.nested._exists = true;

Any record has an autogenerated _id field, which holds a numeric value. It'a special field, every other underscore starting fields area treated as operators.

| Param | Usage | Description | | ------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | _gt | field._gt=[any] | Greater than target | | _gte | field._gte=[any] | Equal or greater than target | | _lt | field._lt=[any] | Lower than target | | _lte | field._lte=[any] | Equal or lower than target | | _in | field._in=[array] | Equal to target or one of comma separated target values | | _not_in | field._not_in=[array] | Different than target or comma separated target values | | _equals | field._equals=[any] | Equal to target | | _not_equal | field._not_equal=[any] | Different than target | | _exists | field._exists=[boolean] | Checks if the value exists, pass true or false | | _type | field._type=[string] | Checks if the value is of given type: string, null, date, ...etc.You can also pass an array of types to evaluate, field._type=null,date | | _regex | field._regex=[string] | Evaluates value based on a regex expression | | _array_match | field._array_match=[any] | Checks if an array field contains the requested value | | _array_all | field._array_all=[array] | Checks if an array field contains all requested values | | _array_size | field._array_size=[number] | Checks if the array field is of specified size | | _and | _and.field=[any] | Filters out records that don't match all conditions : _and.name=someName&_and.value._gt=4 | | _or | _or.field=[any] | Returns records that match at least one condition : _or.fieldName=value&_or.orOtherField._gt=4 | | _nor | _nor.field=[any] | Filters out records that match the conditions: _nor.name=book&_nor.price._gt=19.99 |

Response modifiers

These are treated as reserved keywords and you should avoid using them as database table field names.

| Param | Usage | Description | | ------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | _only | _only=[array] | Specifies which fields should be included with the response. Accepts comma separated fields or a single field. Use either this or '_except' | | _except | _except=[array] | Specifies which fields not to icnlude with the response. Accepts comma separated fields or a single field. Use either this or '_only' | | _skip | _skip=[number] | How many records to skip. Accepts an integer value. | | _limit | _limit=[number] | Caps results number to a specified integer value. | | _sort | _sort.field=[number] | Sorts data by a specified field, should be either 1 (ascending order) or -1 (descending order). | | _slice | _slice=[array] | Gets a range of records. | | _array_slice | _array_slice.field=[num] | Specifies how many values to return from an array field. |

Insert and update

Operators to be used inside a body object of POST or PUT http requests

| Param | Usage | Description | | ------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | _set | _set.field=[any] | Updateds a value to the target value, should be used with http PUT call | | _inc | _inc.field=[number] | Increments a number value by specified positive or negative value. | | _cdate | _cdate.field._type=[string] | Updates a field to a current date or timestamp, _cdate.updated_at._type=date or _cdate.updated_at._type=timestamp. | | _save | _save=[array] | Specifies an array of records to be inserted into database |

Run from terminal

Run a database query using a built-in command line interface

Include this line in you package json scripts

The path argument point to you local files

{
  "scripts": {
    "query": "db-essentials-query path=./PATH/TO/LOCAL_DATABASE mode=persist"
  }
}

A query must be wrapped in quotes


    npm run

    // or

    yarn query "find/users?_only=name,age&_limit=15" true

    // pass true at the end if you want to see the result as a string, otherwise you can leave it

Seeders

You can use a built-in command line script to seed your local database.

Setup

Add a line to your package.json scripts as shown in the example below.

  • first argument is the path to a directory in your app where seeders are stored.
  • second argument is the path which points to your local database files directory.
  • the third argument tells whether it should persist the data, by default it does
{
  "scripts": {
    "seed": "db-essentials-seed path/to/seeders path/to/database_files persist"
  }
}

Usage


    // table name is required

    /*
      count number is optional, defaults to 1
      if no number is specified it will run a single seed
      if you want to seed a fixed number of data at once, an array of objects, usually that's what you need
    */

    npm run seed users 77

    npm run seed categories // creates database records from an array of objects returned from the seeder

    or

    yarn seed users 77

Example

Seeder is just a function that returns an object or array of objects structured after you database model schema. Since it would bring an unncessary overhead the package has no built-in fake data generator. There are devoted packages like https://github.com/faker-js/faker you can try.

// products.js

// your seeder logic

const getRandomNumber = (min, max) =>
  Math.floor(Math.random() * (max - min + 1) + min);

const getRandomArrayElement = (arr) =>
  arr[Math.floor(Math.random() * arr.length)];

const items = [
  "Bag",
  "Battery Pack",
  "Camera",
  "Charger",
  "Cooker",
  "Electric Car",
  "Flash Drive",
  "Fridge",
  "Frying Pan",
  "Headphones",
  "Keyboard",
  "Microphone",
  "Monitor",
  "Mouse",
  "Multitool",
  "Notebok",
  "Phone",
  "Power Bank",
  "Printer",
  "Soundsystem",
  "Speakers",
  "SSD",
  "Tablet",
  "Telescope",
  "TV",
  "Vacuum Cleaner",
  "Wallet",
  "Watch",
];

const adjectives = [
  "Aluminium",
  "Cheap",
  "Elite",
  "Flexible",
  "Gaming",
  "Hybrid",
  "Mega",
  "Mini",
  "Mobile",
  "Modern",
  "Pro",
  "Slim",
  "Smart",
  "Ultra",
  "Universal",
  "Waterproof",
  "Wireless",
];

const brands = [
  "Apple",
  "Dell",
  "Essentials",
  "Hitachi",
  "HP",
  "Huawei",
  "IBM",
  "Ilyama",
  "LG",
  "Microsoft",
  "Nintendo",
  "Philips",
  "Samsung",
  "Sony",
];

// function that returns a seeder object

module.exports = async () => {
  // as you might await smth here

  return {
    name: `${getRandomArrayElement(brands)} ${getRandomArrayElement(
      adjectives
    )} ${getRandomArrayElement(items)}`,
    published: getRandomArrayElement([true, false]),
    created: new Date().toISOString(),
    in_stock: getRandomNumber(100, 0),
    sold: getRandomNumber(1001, 0),
    price: (getRandomNumber(2001, 1) + getRandomNumber(2001, 1)).toFixed(2),
  };
};

Returning an array of objects in a single seed

// fetch data from external source or create your own

module.exports = () => {
  return [
    {
      name: "LG Modern Headphones",
      in_stock: 45,
    },
    {
      name: "Samsung Ultra SSD",
      in_stock: 125,
    },
    {
      name: "Apple Slim Wallet",
      in_stock: 15,
    },
    // ...etc
  ];
};