Database query optimization strategies. Use when improving query performance.
/plugin marketplace add IvanTorresEdge/molcajete.ai/plugin install ivantorresedge-node-tech-stacks-js-node@IvanTorresEdge/molcajete.aiThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill covers database query optimization for Node.js applications.
Use this skill when:
MEASURE FIRST, OPTIMIZE SECOND - Profile before optimizing. The bottleneck is often not where you expect.
// BAD: N+1 queries
const posts = await prisma.post.findMany();
for (const post of posts) {
// This runs a query for each post!
const author = await prisma.user.findUnique({
where: { id: post.authorId },
});
console.log(`${post.title} by ${author?.name}`);
}
// GOOD: Single query with include
const posts = await prisma.post.findMany({
include: {
author: {
select: { id: true, name: true },
},
},
});
for (const post of posts) {
console.log(`${post.title} by ${post.author.name}`);
}
// src/lib/dataloader.ts
import DataLoader from 'dataloader';
import { PrismaClient } from '@prisma/client';
export function createUserLoader(prisma: PrismaClient) {
return new DataLoader<string, User | null>(async (ids) => {
const users = await prisma.user.findMany({
where: { id: { in: [...ids] } },
});
const userMap = new Map(users.map((u) => [u.id, u]));
return ids.map((id) => userMap.get(id) ?? null);
});
}
// Usage
const userLoader = createUserLoader(prisma);
const posts = await prisma.post.findMany();
// Batches all user lookups into single query
const postsWithAuthors = await Promise.all(
posts.map(async (post) => ({
...post,
author: await userLoader.load(post.authorId),
}))
);
-- Columns in WHERE clauses
CREATE INDEX posts_author_id_idx ON posts(author_id);
-- Columns in JOIN conditions
CREATE INDEX comments_post_id_idx ON comments(post_id);
-- Columns in ORDER BY
CREATE INDEX posts_created_at_idx ON posts(created_at DESC);
-- Compound indexes for combined queries
CREATE INDEX posts_published_created_idx ON posts(published, created_at DESC);
model Post {
id String @id
authorId String
published Boolean
createdAt DateTime
@@index([authorId])
@@index([published, createdAt(sort: Desc)])
}
export const posts = pgTable('posts', {
id: text('id').primaryKey(),
authorId: text('author_id'),
published: boolean('published'),
createdAt: timestamp('created_at'),
}, (table) => ({
authorIdx: index('posts_author_idx').on(table.authorId),
publishedCreatedIdx: index('posts_published_created_idx')
.on(table.published, desc(table.createdAt)),
}));
// Prisma
const result = await prisma.$queryRaw`
EXPLAIN ANALYZE
SELECT * FROM posts WHERE author_id = ${userId}
`;
console.log(result);
// Drizzle
const result = await db.execute(sql`
EXPLAIN ANALYZE
SELECT * FROM posts WHERE author_id = ${userId}
`);
// Prisma with query logging
const prisma = new PrismaClient({
log: [
{
emit: 'event',
level: 'query',
},
],
});
prisma.$on('query', (e) => {
if (e.duration > 100) { // Log slow queries (>100ms)
console.warn('Slow query:', {
query: e.query,
duration: `${e.duration}ms`,
});
}
});
// Gets slower as offset increases
const posts = await prisma.post.findMany({
skip: (page - 1) * perPage,
take: perPage,
orderBy: { createdAt: 'desc' },
});
// Constant performance regardless of page
async function getPosts(cursor?: string, limit = 20) {
const posts = await prisma.post.findMany({
take: limit + 1, // Fetch one extra to check if more exist
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' },
});
const hasMore = posts.length > limit;
const items = hasMore ? posts.slice(0, -1) : posts;
const nextCursor = hasMore ? items[items.length - 1]?.id : null;
return { items, nextCursor, hasMore };
}
// BAD: Fetching all columns
const users = await prisma.user.findMany();
// GOOD: Select specific columns
const users = await prisma.user.findMany({
select: {
id: true,
name: true,
email: true,
},
});
// Drizzle equivalent
const users = await db
.select({
id: users.id,
name: users.name,
email: users.email,
})
.from(users);
// BAD: Individual inserts
for (const item of items) {
await prisma.item.create({ data: item });
}
// GOOD: Batch insert
await prisma.item.createMany({
data: items,
});
// Drizzle batch insert
await db.insert(items).values(itemsData);
// Prisma connection pool
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
});
// For serverless, use external pooler (PgBouncer, Supabase Pooler)
// DATABASE_URL=postgres://...?pgbouncer=true
// In-memory cache for frequently accessed data
import { LRUCache } from 'lru-cache';
const userCache = new LRUCache<string, User>({
max: 500,
ttl: 1000 * 60 * 5, // 5 minutes
});
async function getUserById(id: string): Promise<User | null> {
const cached = userCache.get(id);
if (cached) return cached;
const user = await prisma.user.findUnique({ where: { id } });
if (user) userCache.set(id, user);
return user;
}
// Invalidate on update
async function updateUser(id: string, data: UserUpdate): Promise<User> {
const user = await prisma.user.update({ where: { id }, data });
userCache.set(id, user);
return user;
}
// Prisma metrics
import { Prisma } from '@prisma/client';
const prisma = new PrismaClient().$extends({
query: {
$allOperations({ operation, model, args, query }) {
const start = performance.now();
return query(args).finally(() => {
const duration = performance.now() - start;
if (duration > 100) {
console.warn(`Slow ${model}.${operation}: ${duration.toFixed(2)}ms`);
}
});
},
},
});
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.