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

@janiscommerce/model

v8.5.0

Published

A package for managing Janis Models

Downloads

2,394

Readme

model

Build Status Coverage Status npm version

📦 Installation

npm install @janiscommerce/model

Breaking changes Since 6.0.0 :warning:

When use changeKeys param and cannot get any items, it will return an empty object (before returns an empty array)

🔧 Database connection settings

In order to use this package with a DB, you must to add the database connection settings, it can be setted in service settings janiscommercerc.json (Core model) or in session (Client Model).

Configure database connection with databaseKey

Regardless if you use a Core or Client model you may set the databaseKey that your model will use to get the database connection settings. Default: 'default'.

class MyModel extends Model {

	get databaseKey() {
		return 'myDatabaseKey';
	}
}

👀 Either in Core or Client model the databaseKey connection settings structure is the same:

:information_source: The type property is the only one used by this package to fetch the correct DB Driver package to use. :warning: The rest of the connection properties depends entirely by the DB Driver that you will use.

{
	myDatabaseKey: {
		write: {
			"type": "mongodb",
			"host": "http://write-host-name.org",
			// ...
		},
		read: {
			"type": "mongodb",
			"host": "http://read-host-name.org",
			// ...
		}
	}
}

:information_source: Model types

There are two different model types:

:one: Set the databaseKey in your Model extended class

class MyModel extends Model {

	get databaseKey() {
		return 'core';
	}
}

:two: Model will try to find your databaseKey in database service settings

Using Settings, with settings in file /path/to/root/.janiscommercerc.json:

{
	"database": {
		"core": {
			"write":{
				"host": "http://my-host-name.org",
				"type": "mysql",
				// ...
			}
		}
	}
}

💉 Session injection

The session injection is useful when you have a dedicated database per client. Using the public setter session, the session will be stored in the controller instance. All the controllers and models getted using that controller will have the session injected.

:one: Set the databaseKey in your Model extended class

class MyModel extends Model {

	get databaseKey() {
		return 'myDatabaseKey';
	}
}

:two: Database connection configured with session injected

Your client should have the config for read (optional) and/or write (required) databases.

Example of received client:

{
	"name": "Some Client",
	"code": "some-client",

	"databases": {

		"default":{

			"write": {
				"type": "mongodb",
				"host": "http://default-host-name.org",
				// ...
			}
		},

		"myDatabaseKey": {

			"write": {
				"type": "mongodb",
				"host": "http://write-host-name.org",
				// ...
			},
			"read": {
				"type": "mongodb",
				"host": "http://read-host-name.org",
				// ...
			}
		}
	}
}

:outbox_tray: Getters

  • shouldCreateLogs (static getter). Returns if the model should log the write operations. Default: true. For more details about logging, read the logging section.

  • excludeFieldsInLog (static getter). Returns the fields that will be removed from the logs as an array of strings. For example: ['password', 'super-secret']. For more details about logging, read the logging section.

  • statuses (class getter). Returns an object with the default statuses (active / inactive)

  • executionTime (class getter). Returns the time spent in ms on the las query.


:inbox_tray: Setters

  • useReadDB [Boolean] (class setter) Set if model should use the read DB in all read/write DB operations. Default: false.

:gear: API

async getDb()

Example:

const dbDriver = await myModel.getDb();

await myModel.dbDriver.specialMethod(myModel);

async hasReadDB()

Example:

const hasReadDB = await myModel.hasReadDB();

async get(params)

Parameters

  • params is an optional Object with filters, order, paginator.
  • params.readonly as true if you want to use the Read Database.

Example

const items = await myModel.get({ filters: { status: 'active' } });

async getById(id, [params])

Parameters

  • id is required. It can be one ID or an array of IDs
  • params is an optional Object with filters, order, paginator, changeKeys.

Example

const items = await myModel.getById(123, { filters: { status: 'active' } });

async getBy(field, id, [params])

Parameters

  • field is required. A string as a field
  • value is required. It can be one value or an array of values
  • params is an optional Object with filters, order, paginator.

Example

const items = await myModel.getBy(orderId, 123, { filters: { status: 'active' } });

async getPaged(params, callback)

Parameters

  • params See get() method
  • callback A function to be executed for each page. Receives three arguments: the items found, the current page and the page limit

Example

await myModel.getPaged({ filters: { status: 'active' } }, (items, page, limit) => {
	// items is an array with the result from DB
});

Default order

The default order when no order was received is field id using asc as order direction. Since 6.8.3

async getTotals(filters)

Parameters

  • filters is an optional. Object with filters or array of filters. Since 7.1.0

Result object structure:

  • pages: The total pages for the filters applied
  • page: The current page
  • limit: The limit applied in get
  • total: The total number of items in DB for the applied filters

Example

await myModel.get({ filters: { status: 'active' } });
const totals = await myModel.getTotals();
/**
	totals content:
	{
		pages: 3,
		page: 1,
		limit: 500,
		total: 1450
	}
*/
const totals = await myModel.getTotals( { status: 'active' } );
/**
	totals content:
	{
		pages: 3,
		page: 1,
		limit: 500,
		total: 1450
	}
*/

async mapIdBy(field, fieldValues, [params])

Parameters

  • field Field to filter for (String)
  • fieldValues List of values to filter for (Array<strings>)
  • params See get() method

Example

await myModel.mapIdBy('code', ['code-123', 'code-456'], {
	order: { code: 'desc' }
});

/**
	{
		code-456: 'the-code-456-id',
		code-123: 'the-code-123-id'
	}
*/

async mapIdByReferenceId(referencesIds, [params])

Parameters

  • referencesIds List of References Ids (Array<strings>)
  • params See get() method

Example

await myModel.mapIdByReferenceId(['some-ref-id', 'other-ref-id', 'foo-ref-id'], {
	order: { date: 'asc' },
	filters: { foo: 'bar' }
});

/**
	{
		some-ref-id: 'some-id',
		foo-ref-id: 'foo-id'
	}
*/

async distinct(key, params)

Parameters

  • params is an optional Object with filters.

Examples

const uniqueValues = await myModel.distinct('status');
const uniqueValues = await myModel.distinct('status', {
	filters: {
		type: 'some-type'
	}
});

async insert(item)

Example

await myModel.insert({ foo: 'bar' });

const items = await myModel.get({ filters: { foo: 'bar' }});

/**
	itemInserted content:
	[
		{
			foo: 'bar'
		}
		//...
	]
*/

async save(item, setOnInsert)

Parameters

  • setOnInsert to add default values on Insert, optional

Example

await myModel.save({ foo: 'bar' }, { status: 'active' });

const items = await myModel.get({ filters: { foo: 'bar' }});

/**
	items content:
	[
		{
			foo: 'bar',
			status: 'active'
		}
		//...
	]
*/

async update(values, filter, params)

Parameters

  • params optional parameters to define some behavior of the query
    • skipAutomaticSetModifiedData: Boolean. When receive as true, the fields dateModified and userModified are not updated automatically.

Example

await myModel.update({ updated: 1 }, { status: 5 });
// will set updated = 1 for the items that has status = 5

async remove(item)

Example

await myModel.remove({ foo: 'bar' });

const items = await myModel.get({ filters: { foo: 'bar' }});

/**
	items content:
	[]
*/

async multiInsert(items)

Example

await myModel.multiInsert([{ foo: 1 }, { foo: 2 }]);

const items = await myModel.get();

/**
	items content:
	[
		{ foo: 1 },
		{ foo: 2 }
	]
*/

async multiSave(items, setOnInsert)

Parameters

  • setOnInsert to add default values on Insert, optional

Example

await myModel.multiSave([{ foo: 1 }, { foo: 2, status: 'pending' }], { status: 'active' });

const items = await myModel.get();

/**
	items content:
	[
		{ foo: 1, status: 'active' },
		{ foo: 2, status: 'pending' }
	]
*/

async multiRemove(filter)

Example

await myModel.multiRemove({ status: 2 });

const items = await myModel.get({ filters: { status: 2 }});

/**
	items content:
	[]
*/

async increment(filters, incrementData)

Example

await myModel.increment({ uniqueIndex: 'bar' }, { increment: 1, decrement: -1 });

/**

before:
	items content:
	[
		{
			increment: 1,
			decrement: 2
		}
		//...
	]

after:
	items content:
	[
		{
			increment: 2,
			decrement: 1
		}
		//...
	]
*/

static changeKeys(items, newKey)

Parameters

  • items The items array
  • newKey The common field in items that will be used as key for each item

Example

const myItems = await myModel.get();

/*
	[
		{ some: 'item', otherProp: false },
		{ some: 'otherItem', otherProp: true }
	]
*/

const myItemsByKey = MyModel.changeKeys(myItems, 'some');

/*
	{
		item: { some: 'item', otherProp: false },
		otherItem: { some: 'otherItem', otherProp: true }
	}
*/

:information_source: In get methods such as get and getBy you can add the changeKeys param with the newKey value.

const myItems = await myModel.get({ changeKeys: 'some' });

/*
	{
		item: { some: 'item', otherProp: false },
		otherItem: { some: 'otherItem', otherProp: true }
	}
*/

:information_source: Since 6.0.0: When no items were found it will return an empty object

const myItems = await myModel.get({ filters: { status: 'active' }, changeKeys: 'other' });

/*
	{}
*/

async dropDatabase()

Example

await myModel.dropDatabase();

async aggregate(stages)

:warning: Not supported by all database connectors

Parameters

  • stages An array with the aggregation stages

Example


const results = await myModel.aggregate([
	{ $match: { id: '0000000055f2255a1a8e0c54' } }, // find the document with that id
	{ $unset: 'category' }, // Removes the category field
]);

