skills/rails-graphql-patterns/SKILL.md
Analyzes and recommends GraphQL patterns for Rails using graphql-ruby including schema design, types, resolvers, mutations, subscriptions, DataLoader, and query complexity. Use when building GraphQL APIs, defining types, writing mutations, optimizing N+1 queries, or structuring app/graphql. NOT for REST API controllers, ActiveRecord queries outside GraphQL, or Turbo Stream responses.
npx skillsauth add ag0os/rails-dev-plugin rails-graphql-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Analyze and recommend patterns for building GraphQL APIs in Rails with graphql-ruby.
Follow standard graphql-ruby conventions for type definitions, input types, enum types, query types, and basic mutations. This skill focuses on non-obvious patterns and opinionated decisions.
max_complexity and max_depth on the schemanull: true/false on every fieldEvery association field MUST use DataLoader. Never call object.association directly in a resolver.
class Sources::RecordLoader < GraphQL::Dataloader::Source
def initialize(model_class, column: :id)
@model_class = model_class
@column = column
end
def fetch(ids)
records = @model_class.where(@column => ids).index_by { |r| r.send(@column) }
ids.map { |id| records[id] }
end
end
# Usage in type
def author
dataloader.with(Sources::RecordLoader, User).load(object.user_id)
end
See patterns.md for AssociationLoader and CountLoader variants.
Always use connection types for list fields — never return unbounded arrays.
module Types
class PostConnectionType < GraphQL::Types::Connection
edge_type(Types::PostEdgeType)
field :total_count, Integer, null: false
def total_count
object.items.size
end
end
end
# In query type
field :posts, Types::PostConnectionType, null: false, connection: true do
argument :filter, Types::PostFilterInput, required: false
end
# Trigger from model after_create_commit, not from mutations
class Post < ApplicationRecord
after_create_commit :notify_subscribers
private
def notify_subscribers
MyAppSchema.subscriptions.trigger(:post_created, {}, self)
end
end
class MyAppSchema < GraphQL::Schema
max_complexity 300
max_depth 15
default_max_page_size 25
use GraphQL::Dataloader
rescue_from ActiveRecord::RecordNotFound do |_err, _obj, _args, _ctx, field|
raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
end
end
Per-field complexity for expensive operations:
field :expensive_field, String do
complexity 50
end
| Anti-Pattern | Fix |
|-------------|-----|
| N+1 in resolvers | Use DataLoader / Sources |
| No complexity limits | Set max_complexity and max_depth |
| Raising exceptions in mutations | Return { errors: [...] } in payload |
| Authorization only at query root | Check auth at field level |
| Exposing ActiveRecord directly | Define explicit GraphQL types |
| Fat resolvers with business logic | Delegate to service objects |
| No pagination on list fields | Use connection types |
When analyzing or creating GraphQL components, provide:
{ resource: nil, errors: [...] } on validation failurerescue_from in schema for RecordNotFound mapped to GraphQL::ExecutionErrornil or raise GraphQL::ExecutionError for unauthorized accessdevelopment
WHAT: Language-agnostic corrective guidance for the refactoring phase. WHEN: Agent is restructuring code, fixing code smells, reducing complexity, or improving maintainability. NOT FOR: Writing new features, debugging runtime errors, performance tuning, or object design decisions.
tools
Analyzes Rails view templates, partials, layouts, helpers, and form patterns for best practices. Use when reviewing ERB templates, improving view performance with fragment caching, fixing form helpers, organizing partials, adding accessibility attributes, or evaluating collection rendering. NOT for Stimulus/Turbo logic (use hotwire-patterns), controller concerns, or API-only responses.
testing
Analyzes Rails test suites and recommends testing best practices for RSpec and Minitest. Use when writing new tests, reviewing test coverage, fixing flaky tests, improving test performance, choosing between test types (unit, integration, system, request), or setting up factories and fixtures. NOT for production monitoring, deployment verification, or load/stress testing infrastructure.
development
Detects a Rails project's architecture axes — logic placement (native vs extracted) and delivery (html vs api) — so other skills load profile-appropriate guidance without inline conditionals. Use when planning architecture or when a recommendation depends on where business logic lives or whether the app renders HTML or serves JSON. NOT for test framework, job backend, cache store, or auth library choices — those are orthogonal facts detected by project-conventions.