2.0.2 • Published 2 years ago

@db-essentials/base v2.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

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

MethodHttp VerbDescription
findGETReturns all matching records
find_oneGETReturns the first matching record
countGETReturns the number of matching records
delete_oneDELETEDeletes matching record filters
delete_manyDELETEDeletes multiple records, returns an array of deleted id's
save_onePOSTSaves a record
save_manyPOSTSaves multiple records
update_onePUTUpdates the first matching record
update_manyPUTUpdates 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
ArgumentAcceptsDescription
labelany unique stringAn identifier for this particular connection, since you can have many of them running simultaneously.
databasestringA valid path to your local json files directory.
modepersist, no_persistIt tells whether you want to persist write / delete / update operations or not.
Query
ArgumentAcceptsDescription
connectionobjectAn instance of active connection.
urlstringA url that includes a valid http method (see above), name of the collection you wish to address and optional search query.
bodystringA 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.

ParamUsageDescription
_gtfield._gt=anyGreater than target
_gtefield._gte=anyEqual or greater than target
_ltfield._lt=anyLower than target
_ltefield._lte=anyEqual or lower than target
_infield._in=arrayEqual to target or one of comma separated target values
_not_infield._not_in=arrayDifferent than target or comma separated target values
_equalsfield._equals=anyEqual to target
_not_equalfield._not_equal=anyDifferent than target
_existsfield._exists=booleanChecks if the value exists, pass true or false
_typefield._type=stringChecks 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
_regexfield._regex=stringEvaluates value based on a regex expression
_array_matchfield._array_match=anyChecks if an array field contains the requested value
_array_allfield._array_all=arrayChecks if an array field contains all requested values
_array_sizefield._array_size=numberChecks if the array field is of specified size
_and_and.field=anyFilters out records that don't match all conditions : _and.name=someName&_and.value._gt=4
_or_or.field=anyReturns records that match at least one condition : _or.fieldName=value&_or.orOtherField._gt=4
_nor_nor.field=anyFilters 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.

ParamUsageDescription
_only_only=arraySpecifies which fields should be included with the response. Accepts comma separated fields or a single field. Use either this or '_except'
_except_except=arraySpecifies which fields not to icnlude with the response. Accepts comma separated fields or a single field. Use either this or '_only'
_skip_skip=numberHow many records to skip. Accepts an integer value.
_limit_limit=numberCaps results number to a specified integer value.
_sort_sort.field=numberSorts data by a specified field, should be either 1 (ascending order) or -1 (descending order).
_slice_slice=arrayGets a range of records.
_array_slice_array_slice.field=numSpecifies 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

ParamUsageDescription
_set_set.field=anyUpdateds a value to the target value, should be used with http PUT call
_inc_inc.field=numberIncrements a number value by specified positive or negative value.
_cdate_cdate.field._type=stringUpdates a field to a current date or timestamp, _cdate.updated_at._type=date or _cdate.updated_at._type=timestamp.
_save_save=arraySpecifies 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
  ];
};
2.0.2

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago