Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
drizzle-orm
Advanced tools
**DrizzleORM** is an ORM framework for [TypeScript](https://www.typescriptlang.org/). It offers you several levels of Database communication: * Typesafe Table View approach * Typesafe Query Builder * Simple SQL query execution
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.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.