N+1 Query Detection skill
Detect N+1 query anti-patterns specifically — Repo calls inside Enum/for loops, missing preloads on associations.
by oliver-kriska·MIT license·★ 560 Stars on the repo·GitHub ↗
Use now
npx degit oliver-kriska/claude-elixir-phoenix/plugins/elixir-phoenix/skills/n1-check#main ~/.claude/skills/n1-checkChecked ·commit main
Files of N+1 Query Detection
SKILL.md
Show the full text73 lines
N+1 Query Detection
Identify and fix N+1 query anti-patterns in Ecto/Phoenix applications.
Iron Laws - Never Violate These
- Never access associations without preload - Always preload before
Enum.map - No Repo calls inside loops - Restructure to batch queries
- Preload at context boundary - Load associations in context, not controllers/views
- Use joins for filtering - Use
join+preloadwhen filtering by association
Detection Patterns
Pattern 1: Enum.map with Repo
# BAD: N+1 queries
users
|> Enum.map(fn user -> Repo.get(Order, user.order_id) end)
# GOOD: Single query with preload
users
|> Repo.preload(:orders)
Pattern 2: Association Access Without Preload
# BAD: Lazy loading triggers N queries
for user <- users do
user.posts # Triggers query for each user!
end
# GOOD: Eager load first
users = Repo.all(User) |> Repo.preload(:posts)
for user <- users do
user.posts # Already loaded
end
Pattern 3: Nested Association Access
# BAD: N+1 for nested associations
user.posts |> Enum.map(fn post -> post.comments end)
# GOOD: Nested preload
Repo.preload(user, posts: :comments)
Quick Detection Commands
Use Grep with context lines (-B 5 -A 5) to find Enum.map near Repo. calls in lib/**/*.ex.
Use Grep to find association access patterns (.posts, .comments, .orders) in lib/**/*.ex.
Use Grep with context (-B 3) to find Repo.get or Repo.one near loop patterns (for, Enum) in lib/**/*.ex.
Analysis Command
Use Grep to find all Repo. calls in a context module, then verify each query has appropriate preloads.
References
For detailed patterns, see:
${CLAUDE_SKILL_DIR}/references/preload-patterns.md- Efficient preloading strategies${CLAUDE_SKILL_DIR}/references/query-optimization.md- Query batching techniques
| 1 | |
| 2 | name n1-check |
| 3 | description "Detect N+1 query anti-patterns specifically — Repo calls inside Enum/for loops, missing preloads on associations. Use when N+1 is explicitly suspected, NOT for unrelated Ecto questions or wider database performance." |
| 4 | effort medium |
| 5 | |
| 6 | |
| 7 | # N+1 Query Detection |
| 8 | |
| 9 | Identify and fix N+1 query anti-patterns in Ecto/Phoenix applications. |
| 10 | |
| 11 | ## Iron Laws - Never Violate These |
| 12 | |
| 13 | **Never access associations without preload** - Always preload before `Enum.map` |
| 14 | **No Repo calls inside loops** - Restructure to batch queries |
| 15 | **Preload at context boundary** - Load associations in context, not controllers/views |
| 16 | **Use joins for filtering** - Use `join` + `preload` when filtering by association |
| 17 | |
| 18 | ## Detection Patterns |
| 19 | |
| 20 | ### Pattern 1: Enum.map with Repo |
| 21 | |
| 22 | |
| 23 | # BAD: N+1 queries |
| 24 | users |
| 25 | |> Enum.map(fn user -> Repo.get(Order, user.order_id) end) |
| 26 | |
| 27 | # GOOD: Single query with preload |
| 28 | users |
| 29 | |> Repo.preload(:orders) |
| 30 | |
| 31 | |
| 32 | ### Pattern 2: Association Access Without Preload |
| 33 | |
| 34 | |
| 35 | # BAD: Lazy loading triggers N queries |
| 36 | for user <- users do |
| 37 | user.posts # Triggers query for each user! |
| 38 | end |
| 39 | |
| 40 | # GOOD: Eager load first |
| 41 | users = Repo.all(User) |> Repo.preload(:posts) |
| 42 | for user <- users do |
| 43 | user.posts # Already loaded |
| 44 | end |
| 45 | |
| 46 | |
| 47 | ### Pattern 3: Nested Association Access |
| 48 | |
| 49 | |
| 50 | # BAD: N+1 for nested associations |
| 51 | user.posts |> Enum.map(fn post -> post.comments end) |
| 52 | |
| 53 | # GOOD: Nested preload |
| 54 | Repo.preload(user, posts: :comments) |
| 55 | |
| 56 | |
| 57 | ## Quick Detection Commands |
| 58 | |
| 59 | Use Grep with context lines (`-B 5 -A 5`) to find `Enum.map` near `Repo.` calls in `lib/**/*.ex`. |
| 60 | Use Grep to find association access patterns (`.posts`, `.comments`, `.orders`) in `lib/**/*.ex`. |
| 61 | Use Grep with context (`-B 3`) to find `Repo.get` or `Repo.one` near loop patterns (`for`, `Enum`) in `lib/**/*.ex`. |
| 62 | |
| 63 | ## Analysis Command |
| 64 | |
| 65 | Use Grep to find all `Repo.` calls in a context module, then verify each query has appropriate preloads. |
| 66 | |
| 67 | ## References |
| 68 | |
| 69 | For detailed patterns, see: |
| 70 | |
| 71 | `${CLAUDE_SKILL_DIR}/references/preload-patterns.md` - Efficient preloading strategies |
| 72 | `${CLAUDE_SKILL_DIR}/references/query-optimization.md` - Query batching techniques |
| 73 |
Discussion
Browse more free Claude skills.