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

calustra-orm

v0.3.0

Published

A small ORM for those out there who still feel good typing some raw SQL

Downloads

156

Readme

Calustra logo

NPM Version NPM Downloads

Intro

calustra-orm is a small, minimalist ORM, for those out there who still feel good typing some raw SQL.

What it does:

  • Basic ORM-models-like usage
  • CRUD operations out of the box
  • Async/await operations
  • Transactions support
  • Automatically maintain date fields or make some extra check
  • Supports Triggers

What it does not do:

  • Query builder:
    • apart from the aforementioned CRUD stuff, you'll use raw SQL for any other query
  • Table relations:
    • it does not care about relations between tables.
    • it does not provide methods like FatherModel.getChildren()

Currently, supported databases are:

  • PostgreSQL
  • SQLite

Check calustra-conn for more info.

Install

npm install calustra-orm [--save-dev]

Get started

calustra-orm exposes just the method getConnection. It is built on top of calustra-conn. It returns an extended connection object, providing a special addendum: connection.getModel(tableName).

import {getConnection} from 'calustra-orm'

const config= {
  connection: {
    database= {
      host:     'localhost',
      port:      5432,
      database: 'calustra-orm',
      user:     'postgres',
      password: 'postgres'
    },
    options= {
      log: 'info'
    }
  },
  tables: [
    'screw_stock'
  ]
}

const connection = getConnection(config)

//
// screw_stock is a table like:
//  --------------------------------
//   id           serial,
//   screw_type   TEXT NOT NULL,
//   stock        INT
//  --------------------------------
//
const ScrewStock = connection.getModel('screw_stock')


// fill table
const data = [
  {screw_type: 'Wood Screws', stock: 1034},
  {screw_type: 'Machine Screws', stock: 3545},
  {screw_type: 'Thread Cutting Machine Screws', stock: 466},
  {screw_type: 'Sheet Metal Screws', stock: 6436},
  {screw_type: 'Self Drilling Screws', stock: 265},
  {screw_type: 'Hex Bolts', stock: 3653},
  {screw_type: 'Carriage Bolts', stock: 63},
  {screw_type: 'Tag Bolts', stock: 57}
]

for (const d of data) {
  await ScrewStock.insert(d)
}

// select many records
const read_filter = {screw_type: ['Hex Bolts', 'Tag Bolts']}
const screws = await ScrewStock.read(read_filter)

// update one record
const upd_data = {stock: 1234}
const upd_filter = {screw_type: 'Tag Bolts'}
const upd_rows = await ScrewStock.update(upd_data, upd_filter)


// clean some records
const del_filter = {screw_type: 'Tag Bolts'}
const del_rows = await ScrewStock.delete(del_filter)

API

getConnection(options)

Prepares and returns a Connection object.

options.connection

Contains the database config and its options. These both parameters are passed to calustra-conn.

options.tables

List of tables in the database which will be accessible trough getModel(). Each item in the list may be an string (the table name) or an object like this:

{
  name: 'table_name',
  schema: 'public',
  useDateFields: {
    use: false,
    fieldNames: {
      created_at: 'created_at', 
      last_update_at: 'last_update_at'
    },
    now: () => epoch_now()
  },
  
  checkBeforeDelete: [
    "another_table.field_id"
  ],
  
  triggers: {
    beforeRead   : <callback>,
    afterRead    : <callback>,
    beforeInsert : <callback>,
    afterInsert  : <callback>,
    beforeUpdate : <callback>,
    afterUpdate  : <callback>,
    beforeDelete : <callback>,
    afterDelete  : <callback>
  },
}

table.checkBeforeDelete

An array of db fields like ['table.field', 'table.field'].

It is used to prevent unwanted deletions which are not covered by a SQL relation.

It only affects to deletions which are filtered by id. For example:

import {getModel} from 'calustra-orm'
const ScrewStock = getModel(
  config, 
  'screw_stock',
  {checkBeforeDelete: ['screw_stats.screw_stock_id']}
  )

const del_filter = {id: 1}
const del_rows = await ScrewStock.delete(del_filter)

If some record exists in screw_stats table with screw_stock_id= 1, then the ScrewStock.delete will fail.

table.useDateFields

calustra-orm knows that a very extended approach is to have fields like created_at or last_update_at in your tables. This option will help with that.

Here you can specify an object like this:

  import {epoch_now} from 'intre'

  const useDateFields= {
    use: true,
    fieldNames: {
      created_at: 'created_at', 
      last_update_at: 'last_update_at'
    },
    now: () => epoch_now()
  },

You can also simply specify a boolean value. If true, above defaults will be used.

As you can imagine, calustra-orm will automatically update this fields after every insert (created_at field) or update (last_update_at field).

table.triggers

Triggers are used to customize every query phase. A Trigger is a function containing specific parameters and returning specific array of values. Available ones are:

  • beforeInsert(conn params, options) returns [params, options, allow]
  • afterInsert(conn id, params, options) return id
  • beforeUpdate(conn params, filter, options) returns [params, filter, options, allow]
  • afterUpdate(conn rows, params, filter, options) returns rows
  • beforeDelete(conn filter, options) returns [filter, options, allow]
  • afterDelete(conn rows, filter, options) returns rows

You can use them to abort queries (allow=false), to customize params on the fly before the query is executed, to customize the returning results, etc.

Connection object

Returns the same Connection object as in calustra-conn.

But this object is extended with the getModel(tableName) method, which returns a Model object.

connection.getModel(tableName)

To instantiate a Model, we will just do: const TableModel= connection.getModel(tableName).

This Model object will apply the table-related config we have passed at getConnection.

Model object

In calustra-orm, a Model object always refers to the database table; it never refers to a single record.

In other words: unlike other ORMs, you will not do const model= Model.create(); model.fieldA= 'value'; model.save(). In calustra-orm you will do Model.insert({data}) or Model.update({data}, {filter}).

model.insert(data, options)

  • data: an object with "what to insert". Fields that do not exist on Model definition will be discarded.
  • options:
    • transaction: an calustra-orm transaction object

It returns an int with the .id of the newly created record.

model.update(data, filter, options)

  • data: an object with "what to update". Fields that do not exist on Model definition will be discarded.
  • filter: an object with "which records to update". Fields that do not exist on Model definition will be discarded.
  • options:
    • transaction: an calustra-orm transaction object

It returns an int with the number of affected records by the update.

model.delete(filter, options)

  • filter: an object with "which records to delete". Fields that do not exist on Model definition will be discarded.
  • options:
    • transaction: an calustra-orm transaction object

It returns an int with the number of deleted records.

model.read(filter, options)

  • filter: an object with "which records to read". Fields that do not exist on Model definition will be discarded.
  • options:
    • fields: a subset of table's field names to include on the result output
    • sortby: indicates wat field to sort by the read. It may be an string with the field's name (sort will be ASC), or a two elements Array like [field_name, ASC|DESC]
    • limit and offset: to make paginated reads
    • transaction: an calustra-orm transaction object

It returns an Array of objects, empty if no record was found with the specified filter.

model.find(id, options)

  • id: an int with the .id to look for
  • options:
    • transaction: an calustra-orm transaction object

It returns an object with the desired record, empty if none was found.

Todo

  • Transaction creation out of the box