or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

edge-cases.mddocs/examples/

Edge Cases and Advanced Scenarios

Advanced scenarios, corner cases, and complex patterns for Drizzle ORM.

Dynamic Query Building

Building queries conditionally based on runtime conditions.

import { eq, and, sql } from 'drizzle-orm';

// Build query conditionally
const filters: SQL[] = [];

if (nameFilter) {
  filters.push(eq(users.name, nameFilter));
}

if (ageFilter) {
  filters.push(sql`${users.age} >= ${ageFilter}`);
}

if (roleFilter) {
  filters.push(eq(users.role, roleFilter));
}

const query = db.select().from(users);

if (filters.length > 0) {
  query.where(and(...filters));
}

const results = await query;

Pagination Patterns

Offset-Based Pagination

async function getUsers(page: number, pageSize: number) {
  return await db.select()
    .from(users)
    .where(eq(users.active, true))
    .limit(pageSize)
    .offset(page * pageSize)
    .orderBy(users.createdAt);
}

Cursor-Based Pagination

async function getUsersAfterCursor(cursor: number, limit: number) {
  return await db.select()
    .from(users)
    .where(gt(users.id, cursor))
    .limit(limit)
    .orderBy(users.id);
}

Complex Transactions

Nested Transactions with Savepoints

await db.transaction(async (tx) => {
  await tx.insert(users).values({ name: 'John' });

  // Nested transaction (savepoint)
  await tx.transaction(async (tx2) => {
    await tx2.insert(posts).values({ title: 'Post 1', userId: 1 });
    await tx2.insert(posts).values({ title: 'Post 2', userId: 1 });
  });

  // If nested transaction fails, only nested operations are rolled back
  await tx.insert(comments).values({ text: 'Comment', postId: 1 });
});

Transaction with Isolation Levels

// Serializable isolation for critical operations
await db.transaction(async (tx) => {
  // Your operations
}, {
  isolationLevel: 'serializable',
  accessMode: 'read write',
  deferrable: true
});

Prepared Statements for Performance

import { sql } from 'drizzle-orm';

// Prepare a query for repeated execution
const getUserById = db
  .select()
  .from(users)
  .where(eq(users.id, sql.placeholder('id')))
  .prepare('get_user_by_id');

// Execute multiple times efficiently
const user1 = await getUserById.execute({ id: 1 });
const user2 = await getUserById.execute({ id: 2 });
const user3 = await getUserById.execute({ id: 3 });

Batch Operations

// Execute multiple queries efficiently
const [users, posts, comments] = await db.batch([
  db.select().from(users).where(eq(users.active, true)),
  db.select().from(posts).limit(10),
  db.select().from(comments).orderBy(desc(comments.createdAt))
]);

Complex Joins

Self-Joins

import { aliasedTable, eq } from 'drizzle-orm';

const managers = aliasedTable(users, 'managers');
const employees = aliasedTable(users, 'employees');

const result = await db.select({
  employeeName: employees.name,
  managerName: managers.name
})
.from(employees)
.leftJoin(managers, eq(employees.managerId, managers.id));

Multiple Joins with Conditions

const result = await db.select()
  .from(users)
  .innerJoin(orders, eq(users.id, orders.userId))
  .innerJoin(products, eq(orders.productId, products.id))
  .where(and(
    eq(users.active, true),
    gt(orders.total, 100)
  ));

Subqueries and CTEs

Common Table Expressions

import { eq, gt } from 'drizzle-orm';

const activeUsers = db.$with('activeUsers').as(
  db.select().from(users).where(eq(users.active, true))
);

const result = await db.with(activeUsers)
  .select()
  .from(activeUsers)
  .where(gt(activeUsers.age, 18));

Recursive CTEs

const subordinates = db.$with('subordinates').as(
  db.select({
    id: employees.id,
    name: employees.name,
    managerId: employees.managerId,
    level: sql<number>`1`.as('level')
  })
    .from(employees)
    .where(eq(employees.managerId, 1))
    .unionAll(
      db.select({
        id: employees.id,
        name: employees.name,
        managerId: employees.managerId,
        level: sql<number>`${subordinates.level} + 1`
      })
        .from(employees)
        .innerJoin(subordinates, eq(employees.managerId, subordinates.id))
    )
);

const result = await db.with(subordinates)
  .select()
  .from(subordinates);

Raw SQL with Type Safety

import { sql } from 'drizzle-orm';

