The fastest TypeScript ORM for MongoDB, MySQL, PostgreSQL, SQLite.
With data-mapper, active-record, UnitOfWork, identity map, migrations, relations support, and much more.
Use plain TypeScript — no code-generation, schema, or config files required.
Interactive TypeScript playground with SQLite database — in your browser.
Edit your database content from your models, test queries, view relations, or seed your database directly in the browser using Deepkit ORM Browser.
Just write your ORM entities as regular TypeScript classes or interfaces, and annotate properties with database specific features. It's that simple.
import { PrimaryKey, AutoIncrement, Email, Index, MinLength, Unique } from '@deepkit/type'; class User { id: number & PrimaryKey & AutoIncrement = 0; createdAt: Date = new Date; lastName?: string; firstName?: string; email?: string & Email & Index; constructor( public username: string & MinLength<3> & Unique ) {} }
Database entities can also be defined in a interfaces or computed types without the need to use an actual class.
import { PrimaryKey, AutoIncrement, Email, Index, MinLength, Unique } from '@deepkit/type'; interface User { id: number & PrimaryKey; createdAt: Date; lastName?: string; firstName?: string; email?: string & Email & Index; username: string & MinLength<3> & Unique; }
The query API allows you to fetch and manipulate data in a type-safe manner.
Filtering uses an Mongo-Like query interface, that works for every database (even SQL) the same.
The API is designed to build cross-database queries that work on every database the same.
const result = await database.query(User) .orderBy('createdAt') .limit(5) .patchMany({credit: {$inc: 100}}); //reports affected records, e.g. [1, 2, 3, 4, 5] result.primaryKeys; //also returns the new credit value result.returning.credit; //e.g. [100, 200, 100, 300, 100] const result = await database.query(User) .filter({disabled: true}) .deleteMany(); //reports affected records, e.g. [1, 2, 3, 4, 5] result.primaryKeys;
const user = await database.query(User) .filter({username: 'Peter') .findOne(); //returns User class instance const users = await database.query(User) .orderBy('lastLogin') .limit(10) .find(); //returns array of User class instance const users = await database.query(User) //Mongo-Like queries for cross-databases queries // lastLogin > 10 ($gt = greater than) .filter({lastLogin: {$gt: 10}) .limit(10) .find(); //aggregation queries: Get user count in each group const users = await database.query(User) .groupBy('group') .withCount('id', 'users') .find(); // [{group: 'teamA', users: 5}]
Define your relations in terms of object references, and let Deepkit ORM handle the rest.
const books = await database.query(Book) .joinWith('author') .find(); for (const book of books) { //access the eagerly loaded relation book.author.username; } const books = await database.query(Book) .useInnerJoinWith('author') //add additional filter to the join .filter({username: 'Peter'}) .end() .find();
import { PrimaryKey, Reference, BackReference, MinLength } from '@deepkit/type'; class Author { id: number & PrimaryKey = 0; createdAt: Date = new Date; lastName?: string; firstName?: string; books: Book[] & BackReference = []; constructor( public username: string & MinLength<3> ) {} } class Book { id: number & PrimaryKey = 0; createdAt: Date = new Date; description: string = ''; constructor( public title: string & MinLength<3>, public author: Author & Reference, ) {} }
The easiest way to work with your entities. Handle ten-thousands of instances at the same time and let the UoW insert or update them in the most efficient way possible.
The identity map makes sure that all persisted and fetched instances in one session are uniquely identifiable and always the same instance.
const session = database.createSession(); for (let i = 0; i < 10_000; i++) { session.add(new User('User ' + i)); } //all items are inserted extremely fast in one //transaction with a single query await session.commit(); const users = session.query(User).find(); for (const user of users) { if (user.credit < 1000) user.credit += 100; } //changes are automatically detected and //patched to the database await session.commit();
For prototyping purposes Deepkit ORM also supports the ActiveRecord pattern. It allows you to directly work with the entity class, without accessing a Database object.
import { PrimaryKey } from '@deepkit/type'; import { ActiveRecord } from '@deepkit/orm'; class User extends ActiveRecord { id: number & PrimaryKey = 0; createdAt: Date = new Date; //... } const user = new User('Marie'); await user.save(); const users = await User.query() .filter({logins: ${gt: 10}}) .find(); for (const user of users) { user.credit += 10; await user.save(); }
You can hook into the UoW process or query execution using asynchronous event listeners that are for example able to modify the query itself.
This allows you to write plugins or change the behavior of your entities.
//onFetch is called for find(), findOne(), // findOneOrUndefined(), etc database.queryEvents.onFetch.subscribe((event) => { if (event.isSchemaOf(User)) { //modify the query event.query = event.query.addFilter({deleted: false}); } }); //onDeletePost is called after //.deleteOne()/.deleteMany() successfully executed database.queryEvents.onDeletePost.subscribe((event) => { //primaryKeys contains each primary key for //all affected records for (const id of event.deleteResult.primaryKeys) { await event.databaseSession.persist( new AuditLog('deleted', event.classSchema.getName()) ); } });
Migrations help you migrate schema changes for SQL databases in an easy yet effective way.
The migration file is automatically generated based on the difference between the actual schema of your database and your schema defined in your TypeScript code.
You can modify the generated schema migration as you wish and commit to Git, so your colleagues and deploy procedure can update the database schema correctly.
class User { id: number & PrimaryKey & AutoIncrement = 0; username: string = ''; } //version 2 adds new fields class User { id: number & PrimaryKey & AutoIncrement = 0; username: string = ''; lastName: string = ''; firstName: string = ''; }
//my-app/migration/20200917-1727.ts import {Migration} from '@deepkit/framework'; export class SchemaMigration implements Migration { name = `extended user`; adapterName = "sqlite"; version = 1600362068; up() { return [ `ALTER TABLE "user" ADD COLUMN "lastName" TEXT` `ALTER TABLE "user" ADD COLUMN "firstName" TEXT` ]; } down() { return [ `ALTER TABLE "user" DROP COLUMN "lastName"` `ALTER TABLE "user" DROP COLUMN "firstName"` ]; } }
Reuse type-safe query classes and organize database access in the most efficient way.
import { Query } from '@deepkit/orm'; class SoftDeleteQuery<T extends {deletedAt?: Date}> extends Query<T> { findDeleted(): Promise<T[]> { return this.filter({deletedAt: {$ne: undefined}}).find(); } } const deletedUser = await database.query(User) .lift(SoftDeleteQuery) .findDeleted();