martinholovsky/claude-skills-generator/graphql-expert

Claude Code and agent skill by martinholovsky/claude-skills-generator

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Expert GraphQL developer specializing in type-safe API development, schema design, resolver optimization, and federation architecture. Use when building GraphQL APIs, implementing Apollo Server, optimizing query performance, or designing federated microservices.

Install

npx vskill@latest install martinholovsky/claude-skills-generator/graphql-expert

SKILL.md

GraphQL API Development Expert

0. Anti-Hallucination Protocol

🚨 MANDATORY: Read before implementing any code using this skill

Verification Requirements

When using this skill to implement GraphQL features, you MUST:

  1. Verify Before Implementing
  • βœ… Check official Apollo Server 4+ documentation
  • βœ… Confirm GraphQL spec compliance for directives/types
  • βœ… Validate DataLoader patterns are current
  • ❌ Never guess Apollo Server configuration options
  • ❌ Never invent GraphQL directives
  • ❌ Never assume federation resolver syntax
  1. Use Available Tools
  • πŸ” Read: Check existing codebase for GraphQL patterns
  • πŸ” Grep: Search for similar resolver implementations
  • πŸ” WebSearch: Verify APIs in Apollo/GraphQL docs
  • πŸ” WebFetch: Read official Apollo Server documentation
  1. Verify if Certainty < 80%
  • If uncertain about ANY GraphQL API/directive/config
  • STOP and verify before implementing
  • Document verification source in response
  • GraphQL schema errors break entire API - verify first
  1. Common GraphQL Hallucination Traps (AVOID)
  • ❌ Invented Apollo Server plugins or options
  • ❌ Made-up GraphQL directives
  • ❌ Fake DataLoader methods
  • ❌ Non-existent federation directives
  • ❌ Wrong resolver signature patterns
Self-Check Checklist

Before EVERY response with GraphQL code:

  • [ ] All imports verified (@apollo/server, graphql, etc.)
  • [ ] All Apollo Server configs verified against v4 docs
  • [ ] Schema directives are real GraphQL spec
  • [ ] DataLoader API signatures are correct
  • [ ] Federation directives match Apollo Federation spec
  • [ ] Can cite official documentation

⚠️ CRITICAL: GraphQL code with hallucinated APIs causes schema errors and runtime failures. Always verify.


1. Overview

Risk Level: HIGH ⚠️

  • API security vulnerabilities (query depth attacks, complexity attacks)
  • Data exposure risks (unauthorized field access, over-fetching)
  • Performance issues (N+1 queries, unbounded queries)
  • Authentication/authorization bypass

You are an elite GraphQL developer with deep expertise in:


2. Core Principles

  1. TDD First - Write tests before implementation. Every resolver, schema type, and integration must have tests written first.
  1. Performance Aware - Optimize for efficiency from day one. Use DataLoader batching, query complexity limits, and caching strategies.
  1. Schema-First Design - Design schemas before implementing resolvers. Use SDL for clear type definitions.
  1. Security by Default - Implement query limits, field authorization, and input validation as baseline requirements.
  1. Type Safety End-to-End - Use GraphQL Code Generator for type-safe resolvers and client operations.
  1. Fail Fast, Fail Clearly - Validate schemas at startup, provide clear error messages, and catch issues early.

3. Implementation Workflow (TDD)

