Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
@kinshipjs/core
Advanced tools
Readme
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
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 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;
}
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 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")));
// or
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 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'
// Always assign a uuid to a User's id column before they are inserted.
users.beforeInsert((m) => {
m.id = v4();
});
// Use a hook to set up any variables that only need to be set up once.
// parameters that start with "$$" are always accessible to you.
users.beforeInsert((m, { $$itemNumber, numRecordsDoubled, numUsersWithoutMiddleName }) => {
// This function will fire for EVERY record being inserted.
m.id = $$numRecordsDoubled * numUsersWithoutMiddleName; // definitely not recommended, just using it as an example.
}, async ({ $$numRecords }) => {
// This function will fire only ONCE per `.insert()` call.
const x = await users.where(m => m.middleName.equals(null).or(m => m.middleName.equals(""))).count();
// any returned variables are immediately accessible in the main trigger function in the `args` parameter.
return {
numRecordsDoubled: $$numRecords * 2,
numUsersWithoutMiddleName: x
};
});
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 using various clauses.
// any of these clauses can be used in any given order.
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 one or more records.
const user = {
firstName: "John",
lastName: "Doe",
roles: [
{
// insert a new role as well
role: {
title: "New-Role",
description: "This is a new role"
}
}
]
}
// one record
const insertedUser = await users.insert(user);
// many records
const insertedUsers = await users.insert([
// no roles.
{ firstName: "Joanne", lastName: "Doe" },
{ firstName: "Jane" lastName: "Doe" }
]);
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);
// implicitly by the row's primary key.
user.firstName = "Jordan";
const numRowsAffected = await users.update(user);
// explicitly through where clause and setting properties
await users.where(m => m.id.equals(1)).update(m => {
// if a virtual column or identity key is set here, then it is ignored.
m.firstName = "Jordan";
});
// or by returning the object.
await users.where(m => m.id.equals(1)).update(m => {
return {
...m,
firstName: "Jordan"
};
});
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);
// implicitly by the row's primary key.
await users.delete(user);
// explicitly through where clause
await users.where(m => m.id.equals(1)).delete();
Truncate your entire table. (requires property disableSafeDeleteMode
to be true in the options
on the constructor)
await users.truncate();
Call multiple transactional functions where if one fails, then all will fail.
import { transaction } from '@kinshipjs/core';
const config = { }; // ... configuration for database connection
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) =>
{
// REQUIRED for the context to work on this specific transaction
// (alternatively, you can just add `using()` to the context as you're calling the clauses/transactions you want)
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) {
// note: rollback does not need to be thrown here. If any error is thrown, then the transaction is rolled back.
// if no admin role exists, then user's current roles won't be deleted.
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);
/**
* prints: { Id: 1, FirstName: "John", LastName: "Doe", xUserRoles: [ UserId: 1, RoleId: 1, Role: { Id: 1, Title: "Admin", Description: "administrative privileges" } ] }
*/
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';
// login database (pretend its connected to localhost:1433)
const loginsCfg = { }; // ... configuration for database connection
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");
// main database (pretend its connected to localhost:1434)
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 => {
// if this transaction fails, the error will bubble up and also invalidate any transactions that this is in.
const $mainUsers = mainUsers.using(mainTnx);
// .. do stuff here to insert into the login database
const [user] = $users.insert({ FirstName: "John", LastName: "Doe" });
const [mainUser] = await mainUsers.insert({ LoginUserId: user.Id });
return "Success!";
});
});
console.log(msg); // prints "Success!"
mysql2
ORM.mssql
ORM.sqlite3
ORM. (in development)pg
ORM. (in development)KinshipContext
objects for a dapper-like ORM! (in development and on a roadmap)KinshipContext
objects to quickly build a full GraphQL endpoint on an express web server.KinshipContext
objects to interface with the Lucia Auth LibraryFAQs
Database agnostic, type-safe, and syntax-friendly ORM for Node.js users.
The npm package @kinshipjs/core receives a total of 1 weekly downloads. As such, @kinshipjs/core popularity was classified as not popular.
We found that @kinshipjs/core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.