Orm
Instant ORM: JavaScript ORM for Postgres
Install / Use
/learn @instant-dev/OrmREADME
Instant ORM
JavaScript ORM for Postgres with built-in Vector Support
This is the core ORM package for instant.dev.
It is recommended that you use it with the instant command line utility
available at instant-dev/instant for
easy migration management, however, it can be used as a standalone ORM. By
default, upon connecting to a database, the Instant ORM will introspect your
Database schema and determine appropriate models and relationships.
Table of Contents
- Getting Started
- Connecting to a Database
- Loading a Schema
- Loading custom Model logic
- Using Models
- Using Migrations, Seeding and Code Generation
- Acknowledgements
Getting Started
Installing the Instant ORM:
npm i @instant.dev/orm@latest --save
Initializing (CommonJS):
const InstantORM = require('@instant.dev/orm');
const Instant = new InstantORM();
Initializing (ESM):
import InstantORM from '@instant.dev/orm';
const Instant = new InstantORM();
Connecting to a Database
By default, the Instant ORM will attempt to load database credentials from
_instant/db.json[process.env.NODE_ENV]["main"]:
await Instant.connect(); // connects based on _instant/db.json
However, you can also provide custom credentials to any database you'd like
by passing in a cfg configuration object with the credentials in the following
format:
const cfg = {
host: 'my.postgres.host',
port: 5432,
user: 'postgres',
password: '',
database: 'postgres',
ssl: false, // optional: acceptable values are [true, false, "unauthorized"]
in_vpc: false, // optional: if false, will use provided SSH tunnel when deployed
tunnel: { // optional: use this if we need to SSH tunnel into database
host: 'my.ssh.host.com',
port: 22,
user: 'ec2-user',
private_key: 'path/to/private_key.pem'
}
};
await Instant.connect(cfg); // now connected to custom Database
You can also opt to provide a connectionString instead:
const cfg = {
connectionString: 'postgres://postgres:mypass@my.postgres.host:5432/postgres?sslmode=true',
in_vpc: false, // optional: if false, will use provided SSH tunnel when deployed
tunnel: { // optional: use this if we need to SSH tunnel into database
host: 'my.ssh.host.com',
port: 22,
user: 'ec2-user',
private_key: 'path/to/private_key.pem'
}
};
await Instant.connect(cfg); // now connected to custom Database
Connecting to another database
By default, the Instant.connect() method will assign your initial database
connection the alias "main". You can access your Database object directly
via:
const db = Instant.database();
const mainDb = Instant.database('main');
console.log(db === mainDb); // true, "main" is an alias for your main db
To connect to another database, simply use:
// connect
Instant.addDatabase(name, cfg);
// read
const otherDb = Instant.database(name);
Querying your databases directly
Querying your database directly is easy. To run a standalone query;
const db = Instant.database();
const result = await db.query(`SELECT * FROM my_table WHERE x = $1`, [27]);
To execute a batched transaction from prepared statements and queries;
const db = Instant.database();
// Pass in an array of statements
const result = await db.transact([
`SELECT * FROM my_table`,
`INSERT INTO my_table(field) VALUES((1))`,
// Parameterized statements can be passed in as well
[`INSERT INTO my_other_table(other_field) VALUES(($1))`, [2]]
]);
And to create a transaction that you want to work with in real-time, potentially querying third party services before deciding whether or not to commit the query:
const db = Instant.database();
const txn = db.createTransaction();
let result = await txn.query(`SELECT * FROM my_table WHERE x = $1`, [27]);
let result2 = await txn.query(`INSERT INTO my_table(field) VALUES(($1))`, [5]);
let manyQueries = await txn.transact([
`SELECT * FROM my_table`,
`INSERT INTO my_table(field) VALUES((1))`,
]);
// to commit
await txn.commit();
// to rollback
await txn.rollback();
Disconnecting
To disconnect from a specific database:
Instant.closeDatabase(name);
And to disconnect from all open databases and reset your connection:
await Instant.disconnect();
Loading a Schema
When you connect to a database, Instant ORM will attempt to determine the schema of your database in a few ways.
- First, it will check to see if
_instant/cache/schema.jsonexists- If it does, it will load the schema from this file
- Next, it will check to see if an
_instant_migrationstable exists in your database- This table holds all migrations applied to the database and is generated by the instant.dev CLI automatically
- If it does exist and has entries, it will load the schema from the latest migration
- Finally, it will introspect your database structure
- All tables, columns, sequences and constraints will be inspected
- Foreign keys and uniqueness will be used to determine one-to-one and one-to-many relationships
Additionally, you can also pass a custom schema object to the
Instant.connect(cfg) method as a second argument, but this is
not recommended. It is usually reserved for testing purposes.
Loading custom Model logic
By default, the Instant ORM will load models from the _instant/models
directory.
You do not need a model file for every, or even any, table in your database.
These are only meant to extend models in the case you want to add
Lifecycle callbacks, validations, verifications,
calculated fields or hide data. Each file should look something like this;
File: _instant/models/sample_model.mjs
import InstantORM from '@instant.dev/orm';
class SampleModel extends Model {
static tableName = 'sample_models';
async beforeSave (txn) {}
async afterSave (txn) {}
async beforeDestroy (txn) {}
async afterDestroy (txn) {}
}
SampleModel.calculates(/* ... */);
SampleModel.validates(/* ... */);
SampleModel.verifies(/* ... */);
SampleModel.hides(/* ... */);
export default SampleModel;
The Instant ORM will automatically associate each file with the appropriate
table in your database schema, provided SampleModel.tableName matches a table
on your Database. You can access your Models using;
// Note that "SampleModels", "samplemodel", "sample_models" etc.
// will all work as well as long as there's no ambiguity
Instant.Model('SampleModel');
Using Models
Models are accessible via the Instant.Model(modelName) method. This method
will automatically look up the most likely model based on the matching table
in your database schema.
const User = Instant.Model('User');
This method would also accept the strings Users, user, users. If your
table has pluralization and underscores we recommend using the singular version,
but you can access using the table name as well. For example, the table name
object_children could be accessed via:
const ObjectChild = Instant.Model('ObjectChild'); // recommended
However, the following would also work:
Instant.Model('ObjectChildren');
Instant.Model('object_child');
Instant.Model('object_children');
In the case of ambiguity - multiple tables potentially matching the object name -
Instant.Model() will throw an error and ask you to use the specific table.
CRUD Operations
Create
You can create new model instances and save them to the database with
Model.create(data) or new Model(data) and then a subsequent model.save():
const User = Instant.Model('User');
// Model.crea
