What is Kinship?
Kinship is a Query Builder and a relatively new approach (in JavaScript and NodeJS) to interfacing with your back-end databases using strong type mapping and friendly syntax to enhance your development experience.
You can learn more about Kinship on the Kinship website
Get Started
Install dependencies
npm i -D @kinshipjs/core
# adapter:
npm i -D @kinshipjs/mysql2 # Interface with a MySQL database
# or
npm i -D @kinshipjs/mssql # Interface with a Microsoft SQL Server database
npm i -D @kinshipjs/postgres # Interface with a PostGRES database
npm i -D @kinshipjs/sqlite # Interface with a SQLite database/file
npm i -D @kinshipjs/mongodb # Interface with your mongodb database
npm i -D @kinshipjs/json # Interface with a javascript object
Initialize types
Initialize your types for Kinship to help you work with.
interface User {
id?: string;
firstName: string;
lastName: string;
username: string;
userRoles?: xUserRole[];
};
interface Role {
id?: number;
title: string;
description: string;
userRoles?: xUserRole[];
};
interface xUserRole {
userId?: string;
roleId?: number;
user?: User;
role?: Role;
}
Initialize contexts
Construct KinshipContext
objects to connect to your database tables.
import { KinshipContext } from '@kinshipjs/core';
import { adapter, createMySql2Pool } from '@kinshipjs/mysql2';
const pool = createMySql2Pool({
host: 'localhost',
port: 3306,
database: 'auth',
user: 'root',
password: 'root'
});
const connection = adapter(pool);
const users = new KinshipContext<User>(connection, "User");
const roles = new KinshipContext<Role>(connection, "Role");
const userRoles = new KinshipContext<xUserRole>(connection, "xUserRole");
Configure relationships (optional)
Configure one-to-one and one-to-many relationships between tables.
users.hasMany(m => m.userRoles.fromTable("xUserRole").withKeys("id", "userId")
.andThatHasOne(m => m.role.fromTable("Role").withKeys("roleId", "id")));
roles.hasMany(m => m.userRoles.fromTable("xUserRole").withKeys("id", "roleId")
.andThatHasOne(m => m.role.fromTable("User").withKeys("userId", "id")));
users.hasMany(m => m.userRoles.from(xUserRoles, m => m.id, m => m.userId)
.andThatHasOne(m => m.role.from(roles, m => m.roleId, m => m.id)));
Configure triggers (optional and allows for advanced work)
Configure triggers to execute before or after certain commands are executed.
Triggers can be helpful if your application is planned to handle any sort of default values.
import { v4 } from 'uuid'
users.beforeInsert((m) => {
m.id = v4();
});
users.beforeInsert((m, { $$itemNumber, numRecordsDoubled, numUsersWithoutMiddleName }) => {
m.id = $$numRecordsDoubled * numUsersWithoutMiddleName;
}, async ({ $$numRecords }) => {
const x = await users.where(m => m.middleName.equals(null).or(m => m.middleName.equals(""))).count();
return {
numRecordsDoubled: $$numRecords * 2,
numUsersWithoutMiddleName: x
};
});
Configure event handlers (optional)
Configure event handlers to execute after a command successfully or unsuccessfully executes.
users.onSuccess(({ dateISO, cmdRaw }) => {
console.log(`${dateISO}: ${cmdRaw}`);
});
users.onFail(({ dateISO, cmdRaw, err }) => {
console.log(`${dateISO}: ${cmdRaw}`);
console.error(err);
});
Query records
Query records using various clauses.
const allUsers = await users;
const allUsersAndRoles = await users.include(m => m.userRoles.thenInclude(m => m.role));
const onlyUsersWithFirstNameJohn = await users.where(m => m.firstName.equals("John"));
const usersSortedByLastNameZtoA = await users.sortBy(m => m.lastName.desc());
const usersGroupedByFirstName = await users.groupBy((m, aggregates) => [m.firstName, aggregates.total()]);
const firstUser = await users.take(1);
const secondUser = await users.skip(1).take(1);
const onlyIds = await users.select(m => m.Id);
const onlyFirstNameAndLastName = await users.select(m => [m.FirstName, m.LastName]);
Insert records
Insert one or more records.
const user = {
firstName: "John",
lastName: "Doe",
roles: [
{
role: {
title: "New-Role",
description: "This is a new role"
}
}
]
}
const insertedUser = await users.insert(user);
const insertedUsers = await users.insert([
{ firstName: "Joanne", lastName: "Doe" },
{ firstName: "Jane" lastName: "Doe" }
]);
Update records
Update one or more records implicitly (using objects that have the primary key already defined) or explicitly (using a where clause)
const [user] = await users.take(1);
user.firstName = "Jordan";
const numRowsAffected = await users.update(user);
await users.where(m => m.id.equals(1)).update(m => {
m.firstName = "Jordan";
});
await users.where(m => m.id.equals(1)).update(m => {
return {
...m,
firstName: "Jordan"
};
});
Delete records
Delete one or more records implicitly (using objects that have the primary key already defined) or explicitly (using a where clause)
const [user] = await users.take(1);
await users.delete(user);
await users.where(m => m.id.equals(1)).delete();
Truncate records
Truncate your entire table. (requires property disableSafeDeleteMode
to be true in the options
on the constructor)
await users.truncate();
All or nothing transactions
Call multiple transactional functions where if one fails, then all will fail.
import { transaction } from '@kinshipjs/core';
const config = { };
const cnn = adapter(createMssqlPool(config));
const users = new KinshipContext<{ Id?: number, FirstName: string, LastName: string }>(cnn, "dbo.User");
const xUserRoles = new KinshipContext<{ UserId?: number, RoleId?: number }>(cnn, "dbo.xUserRole");
const roles = new KinshipContext<{ Id?: number, Title: string, Description?: string }>(cnn, "dbo.Role");
async function giveUserAdminRole(firstName: string, lastName: string) {
return await transaction(cnn)
.execute(async (tnx) =>
{
const $users = users.using(tnx);
const $xUserRoles = xUserRoles.using(tnx);
const $roles = roles.using(tnx);
const [johnDoe] = await $users
.where(m => m.FirstName.equals(firstName)
.and(m => m.LastName.equals(lastName)));
const johnDoesCurrentRoles = await $xUserRoles.where(m => m.UserId.equals(johnDoe.Id));
await $xUserRoles.delete(johnDoesCurrentRoles);
const [adminRole] = await $roles.where(m => m.Title.equals("Admin"));
if(!adminRole) {
throw rollback();
}
const [xUserRole] = await $xUserRoles.insert({
UserId: johnDoe.Id,
RoleId: adminRole.Id
});
return { ...johnDoe, xUserRoles: [{ ...xUserRole, Role: adminRole }]};
});
}
const johnDoe = await giveUserAdminRole("John", "Doe");
console.log(johnDoe);
If, for example, you have two different database connections, then you would do something like this:
import { createMssqlPool } from '@kinshipjs/mssql';
import { transaction } from '@kinshipjs/core';
const loginsCfg = { };
const loginsCnn = adapter(createMssqlPool(loginsCfg));
const users = new KinshipContext<{ Id?: number, FirstName: string, LastName: string }>(loginsCnn, "dbo.User");
const xUserRoles = new KinshipContext<{ UserId?: number, RoleId?: number }>(loginsCnn, "dbo.xUserRole");
const roles = new KinshipContext<{ Id?: number, Title: string, Description?: string }>(loginsCnn, "dbo.Role");
const mainCfg = { };
const mainCnn = adapter(createMssqlPool(mainCfg));
const mainUsers = new KinshipContext<{ Id?: number, LoginUserId?: number }>(mainCnn, "dbo.User");
const msg = await transaction(loginsCnn).execute(async tnx => {
const $users = users.using(tnx);
const $xUserRoles = xUserRoles.using(tnx);
const $roles = roles.using(tnx);
return await transaction(mainCnn).execute(async mainTnx => {
const $mainUsers = mainUsers.using(mainTnx);
const [user] = $users.insert({ FirstName: "John", LastName: "Doe" });
const [mainUser] = await mainUsers.insert({ LoginUserId: user.Id });
return "Success!";
});
});
console.log(msg);
Kinship Adapters
- @kinshipjs/json: Connect to a JSON-like schema/database and manage that object using Kinship. (Good for development/testing or local storage!)
- @kinshipjs/mysql2: Connect to a MySQL database using the Node.js
mysql2
ORM. - @kinshipjs/mssql: Connect to a SQL Server database using the Node.js
mssql
ORM. - @kinshipjs/sqlite3: Connect to a SQLite file database using the Node.js
sqlite3
ORM. (in development) - @kinshipjs/postgres: Connect to a PostgreSQL database using the Node.js
pg
ORM. (in development)
More Kinship Tools