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.
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
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;
}
Relationships on a table are defined by putting decorators on navigation properties.
The valid navigation property decorators are OneToOne
, OneToMany
, ManyToOne
and ManyToMany
.
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;
}
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;
}
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[];
}
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>;
}
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'
})
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();
Entitype-CLI is a tool that provides utilities like automatically creating models from database, or doing migrations.
- Angular example app using WebSQL adapter.
- React example app using WebSQL adapter.