Skill
graphql-architect
Use proactively for GraphQL API design, schema optimization, or N+1 query issues. Designs schemas, resolvers, and subscriptions using graphql-ruby patterns.
From majestic-railsInstall
1
Run in your terminal$
npx claudepluginhub majesticlabs-dev/majestic-marketplace --plugin majestic-railsTool Access
This skill is limited to using the following tools:
Read Write Edit Grep Glob Bash WebSearch
Skill Content
GraphQL Architect for Rails
Audience: Rails developers building GraphQL APIs
Goal: Design schemas, resolvers, and subscriptions using graphql-ruby best practices
Key Capabilities
- Design GraphQL schemas with proper types, interfaces, and unions
- Prevent N+1 queries using graphql-batch or batch-loader patterns
- Implement subscriptions with ActionCable integration
- Set up authorization with Pundit or custom policies
- Configure pagination using connections and cursor-based patterns
Schema Design
Type Definitions
module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :email, String, null: false
field :posts, [Types::PostType], null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
Query Root
module Types
class QueryType < Types::BaseObject
field :user, Types::UserType, null: true do
argument :id, ID, required: true
end
field :users, Types::UserType.connection_type, null: false
def user(id:) = User.find_by(id: id)
def users = User.all
end
end
Mutations
module Mutations
class CreateUser < BaseMutation
argument :email, String, required: true
field :user, Types::UserType, null: true
field :errors, [String], null: false
def resolve(email:)
user = User.new(email: email)
user.save ? { user:, errors: [] } : { user: nil, errors: user.errors.full_messages }
end
end
end
N+1 Prevention with graphql-batch
# app/graphql/loaders/association_loader.rb
class Loaders::AssociationLoader < GraphQL::Batch::Loader
def initialize(model, association_name)
@model = model
@association_name = association_name
end
def perform(records)
preloader = ActiveRecord::Associations::Preloader.new(records:, associations: @association_name)
preloader.call
records.each { |record| fulfill(record, record.send(@association_name)) }
end
end
# Usage in type
class Types::UserType < Types::BaseObject
field :posts, [Types::PostType], null: false
def posts
Loaders::AssociationLoader.for(User, :posts).load(object)
end
end
ActionCable Subscriptions
module Types
class SubscriptionType < Types::BaseObject
field :post_created, Types::PostType, null: false
def post_created = object
end
end
# Triggering
AppSchema.subscriptions.trigger(:post_created, {}, post)
Authorization
class Types::BaseObject < GraphQL::Schema::Object
def self.authorized?(object, context)
Pundit.policy(context[:current_user], object).show?
end
end
Pagination (Connections)
field :posts, Types::PostType.connection_type, null: false do
argument :status, Types::PostStatusEnum, required: false
end
def posts(status: nil)
scope = Post.all
scope = scope.where(status:) if status
scope.order(created_at: :desc)
end
Error Handling
class AppSchema < GraphQL::Schema
rescue_from ActiveRecord::RecordNotFound do |err, obj, args, ctx, field|
raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
end
end
Performance
class AppSchema < GraphQL::Schema
max_complexity 200
max_depth 10
end
Deliverables
When designing GraphQL APIs, provide:
- Schema Definition: Types, queries, mutations
- N+1 Prevention: Loader implementations
- Authorization: Access control
- Subscriptions: Real-time features
- Testing: RSpec examples
Similar Skills
Stats
Parent Repo Stars30
Parent Repo Forks6
Last CommitMar 15, 2026