/**
	[
		{
			id: '0000000055f2255a1a8e0c54',
			name: 'Product 1',
			description: 'Product 1 description'
		}
	]
*/

:bookmark_tabs: Indexes Manipulation

:warning: Only if DB Driver supports it

async getIndexes()

Example

await myModel.getIndexes();

/*
	[
		{ name: '_id_', key: { _id_: 1}, unique: true },
		{ name: 'code', key: { code: 1} }
	]
*/

async createIndex(index)

Example

await myModel.createIndex({ name: 'name', key: { name: 1}, unique: true });

async createIndexes(indexes)

Example

await myModel.createIndexes([{ name: 'name', key: { name: 1}, unique: true }, { name: 'code', key: { code: -1 }}]);

async dropIndex(name)

Example

await myModel.dropIndex('name');

async dropIndexes(names)

Example

await myModel.dropIndexes(['name', 'code']);

async getIdStruct()

Example (for mongodb DB)

const idStruct = await myModel.getIdStruct();

/*
	struct('objectId')
*/

:clipboard: Logging

This package automatically logs any write operation such as:

  • insert()
  • multiInsert()
  • update()
  • save()
  • multiSave()
  • increment()
  • remove()
  • multiRemove()

:information_source: The logs will be added using the package @janiscommerce/log.

Automatic data in log

The package will add automatic fields in the log Object field.

  • executionTime. Each log will have the time spent on the query. Since 6.6.0
  • itemsBatch. Exclusively for methods multiInsert() and multiSave(), will be added the quantity of items inserted or updated in the same query. Since 6.6.0

:no_mouth: Disabling automatic logging

  1. For all Model operation: by setting the static getter shouldCreateLogs to false.

Example

class MyModel extends Model {

	static get shouldCreateLogs() {
		return false;
	}
}
  1. For the "next" operation: by using the method disableLogs(). Since 8.3.0

:information_source: The logs are disabled only for the following operation

Example


// will not save logs
await myModel.disableLogs().insert({
	pet: 'roger',
	animal: 'dog',
	age: 8
});

// will save logs normally
await myModel.insert({
	pet: 'ringo',
	animal: 'dog',
	age: 7
});

:no_entry_sign: Excluding fields from logs

Specify the fields to exclude by setting them in the static getter excludeFieldsInLog:

class MyModel extends Model {

	static get excludeFieldsInLog() {
		return [
			'password',
			'address',
			'secret'
		]
	}
}

By setting this when you do an operation with an item like:

await myModel.insert({
	user: 'johndoe',
	password: 'some-password',
	country: 'AR',
	address: 'Fake St 123'
});

It will be logged as:

{
	id: '5ea30bcbe28c55870324d9f0',
	user: 'johndoe',
	country: 'AR'
}

:memo: Set custom log data

Adding a custom message:

await myModel
  .setLogData('custom message!')
  .insert({ foo: 'bar' });

/*
	Log: {
		...logData,
		message: 'custom message!'
	}
*/

Adding a custom object data:

await myModel
  .setLogData({message:'custom message!', type:'some type'})
  .insert({ foo: 'bar' });

/*
	Log: {
		...logData,
		message: 'custom message!',
		type:'some type'
	}
*/

Adding a custom object data with log property name:

await myModel
  .setLogData({message:'custom message!', type:'some type', log: { isTest: true }})
  .insert({ foo: 'bar' });

/*
	Log: {
		...logData,
		message: 'custom message!',
		type:'some type',
		log:{
			...defaultModelLogData,
			isTest: true
		}
	}
*/

🔑 Secrets

The package will get the secret using the JANIS_SERVICE_NAME environment variable. If the secret is found, the result will be merged with the settings found in the janiscommercerc.json file or in the Client databases configuration. See Database connection settings.

The Secrets are stored in AWS Secrets Manager and obtained with the package @janiscommerce/aws-secrets-manager

  1. Settings in file or Client.
{
	"core": {
		"write": {
			"type": "mongodb",
			"database": "core",
			"otherDBDriverConfig": 100
		}
	}
}
  1. Secret fetched.
{
	"databases": {
		"core": {
			"write": {
				"host": "mongodb+srv://some-host.mongodb.net",
				"user": "secure-user",
				"password": "secure-password",
			}
		}
	}
}
  1. Config passed to the Database Driver.
{
	"core": {
		"write": {
			"type": "mongodb",
			"database": "core",
			"otherDBDriverConfig": 100,
			"host": "mongodb+srv://some-host.mongodb.net",
			"user": "secure-user",
			"password": "secure-password",
		}
	}
}

Skip Credential Fetching

To skip the fetch of the credentials, it can be used the setting skipFetchCredentials set as true.

{
	"core": {
		"write": {
			"skipFetchCredentials": true,
			"type": "mongodb",
			"protocol": "mongodb+srv://",
			"host": "mongodb+srv://some-host.mongodb.net",
			"user": "some-user",
			"password": "insecure-password"
		}
	}
}