Skip to content
Back to skills

Ts Db Perf

ASecurity

Optimize TypeScript code that interacts with databases. Use this skill when the user wants to fix N+1 queries, add caching, improve transaction safety, prevent race conditions, simplify async flows, or generally speed up a TypeScript backend. Triggers on phrases like "optimize", "slow query", "N+1", "race condition", "caching", "performance", "bottleneck", or when the user points at TypeScript code that reads or writes to a database and asks for improvements.

  • 7 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added May 26, 2026
databasestypescriptsqlapidatabasebackendperformance

Works with

  • cursor
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add widnyana/eyay-toolkits --skill ts-db-perf --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ts Db Perf?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ts Db Perf
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/widnyana-ts-db-perf/badge)](https://www.skillsdirectory.com/skills/widnyana-ts-db-perf)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ts-db-perf
description: |
  Optimize TypeScript code that interacts with databases. Use this skill when the user wants to fix N+1 queries, add caching, improve transaction safety, prevent race conditions, simplify async flows, or generally speed up a TypeScript backend. Triggers on phrases like "optimize", "slow query", "N+1", "race condition", "caching", "performance", "bottleneck", or when the user points at TypeScript code that reads or writes to a database and asks for improvements.
---

# TypeScript Database Optimization

Optimize: $ARGUMENTS

## 1. N+1 Query Elimination

The classic trap: fetching a list, then querying per item in a loop.

```typescript
// N+1: one query for the list, one per item
const orders = await db.order.findMany();
for (const order of orders) {
  order.customer = await db.customer.findUnique({ where: { id: order.customerId } });
}

// Resolved: single query with join/include
const orders = await db.order.findMany({
  include: { customer: true },
});
```

If the ORM doesn't support `include`, use a `WHERE id IN (...)` or a JOIN.

## 2. Select Only What You Need

```typescript
// Over-fetching
const users = await db.user.findMany();

// Tight select
const users = await db.user.findMany({
  select: { id: true, email: true },
});
```

Applies to raw SQL too -- avoid `SELECT *` when you only need a few columns.

## 3. Pagination

Always paginate list endpoints. Cursor-based for large/real-time datasets, offset-based for simple cases.

```typescript
// Offset-based
const [data, total] = await Promise.all([
  db.user.findMany({ skip: (page - 1) * limit, take: limit }),
  db.user.count(),
]);

// Cursor-based (no count query, stable under writes)
const items = await db.message.findMany({
  take: limit,
  cursor: cursor ? { id: cursor } : undefined,
  orderBy: { createdAt: "desc" },
});
```

## 4. Caching

Cache where data is read-heavy and stale reads are tolerable.

```typescript
async function getExchangeRate(from: string, to: string): Promise<number> {
  const key = `rate:${from}:${to}`;
  const cached = await cache.get(key);
  if (cached !== null) return Number(cached);

  const rate = await fetchRateFromAPI(from, to);
  await cache.set(key, String(rate), { ttl: 60 }); // 60s TTL
  return rate;
}
```

For repository-level caching, wrap the lookup:

```typescript
async findById(id: string): Promise<User | null> {
  const cached = await cache.get(`user:${id}`);
  if (cached) return JSON.parse(cached);

  const user = await db.user.findUnique({ where: { id } });
  if (user) await cache.set(`user:${id}`, JSON.stringify(user), { ttl: 300 });
  return user;
}
```

Invalidate on writes. Prefer short TTLs over complex invalidation logic.

## 5. Batch Writes in Transactions

```typescript
// Sequential writes outside a transaction -- slow and non-atomic
for (const item of items) {
  await db.item.update({ where: { id: item.id }, data: item });
}

// Batched in a transaction -- fast and atomic
await db.$transaction(
  items.map((item) =>
    db.item.update({ where: { id: item.id }, data: item })
  )
);
```

For large batches, chunk to avoid exceeding connection limits or statement size:

```typescript
const CHUNK = 100;
for (let i = 0; i < items.length; i += CHUNK) {
  await db.$transaction(
    items.slice(i, i + CHUNK).map((item) =>
      db.item.update({ where: { id: item.id }, data: item })
    )
  );
}
```

## 6. Race Condition Prevention

Critical for balance updates, inventory, counters -- anything that reads-then-writes.

**Optimistic locking** (version column):

```typescript
const result = await db.account.update({
  where: { id: accountId, version: currentVersion },
  data: { balance: newBalance, version: { increment: 1 } },
});
if (!result) throw new ConflictError("Concurrent modification");
```

**Atomic updates** (no read needed):

```typescript
await db.account.update({
  where: { id: accountId },
  data: { balance: { increment: amount } },
});
```

**Distributed lock** (Redis/Valkey, for cross-service coordination):

```typescript
const lock = await acquireLock(`balance:${accountId}`, 30_000);
try {
  await db.$transaction(async (tx) => {
    await tx.$executeRaw`
      UPDATE account SET balance = balance + ${amount} WHERE id = ${accountId}
    `;
  });
} finally {
  await releaseLock(`balance:${accountId}`, lock);
}
```

**Idempotency** (webhooks, retries):

```typescript
const key = `processed:${eventId}`;
if (await cache.get(key)) return { status: "already_processed" };
await cache.set(key, "processing", { ttl: 3600 });
// ... do work ...
await cache.set(key, "done", { ttl: 86_400 });
```

## 7. Parallel Async

Independent lookups should run concurrently.

```typescript
// Sequential -- 2 round-trips
const user = await db.user.findUnique({ where: { id } });
const settings = await db.settings.findUnique({ where: { userId: id } });

// Parallel -- 1 round-trip wall time
const [user, settings] = await Promise.all([
  db.user.findUnique({ where: { id } }),
  db.settings.findUnique({ where: { userId: id } }),
]);
```

Use `Promise.allSettled` when partial failures are acceptable.

## 8. Simplify Control Flow

**Early returns** over nesting:

```typescript
// Nested
async function process(data: Input | null) {
  if (data) {
    if (data.valid) {
      // actual logic
    }
  }
}

// Flat
async function process(data: Input | null) {
  if (!data || !data.valid) return;
  // actual logic
}
```

**Extract helpers** for repeated patterns:

```typescript
function ensureFound<T>(value: T | null | undefined, label: string): T {
  if (value == null) throw new NotFoundError(`${label} not found`);
  return value;
}
```

## Checklist

- [ ] Identify the bottleneck (profile, don't guess)
- [ ] Eliminate N+1 queries
- [ ] Add pagination to list endpoints
- [ ] Tighten selects to needed fields only
- [ ] Cache read-heavy, staleness-tolerant data
- [ ] Batch writes in transactions
- [ ] Chunk large batches
- [ ] Guard read-then-write with optimistic locking or atomic updates
- [ ] Use distributed locks for cross-service coordination
- [ ] Add idempotency keys for webhooks
- [ ] Parallelize independent async work
- [ ] Verify optimization doesn't change behavior

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.