git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tools/gql-guide" ~/.claude/skills/diegosouzapw-awesome-omni-skill-gql-guide && rm -rf "$T"
skills/tools/gql-guide/SKILL.mdGraphQL Guide Skill
This skill provides interactive guidance on soda-gql features, syntax patterns, and best practices. Use
$ARGUMENTS to route to specific topics, or ask the user to choose a topic.
Topic Routing
Parse
$ARGUMENTS to determine the user's intent, or use AskUserQuestion to offer topic selection.
Available Topics
- tagged-template — Tagged template syntax for fragments and operations
- fragment — Fragment definitions and spreading patterns
- operation — Query, mutation, and subscription operations
- union — Union type handling and member selection
- directive — GraphQL directives (@include, @skip, custom directives)
- metadata — Fragment metadata and field-level callbacks
- setup — Project setup, config, and initial codegen
- lsp — LSP integration, editor setup, and diagnostics
- codegen — Schema codegen, typegen, and build integration
- colocation — Fragment colocation patterns ($colocate directive)
Topic Selection
If
$ARGUMENTS is empty or unclear, use AskUserQuestion:
Question: "What would you like guidance on?" Options:
- "Tagged template syntax" → tagged-template
- "Fragment patterns" → fragment
- "Operations (queries/mutations)" → operation
- "Union types" → union
- "Directives" → directive
- "Metadata and callbacks" → metadata
- "Project setup" → setup
- "LSP and editor integration" → lsp
- "Codegen and build tools" → codegen
- "Fragment colocation" → colocation
Topic Content
For each topic, provide:
- Concept explanation
- References to documentation files
- Code examples from playground
- Common patterns and anti-patterns
- Related topics
Topic 1: tagged-template
Concept
soda-gql supports tagged template syntax for writing GraphQL fragments and operations. Only
gql is exported from the generated runtime:
import { gql } from "<outdir-path>"; // e.g. "@/graphql-system" // Fragment with tagged template const userFragment = gql.default(({ fragment }) => fragment("UserFields", "User")`{ id name email }`(), ); // Operation with tagged template const getUserQuery = gql.default(({ query }) => query("GetUser")`($id: ID!) { user(id: $id) { id name } }`(), );
When to Use Tagged Template vs Callback Builder
Decision Tree:
-
Fragment definition:
- ✅ Use tagged template: Simple field selection, no aliases
- ❌ Use callback builder: Field aliases needed
-
Fragment spreading (Fragment → Fragment):
- ✅ Use tagged template interpolation:
...${otherFragment}
- ✅ Use tagged template interpolation:
-
Operation definition:
- ✅ Use tagged template: No fragment spreads via
, no aliases, no $colocate.spread() - ❌ Use callback builder: Has fragment spreads via
, aliases, or $colocate.spread()
- ✅ Use tagged template: No fragment spreads via
-
Special features:
- Metadata callbacks with variable access → callback builder only
- Operations with
→ callback builder only.spread()
Key Constraint
Both tagged templates reject interpolation with values:
${value}`` → ❌ Throws errorfragment("Name", "Type")\
{ field(id: ${id}) }`` → ❌ Throws errorquery("Name")\
The only valid interpolation is fragment-to-fragment spreading:
fragment("Name", "Type")\...${otherFragment} ...``.
Operations with fragment spreads MUST use callback builder syntax with
.spread() instead of tagged template interpolation.
Documentation References
- Callback-only features:
playgrounds/vite-react/src/graphql/callback-builder-features.ts - Fragment patterns:
playgrounds/vite-react/src/graphql/fragment-spread-patterns.md
Code Examples
Tagged template fragment:
// playgrounds/vite-react/src/graphql/fragments.ts const userFields = gql.default(({ fragment }) => fragment("UserFields", "User")`{ id name email createdAt }`(), );
Tagged template operation (no spreads):
// playgrounds/vite-react/src/graphql/operations.ts const simpleQuery = gql.default(({ query }) => query("GetUsers")`{ users { id name } }`(), );
Callback builder operation (with spreads):
// playgrounds/vite-react/src/graphql/callback-builder-features.ts const userQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!"), }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userFields.spread(), })), }), }), );
Common Patterns
✅ Simple fragment with tagged template:
const fields = gql.default(({ fragment }) => fragment("UserBasic", "User")`{ id name email }`(), );
✅ Fragment spreading another fragment (tagged template):
const extendedFields = gql.default(({ fragment }) => fragment("ExtendedUser", "User")`{ ...${userFields} createdAt updatedAt }`(), );
❌ Operation with fragment spread (WRONG - tagged template):
// This will FAIL - cannot use tagged template interpolation for fragment spreads in operations const badQuery = gql.default(({ query }) => query("BadQuery")`{ user { ${userFields} } }`(), );
✅ Operation with fragment spread (CORRECT - callback builder):
const userQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!") }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userFields.spread(), })), }), }), );
Related Topics
- fragment — Fragment definition and spreading patterns
- operation — Operation structure and variable handling
- metadata — Callback-builder-only metadata feature
Topic 2: fragment
Concept
Fragments define reusable field selections with type safety. They can be spread into operations or composed into other fragments.
Fragment Types
- Tagged template fragment — Simple field selection
- Callback builder fragment — Aliases, nested spreads, metadata
- Fragment spreading — Composing fragments together
Variable Declaration Pattern
"Fragments declare requirements; operations declare contract"
- Fragments can declare variables they need in the GraphQL definition
- Operations must explicitly declare ALL variables, including fragment requirements
- No auto-merge: operation variables are the source of truth
Documentation References
- Spread patterns:
playgrounds/vite-react/src/graphql/fragment-spread-patterns.md - Nested spreads:
playgrounds/vite-react/src/graphql/nested-fragment-verification.ts
Code Examples
Simple fragment:
const userBasic = gql.default(({ fragment }) => fragment("UserBasic", "User")`{ id name email }`(), );
Fragment with variables:
const userConditional = gql.default(({ fragment }) => fragment("ConditionalUser", "User")`($includeEmail: Boolean!) { id name email @include(if: $includeEmail) }`(), );
Fragment spreading (Fragment → Fragment):
const userExtended = gql.default(({ fragment }) => fragment("ExtendedUser", "User")`{ ...${userBasic} createdAt updatedAt }`(), );
Operation spreading fragment (callback builder):
const getUserQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!"), ...$var("includeEmail").Boolean("!"), }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userConditional.spread({ includeEmail: $.includeEmail }), })), }), }), );
Common Patterns
✅ Fragment composition via tagged template:
const baseFields = gql.default(({ fragment }) => fragment("UserBase", "User")`{ id name }`(), ); const extendedFields = gql.default(({ fragment }) => fragment("UserExtended", "User")`{ ...${baseFields} email }`(), );
✅ Operation declares all variables:
const getUserQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!"), ...$var("includeEmail").Boolean("!"), // ALL variables, including fragment requirements }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userConditional.spread({ includeEmail: $.includeEmail }), })), }), }), );
❌ Auto-merge expectation (WRONG):
// Fragment declares $includeEmail const frag = gql.default(({ fragment }) => fragment("F", "User")`($includeEmail: Boolean!) { id name email @include(if: $includeEmail) }`(), ); // Operation does NOT auto-inherit variables — must declare includeEmail explicitly const badQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!") }, // Missing includeEmail! fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...frag.spread(), // Will fail — $includeEmail not in scope })), }), }), );
Related Topics
- tagged-template — Syntax for fragment definitions
- operation — How operations use fragments
- metadata — Fragment-level metadata callbacks
Topic 3: operation
Concept
Operations define the GraphQL query/mutation/subscription structure with variables, arguments, and field selections.
Operation Types
- Tagged template operation — Simple operations without fragment spreads
- Callback builder operation — Complex operations with spreads, aliases, $colocate
Variable Handling
Operations declare the contract:
- All variables must be declared at operation level
- Fragment variables are NOT auto-merged
- In callback builder, use
to declare variables$var("name").Type("!")
Documentation References
- Examples:
playgrounds/vite-react/src/graphql/operations.ts - Callback-only features:
playgrounds/vite-react/src/graphql/callback-builder-features.ts
Code Examples
Simple query (tagged template):
const getUsers = gql.default(({ query }) => query("GetUsers")`{ users { id name } }`(), );
Query with variables (tagged template):
const getUser = gql.default(({ query }) => query("GetUser")`($id: ID!) { user(id: $id) { id name email } }`(), );
Query with fragment spread (callback builder):
const getUserWithFragment = gql.default(({ query, $var }) => query.operation({ name: "GetUserWithFragment", variables: { ...$var("id").ID("!"), }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userFields.spread(), })), }), }), );
Mutation (tagged template):
const createUser = gql.default(({ mutation }) => mutation("CreateUser")`($input: CreateUserInput!) { createUser(input: $input) { id name } }`(), );
Common Patterns
✅ Simple query (tagged template):
const getUserQuery = gql.default(({ query }) => query("GetUser")`($id: ID!) { user(id: $id) { id name posts { id title } } }`(), );
✅ Operation with multiple variables (callback builder):
const getUserPosts = gql.default(({ query, $var }) => query.operation({ name: "GetUserPosts", variables: { ...$var("id").ID("!"), ...$var("limit").Int(), ...$var("offset").Int(), }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...f.posts({ limit: $.limit, offset: $.offset })(({ f }) => ({ id: f.id, title: f.title, })), })), }), }), );
Related Topics
- tagged-template — Operation syntax options
- fragment — Using fragments in operations
- directive — Adding directives to operations
Topic 4: union
Concept
Union types in GraphQL represent a value that could be one of several types. soda-gql handles union types using standard GraphQL inline fragment syntax in tagged templates.
Union Member Selection
Union handling uses standard GraphQL inline fragment syntax (
... on TypeName { fields }). Always include __typename for type discrimination:
const searchQuery = gql.default(({ query }) => query("Search")`($term: String!) { search(term: $term) { __typename ... on User { id name } ... on Organization { id name members } } }`(), );
Documentation References
- Union verification:
playgrounds/vite-react/src/graphql/union-type-verification.ts - Callback-only features:
playgrounds/vite-react/src/graphql/callback-builder-features.ts
Code Examples
Union field selection (tagged template):
const searchQuery = gql.default(({ query }) => query("Search")`($term: String!) { search(term: $term) { __typename ... on User { id name email } ... on Post { id title content } } }`(), );
Union with fragment spread (callback builder):
const userFields = gql.default(({ fragment }) => fragment("UserFields", "User")`{ id name email }`(), ); const postFields = gql.default(({ fragment }) => fragment("PostFields", "Post")`{ id title content }`(), ); const searchQuery = gql.default(({ query, $var }) => query.operation({ name: "Search", variables: { ...$var("term").String("!") }, fields: ({ f, $ }) => ({ ...f.search({ term: $.term })(({ f }) => ({ __typename: f.__typename, ...userFields.spread(), ...postFields.spread(), })), }), }), );
Common Patterns
✅ Always include __typename:
const q = gql.default(({ query }) => query("Search")`($term: String!) { search(term: $term) { __typename ... on TypeA { id fieldA } ... on TypeB { id fieldB } } }`(), );
✅ Exhaustive member handling:
const q = gql.default(({ query }) => query("SearchAll")`($term: String!) { search(term: $term) { __typename ... on User { id name } ... on Organization { id name } ... on Bot { id label } } }`(), );
Related Topics
- fragment — Using fragments with union members
- metadata — Type-specific metadata callbacks
Topic 5: directive
Concept
GraphQL directives modify field behavior (@include, @skip) or provide metadata for tools. soda-gql supports standard and custom directives.
Directive Types
- Standard directives:
,@include(if: Boolean)@skip(if: Boolean) - Custom directives: Defined in schema, used for metadata or tooling
Directive Syntax
Tagged template with static values:
gql.default(({ fragment }) => fragment("UserFields", "User")`{ id name email @include(if: true) }`(), );
Tagged template with variables:
gql.default(({ fragment }) => fragment("ConditionalUser", "User")`($includeEmail: Boolean!) { id name email @include(if: $includeEmail) }`(), );
Documentation References
- Directive verification:
playgrounds/vite-react/src/graphql/directive-verification.ts
Code Examples
@include directive:
const conditionalFields = gql.default(({ fragment }) => fragment("ConditionalUser", "User")`($showEmail: Boolean!) { id name email @include(if: $showEmail) }`(), );
@skip directive:
const fields = gql.default(({ fragment }) => fragment("SkipEmail", "User")`($hideEmail: Boolean!) { id name email @skip(if: $hideEmail) }`(), );
Custom directive:
// Assuming schema has: directive @sensitive on FIELD_DEFINITION const userFields = gql.default(({ fragment }) => fragment("SensitiveUser", "User")`{ id name socialSecurityNumber @sensitive }`(), );
Common Patterns
✅ Conditional field inclusion (callback builder with fragment spread):
const detailsFragment = gql.default(({ fragment }) => fragment("UserDetails", "User")`($includeDetails: Boolean!) { bio @include(if: $includeDetails) website @include(if: $includeDetails) }`(), ); const getUserQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!"), ...$var("includeDetails").Boolean("!"), }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ id: f.id, name: f.name, ...detailsFragment.spread({ includeDetails: $.includeDetails }), })), }), }), );
Related Topics
- fragment — Directives in fragment definitions
- metadata — Directive metadata for tooling
Topic 6: metadata
Concept
soda-gql allows attaching metadata to fragments and operations for build-time processing (e.g., component mapping, documentation generation). Metadata is passed as an argument to the template call.
Metadata APIs
Static metadata — passed as argument to the template call:
const frag = gql.default(({ fragment }) => fragment("UserFields", "User")`{ id name }`({ metadata: { component: "UserCard" }, }), );
Callback metadata — receives variables for dynamic values:
const frag = gql.default(({ fragment }) => fragment("UserFields", "User")`($userId: ID!) { id name }`({ metadata: ({ $ }: { $: { userId: string } }) => ({ cacheKey: `user:${$.userId}`, }), }), );
Documentation References
- Callback-only features:
playgrounds/vite-react/src/graphql/callback-builder-features.ts - Metadata verification:
playgrounds/vite-react/src/graphql/metadata-verification.ts
Code Examples
Static fragment metadata:
const userFragment = gql.default(({ fragment }) => fragment("UserCard", "User")`{ id name email }`({ metadata: { component: "UserCard", description: "User profile data" }, }), );
Callback metadata with variables:
const userFragment = gql.default(({ fragment }) => fragment("CachedUser", "User")`($userId: ID!) { id name email }`({ metadata: ({ $ }: { $: { userId: string } }) => ({ cacheKey: `user:${$.userId}`, }), }), );
Callback builder operation with metadata:
const q = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!") }, metadata: ({ $, fragmentMetadata }) => ({ entityId: $.id, fragmentCount: fragmentMetadata?.length ?? 0, }), fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...userFragment.spread(), })), }), }), );
Common Patterns
✅ Component mapping:
gql.default(({ fragment }) => fragment("UserProfile", "User")`{ id name }`({ metadata: { component: "UserProfile" }, }), );
✅ Dynamic cache key:
gql.default(({ fragment }) => fragment("CachedUser", "User")`($id: ID!) { id name }`({ metadata: ({ $ }: { $: { id: string } }) => ({ cacheKey: `user:${$.id}`, }), }), );
Related Topics
- fragment — Metadata on fragments
- colocation — $colocate with metadata
Topic 7: setup
Concept
Setting up a new soda-gql project involves config file creation, schema setup, and initial codegen.
Setup Steps
-
Install dependencies:
bun add @soda-gql/core @soda-gql/builder bun add -d @soda-gql/cli -
Add framework plugin (optional but recommended):
# For Vite bun add -d @soda-gql/vite-plugin # For Next.js bun add -d @soda-gql/next-plugin -
Create config file (
):soda-gql.config.tsimport { defineConfig } from '@soda-gql/config'; export default defineConfig({ outdir: './src/graphql/generated', schemas: { default: { schemaFiles: ['./schema.graphql'], }, }, }); -
Run initial codegen:
bun run soda-gql codegen schema -
Configure build plugin (Vite example):
// vite.config.ts import { sodaGql } from '@soda-gql/vite-plugin'; export default { plugins: [sodaGql()], };
Documentation References
- Main README:
— Installation and quick startREADME.md - Playground examples:
— Working examples of all featuresplaygrounds/vite-react/src/graphql/
Common Issues
Issue: codegen fails with "config not found"
- Check config file is named correctly:
soda-gql.config.{ts,js,mjs} - Check config exports with
export default
Issue: "Cannot find module '@soda-gql/core'"
- Run
to install dependenciesbun install - Check package.json includes @soda-gql packages
Issue: Types not updating in editor
- Restart TypeScript server
- Check outdir matches tsconfig include paths
Related Topics
- codegen — Running codegen and typegen
- lsp — Editor integration
Topic 8: lsp
Concept
soda-gql provides LSP (Language Server Protocol) integration for real-time diagnostics, autocomplete, and hover information in editors.
LSP Features
- Real-time diagnostics: Type errors, invalid fields, syntax errors
- Autocomplete: Field names, types, directives
- Hover information: Type info, documentation
- Go to definition: Jump to schema type definitions
Editor Setup
VS Code:
- Install LSP extension (if available)
- Configure workspace settings:
{ "soda-gql.configPath": "./soda-gql.config.ts" }
Other editors:
- Use language server client compatible with your editor
- Point to soda-gql LSP server
LSP Diagnostics
The LSP validates:
- Field existence on types
- Argument types and required args
- Fragment type compatibility
- Variable types
- Directive usage
Common Issues
Issue: LSP not providing diagnostics
- Check config file is found (
in project detection)found: true - Restart editor/LSP server
- Check LSP logs for errors
Issue: False positive errors
- Run
to sync generated typesbun run soda-gql codegen schema - Check schema files are up to date
Related Topics
- setup — Initial LSP configuration
- codegen — Keeping LSP in sync with schema
Topic 9: codegen
Concept
soda-gql codegen generates TypeScript types from GraphQL schemas (schema codegen) and validates/generates types from tagged templates (typegen).
Codegen Commands
-
Schema codegen:
bun run soda-gql codegen schema- Reads schema files from config
- Generates runtime type system in outdir
- Creates fragment and query builder types
-
Type generation (typegen):
bun run soda-gql typegen- Scans codebase for tagged templates
- Validates field selections against schema
- Generates TypeScript types for fragments/operations
Build Integration
Development workflow:
- Edit schema files
- Run
bun run soda-gql codegen schema - Edit fragments/operations
- Build plugin auto-runs typegen during build
Watch mode (if supported):
bun run soda-gql codegen schema --watch
Documentation References
- Monorepo infrastructure:
— Build systemdocs/guides/monorepo-infrastructure.md
Common Issues
Issue: "Schema file not found"
- Check schemaFiles paths in config are relative to config file
- Use Read tool to verify file exists at expected path
Issue: Typegen shows "unknown field"
- Run schema codegen first:
bun run soda-gql codegen schema - Check field name spelling matches schema
Issue: Generated types not updating
- Delete generated directory and re-run codegen
- Check outdir in config matches import paths
Related Topics
- setup — Initial codegen configuration
- lsp — Real-time validation vs build-time codegen
Topic 10: colocation
Concept
Fragment colocation places fragment definitions near the components that use them, improving code organization and enabling build-time optimizations.
Colocation Patterns
$colocate in callback builder spread:
const userQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUser", variables: { ...$var("id").ID("!") }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ id: f.id, ...userFragment.spread({ $colocate: true }), })), }), }), );
Component colocation:
// UserCard.tsx export const userCardFragment = gql.default(({ fragment }) => fragment("UserCardFields", "User")`{ id name email avatarUrl }`(), ); export function UserCard({ user }) { // Component uses fragment data }
Build-Time Processing
Colocation enables:
- Dead code elimination (unused fragments removed)
- Component-fragment association tracking
- Automatic fragment composition
Documentation References
- Callback-only features:
— $colocate examplesplaygrounds/vite-react/src/graphql/callback-builder-features.ts
Common Patterns
✅ Component-fragment pair:
// UserProfile.tsx const userProfileFragment = gql.default(({ fragment }) => fragment("UserProfile", "User")`{ id name email bio }`(), ); function UserProfile({ data }) { // Use fragment data }
✅ Fragment composition with colocation:
const parentQuery = gql.default(({ query, $var }) => query.operation({ name: "GetUserPage", variables: { ...$var("id").ID("!") }, fields: ({ f, $ }) => ({ ...f.user({ id: $.id })(({ f }) => ({ ...profileFragment.spread({ $colocate: true }), ...settingsFragment.spread({ $colocate: true }), })), }), }), );
Related Topics
- fragment — Fragment spreading and composition
- metadata — Component metadata with colocation
Interactive Mode
If the user's question doesn't match a specific topic, use Grep to search documentation:
- Extract keywords from user's question
- Use Grep to search docs/guides/ and playgrounds/
- Read matching files and synthesize answer
- Reference specific documentation sections
Example: User asks "How do I add pagination?"
- Grep for "pagination" in docs and playground
- If not found, explain general approach:
- Add limit/offset arguments to field
- Use variables in operation
- Show example with args
- Suggest related topics: operation, fragment
Validation Checklist
Before completing this skill, ensure:
- ✅ Topic was identified from $ARGUMENTS or user selection
- ✅ Relevant documentation was referenced
- ✅ Code examples were provided from playground or synthesized
- ✅ Common patterns and anti-patterns were shown
- ✅ Related topics were suggested for further exploration