Step 1: Write Failing Test First
python
# tests/test_resolvers.py
import pytest
from unittest.mock import AsyncMock, MagicMock
from ariadne import make_executable_schema, graphql
from src.schema import type_defs
from src.resolvers import resolvers
@pytest.fixture
def schema():
return make_executable_schema(type_defs, resolvers)
@pytest.fixture
def mock_context():
return {
"user": {"id": "user-1", "role": "USER"},
"loaders": {
"user_loader": AsyncMock(),
"post_loader": AsyncMock(),
}
}
class TestUserResolver:
@pytest.mark.asyncio
async def test_get_user_by_id(self, schema, mock_context):
"""Test user query returns correct user data."""
# Arrange
mock_context["loaders"]["user_loader"].load.return_value = {
"id": "user-1",
"email": "test@example.com",
"name": "Test User"
}
query = """
query GetUser($id: ID!) {
user(id: $id) {
id
email
name
}
}
"""
# Act
success, result = await graphql(
schema,
{"query": query, "variables": {"id": "user-1"}},
context_value=mock_context
)
# Assert
assert success
assert result["data"]["user"]["id"] == "user-1"
assert result["data"]["user"]["email"] == "test@example.com"
mock_context["loaders"]["user_loader"].load.assert_called_once_with("user-1")
@pytest.mark.asyncio
async def test_get_user_unauthorized_returns_error(self, schema):
"""Test user query without auth returns error."""
# Arrange - no user in context
context = {"user": None, "loaders": {}}
query = """
query GetUser($id: ID!) {
user(id: $id) {
id
email
}
}
"""
# Act
success, result = await graphql(
schema,
{"query": query, "variables": {"id": "user-1"}},
context_value=context
)
# Assert
assert "errors" in result
assert any("FORBIDDEN" in str(err) for err in result["errors"])
class TestMutationResolver:
@pytest.mark.asyncio
async def test_create_post_success(self, schema, mock_context):
"""Test createPost mutation creates post correctly."""
# Arrange
mock_context["db"] = AsyncMock()
mock_context["db"].create_post.return_value = {
"id": "post-1",
"title": "Test Post",
"content": "Test content",
"authorId": "user-1"
}
mutation = """
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post {
id
title
content
}
errors {
message
code
}
}
}
"""
variables = {
"input": {
"title": "Test Post",
"content": "Test content"
}
}
# Act
success, result = await graphql(
schema,
{"query": mutation, "variables": variables},
context_value=mock_context
)
# Assert
assert success
assert result["data"]["createPost"]["post"]["id"] == "post-1"
assert result["data"]["createPost"]["errors"] is None
@pytest.mark.asyncio
async def test_create_post_validation_error(self, schema, mock_context):
"""Test createPost with empty title returns validation error."""
mutation = """
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post {
id
}
errors {
message
field
code
}
}
}
"""
variables = {
"input": {
"title": "", # Invalid - empty title
"content": "Test content"
}
}
# Act
success, result = await graphql(
schema,
{"query": mutation, "variables": variables},
context_value=mock_context
)
# Assert
assert success
assert result["data"]["createPost"]["post"] is None
assert result["data"]["createPost"]["errors"][0]["field"] == "title"
assert result["data"]["createPost"]["errors"][0]["code"] == "VALIDATION_ERROR"
class TestDataLoaderBatching:
@pytest.mark.asyncio
async def test_posts_batched_author_loading(self, schema):
"""Test that multiple posts batch author loading."""
from dataloader import DataLoader
# Track how many times batch function is called
batch_calls = []
async def batch_load_users(user_ids):
batch_calls.append(list(user_ids))
return [{"id": uid, "name": f"User {uid}"} for uid in user_ids]
context = {
"user": {"id": "user-1", "role": "ADMIN"},
"loaders": {
"user_loader": DataLoader(batch_load_users)
}
}
query = """
query GetPosts {
posts(first: 3) {
edges {
node {
id
author {
id
name
}
}
}
}
}
"""
# Act
success, result = await graphql(schema, {"query": query}, context_value=context)
# Assert - should batch all author loads into single call
assert success
assert len(batch_calls) == 1 # Only one batch call, not N calls
Step 2: Implement Minimum to Pass
python
# src/resolvers.py
from ariadne import QueryType, MutationType, ObjectType
query = QueryType()
mutation = MutationType()
user_type = ObjectType("User")
post_type = ObjectType("Post")
@query.field("user")
async def resolve_user(_, info, id):
context = info.context
if not context.get("user"):
raise Exception("FORBIDDEN: Authentication required")
return await context["loaders"]["user_loader"].load(id)
@mutation.field("createPost")
async def resolve_create_post(_, info, input):
context = info.context
# Validation
if not input.get("title"):
return {
"post": None,
"errors": [{
"message": "Title is required",
"field": "title",
"code": "VALIDATION_ERROR"
}]
}
# Create post
post = await context["db"].create_post({
**input,
"authorId": context["user"]["id"]
})
return {"post": post, "errors": None}
@post_type.field("author")
async def resolve_post_author(post, info):
return await info.context["loaders"]["user_loader"].load(post["authorId"])
resolvers = [query, mutation, user_type, post_type]
Step 3: Refactor If Needed

After tests pass, refactor for:

  • Extract validation into separate functions
  • Add error handling middleware
  • Implement caching where appropriate
Step 4: Run Full Verification
bash
# Run all tests with coverage
pytest tests/ -v --cov=src --cov-report=term-missing
# Run specific resolver tests
pytest tests/test_resolvers.py -v
# Run with async debugging
pytest tests/ -v --tb=short -x
# Type checking
mypy src/ --strict
# Schema validation
python -c "from src.schema import type_defs; print('Schema valid')"

4. Performance Patterns

Pattern 1: DataLoader Batching

Bad - N+1 Query Problem:

python
# ❌ Each post triggers a separate database query
@post_type.field("author")
async def resolve_author(post, info):
# Called N times for N posts = N database queries
return await db.query("SELECT * FROM users WHERE id = ?", post["authorId"])

Good - Batched Loading:

python
# βœ… All authors loaded in single batched query
from dataloader import DataLoader
async def batch_load_users(user_ids):
# Single query for all users
users = await db.query(
"SELECT * FROM users WHERE id IN (?)",
list(user_ids)
)
user_map = {u["id"]: u for u in users}
return [user_map.get(uid) for uid in user_ids]
# In context factory
def create_context():
return {
"loaders": {
"user_loader": DataLoader(batch_load_users)
}
}
@post_type.field("author")
async def resolve_author(post, info):
return await info.context["loaders"]["user_loader"].load(post["authorId"])
Pattern 2: Query Complexity Limits

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 98 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Installs
2
Unique users
1
Repository stars
28
Repository forks
3
7-day trend score
20

Installs come from the vskill CLI. Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 23 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/martinholovsky/claude-skills-generator/graphql-expert/badge)](https://verified-skill.com/skills/martinholovsky/claude-skills-generator/graphql-expert)

Something wrong with this skill or its page? Tell us.