As33
@periodic/
arsenic
fan_out
⚠️ Warning

Single request triggered too many database queries

Request fans out into many DB queries, indicating architectural issues or missing data aggregation. High query fan-out increases latency and puts pressure on connection pools.

Common Causes

  • Missing aggregation — data fetched piecemeal
  • No DataLoader for nested resolvers
  • Redundant queries for the same data
  • Missing joins or batch operations

How to Fix

  1. 1.Use batch queries or DataLoader
  2. 2.Implement response caching
  3. 3.Aggregate data on the backend
  4. 4.Redesign data model for fewer round-trips

Multiplied by request volume

10 queries per request at 100 req/s is 1,000 DB queries per second. Fan-out makes your database load proportional to your traffic spikes.

Example

typescript
// BAD — fan-out: one query per post author
const posts = await Post.find({ published: true });
const postsWithAuthors = await Promise.all(
  posts.map(post => User.findById(post.authorId))
);

// GOOD — single joined query
const posts = await Post.find({ published: true }).populate('author');

Prisma field selection

typescript
// BAD — selects all columns including large text fields
const users = await prisma.user.findMany();

// GOOD — only what the caller needs
const users = await prisma.user.findMany({
  select: { id: true, name: true, email: true },
});

// GOOD — Mongoose projection
const users = await User.find().select('name email -_id');

Multiplied by request volume

10 queries per request at 100 req/s is 1,000 DB queries per second. Fan-out makes your database load proportional to traffic spikes.

Parallel fan-out with Promise.all

typescript
// BAD — sequential fan-out (slow + many queries)
for (const id of userIds) {
  const profile = await Profile.findOne({ userId: id });
}

// BETTER — parallel but still N queries
const profiles = await Promise.all(
  userIds.map(id => Profile.findOne({ userId: id }))
);

// BEST — single IN query
const profiles = await Profile.find({ userId: { $in: userIds } });
// Then map back to userIds order if needed
const profileMap = new Map(profiles.map(p => [p.userId.toString(), p]));