Skip to content

Add fragment-based query utilities for Keystone migration#359

Merged
borisno2 merged 5 commits intomainfrom
claude/add-graphql-query-runner-QOPIF
Mar 9, 2026
Merged

Add fragment-based query utilities for Keystone migration#359
borisno2 merged 5 commits intomainfrom
claude/add-graphql-query-runner-QOPIF

Conversation

@borisno2
Copy link
Member

@borisno2 borisno2 commented Mar 9, 2026

Summary

This PR introduces defineFragment, runQuery, and runQueryOne — composable, type-safe query utilities that replace context.graphql.run() for developers migrating from KeystoneJS. These utilities provide the same benefits as GraphQL fragments (reusability, type inference, nested relationship support) without requiring a GraphQL runtime.

Key Changes

  • New query module (packages/core/src/query/index.ts):

    • defineFragment<T>() — factory function to create reusable, typed field selections
    • runQuery() — execute list queries with where/orderBy/pagination, respecting access control
    • runQueryOne() — fetch a single record by where clause
    • ResultOf<F> — type helper to infer the exact shape returned by a fragment
    • QueryArgs — standardized query argument type (where, orderBy, take, skip)
  • Comprehensive test suite (packages/core/src/query/index.test.ts):

    • 510 lines of tests covering fragment construction, type inference, scalar/relationship fields, nested relationships (3+ levels), many-to-many, null handling, and pagination
    • Tests verify both runtime behavior (correct Prisma calls, field picking) and type correctness
  • Migration documentation:

    • New spec (specs/keystone-migration.md) — 615-line comprehensive guide covering config migration, fragment patterns (A–G), access control, hooks, field mapping, M2M join tables, auth, and a migration checklist
    • Updated docs/content/guides/migration.md with quick reference and examples
    • Updated migration skill docs with nested data patterns and runQuery recommendations
  • Public API export — added to packages/core/src/index.ts

  • Changeset documenting the feature for release notes

Implementation Details

  • Type-safe composition: Fragments are composable — pass a fragment as the value for any relationship field to nest queries and preserve type information through multiple levels
  • Prisma integration: Internally builds Prisma include maps and uses pickFields() to strip unselected fields from results, ensuring runtime shape matches ResultOf type
  • Access control compatible: Calls context.db[listKey].findMany/findFirst, so all configured access rules are enforced
  • No GraphQL required: Pure TypeScript — fragments are plain objects, no codegen step needed
  • Null/array preservation: Correctly handles nullable relationships, many relationships (arrays), and deeply nested structures

Migration Path

Developers migrating from Keystone can now:

  1. Replace GraphQL fragment strings with defineFragment<T>()(fields)
  2. Replace context.graphql.run() calls with runQuery() or runQueryOne()
  3. Use ResultOf<typeof fragment> instead of GraphQL codegen types
  4. Compose fragments by referencing them in parent fragments — no string concatenation needed

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU

claude added 3 commits March 9, 2026 08:40
Adds defineFragment, runQuery, and runQueryOne to @opensaas/stack-core.

These utilities provide composable, type-safe query helpers that replace
context.graphql.run() for teams migrating from KeystoneJS:

- defineFragment<T>()({ field: true, rel: nestedFragment }) — creates
  a reusable field-selection descriptor with full TypeScript inference
- ResultOf<typeof fragment> — infers the exact result shape, analogous
  to gql.tada's ResultOf (no GraphQL codegen step required)
- runQuery(context, listKey, fragment, args?) — executes findMany through
  context.db, respecting all access control rules
- runQueryOne(context, listKey, fragment, where) — executes findFirst

Also adds:
- specs/keystone-migration.md — comprehensive migration guide for both
  humans and AI agents covering all context.graphql patterns
- docs/content/guides/migration.md — new "Migrating context.graphql.run"
  section with before/after examples
- claude-plugins migration skills updated to recommend fragments for
  nested data patterns

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU
- Add `context.db.post.findMany({ query: fragment, where, orderBy, take, skip })`
  and `context.db.post.findUnique({ where, query: fragment })` as the primary
  API for type-safe, fragment-scoped queries
- Add `RelationSelector` type: union of Fragment shorthand or
  `{ query, where?, orderBy?, take?, skip? }` for nested relationship filtering
- Export `buildInclude`, `pickFields`, `isFragment` helpers from query module
- Add `AugmentedFindMany`, `AugmentedFindUnique`, `FindManyQueryArgs` types to
  access/types.ts and re-export from public API