// Type-safe raw SQL
const result = await db.select({
  id: users.id,
  name: users.name,
  upperName: sql<string>`upper(${users.name})`
})
.from(users);

// Complex raw SQL with subqueries
const avgPrice = sql`(SELECT AVG(price) FROM ${products})`;
await db.select({
  name: products.name,
  priceVsAvg: sql`${products.price} - ${avgPrice}`
})
.from(products);

Database-Specific Features

PostgreSQL: DISTINCT ON

// Get first occurrence of each role (ordered)
const firstUsers = await db.selectDistinctOn([users.role])
  .from(users)
  .orderBy(users.role, users.createdAt);

PostgreSQL: Row Locking

// FOR UPDATE SKIP LOCKED - useful for job queues
const available = await db.select()
  .from(jobs)
  .where(eq(jobs.status, 'pending'))
  .limit(1)
  .for('update', { skipLocked: true });

MySQL: ON DUPLICATE KEY UPDATE

await db.insert(users)
  .values({ email: 'john@example.com', name: 'John', loginCount: 1 })
  .onDuplicateKeyUpdate({
    set: {
      name: sql`VALUES(name)`,
      loginCount: sql`loginCount + 1`,
      updatedAt: new Date()
    }
  });

SQLite: ON CONFLICT

await db.insert(users)
  .values({ id: 1, name: 'John', email: 'john@example.com' })
  .onConflictDoUpdate({
    target: users.id,
    set: {
      name: sql`excluded.name`,
      updatedAt: new Date()
    }
  });

Error Handling Patterns

import { DrizzleQueryError, TransactionRollbackError } from 'drizzle-orm';

try {
  await db.transaction(async (tx) => {
    await tx.insert(users).values({ name: 'John', email: 'john@example.com' });
    await tx.insert(users).values({ name: 'Jane', email: 'john@example.com' }); // Duplicate
  });
} catch (error) {
  if (error instanceof TransactionRollbackError) {
    console.log('Transaction was explicitly rolled back');
  } else if (error instanceof DrizzleQueryError) {
    console.log('Query failed:', error.query);
    console.log('Params:', error.params);
    console.log('Cause:', error.cause);
  } else {
    console.log('Unexpected error:', error);
  }
}

Read Replicas

import { withReplicas } from 'drizzle-orm/node-postgres';

const replica1 = new Pool({ connectionString: process.env.REPLICA1_URL });
const replica2 = new Pool({ connectionString: process.env.REPLICA2_URL });

const db = withReplicas(masterPool, [replica1, replica2]);

// Reads automatically go to replicas
const users = await db.select().from(users);

// Writes go to master
await db.insert(users).values({ name: 'John' });

Query Result Caching

import { UpstashCache } from 'drizzle-orm/upstash';

const cache = new UpstashCache({
  url: process.env.UPSTASH_REDIS_URL,
  token: process.env.UPSTASH_REDIS_TOKEN,
});

const db = drizzle(pool, {
  cache,
  schema,
});

// Queries are automatically cached
const users = await db.select().from(users);

Custom Column Types

import { customType } from 'drizzle-orm/pg-core';

// Custom type for currency stored as integer (cents)
const currency = customType<{
  data: number;
  driverData: number;
}>({
  dataType() {
    return 'integer';
  },
  toDriver(value: number): number {
    return Math.round(value * 100);
  },
  fromDriver(value: number): number {
    return value / 100;
  },
});

export const products = pgTable('products', {
  id: integer('id').primaryKey(),
  price: currency('price').notNull(),
});

// Usage
await db.insert(products).values({ id: 1, price: 19.99 });  // Stores 1999
const product = await db.select().from(products).where(eq(products.id, 1));
console.log(product[0].price);  // Returns 19.99

Generated Columns

import { sql } from 'drizzle-orm';

export const users = pgTable('users', {
  id: integer('id').primaryKey(),
  firstName: text('first_name').notNull(),
  lastName: text('last_name').notNull(),
  fullName: text('full_name').generatedAlwaysAs(
    sql`first_name || ' ' || last_name`
  ),
});

Identity Columns (PostgreSQL)

export const users = pgTable('users', {
  id: integer('id').generatedAlwaysAsIdentity({ startWith: 1000 }),
  name: text('name').notNull(),
});

export const posts = pgTable('posts', {
  id: integer('id').generatedByDefaultAsIdentity(),  // Can override on insert
  title: text('title').notNull(),
});