Drizzle ORM 버전
0.29.0은 최소 Drizzle Kit 버전0.20.0이 필요하며, 그 반대도 마찬가지입니다. 따라서 Drizzle ORM을 새 버전으로 업그레이드할 때는 Drizzle Kit도 함께 업그레이드해야 합니다. 특히 Drizzle Kit을 업그레이드해야 하는데 Drizzle ORM 버전이<0.28.0보다 낮은 경우, 버전 전반에 걸쳐 일부 호환성 문제가 발생할 수 있습니다.
새로운 기능
🎉 MySQL unsigned 옵션 (bigint)
이제 bigint unsigned 타입을 지정할 수 있습니다
const table = mysqlTable('table', {
id: bigint('id', { mode: 'number', unsigned: true }),
});자세한 내용은 문서를 참조하세요.
🎉 개선된 쿼리 빌더 타입
0.29.0부터 기본적으로, Drizzle의 모든 쿼리 빌더는 가능한 한 SQL 표준을 따르기 위해 대부분의 메서드를 한 번만 호출할 수 있습니다. 예를 들어, SELECT 문에는 WHERE 절이 하나만 있을 수 있으므로 .where()는 한 번만 호출할 수 있습니다:
const query = db
.select()
.from(users)
.where(eq(users.id, 1))
.where(eq(users.name, 'John')); // ❌ Type error - where() can only be invoked once이 동작은 전체 쿼리를 한 번에 작성하는 기존의 쿼리 빌딩에는 유용합니다. 하지만 쿼리를 동적으로 빌드하려는 경우, 예를 들어 쿼리 빌더를 받아서 확장하는 공유 함수가 있는 경우에는 문제가 됩니다. 이 문제를 해결하기 위해 Drizzle은 쿼리 빌더를 위한 특별한 ‘dynamic’ 모드를 제공합니다. 이 모드는 메서드를 한 번만 호출해야 하는 제한을 제거합니다. 이를 활성화하려면 쿼리 빌더에서 .$dynamic()을 호출해야 합니다.
제공된 페이지 번호와 선택적 페이지 크기를 기반으로 쿼리에 LIMIT 및 OFFSET 절을 추가하는 간단한 withPagination 함수를 구현하여 작동 방식을 살펴보겠습니다:
function withPagination<T extends PgSelect>(
qb: T,
page: number,
pageSize: number = 10,
) {
return qb.limit(pageSize).offset(page * pageSize);
}
const query = db.select().from(users).where(eq(users.id, 1));
withPagination(query, 1); // ❌ Type error - the query builder is not in dynamic mode
const dynamicQuery = query.$dynamic();
withPagination(dynamicQuery, 1); // ✅ OKwithPagination 함수는 제네릭이므로, 예를 들어 조인을 추가하는 등 함수 내부에서 쿼리 빌더의 결과 타입을 수정할 수 있습니다:
function withFriends<T extends PgSelect>(qb: T) {
return qb.leftJoin(friends, eq(friends.userId, users.id));
}
let query = db.select().from(users).where(eq(users.id, 1)).$dynamic();
query = withFriends(query);자세한 내용은 문서를 참조하세요.
🎉 기본 키 및 외래 키의 이름 지정 기능
제약 조건 이름이 데이터베이스의 64자 제한을 초과하는 경우 문제가 발생합니다. 이로 인해 데이터베이스 엔진이 이름을 잘라내어 문제가 발생할 수 있습니다. 0.29.0부터 primaryKey() 및 foreignKey() 모두에 대해 사용자 정의 이름을 지정할 수 있습니다. 또한 이전 primaryKey() 구문은 더 이상 사용되지 않지만 여전히 사용할 수 있으며 향후 릴리스에서 제거될 예정입니다.
const table = pgTable('table', {
id: integer('id'),
name: text('name'),
}, (table) => ({
cpk: primaryKey({ name: 'composite_key', columns: [table.id, table.name] }),
cfk: foreignKey({
name: 'fkName',
columns: [table.id],
foreignColumns: [table.name],
}),
}));자세한 내용은 문서를 참조하세요.
🎉 읽기 전용 복제본 지원
이제 Drizzle withReplica 함수를 사용하여 읽기 전용 복제본과 쓰기 작업을 위한 메인 인스턴스에 대해 서로 다른 데이터베이스 연결을 지정할 수 있습니다. 기본적으로 withReplicas는 읽기 작업에 임의의 읽기 전용 복제본을 사용하고, 다른 모든 데이터 수정 작업에는 메인 인스턴스를 사용합니다. 또한 사용할 읽기 전용 복제본 연결을 선택하는 사용자 정의 로직을 지정할 수도 있습니다. 가중치를 적용하거나 사용자 정의 결정을 내릴 수 있습니다. 다음은 사용 예제입니다:
const primaryDb = drizzle({ client });
const read1 = drizzle({ client });
const read2 = drizzle({ client });
const db = withReplicas(primaryDb, [read1, read2]);
// read from primary
db.$primary.select().from(usersTable);
// read from either read1 connection or read2 connection
db.select().from(usersTable)
// use primary database for delete operation
db.delete(usersTable).where(eq(usersTable.id, 1))첫 번째 복제본이 70% 확률로 선택되고 두 번째 복제본이 30% 확률로 선택되는 읽기 전용 복제본 선택을 위한 사용자 정의 로직 구현 예제입니다. 읽기 전용 복제본에 대해 어떤 유형의 랜덤 선택이든 구현할 수 있습니다:
const db = withReplicas(primaryDb, [read1, read2], (replicas) => {
const weight = [0.7, 0.3];
let cumulativeProbability = 0;
const rand = Math.random();
for (const [i, replica] of replicas.entries()) {
cumulativeProbability += weight[i]!;
if (rand < cumulativeProbability) return replica;
}
return replicas[0]!
});withReplicas 함수는 Drizzle ORM의 모든 dialect에서 사용할 수 있습니다.
자세한 내용은 문서를 참조하세요.
🎉 집합 연산자 지원 (UNION, UNION ALL, INTERSECT, INTERSECT ALL, EXCEPT, EXCEPT ALL)
API 논의부터 적절한 타입 체크 및 런타임 로직, 광범위한 테스트 세트에 이르기까지 중요한 기여를 해주신 @Angelelz에게 큰 감사를 드립니다. 이는 이번 릴리스에서 이 기능을 제공하는 데 큰 도움이 되었습니다.
사용 예제:
모든 집합 연산자는 두 가지 방식으로 사용할 수 있습니다: import 방식 또는 빌더 방식
// Import approach
import { union } from 'drizzle-orm/pg-core'
const allUsersQuery = db.select().from(users);
const allCustomersQuery = db.select().from(customers);
const result = await union(allUsersQuery, allCustomersQuery)// Builder approach
const result = await db.select().from(users).union(db.select().from(customers));자세한 내용은 문서를 참조하세요.
🎉 새로운 MySQL 프록시 드라이버
MySQL 데이터베이스를 사용하는 HTTP 드라이버에 대한 자체 구현을 만들 수 있는 새로운 드라이버가 릴리스되었습니다. ./examples/mysql-proxy 폴더에서 사용 예제를 찾을 수 있습니다.
쿼리 및 마이그레이션에 사용될 두 개의 엔드포인트를 서버에 구현해야 합니다(마이그레이션 엔드포인트는 선택 사항이며 drizzle 마이그레이션을 사용하려는 경우에만 필요합니다). 서버 및 드라이버 구현은 모두 사용자에게 달려 있으므로 어떤 방식으로든 제한되지 않습니다. 사용자 정의 매핑, 로깅 등을 추가할 수 있습니다.
서버 및 드라이버 구현 예제는 모두 ./examples/mysql-proxy 폴더에서 찾을 수 있습니다.
// Driver
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/mysql-proxy';
import { migrate } from 'drizzle-orm/mysql-proxy/migrator';
import { cities, users } from './schema';
async function main() {
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, {
sql,
params,
method,
});
return { rows: rows.data };
} catch (e: any) {
console.error('Error from pg proxy server:', e.response.data);
return { rows: [] };
}
});
await migrate(db, async (queries) => {
try {
await axios.post(`${process.env.REMOTE_DRIVER}/migrate`, { queries });
} catch (e) {
console.log(e);
throw new Error('Proxy server cannot run migrations');
}
}, { migrationsFolder: 'drizzle' });
await db.insert(cities).values({ id: 1, name: 'name' });
await db.insert(users).values({
id: 1,
name: 'name',
email: 'email',
cityId: 1,
});
const usersToCityResponse = await db.select().from(users).leftJoin(
cities,
eq(users.cityId, cities.id),
);
}자세한 내용은 문서를 참조하세요.
🎉 새로운 PostgreSQL 프록시 드라이버
MySQL과 마찬가지로 이제 PostgreSQL 데이터베이스용 자체 HTTP 드라이버를 구현할 수 있습니다. ./examples/pg-proxy 폴더에서 사용 예제를 찾을 수 있습니다.
쿼리 및 마이그레이션에 사용될 두 개의 엔드포인트를 서버에 구현해야 합니다(마이그레이션 엔드포인트는 선택 사항이며 drizzle 마이그레이션을 사용하려는 경우에만 필요합니다). 서버 및 드라이버 구현은 모두 사용자에게 달려 있으므로 어떤 방식으로든 제한되지 않습니다. 사용자 정의 매핑, 로깅 등을 추가할 수 있습니다.
서버 및 드라이버 구현 예제는 모두 ./examples/pg-proxy 폴더에서 찾을 수 있습니다.
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/pg-proxy';
import { migrate } from 'drizzle-orm/pg-proxy/migrator';
import { cities, users } from './schema';
async function main() {
const db = drizzle(async (sql, params, method) => {
try {
const rows = await axios.post(`${process.env.REMOTE_DRIVER}/query`, { sql, params, method });
return { rows: rows.data };
} catch (e: any) {
console.error('Error from pg proxy server:', e.response.data);
return { rows: [] };
}
});
await migrate(db, async (queries) => {
try {
await axios.post(`${process.env.REMOTE_DRIVER}/query`, { queries });
} catch (e) {
console.log(e);
throw new Error('Proxy server cannot run migrations');
}
}, { migrationsFolder: 'drizzle' });
const insertedCity = await db.insert(cities).values({ id: 1, name: 'name' }).returning();
const insertedUser = await db.insert(users).values({ id: 1, name: 'name', email: 'email', cityId: 1 });
const usersToCityResponse = await db.select().from(users).leftJoin(cities, eq(users.cityId, cities.id));
}자세한 내용은 문서를 참조하세요.
🎉 D1 Batch API 지원
참조: https://developers.cloudflare.com/d1/platform/client-api/#dbbatch
Batch API 사용 예제:
const batchResponse = await db.batch([
db.insert(usersTable).values({ id: 1, name: 'John' }).returning({
id: usersTable.id,
}),
db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)),
db.query.usersTable.findMany({}),
db.select().from(usersTable).where(eq(usersTable.id, 1)),
db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(
usersTable,
),
]);type BatchResponse = [
{
id: number;
}[],
D1Result,
{
id: number;
name: string;
verified: number;
invitedBy: number | null;
}[],
{
id: number;
name: string;
verified: number;
invitedBy: number | null;
}[],
{
id: number;
invitedBy: number | null;
}[],
];db.batch 내부에서 사용할 수 있는 모든 빌더:
`db.all()`,
`db.get()`,
`db.values()`,
`db.run()`,
`db.query.<table>.findMany()`,
`db.query.<table>.findFirst()`,
`db.select()...`,
`db.update()...`,
`db.delete()...`,
`db.insert()...`,더 많은 사용 예제는 integration-tests/tests/d1-batch.test.ts 및 문서에서 확인할 수 있습니다.
Drizzle Kit 0.20.0
defineConfig함수를 사용한 새로운 drizzle.config 정의 방법- wrangler.toml 파일을 사용하여 Drizzle Studio로 Cloudflare D1에 액세스하는 기능
- Drizzle Studio가 https://local.drizzle.studio/로 마이그레이션
bigint unsigned지원primaryKeys및foreignKeys에 사용자 정의 이름 지정 가능- 환경 변수가 이제 자동으로 가져옴
- 일부 버그 수정 및 개선 사항
drizzle-kit 업데이트에 대한 자세한 내용은 여기에서 확인할 수 있습니다.