- Add `orderBy` support to `createFindMany` (previously silently ignored)
- 42 tests covering all new paths: RelationSelector buildInclude, pickFields,
  runQuery integration, factory function (variables) pattern, isFragment guard

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU
…gment API

- Primary API is now context.db.post.findMany({ query: fragment, where, orderBy, ... })
  and context.db.post.findUnique({ where, query: fragment })
- Document RelationSelector pattern: { query, where?, orderBy?, take?, skip? }
- Document factory function (variables) pattern
- Update changeset to describe full feature set including new primary API
- Update migrate-context-calls and opensaas-migration skills
- Update specs/keystone-migration.md with patterns H (RelationSelector) and I (factory fn)

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU
@vercel
Copy link

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-docs Ready Ready Preview, Comment Mar 9, 2026 9:29am

@changeset-bot
Copy link

changeset-bot bot commented Mar 9, 2026

🦋 Changeset detected

Latest commit: c6f12d8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@opensaas/stack-core Minor
@opensaas/stack-auth Minor
@opensaas/stack-cli Minor
@opensaas/stack-rag Minor
@opensaas/stack-storage Minor
@opensaas/stack-tiptap Minor
@opensaas/stack-ui Minor
@opensaas/stack-storage-s3 Minor
@opensaas/stack-storage-vercel Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

…nique

The eslint-disable directive was two lines above the `any` annotation,
making it unused (triggering a lint error). Moved it to the line
directly above `query?: any`.

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU
- Fix `SelectedFields` mapped type: use `[ExtractFragment<T>] extends [never]`
  tuple-wrapping to correctly identify scalar fields; previously `never extends
  Fragment<any,any>` was vacuously true, causing scalar fields to resolve to
  `never` under strict tsc (vitest skips type-checking so this was hidden)
- Fix test: use `null` instead of `undefined` in findFirst mock (undefined is
  not assignable to `{} | null`)
- Fix test: remove invalid `commentWithAuthorFrag._fields.post` access (the
  fragment was defined without a `post` field)

https://claude.ai/code/session_012ismTCY2S84rCjRWL4jnwU
@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for Core Package Coverage (./packages/core)

Status Category Percentage Covered / Total
🔵 Lines 86.17% 455 / 528
🔵 Statements 85.45% 464 / 543
🔵 Functions 97.22% 70 / 72
🔵 Branches 73.86% 325 / 440
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for UI Package Coverage (./packages/ui)

Status Category Percentage Covered / Total
🔵 Lines 76.03% 92 / 121
🔵 Statements 75.39% 95 / 126
🔵 Functions 75.6% 31 / 41
🔵 Branches 65.78% 75 / 114
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for CLI Package Coverage (./packages/cli)

Status Category Percentage Covered / Total
🔵 Lines 75.73% 1167 / 1541
🔵 Statements 75.35% 1211 / 1607
🔵 Functions 79.59% 156 / 196
🔵 Branches 65.06% 540 / 830
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for Auth Package Coverage (./packages/auth)

Status Category Percentage Covered / Total
🔵 Lines 64.49% 89 / 138
🔵 Statements 61.03% 94 / 154
🔵 Functions 74.46% 35 / 47
🔵 Branches 62.79% 54 / 86
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for Storage Package Coverage (./packages/storage)

Status Category Percentage Covered / Total
🔵 Lines 42.44% 73 / 172
🔵 Statements 42.77% 74 / 173
🔵 Functions 42.85% 15 / 35
🔵 Branches 40.13% 61 / 152
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for RAG Package Coverage (./packages/rag)

Status Category Percentage Covered / Total
🔵 Lines 47.97% 355 / 740
🔵 Statements 48.14% 377 / 783
🔵 Functions 54.26% 70 / 129
🔵 Branches 42.55% 180 / 423
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)

Status Category Percentage Covered / Total
🔵 Lines 100% 40 / 40
🔵 Statements 100% 40 / 40
🔵 Functions 100% 9 / 9
🔵 Branches 100% 19 / 19
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)

Status Category Percentage Covered / Total
🔵 Lines 100% 38 / 38
🔵 Statements 100% 38 / 38
🔵 Functions 100% 8 / 8
🔵 Branches 100% 22 / 22
File CoverageNo changed files found.
Generated in workflow #977 for commit c6f12d8 by the Vitest Coverage Report Action

@borisno2 borisno2 merged commit 28be231 into main Mar 9, 2026
6 checks passed
@borisno2 borisno2 deleted the claude/add-graphql-query-runner-QOPIF branch March 9, 2026 09:36
@github-actions github-actions bot mentioned this pull request Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants