Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
drizzle-orm
Advanced tools
Drizzle ORM is a TypeScript-first ORM for Node.js that focuses on type safety, performance, and simplicity. It provides a fluent API for defining and querying your database schema, making it easier to work with SQL databases in a type-safe manner.
Schema Definition
Drizzle ORM allows you to define your database schema using a fluent API. This makes it easy to create and manage your database tables with type safety.
const { defineSchema, types } = require('drizzle-orm');
const schema = defineSchema({
users: {
id: types.int().primaryKey().autoIncrement(),
name: types.string().notNull(),
email: types.string().unique().notNull()
}
});
Query Building
You can build and execute SQL queries using a fluent API. This example demonstrates how to select users with the name 'John Doe'.
const { select } = require('drizzle-orm');
const users = await select(schema.users)
.where(schema.users.name.eq('John Doe'))
.execute();
Type Safety
Drizzle ORM ensures type safety throughout your database operations. This example shows how to insert a new user into the users table with type-checked values.
const { insert } = require('drizzle-orm');
await insert(schema.users).values({
name: 'Jane Doe',
email: 'jane.doe@example.com'
}).execute();
Migrations
Drizzle ORM supports database migrations, allowing you to evolve your database schema over time. This example demonstrates how to create and drop a 'posts' table.
const { migrate } = require('drizzle-orm');
await migrate(schema, {
migrations: [
{
up: async (db) => {
await db.schema.createTable('posts', {
id: types.int().primaryKey().autoIncrement(),
title: types.string().notNull(),
content: types.text().notNull(),
userId: types.int().references(schema.users.id)
});
},
down: async (db) => {
await db.schema.dropTable('posts');
}
}
]
});
Sequelize is a promise-based Node.js ORM for Postgres, MySQL, MariaDB, SQLite, and Microsoft SQL Server. It features solid transaction support, relations, eager and lazy loading, read replication, and more. Compared to Drizzle ORM, Sequelize has a larger community and more extensive documentation but may lack some of the type safety features that Drizzle ORM offers.
TypeORM is an ORM for TypeScript and JavaScript (ES7, ES6, ES5). It supports many database systems including MySQL, MariaDB, PostgreSQL, SQLite, and more. TypeORM is known for its extensive feature set and active community. It provides decorators for defining entities and supports advanced features like migrations, caching, and more. Compared to Drizzle ORM, TypeORM offers more features but may be more complex to set up and use.
Knex.js is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle. It features a flexible and powerful API for building SQL queries and managing database schema. While Knex.js is not a full-fledged ORM, it can be used in conjunction with other libraries to provide ORM-like functionality. Compared to Drizzle ORM, Knex.js offers more control over raw SQL queries but lacks the built-in type safety and higher-level abstractions.
DrizzleORM is an ORM framework for TypeScript. It offers you several levels of Database communication:
Drizzle ORM is highly influenced by Exposed and Jetbrains development methodology
In Progress
npm install drizzle-orm drizzle-kit
yarn add drizzle-orm drizzle-kit
bower install drizzle-orm drizzle-kit
import { DbConnector } from "drizzle-orm";
// connect via postgresql connection url
const db = await new DbConnector()
.connectionString("postgres://user:password@host:port/db")
.connect();
// or by params
const db = await new DbConnector()
.params({
host: '0.0.0.0',
port: 5432,
user: 'user',
password: 'password',
db: 'optional_db_name'
}).connect();
export const rolesEnum = createEnum({ alias: 'test-enum', values: ['user', 'guest', 'admin'] });
export default class UsersTable extends AbstractTable<UsersTable> {
public id = this.serial('id').primaryKey();
public fullName = this.text('full_name');
public phone = this.varchar('phone', { size: 256 });
public media = this.jsonb<string[]>('media');
public decimalField = this.decimal('test', { precision: 100, scale: 2 }).notNull();
public bigIntField = this.bigint('test1', 'max_bytes_53');
public role = this.type(rolesEnum, 'name_in_table').notNull();
public createdAt = this.timestamp('created_at').notNull();
public createdAtWithTimezone = this.timestamptz('created_at_time_zone');
public updatedAt = this.timestamp('updated_at').defaultValue(Defaults.CURRENT_TIMESTAMP);
public isArchived = this.bool('is_archived').defaultValue(false);
public phoneFullNameIndex = this.index([this.phone, this.fullName]);
public phoneIndex = this.uniqueIndex(this.phone);
public tableName(): string {
return 'users';
}
}
interface CityMeta {
population: number,
connection: string,
}
export default class CitiesTable extends AbstractTable<CitiesTable> {
public id = this.serial('id').primaryKey();
public foundationDate = this.timestamp('name').notNull();
public location = this.varchar('page', { size: 256 });
public userId = this.int('user_id').foreignKey(UsersTable, (table) => table.id, { onUpdate: 'CASCADE' });
public metadata = this.jsonb<CityMeta>('metadata');
public tableName(): string {
return 'cities';
}
}
export default class UserGroupsTable extends AbstractTable<UserGroupsTable> {
public id = this.serial('id').primaryKey();
public name = this.varchar('name');
public description = this.varchar('description');
public tableName(): string {
return 'user_groups';
}
}
export default class UsersToUserGroupsTable extends AbstractTable<UsersToUserGroupsTable> {
public groupId = this.int('city_id').foreignKey(UserGroupsTable, (table) => table.id, { onDelete: 'CASCADE' });
public userId = this.int('user_id').foreignKey(UsersTable, (table) => table.id, { onDelete: 'CASCADE' });
public manyToManyIndex = this.index([this.groupId, this.userId]);
public tableName(): string {
return 'users_to_user_groups';
}
}
const db = await new DbConnector()
.connectionString('postgresql://postgres@127.0.0.1/drizzle')
.connect();
const usersTable = new UsersTable(db);
// select all
const allSelect = await usersTable.select().all();
// select first
const firstSelect = await usersTable.select().findOne();
Users
where phone is "hello"
const eqSelect = await usersTable.select().where(
eq(usersTable.phone, 'hello')
).all();
Users
where both phone is "hello"
and phone is "hello"
const andSelect = await usersTable.select().where(
and([
eq(usersTable.phone, 'hello'),
eq(usersTable.phone, 'hello')
]),
).all();
Users
where either phone is "hello"
or phone is "hello"
const orSelect = await usersTable.select().where(
or([eq(usersTable.phone, 'hello')]),
).all();
Users
using LIMIT and OFFSETconst limitOffsetSelect = await usersTable.select().limit(10).offset(10).all();
Users
where phone
contains "hello"
const likeSelect = await usersTable.select().where(
like(usersTable.phone, '%hello%')
).all();
Users
where phone
equals to some of values from arrayconst inArraySelect = usersTable.select().where(
inArray(usersTable.phone, ['hello'])
).all();
Users
where phone
greater(>) than "hello"
const greaterSelect = usersTable.select().where(
greater(usersTable.phone, 'hello')
).all();
Users
where phone
less(<) than "hello"
const lessSelect = usersTable.select().where(
less(usersTable.phone, 'hello')
).all();
Users
where phone
greater or equals(>=) than "hello"
const greaterEqSelect = usersTable.select().where(
greaterEq(usersTable.phone, 'hello')
).all();
Users
where phone
less or equals(<=)const lessEqSelect = usersTable.select().where(
lessEq(usersTable.phone, 'hello')
).all();
Users
where phone
is NULLconst isNullSelect = usersTable.select().where(
isNull(usersTable.phone)
).all();
Users
where phone
not equals to "hello"
const notEqSelect = usersTable.select().where(
notEq(usersTable.phone, 'hello')
).all();
Users
ordered by phone
in ascending orderconst ordered = await usersTable.select().orderBy((table) => table.phone, Order.ASC).all();
const partialSelect = await usersTable.select({
mappedId: usersTable.id,
mappedPhone: usersTable.phone,
}).all();
// Usage
const { mappedId, mappedPhone } = partialSelect;
fullName
to newName
in Users
where phone is "hello"
await usersTable.update()
.where(eq(usersTable.phone, 'hello'))
.set({ fullName: 'newName' })
.execute();
fullName
to newName
in Users
where phone is "hello"
returning updated User
modelawait usersTable.update()
.where(eq(usersTable.phone, 'hello'))
.set({ fullName: 'newName' })
.all();
fullName
to newName
in Users
where phone is "hello"
returning updated User
modelawait usersTable.update()
.where(eq(usersTable.phone, 'hello'))
.set({ fullName: 'newName' })
.findOne();
user
where phone is "hello"
await usersTable.delete()
.where(eq(usersTable.phone, 'hello'))
.execute();
user
where phone is "hello"
returning updated User
modelawait usersTable.delete()
.where(eq(usersTable.phone, 'hello'))
.all();
user
where phone is "hello"
returning updated User
modelawait usersTable.delete()
.where(eq(usersTable.phone, 'hello'))
.findOne();
user
with required fieldsawait usersTable.insert({
test: 1,
createdAt: new Date(),
}).execute();
user
with required fields and get all rows as arrayconst user = await usersTable.insert({
test: 1,
createdAt: new Date(),
}).all();
user
with required fields and get inserted entityconst user = await usersTable.insert({
test: 1,
createdAt: new Date(),
}).findOne();
users
with required fields and get all inserted entitiesconst users = await usersTable.insertMany([{
test: 1,
createdAt: new Date(),
}, {
test: 2,
createdAt: new Date(),
}]).all();
users
with required fields and get all inserted entities. If such user already exists - update phone
fieldawait usersTable.insertMany([{
test: 1,
createdAt: new Date(),
}, {
test: 2,
createdAt: new Date(),
}])
.onConflict(
(table) => table.phoneIndex,
{ phone: 'confilctUpdate' },
).all();
const usersTable = new UsersTable(db);
const citiesTable = new CitiesTable(db);
const userWithCities = await citiesTable.select()
.where(eq(citiesTable.id, 1))
.leftJoin(UsersTable,
(city) => city.userId,
(users) => users.id)
.execute();
const citiesWithUserObject = userWithCities.map((city, user) => ({ ...city, user }));
const usersWithUserGroups = await usersToUserGroupsTable.select()
.where(eq(userGroupsTable.id, 1))
.leftJoin(UsersTable,
(userToGroup) => userToGroup.userId,
(users) => users.id)
.leftJoin(UsersToUserGroupsTable, UserGroupsTable,
(userToGroup) => userToGroup.groupId,
(users) => users.id)
.execute();
const userGroupWithUsers = usersWithUserGroups.group({
one: (_, dbUser, dbUserGroup) => dbUser!,
many: (_, dbUser, dbUserGroup) => dbUserGroup!,
});
const userWithGroups: ExtractModel<UsersTable> & { groups: ExtractModel<UserGroupsTable>[] } = {
...userGroupWithUsers.one,
groups: userGroupWithUsers.many,
};
const usersWithUserGroups = await usersToUserGroupsTable.select()
.where(eq(userGroupsTable.id, 1))
.leftJoin(UsersTable,
(userToGroup) => userToGroup.userId,
(users) => users.id)
.leftJoin(UsersToUserGroupsTable, UserGroupsTable,
(userToGroup) => userToGroup.groupId,
(users) => users.id)
.execute();
const userGroupWithUsers = usersWithUserGroups.group({
one: (_, dbUser, dbUserGroup) => dbUserGroup!,
many: (_, dbUser, dbUserGroup) => dbUser!,
});
const userWithGroups: ExtractModel<UserGroupsTable> & { users: ExtractModel<UsersTable>[] } = {
...userGroupWithUsers.one,
users: userGroupWithUsers.many,
};
await citiesTable.select({
id: citiesTable.id,
userId: citiesTable.userId,
})
.where(eq(citiesTable.id, 1))
.leftJoin(UsersTable,
(city) => city.userId,
(users) => users.id,
{
id: usersTable.id,
})
.execute();
const citiesWithUserObject = userWithCities.map((city, user) => ({ ...city, user }));
Migrator
classawait drizzle.migrator(db).migrate('src/drizzle.config.yaml');
await drizzle.migrator(db).migrate({ migrationFolder: 'drizzle' });
rawQuery
executionconst res: QueryResult<any> = await db.session().execute('SELECT * FROM users WHERE user.id = $1', [1]);
FAQs
Drizzle ORM package for SQL databases
We found that drizzle-orm demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers 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
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.