An ORM framework for Typescript that lets you fluently query the database with a strong typed programming interface.
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.vscode
docs
packages
test
.editorconfig
.gitignore
.markdownlint.json
.nycrc.json
.textlintrc.json
.travis.yml
LICENSE
README.md
lerna.json
package.json
tsconfig.json
tslint.json

README.md

Entitype

Build Status Coverage Status Dependencies Chat

Entitype is an ORM framework that provides a strong-typed, fluent programming interface. You can query the database of your choice with the help of IntelliSense without having to write any SQL or any other DSL.

The project is heavily influenced by other ORM frameworks like TypeORM and Entity Frameork. Its API is designed to resemble Entity Framework but also to conform to TypeScript coding conventions and make IntelliSense possible.

Entitype can be used in any JavaScript environment supporting ES6.

This is a work in progress. By now, only the querying is completed. If you are looking for a more mature project, try TypeORM.

Table of Contents

Installation

Install the npm package with:

npm install --save entitype

Select a plugin for database adapters and install it as well:

npm install --save entitype-mysql

Quick Start

Defining a Model

Entitype models are classes where the class and its properties are "decorated".

Example
import { Column, Entity } from 'entitype';

@Entity('customers')
export class Customer {

  @Column().type.int().primaryKey(true)
  id: number;

  @Column(`last_name`).type.varchar(50).index()
  name?: string;
}

Defining Relationships

Relationships on a table are defined by putting decorators on navigation properties.

The valid navigation property decorators are OneToOne, OneToMany, ManyToOne and ManyToMany.

Defining a One-to-Many Relationship

A One-to-Many relationship is defined by using OneToMany and ManyToOne decorators. For first parameter, the entity type which owns the foreign key for this relationship is passed. The second parameter is for declaring the foreign key.

Example
export class Customer {
  /* ........... */

  @OneToMany(type => Order, x => x.customerId)
  orders: Order[];
}

@Entity('orders')
export class Order {
  @Column().type.int().primaryKey(true)
  id: number;

  @Column(`customer_id`)
  customerId: number;

  @ManyToOne(type => Order, x => x.customerId)
  customer: Customer;
}

Defining a One-to-One Relationship

A One-to-One relationship is a special case of One-to-Many relationship and is not much different. Instead of an array for property type, a singular value is used. OneToOne decorator is used to specify this kind of relationship.

Example
export class Customer {
  /* ........... */

  @OneToOne(type => Order, x => x.customerId)
  orders: Order;  // <------ Note the difference here
}

@Entity('orders')
export class Order {
  @Column().type.int().primaryKey(true)
  id: number;

  @Column(`customer_id`)
  customerId: number;

  @OneToOne(type => Order, x => x.customerId)
  customer: Customer;
}

Defining a Many-to-Many Relationship

A Many-to-Many relationship can also be defined in Entitype. For this relationship, a mapping entity must be defined like any other entity.

Example
@Entity('employee_privileges_mapping_table')
export class EmployeePrivilege {

  @Column('employee_id').primaryKey()
  employeeId: number;

  @Column('privilege_id').primaryKey()
  privilegeId: number;
}

The entities to be mapped can be decorated with ManyToMany decorator using the mapping entity defined above. First parameter is for array type, second parameter is the mapping entity type. Third and fourth parameters are for left key and right key respectively.

Example
@Entity('employees')
export class Employee {
  /* ---- Other properties, including the primary key ----  */

  @ManyToMany(type => Privilege, joinType => EmployeePrivilege, x => x.employeeId, x => x.privilegeId)
  employeePrivileges: Privilege[];
}

@Entity('privileges')
export class Privilege {
  /* ---- Other properties, including the primary key ----  */

  @ManyToMany(type => Employee, joinType => EmployeePrivilege, x => x.privilegeId, x => x.employeeId)
  employeePrivileges: Employee[];
}

Defining the Context

A context must be defined before starting using Entitype.

Example
import { DbCollection, EntitypeContext, DbSet } from 'entitype';

export class MyContext extends EntitypeContext {

  @DbCollection(() => Customer)
  customers: DbSet<Customer>;

  @DbCollection(() => Order)
  orders: DbSet<Order>;
}

Configuring the Connection

You can specify the configuration which Entitype will use to connect to database. This must be done only once in the lifecycle of your program.

Also the plugin must be imported atleast once to resolve dependencies. The plugin to be used is specified with adapter property of the options object. Plugins are conventionally named entitype-[pluginName].

Example
import { useConfiguration } from 'entitype';
import { MysqlConnectionOptions } from 'entitype-mysql';

useConfiguration(<MysqlConnectionOptions>{
  adapter: 'mysql',
  database: 'mydb',
  host: 'localhost',
  port: 3306,
  password: '*********',
  user: 'root'
})

Querying API

The query interface of Entitype will be available over the context class. The IntelliSense helps along while writing a query so the queries can be written like a native language.

Firstly, create an instance of the context:

let ctx = new MyContext();
Query all customers:
let customers = await ctx.customers.toList();
Query only the name of the first customer:
// name is of type 'string'
let name = await ctx.customers
  .select(x => x.name)
  .first();
Query methods return a Promise as result so it can be used with `await` keyword. The result can also be used with `then` if you are not able to use *ES7 await* feature:
ctx.customers
  .select(x => x.name)
  .first()
  .then(name => console.log(`My first customer's name is ` + name));
Query the names of all the customers:
// names is of type 'string[]'
let names = await ctx.customers
  .select(x => x.name)
  .toList();
Navigation properties can also be queried. Query name and orders of all customers;
// namesAndOrders is of type '{ name: string, orders: Order[] }'
let namesAndOrders = await ctx.customers
  .select(x => ({name: x.name, orders: x.orders }))
  .toList();
A combined where query:
let customers = await ctx.customers
  .where(x => x.name).not.isNull()
  .or
  .where(x => x.id).in([5,6,7])
  .toList();
Order customers by their name, take first 10 after skipping first 5:
let customerNamesOrdered = await ctx.customers
  .orderByAscending(x => x.name)
  .skip(5)
  .take(10)
  .toList();
By tefault, navigation properties are not loaded if they are not referenced in any part of the query. Explicitly specify an include statement to load them:
let customerNamesOrdered = await ctx.customers
  .include(x => x.orders)
  .toList();

CLI

Entitype-CLI is a tool that provides utilities like automatically creating models from database, or doing migrations.

Examples