This report is generated based on example business code and official examples.
Scores in this report are given by AI after horizontal comparison.
Evaluation Date: January 2026
Evaluation Version: grats@0.0.34
Evaluation Method: Deep source code audit based on example business code and official examples
📋 Basic Information
| Item | Content |
|---|---|
| Current Version | 0.0.34 |
| GitHub | https://github.com/captbaritone/grats |
| Documentation | https://grats.capt.dev |
| First Commit | 2023-03-05 |
| Latest Commit | 2025-12-21 |
📊 Overall Score
| Dimension | Score (1-5) | Brief Comment |
|---|---|---|
| 1. Architecture | 3.5 | Lightweight dependencies, uses standard JSDoc comments, but requires build step, configuration is slightly complex |
| 2. Type Definition | 4.5 | Fully supports native TypeScript syntax, powerful type inference, automatic interface field inheritance |
| 3. Resolvers & Validation | 2.8 | Functional API is concise, parameter types are automatically inferred, but no built-in validation and DataLoader |
| 4. Built-in Features | 2.4 | Core features are complete (Directives, Scalars, Subscriptions), but advanced features are missing |
| 5. Ecosystem Integration | 3.0 | Excellent GraphQL Server compatibility, but ORM and validation libraries require manual integration |
1. Architecture
Grats adopts the Static Analysis architecture pattern, analyzing JSDoc comments and type definitions in source code through the TypeScript compiler API to generate GraphQL Schema at compile time. This is an Implementation-First approach, using TypeScript code as the single source of truth.
Architecture Overview
Core Mechanisms:
-
Static Analysis: Uses TypeScript compiler API (
ts.Program) to analyze source code (src/Extractor.ts) -
JSDoc-driven: Marks GraphQL entities through JSDoc comments (
@gqlType,@gqlField, etc.) - Code Generation: CLI tool generates executable TypeScript Schema files and GraphQL SDL files
- Compiler Plugin: Provides optional TypeScript language service plugin for IDE real-time diagnostics
Workflow (src/lib.ts:90-146):
- Create
Programusing TypeScript compiler API - Extract all definitions with JSDoc tags (
extractSnapshotsFromProgram) - Resolve type references, validate interface merging, handle default nullability
- Generate GraphQL AST (
DocumentNode) - Validate Schema and generate executable code (
src/codegen/schemaCodegen.ts)
1.1 Dependency Complexity
Score: 4.0
Evidence:
-
Runtime Dependencies (
grats/package.jsonlines 20-24):-
commander: ^14.0.1- CLI command-line tool -
graphql: ^16.11.0- GraphQL standard library -
semver: ^7.7.2- Version comparison tool -
typescript: 5.9.2- TypeScript compiler (fixed version)
-
-
No Decorator Dependencies: Does not require
reflect-metadataor decorator support - No Reflection Library: Does not depend on runtime reflection mechanisms
-
Compiler Plugin (
grats-ts-plugin/package.json): Independent optional package for IDE support
Analysis:
- ✅ Only 4 runtime dependencies, lighter than decorator pattern (requires
reflect-metadata,class-validator, etc.) - ✅ Core dependencies are only
graphqlandtypescript, following minimal dependency principle - ⚠️
typescriptis a runtime dependency (notdevDependency), because compiler API is needed for static analysis - ✅ Compiler plugin (
grats-ts-plugin) is optional dependency, does not affect core functionality
Actual Usage (typescript-graphql-schemas/grats/package.json):
- Business code only needs to install
gratsasdevDependency - Runtime only depends on
graphqland generated Schema files
1.2 Build Flow
Score: 3.0
Evidence:
-
Must Run CLI: Must execute
npx gratsorgratscommand to generate Schema (src/cli.ts:47-53) -
Generates Two Files:
-
.graphqlfile: GraphQL SDL definition (src/cli.ts:187-191) -
.tsfile: Executable TypeScript Schema (src/cli.ts:182-185)
-
-
Supports Watch Mode:
--watchoption supports automatic regeneration on file changes (src/cli.ts:48-49) -
Transparent Build Process: Uses TypeScript compiler API, build logic is traceable (
src/lib.ts:72-77)
Actual Usage Example (typescript-graphql-schemas/grats/package.json lines 7-8):
{
"scripts": {
"print": "grats && node src/print.ts",
"test": "grats && node src/schema.test.ts"
}
}
Generated Schema File (typescript-graphql-schemas/grats/src/schema.ts):
- Contains complete
GraphQLSchemainstance generation code - Automatically imports all Resolver functions
- Type-safe Schema configuration interface (
SchemaConfig)
Build Flow Details (src/cli.ts:173-211):
- Read
tsconfig.jsonand parse Grats configuration (getTsConfig) - Use TypeScript compiler API to analyze source code (
extractSchemaAndDoc) - Generate TypeScript Schema code (
printExecutableSchema) - Generate GraphQL SDL (
printGratsSDL) - Optional: Generate enums module (
printEnumsModule)
Analysis:
- ⚠️ Must run build command, cannot "write and use immediately"
- ✅ Supports Watch mode, good development experience
- ✅ Generated code is readable, easy to debug
- ⚠️ Need to integrate build step into development workflow (e.g.,
package.jsonscripts)
1.3 Config & Language Magic
Score: 4.0
Evidence:
-
JSDoc Comment-driven: Uses standard JSDoc comments to mark GraphQL entities (
src/Extractor.ts:60-73):-
@gqlType- Object type -
@gqlField- Field -
@gqlQueryField- Query field -
@gqlMutationField- Mutation field -
@gqlEnum- Enum type -
@gqlUnion- Union type -
@gqlInterface- Interface type
-
-
TypeScript Compiler Plugin (optional): Provides IDE real-time diagnostics (
src/tsPlugin/initTsPlugin.ts) -
Configuration in tsconfig.json: Grats configuration is in the
gratsfield oftsconfig.json(src/gratsConfig.ts:33)
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/**
* User information
* @gqlType
*/
export type User = {
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
email: string
}
/** @gqlQueryField */
export function users(): User[] {
return Array.from(userMap.values())
}
Configuration Example (typescript-graphql-schemas/grats/tsconfig.json):
{
"grats": {
"importModuleSpecifierEnding": ".ts",
"tsSchema": "./src/schema.ts"
},
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"strict": true
}
}
Compiler Plugin Configuration (examples/apollo-server/tsconfig.json):
{
"compilerOptions": {
"plugins": [
{
"name": "grats-ts-plugin"
}
]
}
}
Analysis:
- ✅ Uses standard JSDoc comments, conforms to TypeScript best practices
- ✅ Does not depend on decorators or reflection metadata, no special compilation options needed
- ⚠️ Requires many JSDoc comments, code is slightly verbose
- ✅ Compiler plugin is optional, but provides better development experience
- ⚠️ Configuration needs to be declared in
tsconfig.json, increases configuration complexity
1.4 Ecosystem Integration
Score: 4.0
Evidence:
-
Standard npm Installation: Install via
npm install gratsorpnpm add grats(grats/package.json) -
TypeScript Compiler Plugin: Provides
grats-ts-pluginpackage, supports IDE real-time diagnostics (grats-ts-plugin/package.json) -
Multiple GraphQL Server Support: Official examples include (
grats/examples/):- Apollo Server (
examples/apollo-server/) - GraphQL Yoga (
examples/yoga/) - Express GraphQL HTTP (
examples/express-graphql-http/) - Next.js (
examples/next-js/)
- Apollo Server (
-
Standard GraphQL Schema: Generated Schema is a standard
GraphQLSchemainstance, compatible with all GraphQL Servers
Actual Integration Example (typescript-graphql-schemas/grats/src/server.ts):
import { getSchema } from './schema.ts'
import { createYoga } from 'graphql-yoga'
const schema = getSchema({
scalars: {
DateTime: {
// Custom scalar configuration
}
}
})
const yoga = createYoga({ schema })
Apollo Server Integration (examples/apollo-server/server.ts):
import { getSchema } from './schema'
const server = new ApolloServer({
schema: getSchema(),
})
Next.js Integration (examples/next-js/app/api/graphql/route.ts):
- Supports Next.js App Router
- Uses standard GraphQL HTTP endpoint
Analysis:
- ✅ Standard npm package, simple installation
- ✅ Generated Schema is a standard GraphQL.js instance, excellent compatibility
- ✅ Provides multiple Server integration examples, covering mainstream scenarios
- ✅ Compiler plugin enhances IDE experience, but is optional
- ⚠️ Need to manually configure
gratsfield intsconfig.json
Score Details
1.1 Dependency Complexity
Score: 4.0
Reasons:
- Only 4 runtime dependencies (
commander,graphql,semver,typescript) - No decorators, no reflection metadata dependencies
- Compiler plugin is optional dependency
- Lighter than decorator pattern (requires
reflect-metadata,class-validator, etc.)
1.2 Build Flow
Score: 3.0
Reasons:
- Must run CLI command to generate Schema, cannot "write and use immediately"
- Supports Watch mode, good development experience
- Generated code is readable, easy to debug
- Need to integrate build step into development workflow
1.3 Config & Language Magic
Score: 4.0
Reasons:
- Uses standard JSDoc comments, conforms to TypeScript best practices
- Does not depend on decorators or reflection metadata
- Requires many JSDoc comments, code is slightly verbose
- Configuration needs to be declared in
tsconfig.json
1.4 Ecosystem Integration
Score: 4.0
Reasons:
- Standard npm installation, simple process
- Generated Schema is a standard GraphQL.js instance, excellent compatibility
- Provides multiple Server integration examples
- Compiler plugin enhances IDE experience (optional)
Overall Score
Architecture Total Score: 3.5
Advantages:
- Lightweight dependencies, no runtime overhead
- Uses standard JSDoc comments, conforms to TypeScript best practices
- Generated code is readable, easy to debug
- Compatible with all standard GraphQL Servers
Disadvantages:
- Must run build command, cannot "write and use immediately"
- Requires many JSDoc comments, code is slightly verbose
- Configuration needs to be declared in
tsconfig.json, increases configuration complexity
2. Type Definition
Grats adopts the Implementation-First approach, with TypeScript code as the single source of truth. Through static analysis of TypeScript type definitions and JSDoc comments, it automatically generates GraphQL Schema. Type inference is based on the TypeScript compiler API, capable of deep analysis of type structures.
2.1 Single Source of Truth Implementation
Score: 4.5
Evidence:
-
TypeScript Code is Schema: TypeScript type definitions in
typescript-graphql-schemas/grats/src/models/user.tsdirectly generate GraphQL Schema -
Automatic Type Extraction: Analyzes source code through TypeScript compiler API (
src/Extractor.ts) to extract type information - Bidirectional Sync: Both TypeScript types and GraphQL Schema are generated from the same code, no need to manually maintain two sets of definitions
-
Requires Build Step: Must run
gratsCLI command to generate Schema (src/cli.ts:182-191)
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/**
* User information
* @gqlType
*/
export type User = {
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
email: string
}
Generated GraphQL Schema (schema.graphql lines 83-89):
"""User information"""
type User {
email: String
id: Int
name: String
orders: [Order!]
}
Generated TypeScript Schema (src/schema.ts lines 140-166):
- Automatically generates executable
GraphQLSchemainstance - Automatically imports all Resolver functions
- Type-safe Schema configuration interface
Analysis:
- ✅ TypeScript code is the single source of truth, GraphQL Schema is automatically extracted from it
- ✅ Type definitions and Schema definitions are completely synchronized, no need to manually maintain
- ⚠️ Need to run build command to generate Schema, cannot "write and use immediately"
- ✅ Generated code is readable, easy to debug and verify
2.2 Enum & String Union Support
Score: 5.0
Evidence:
-
Supports TypeScript Enum:
src/Extractor.ts:2032-2082enumEnumDeclarationmethod supports TypeScriptenumdeclarations -
Supports String Union Types:
src/Extractor.ts:2084-2117enumTypeAliasDeclarationmethod supportstype Enum = 'A' | 'B'syntax - Zero Configuration: Directly uses native TypeScript syntax, no manual registration or mapping needed
Actual Usage Example (typescript-graphql-schemas/grats/src/models/menu.ts lines 6-10):
/**
* Sugar level for coffee
* @gqlEnum
*/
export type SugarLevel = 'NONE' | 'LOW' | 'MEDIUM' | 'HIGH'
Generated GraphQL Schema (schema.graphql lines 13-19):
"""Sugar level for coffee"""
enum SugarLevel {
HIGH
LOW
MEDIUM
NONE
}
TypeScript Enum Support (src/Extractor.ts:2032-2082):
- Supports standard TypeScript
enumdeclarations - Requires enum members to use string literal initialization (
src/Extractor.ts:2186-2194) - If
tsClientEnumsis configured, requires enum to be exported (src/Extractor.ts:2045-2052)
String Union Type Support (src/Extractor.ts:2119-2177):
- Supports
type Enum = 'A' | 'B' | 'C'syntax - Automatically extracts all string literals as enum values
- If
tsClientEnumsis configured, does not support type alias enums (src/Extractor.ts:2094-2097)
Analysis:
- ✅ Fully supports native TypeScript enum syntax, zero configuration
- ✅ Supports two approaches: TypeScript Enum and string union types
- ✅ Type-safe: Enum values are completely synchronized in TypeScript and GraphQL
- ✅ No manual mapping or registration needed, can be used directly
2.3 Interface Inheritance & Union Type Experience
Score: 4.5
Evidence:
-
Automatic Interface Field Inheritance:
src/transforms/addInterfaceFields.tsautomatically adds interface fields to implementing types -
TypeScript
implementsSupport:typescript-graphql-schemas/grats/src/models/menu.tsusesimplements Foodsyntax -
Union Type Support:
src/Extractor.ts:666-702unionTypeAliasDeclarationsupportstype Union = A | Bsyntax -
Requires Manual
__typename: Union types require manual definition of__typenamefield in classes (typescript-graphql-schemas/grats/src/models/menu.tslines 17, 43)
Interface Inheritance Example (typescript-graphql-schemas/grats/src/interfaces/Food.ts):
/**
* Food interface with common fields
* @gqlInterface
*/
export interface Food {
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
price: Float
}
Implementing Interface (typescript-graphql-schemas/grats/src/models/menu.ts lines 16-36):
/**
* Coffee menu item
* @gqlType
*/
export class Coffee implements Food {
__typename = 'Coffee' as const
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
price: Float
/** @gqlField */
sugarLevel: SugarLevel
/** @gqlField */
origin: string
// ... Interface fields are automatically inherited, no need to redeclare
}
Generated GraphQL Schema (schema.graphql lines 31-38):
"""Coffee menu item"""
type Coffee implements Food {
id: Int
name: String
origin: String
price: Float
sugarLevel: SugarLevel
}
Interface Field Auto-Add Mechanism (src/transforms/addInterfaceFields.ts:24-53):
- If functional fields are defined on the interface (using
@gqlField), they are automatically added to all implementing types - Calculates interface implementation relationships through
computeInterfaceMap(src/InterfaceGraph.ts:11-59) - Automatically merges interface fields when building Schema
Union Type Example (typescript-graphql-schemas/grats/src/models/menu.ts lines 61-65):
/**
* Menu item union type
* @gqlUnion
*/
export type MenuItem = Coffee | Dessert
Generated GraphQL Schema (schema.graphql lines 21-22):
"""Menu item union type"""
union MenuItem = Coffee | Dessert
Union Type Resolution (src/validations/validateTypenames.ts:22-68):
- Requires all Union member types to have
__typenamefield - Tracks which types have
__typenamefield throughhasTypenameSet - If a type does not have
__typenamefield, an error is reported (src/validations/validateTypenames.ts:42-60)
Analysis:
- ✅ Interface fields are automatically inherited, implementing types don't need to redeclare common fields
- ✅ Supports TypeScript
implementssyntax, conforms to native TypeScript practices - ✅ Supports defining functional fields on interfaces, automatically added to all implementing types
- ⚠️ Union types require manual definition of
__typenamefield, adds some boilerplate code - ✅ Union types support native TypeScript Union syntax, zero configuration
2.4 Type Inference Strength & Explicit Declaration Balance
Score: 4.0
Evidence:
-
Automatic Basic Type Inference:
src/Extractor.tsautomatically infers basic types likestring,number,booleanthrough TypeScript compiler API -
Automatic Array Type Inference: Supports
Type[]andArray<Type>syntax, automatically infers as GraphQL List type -
Automatic Optional Type Inference: Supports
Type | nullandType?syntax, automatically infers nullability -
Requires Explicit Type Aliases: For GraphQL-specific types (like
Int,Float), need to use type aliases provided bygratspackage (typescript-graphql-schemas/grats/src/models/user.tsline 5)
Type Inference Example (typescript-graphql-schemas/grats/src/models/user.ts):
import type { Int } from 'grats'
/**
* User information
* @gqlType
*/
export type User = {
/** @gqlField */
id: Int // Explicitly use Int type alias
/** @gqlField */
name: string // Automatically inferred as String
/** @gqlField */
email: string // Automatically inferred as String
}
Array Type Inference (typescript-graphql-schemas/grats/src/models/order.ts line 26):
/** @gqlField */
itemIds: Int[] // Automatically inferred as [Int!]
Optional Type Inference (typescript-graphql-schemas/grats/src/models/menu.ts line 83):
/** @gqlQueryField */
export function menuItem(id: Int): MenuItem | null {
// Return type automatically inferred as MenuItem (nullable)
}
Type Resolution Mechanism (src/transforms/resolveTypes.ts:41-47):
- Uses two-phase resolution: first extract all declarations, then resolve type references
- Supports generic type parameters (
src/transforms/resolveTypes.ts:73-98) - Tracks type declarations and references through
TypeContext(src/TypeContext.ts:62-298)
Default Nullability Handling (src/transforms/applyDefaultNullability.ts):
- If
nullableByDefault: trueis configured, all fields default to nullable - Can mark fields as non-nullable through
@killsParentOnException - Supports strict semantic nullability (
strictSemanticNullability)
Analysis:
- ✅ Powerful type inference: automatically infers basic types, arrays, optional types
- ✅ Deep type analysis: semantic analysis through TypeScript compiler API
- ⚠️ Requires explicit type aliases: for GraphQL-specific types (
Int,Float), need to use type aliases - ✅ Supports complex types: generics, union types, interfaces can all be correctly inferred
- ✅ Type-safe: all type inference is type-safe, compile-time checked
Score Details
2.1 Single Source of Truth Implementation
Score: 4.5
Reasons:
- TypeScript code is the single source of truth, GraphQL Schema is automatically extracted from it
- Type definitions and Schema definitions are completely synchronized, no need to manually maintain
- Need to run build command to generate Schema, cannot "write and use immediately"
- Generated code is readable, easy to debug and verify
2.2 Enum & String Union Support
Score: 5.0
Reasons:
- Fully supports native TypeScript enum syntax, zero configuration
- Supports two approaches: TypeScript Enum and string union types
- Type-safe: Enum values are completely synchronized in TypeScript and GraphQL
- No manual mapping or registration needed, can be used directly
2.3 Interface Inheritance & Union Type Experience
Score: 4.5
Reasons:
- Interface fields are automatically inherited, implementing types don't need to redeclare common fields
- Supports TypeScript
implementssyntax, conforms to native TypeScript practices - Supports defining functional fields on interfaces, automatically added to all implementing types
- Union types require manual definition of
__typenamefield, adds some boilerplate code
2.4 Type Inference Strength & Explicit Declaration Balance
Score: 4.0
Reasons:
- Powerful type inference: automatically infers basic types, arrays, optional types
- Deep type analysis: semantic analysis through TypeScript compiler API
- Requires explicit type aliases: for GraphQL-specific types (
Int,Float), need to use type aliases - Supports complex types: generics, union types, interfaces can all be correctly inferred
Overall Score
Type Definition Total Score: 4.5
Advantages:
- Fully supports native TypeScript syntax, zero configuration
- Powerful type inference capability, automatically analyzes complex type structures
- Interface fields are automatically inherited, reduces duplicate code
- Type definitions and Schema definitions are completely synchronized
Disadvantages:
- Need to run build command to generate Schema
- Union types require manual definition of
__typenamefield - Need to use type aliases for GraphQL-specific types
3. Resolvers & Validation
Grats Resolver definitions use a Functional API + JSDoc Comments approach. All Resolvers are ordinary TypeScript functions, marked as GraphQL fields through JSDoc comments. Parameter types are automatically inferred through the TypeScript type system, but parameters need to be explicitly declared.
Core Implementation Mechanism
Source Code Evidence:
- Resolver signature handling:
src/resolverSignature.tsdefines the Resolver parameter type system - Parameter resolution:
src/transforms/resolveResolverParams.tsresolves Resolver parameters, distinguishes GraphQL parameters, Context, Info - Context injection: Marks Context type through
@gqlContext(src/Extractor.ts:74)
3.1 Development Experience (Code Conciseness)
Score: 4.0
Evidence:
-
Functional API: Resolvers in
typescript-graphql-schemas/grats/src/models/user.tsare all ordinary functions -
JSDoc Comment Marking: Marked through
@gqlQueryField,@gqlMutationField,@gqlField - Automatic Parameter Type Inference: Function parameter types are automatically inferred through TypeScript
- Requires JSDoc Comments: Each Resolver requires JSDoc comments, code is slightly verbose
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/** @gqlQueryField */
export function users(): User[] {
return Array.from(userMap.values())
}
/** @gqlQueryField */
export function user(id: Int): User {
const user = userMap.get(id)
if (!user) throw new GraphQLError('User not found')
return user
}
/** @gqlField */
export function orders(user: User): Order[] {
return getOrdersByUserId(user.id)
}
/** @gqlMutationField */
export function createUser(name: string, email: string): User {
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
const id = incrementId()
const newUser = { id, name, email } as unknown as User
userMap.set(id, newUser)
return newUser
}
Code Volume Analysis:
- Query Resolver: Each function about 3-5 lines of code + 1 line JSDoc comment
- Mutation Resolver: Each function about 5-10 lines of code + 1 line JSDoc comment
- Field Resolver: Each function about 2-4 lines of code + 1 line JSDoc comment
Analysis:
- ✅ Functional API is concise and intuitive, conforms to TypeScript best practices
- ✅ Parameter types are automatically inferred, no manual declaration needed
- ⚠️ Requires JSDoc comments, each Resolver needs to be marked
- ✅ Code structure is clear, easy to understand and maintain
3.2 Modular Design (Domain-Driven Development Support)
Score: 4.0
Evidence:
-
Organized by Domain:
typescript-graphql-schemas/grats/src/models/directory is organized by domain modules (user.ts,menu.ts,order.ts) - Type Definition and Resolver Combined: Each module contains type definitions, Query, Mutation, and Field Resolver
- No Forced Modularization: Can split files by domain, or put all Resolvers in one file
- No Module Boundaries: No forced module boundaries, developers need to consciously follow
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
// User module: contains type definitions, Query, Mutation, Field Resolver
/**
* User information
* @gqlType
*/
export type User = {
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
email: string
}
/** @gqlQueryField */
export function users(): User[] { ... }
/** @gqlQueryField */
export function user(id: Int): User { ... }
/** @gqlField */
export function orders(user: User): Order[] { ... }
/** @gqlMutationField */
export function createUser(name: string, email: string): User { ... }
Module Organization:
- Each module file contains:
- Type definitions (
@gqlType,@gqlInterface,@gqlUnion,@gqlEnum) - Query Resolver (
@gqlQueryField) - Mutation Resolver (
@gqlMutationField) - Field Resolver (
@gqlField)
- Type definitions (
Analysis:
- ✅ Supports organizing code by domain modules, type definitions and Resolvers are in the same module
- ✅ Provides modular API, can split files by domain
- ⚠️ Does not force modularization, can write coupled giant files (but can be avoided through design)
- ✅ Code organization is clear, easy to maintain and test
3.3 Parameter Definition & Type Inference
Score: 4.0
Evidence:
-
Automatic Parameter Type Inference: In
typescript-graphql-schemas/grats/src/models/user.tsline 28user(id: Int),idtype is automatically inferred asInt -
Parameter Resolution Mechanism:
src/transforms/resolveResolverParams.ts:33-68automatically resolves Resolver parameters, distinguishes GraphQL parameters, Context, Info - Complete IDE Hints: TypeScript fully understands parameter types, IDE autocomplete works normally
-
Requires Explicit Parameter Declaration: Function parameters need explicit type declaration (e.g.,
id: Int)
Parameter Type System (src/resolverSignature.ts:48-97):
-
SourceResolverArgument: Parent object (source) -
ArgumentsObjectResolverArgument: Arguments object (args: { id: Int }) -
ContextResolverArgument: Context parameter (ctx: Ctx) -
DerivedContextResolverArgument: Derived Context (through@gqlContextfunction) -
InformationResolverArgument: Info parameter (info: GraphQLResolveInfo) -
NamedResolverArgument: Named parameter (id: Int)
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/** @gqlQueryField */
export function user(id: Int): User {
// id type automatically inferred as Int, IDE hints complete
const user = userMap.get(id)
if (!user) throw new GraphQLError('User not found')
return user
}
/** @gqlMutationField */
export function updateUser(
id: Int,
name?: string | null,
email?: string | null
): User {
// All parameter types automatically inferred, optional parameters automatically handled
const user = userMap.get(id)
if (!user) throw new GraphQLError('User not found')
if (name != null) user.name = name
if (email != null) {
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
user.email = email
}
return user
}
Context Injection Example (examples/production-app/ViewerContext.ts lines 48-54):
/** @gqlContext */
export type Ctx = YogaInitialContext & { vc: VC; credits: number }
/** @gqlContext */
export function getVc(ctx: Ctx): VC {
return ctx.vc
}
// Used in Resolver
/** @gqlQueryField */
export function currentUser(vc: VC): User {
// vc type automatically inferred as VC, injected through derived Context
return vc.getUserById(vc.userId())
}
Parameter Resolution Mechanism (src/transforms/resolveResolverParams.ts:123-294):
- Automatically identifies parameter types: identifies whether parameters are GraphQL parameters, Context, or Info through TypeScript type system
- Supports derived Context: automatically injects derived Context defined through
@gqlContextfunction - Validates parameter combinations: ensures parameter combinations are valid (e.g., cannot use positional parameters and arguments object simultaneously)
Analysis:
- ✅ Parameter types are mostly automatically inferred: automatically infers parameter types through TypeScript type system
- ✅ Complete IDE hints: TypeScript fully understands parameter types, IDE autocomplete works normally
- ⚠️ Requires explicit parameter declaration: function parameters need explicit type declaration (although this is a TypeScript requirement, it increases code volume)
- ✅ Type-safe: parameter types are completely synchronized with Schema definitions
3.4 Input Validation Mechanism
Score: 2.0
Evidence:
-
No Built-in Validation: All validation logic in
typescript-graphql-schemas/grats/src/models/user.ts(lines 43-44, 58-60) needs to be manually written -
Manual Error Throwing: Uses
GraphQLErrorto manually throw validation errors - Validation Logic Scattered: Validation code is scattered across various Resolvers, difficult to reuse
-
No Declarative Validation API: Does not provide declarative validation API like
.refine()
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/** @gqlMutationField */
export function createUser(name: string, email: string): User {
// Manually write validation logic
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
const id = incrementId()
const newUser = { id, name, email } as unknown as User
userMap.set(id, newUser)
return newUser
}
/** @gqlMutationField */
export function updateUser(
id: Int,
name?: string | null,
email?: string | null
): User {
const user = userMap.get(id)
if (!user) throw new GraphQLError('User not found')
if (name != null) user.name = name
if (email != null) {
// Validation logic repeated
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
user.email = email
}
return user
}
Complex Validation Example (typescript-graphql-schemas/grats/src/models/order.ts lines 77-82):
/** @gqlMutationField */
export function createOrder(userId: Int, items: Int[]): Order {
// Multiple validation conditions need to be manually written
if (!userMap.has(userId)) throw new GraphQLError('User not found')
if (items.length === 0) throw new GraphQLError('At least one item is required')
for (const id of items) {
if (!menuMap.has(id)) throw new GraphQLError(`Menu item not found`)
}
// ...
}
Analysis:
- ❌ No built-in validation: all validation logic needs to be manually written
- ❌ Validation logic scattered: validation code is scattered across various Resolvers, difficult to reuse
- ❌ No declarative validation API: does not provide declarative validation API like
.refine() - ⚠️ Can be implemented through helper functions: can create validation helper functions, but need to manually call them
3.5 Batch Loading (DataLoader) Integration
Score: 0.0
Evidence:
- No Built-in DataLoader Support: No DataLoader-related implementation found in framework source code
-
Requires Manual Implementation:
examples/production-app/ViewerContext.tsshows need to manually create DataLoader instances - Requires Manual Context Configuration: Need to manually add DataLoader instances to Context
Actual Usage Example (examples/production-app/ViewerContext.ts lines 18-36):
import DataLoader from "dataloader";
import { getPostsByIds, getUsersByIds, getLikesByIds } from "./Database";
export class VC {
_postLoader: DataLoader<string, Post>;
_userLoader: DataLoader<string, User>;
_likeLoader: DataLoader<string, Like>;
constructor() {
// Need to manually create DataLoader instances
this._postLoader = new DataLoader((ids) => getPostsByIds(this, ids));
this._userLoader = new DataLoader((ids) => getUsersByIds(this, ids));
this._likeLoader = new DataLoader((ids) => getLikesByIds(this, ids));
}
async getPostById(id: string): Promise<Post> {
return this._postLoader.load(id);
}
// ...
}
/** @gqlContext */
export type Ctx = YogaInitialContext & { vc: VC; credits: number };
Usage in Resolver (requires manual call):
/** @gqlField */
export function user(post: Post, vc: VC): User {
// Need to manually call DataLoader
return vc.getUserById(post.userId);
}
Analysis:
- ❌ No built-in support: framework does not provide any DataLoader-related API or tools
- ❌ Requires lots of boilerplate code: need to manually create DataLoader instances, define Context types, configure Context injection
- ❌ No automatic batching: need to manually implement batching logic
- ❌ No cache control: need to manually implement cache logic
Score Details
3.1 Development Experience (Code Conciseness)
Score: 4.0
Reasons:
- Functional API is concise and intuitive, conforms to TypeScript best practices
- Parameter types are automatically inferred, no manual declaration needed
- Requires JSDoc comments, each Resolver needs to be marked
- Code structure is clear, easy to understand and maintain
3.2 Modular Design (Domain-Driven Development Support)
Score: 4.0
Reasons:
- Supports organizing code by domain modules, type definitions and Resolvers are in the same module
- Provides modular API, can split files by domain
- Does not force modularization, can write coupled giant files (but can be avoided through design)
- Code organization is clear, easy to maintain and test
3.3 Parameter Definition & Type Inference
Score: 4.0
Reasons:
- Parameter types are mostly automatically inferred: automatically infers parameter types through TypeScript type system
- Complete IDE hints: TypeScript fully understands parameter types, IDE autocomplete works normally
- Requires explicit parameter declaration: function parameters need explicit type declaration (although this is a TypeScript requirement, it increases code volume)
- Type-safe: parameter types are completely synchronized with Schema definitions
3.4 Input Validation Mechanism
Score: 2.0
Reasons:
- No built-in validation: all validation logic needs to be manually written
- Validation logic scattered: validation code is scattered across various Resolvers, difficult to reuse
- No declarative validation API: does not provide declarative validation API like
.refine() - Can be implemented through helper functions: can create validation helper functions, but need to manually call them
3.5 Batch Loading (DataLoader) Integration
Score: 0.0
Reasons:
- No built-in support: framework does not provide any DataLoader-related API or tools
- Requires lots of boilerplate code: need to manually create DataLoader instances, define Context types, configure Context injection
- No automatic batching: need to manually implement batching logic
- No cache control: need to manually implement cache logic
Overall Score
Resolvers & Validation Total Score: 2.8
Advantages:
- Functional API is concise and intuitive, conforms to TypeScript best practices
- Parameter types are automatically inferred, complete IDE hints
- Supports organizing code by domain modules
- Type-safe, parameter types are completely synchronized with Schema definitions
Disadvantages:
- Requires JSDoc comments, code is slightly verbose
- No built-in validation, need to manually implement all validation logic
- No DataLoader support, need to manually implement batching
- Validation logic scattered, difficult to reuse
4. Built-in Features
Grats provides some core built-in features, but advanced feature support is limited. Core features (Directives, Scalars, Subscriptions, Context) all have native support, but middleware, query complexity analysis, and other advanced features need to be manually implemented or through third-party libraries.
Feature Overview Table
| Feature | Status | Description |
|---|---|---|
| Directive Support | ✅ Built-in | Define directives through @gqlDirective, use directives through @gqlAnnotate, supports all GraphQL directive locations, API is concise and type-safe |
| Extension Support | ⚠️ Plugin/Additional Implementation | Not built-in, but can be implemented through GraphQLError's extensions parameter, requires manual handling |
| Custom Scalars | ✅ Built-in | Mark custom scalar types through @gqlScalar, API is intuitive and type-safe |
| Subscription | ✅ Built-in | Native GraphQL Subscriptions support, implemented through @gqlSubscriptionField and AsyncIterable, supports real-time data push, underlying transport protocol has good compatibility (WebSocket, SSE, etc.), API is concise |
| Context Injection | ✅ Built-in | Native support for injecting context in Resolvers, mark Context type through @gqlContext, supports derived Context, type inference is complete, IDE hints are good |
| Middleware | ⛔ Cannot Implement | Completely does not support middleware mechanism, cannot inject logic before/after Resolver execution (such as logging, permission checks, performance monitoring) |
| Query Complexity Analysis | ⛔ Cannot Implement | Completely does not support query complexity analysis, cannot prevent complex query attacks |
| Depth Limiting | ⛔ Cannot Implement | Completely does not support depth limiting, cannot prevent deep query attacks |
| Batch Loading (DataLoader) | ⛔ Cannot Implement | No built-in support, need to manually create DataLoader instances (already evaluated in Phase 3) |
Detailed Analysis
4.1 Directive Support
Status: ✅ Built-in
Evidence:
-
Directive Definition:
src/Extractor.ts:67definesDIRECTIVE_TAG = "gqlDirective" -
Directive Usage: Mark directive usage through
@gqlAnnotate(src/Extractor.ts:68) -
Supports All Directive Locations: Supports
FIELD_DEFINITION,ARGUMENT_DEFINITION,OBJECT,INTERFACE,ENUM,SCALAR,INPUT_OBJECT,INPUT_FIELD_DEFINITION,ENUM_VALUE,FRAGMENT_SPREAD,INLINE_FRAGMENTand all other GraphQL directive locations -
Directive Argument Validation:
src/validations/validateDirectiveArguments.tsvalidates directive argument types
Actual Usage Example (src/tests/fixtures/directives/directiveOnFieldDefinition.ts):
/**
* This is my custom directive.
* @gqlDirective on FIELD_DEFINITION
*/
export function max(args: { foo: Int }) {}
/**
* All likes in the system. Note that there is no guarantee of order.
* @gqlQueryField
* @gqlAnnotate max(foo: 10)
*/
export function likes(args: { first?: Int | null }): string {
return "hello";
}
Generated GraphQL Schema:
"""This is my custom directive."""
directive @max(foo: Int!) on FIELD_DEFINITION
type Query {
"""All likes in the system. Note that there is no guarantee of order."""
likes(first: Int): String @max(foo: 10)
}
Directive Implementation Example (examples/production-app/graphql/directives.ts lines 6-20):
/**
* Some fields cost credits to access. This directive specifies how many credits
* a given field costs.
*
* @gqlDirective cost on FIELD_DEFINITION
*/
export function debitCredits(args: { credits: Int }, context: Ctx): void {
if (context.credits < args.credits) {
throw new GraphQLError(
`Insufficient credits remaining. This field cost ${args.credits} credits.`,
);
}
context.credits -= args.credits;
}
Analysis:
- ✅ Native support for defining and using GraphQL Directives
- ✅ Supports all GraphQL directive locations
- ✅ API is concise, defined and used through JSDoc comments
- ✅ Directive arguments are type-safe, automatically validated
- ⚠️ Directive implementation needs to be manually written (like
debitCreditsfunction), needs to be used with@graphql-tools/utils'smapSchema
4.2 Extension Support
Status: ⚠️ Plugin/Additional Implementation
Evidence:
- Not Built-in: No native API related to Extensions found in framework source code
-
Can be Implemented through GraphQLError: Can implement error extensions through
GraphQLError'sextensionsparameter - Cannot Declare Query Complexity: Cannot declare query complexity, execution time, and other extension information
Code Example (requires manual implementation):
import { GraphQLError } from 'graphql'
// Can be implemented through GraphQLError's extensions parameter
throw new GraphQLError('Expected error with extensions', {
extensions: { code: 'EXPECTED_ERROR' }
})
Analysis:
- ⚠️ Not built-in support for Extensions native API
- ⚠️ Can be implemented through
GraphQLError'sextensionsparameter, but requires manual handling - ⚠️ Cannot declare query complexity, execution time, and other extension information
- ⚠️ Needs to be used with GraphQL Server's middleware or plugins
4.3 Custom Scalars
Status: ✅ Built-in
Evidence:
-
Scalar Definition:
src/Extractor.ts:62definesSCALAR_TAG = "gqlScalar" -
Actual Usage:
typescript-graphql-schemas/grats/src/graphql/CustomScalars.tsshows custom scalar definition - Type-safe: Scalar types are completely synchronized with TypeScript types
Actual Usage Example (typescript-graphql-schemas/grats/src/graphql/CustomScalars.ts):
/** @gqlScalar */
export type DateTime = Date
Generated GraphQL Schema (schema.graphql line 4):
scalar DateTime
Generated TypeScript Schema (src/schema.ts lines 121-124):
const DateTimeType: GraphQLScalarType = new GraphQLScalarType({
name: "DateTime",
...config.scalars.DateTime
});
Analysis:
- ✅ Easy to define new scalar types, just mark with
@gqlScalar - ✅ API is intuitive, type-safe
- ⚠️ Does not include common scalars (like DateTime, JSON, BigInt), need to manually define or use third-party libraries (like
graphql-scalars) - ⚠️ Scalar serialization/deserialization logic needs to be manually provided in Schema configuration
4.4 Subscription
Status: ✅ Built-in
Evidence:
-
Subscription Field Marking:
src/Extractor.ts:72definesSUBSCRIPTION_FIELD_TAG = "gqlSubscriptionField" -
AsyncIterable Support:
src/validations/validateAsyncIterable.tsvalidates that subscription fields must returnAsyncIterable -
Actual Usage:
examples/strict-semantic-nullability/Subscription.tsshows subscription usage
Actual Usage Example (examples/strict-semantic-nullability/Subscription.ts lines 3-9):
/** @gqlSubscriptionField */
export async function* countdown(args: { from: Int }): AsyncIterable<Int> {
for (let i = args.from; i >= 0; i--) {
await new Promise((resolve) => setTimeout(resolve, 1000));
yield i;
}
}
Generated GraphQL Schema:
type Subscription {
countdown(from: Int!): Int!
}
AsyncIterable Validation (src/validations/validateAsyncIterable.ts):
- Requires all Subscription fields to return
AsyncIterable - Automatically validated when building Schema
Analysis:
- ✅ Native support for GraphQL Subscriptions
- ✅ Supports real-time data push, implemented through async generator
- ✅ Underlying transport protocol has good compatibility (supports WebSocket, SSE, etc. through graphql-yoga)
- ✅ API is concise, type-safe
- ✅ Automatically validates subscription field return types
4.5 Context Injection
Status: ✅ Built-in
Evidence:
-
Context Marking:
src/Extractor.ts:74definesCONTEXT_TAG = "gqlContext" -
Derived Context: Supports defining derived Context through
@gqlContextfunction -
Parameter Resolution:
src/transforms/resolveResolverParams.tsautomatically identifies and injects Context parameters
Actual Usage Example (examples/production-app/ViewerContext.ts lines 48-54):
/** @gqlContext */
export type Ctx = YogaInitialContext & { vc: VC; credits: number }
/** @gqlContext */
export function getVc(ctx: Ctx): VC {
return ctx.vc
}
// Used in Resolver
/** @gqlQueryField */
export function currentUser(vc: VC): User {
// vc type automatically inferred as VC, injected through derived Context
return vc.getUserById(vc.userId())
}
Analysis:
- ✅ Native support for injecting context in Resolvers
- ✅ Supports derived Context, defined through
@gqlContextfunction - ✅ Context type inference is complete, IDE hints are good
- ✅ Automatically identifies Context parameters, no manual configuration needed
4.6 Middleware
Status: ⛔ Cannot Implement
Evidence:
- No Related Support Found: No middleware-related code found in framework source code
- No Middleware API: Does not provide API for injecting logic before/after Resolver execution
Analysis:
- ❌ Completely does not support middleware mechanism
- ❌ Cannot inject logic before/after Resolver execution (such as logging, permission checks, performance monitoring)
- ⚠️ Can implement similar functionality by manually wrapping Resolvers, but requires additional boilerplate code
- ⚠️ Can be implemented through GraphQL Server's middleware (like graphql-yoga's plugin system)
4.7 Query Complexity Analysis
Status: ⛔ Cannot Implement
Evidence:
- No Related Support Found: No query complexity-related code found in framework source code
- No Complexity Calculation API: Does not provide query complexity calculation and analysis API
Analysis:
- ❌ Completely does not support query complexity analysis
- ❌ Cannot prevent complex query attacks
- ⚠️ Can be implemented through graphql-yoga's plugins (like
graphql-query-complexity), but requires additional configuration
4.8 Depth Limiting
Status: ⛔ Cannot Implement
Evidence:
- No Related Support Found: No depth limiting-related code found in framework source code
- No Depth Limiting API: Does not provide query depth limiting API
Analysis:
- ❌ Completely does not support depth limiting
- ❌ Cannot prevent deep query attacks
- ⚠️ Can be implemented through graphql-yoga's plugins (like
graphql-depth-limit), but requires additional configuration
Built-in Features Overall Score
Score: 2.4
Scoring Basis:
- Directives: ✅ Built-in (5 points) - Define through
@gqlDirective, supports all GraphQL directive locations - Extensions: ⚠️ Plugin/Additional Implementation (2 points) - Can be implemented through
GraphQLError'sextensionsparameter - DataLoader: ⛔ Cannot Implement (0 points) - No built-in support, need to manually create DataLoader instances
- Scalars: ✅ Built-in (5 points) - Mark custom scalar types through
@gqlScalar - Subscription: ✅ Built-in (5 points) - Native support, through
@gqlSubscriptionFieldandAsyncIterable - Context: ✅ Built-in (5 points) - Native support, mark Context type through
@gqlContext - Middleware: ⛔ Cannot Implement (0 points) - Completely does not support middleware mechanism
- Query Complexity: ⛔ Cannot Implement (0 points) - Completely does not support query complexity analysis
- Depth Limiting: ⛔ Cannot Implement (0 points) - Completely does not support depth limiting
Total Score: 22/45 = 2.4/5.0
Scoring Basis:
- Core features are well supported: Directives, Scalars, Subscriptions, Context all provide native support
- Advanced features have limited support: Middleware, Query Complexity, Depth Limiting are not supported
- Feature completeness: Out of 9 features, 5 are built-in, 1 is plugin/additional implementation, 3 cannot be implemented
Advantages:
- Core Features Complete: Directives, Scalars, Subscriptions, Context all provide native support
- Type-safe: All supported features have complete type inference
- API is Concise: Supported features have intuitive and easy-to-use APIs
Disadvantages:
- Advanced Features Missing: Middleware, Query Complexity, Depth Limiting are not supported
- Requires Manual Implementation: Some features (like directive implementation) need to be manually written
- Depends on Third-party Libraries: Some features need to be used with GraphQL Server's plugins
5. Ecosystem Integration
Grats adopts a Standard GraphQL Schema Output + Manual Integration strategy. Outputs standard GraphQLSchema through getSchema(), can integrate with any GraphQL Server, but ORM and validation libraries require manual integration.
Core Integration Strategy
Source Code Evidence:
- Schema output:
typescript-graphql-schemas/grats/src/schema.ts'sgetSchema()function outputs standardGraphQLSchemainstance - Standard compatibility: Generated Schema is a standard GraphQL.js instance, compatible with all GraphQL Servers
- Official examples:
examples/directory contains multiple integration examples (Apollo Server, Yoga, Express GraphQL HTTP, Next.js)
5.1 ORM Integration Depth
Score: <-To Be Scored->
Evidence:
- No Official Plugins: No official plugins found for Prisma, Drizzle, TypeORM, and other ORMs
-
Requires Manual Integration: All database operations in
typescript-graphql-schemas/grats/src/models/user.tsare manually implemented (using in-memory Map) - Type Sync Requires Manual Maintenance: ORM model definitions are separated from GraphQL Schema definitions, need to manually maintain synchronization
-
Official Example:
examples/production-app/Database.tsshows manually written database functions
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
// Need to manually define GraphQL types
/**
* User information
* @gqlType
*/
export type User = {
/** @gqlField */
id: Int
/** @gqlField */
name: string
/** @gqlField */
email: string
}
// Need to manually implement database queries
export const userMap = new Map<number, User>(USERS.map((u) => [u.id, { ...u } as unknown as User]))
/** @gqlQueryField */
export function users(): User[] {
return Array.from(userMap.values()) // Manual query
}
/** @gqlQueryField */
export function user(id: Int): User {
const user = userMap.get(id) // Manual query
if (!user) throw new GraphQLError('User not found')
return user
}
Official Example (examples/production-app/Database.ts):
- All database operations are manually written functions
- Uses in-memory arrays to simulate database
- Need to manually implement all CRUD operations
Analysis:
- ❌ No official plugins: Does not provide official plugins for Prisma, Drizzle, TypeORM, and other ORMs
- ❌ Requires lots of glue code: Must manually write all database query logic
- ❌ Type sync requires manual maintenance: ORM model definitions are separated from GraphQL Schema definitions, need to manually maintain synchronization
- ⚠️ Can manually integrate: Although can manually integrate ORM, requires lots of boilerplate code, and type sync needs to be manually maintained
5.2 Validation Library Integration
Score: <-To Be Scored->
Evidence:
- No Official Plugins: No official plugins found for Zod, Yup, Valibot, and other validation libraries
-
Requires Manual Integration: All validation logic needs to be manually written in Resolvers (
typescript-graphql-schemas/grats/src/models/user.tslines 43-44, 58-60) - Validation Logic Separated from Schema Definition: Validation code is scattered across various Resolvers, difficult to reuse
Actual Usage Example (typescript-graphql-schemas/grats/src/models/user.ts):
/** @gqlMutationField */
export function createUser(name: string, email: string): User {
// Need to manually write validation logic
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
const id = incrementId()
const newUser = { id, name, email } as unknown as User
userMap.set(id, newUser)
return newUser
}
/** @gqlMutationField */
export function updateUser(
id: Int,
name?: string | null,
email?: string | null
): User {
const user = userMap.get(id)
if (!user) throw new GraphQLError('User not found')
if (name != null) user.name = name
if (email != null) {
// Validation logic repeated
if (!email.includes('@')) {
throw new GraphQLError('Invalid email format')
}
user.email = email
}
return user
}
Complex Validation Example (typescript-graphql-schemas/grats/src/models/order.ts lines 77-82):
/** @gqlMutationField */
export function createOrder(userId: Int, items: Int[]): Order {
// Multiple validation conditions need to be manually written
if (!userMap.has(userId)) throw new GraphQLError('User not found')
if (items.length === 0) throw new GraphQLError('At least one item is required')
for (const id of items) {
if (!menuMap.has(id)) throw new GraphQLError(`Menu item not found`)
}
// ...
}
Analysis:
- ❌ No official plugins: Does not provide official plugins for Zod, Yup, Valibot, and other validation libraries
- ❌ Requires manual integration: All validation logic needs to be manually written in Resolvers
- ❌ Validation logic separated from Schema definition: Validation code is scattered across various Resolvers, difficult to reuse
- ❌ Requires lots of boilerplate code: Each validation scenario needs to be manually written
- ⚠️ Can manually integrate: Although can manually integrate validation libraries (like calling Zod in Resolvers), requires lots of boilerplate code
5.3 GraphQL Server Compatibility
Score: <-To Be Scored->
Evidence:
-
Outputs Standard GraphQL Schema:
typescript-graphql-schemas/grats/src/schema.ts'sgetSchema()function outputs standardGraphQLSchemainstance - Compatible with Standard GraphQL.js: Generated Schema is a standard GraphQL.js instance, compatible with all GraphQL Servers
-
Official Examples Cover Mainstream Servers:
examples/directory contains multiple integration examples:- Apollo Server (
examples/apollo-server/) - GraphQL Yoga (
examples/yoga/) - Express GraphQL HTTP (
examples/express-graphql-http/)
- Apollo Server (
Apollo Server Integration (examples/apollo-server/server.ts):
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
import { getSchema } from "./schema";
async function main() {
const server = new ApolloServer({ schema: getSchema() });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`Running a GraphQL API server at ${url}`);
}
GraphQL Yoga Integration (examples/yoga/server.ts):
import { createServer } from "node:http";
import { createYoga } from "graphql-yoga";
import { getSchema } from "./schema";
const yoga = createYoga({
schema: getSchema(),
});
const server = createServer(yoga);
server.listen(4000, () => {
console.log("Running a GraphQL API server at http://localhost:4000/graphql");
});
Express GraphQL HTTP Integration (examples/express-graphql-http/server.ts):
import * as express from "express";
import { createHandler } from "graphql-http/lib/use/express";
import { getSchema } from "./schema";
const app = express();
app.post(
"/graphql",
createHandler({
schema: getSchema(),
}),
);
app.listen(4000);
Analysis:
- ✅ Fully compatible: Fully compatible with all mainstream GraphQL Servers
- ✅ Zero configuration: Just pass the generated Schema to the Server
- ✅ Official examples are complete: Provides multiple mainstream Server integration examples
- ✅ Type-safe: Schema configuration interface is type-safe (
SchemaConfig)
5.4 Toolchain Integration
Score: <-To Be Scored->
Evidence:
TypeScript/JavaScript Support
TypeScript Support:
- Core Mechanism: Grats is a TypeScript compiler plugin, generates GraphQL Schema through static analysis of TypeScript code
-
TypeScript Compiler Dependency:
grats/src/cli.tsusestypescriptpackage and TypeScript compiler API (ts.createProgram,ts.createCompilerHost) -
TypeScript Plugin:
grats/src/tsPlugin/initTsPlugin.tsprovides TypeScript Language Service plugin, supports IDE real-time diagnostics -
All Examples are TypeScript: All official examples (
examples/apollo-server/,examples/yoga/,examples/next-js/, etc.) use TypeScript
JavaScript Support:
-
allowJs Option Support:
examples/next-js/tsconfig.jsonline 8 configures"allowJs": true, indicating JavaScript files can be processed -
But Requires TypeScript Compiler: Grats itself needs TypeScript compiler to run, even processing JavaScript files requires
tsconfig.jsonconfiguration -
Generated Code is TypeScript:
examples/next-js/schema.tsshows generated Schema file is TypeScript format
Code Evidence:
grats/src/cli.ts (lines 1-50):
#!/usr/bin/env node
import * as E from "./Errors";
import { Location } from "graphql";
import { getParsedTsConfig } from "./";
import {
SchemaAndDoc,
buildSchemaAndDocResult,
extractSchemaAndDoc,
} from "./lib";
import { Command } from "commander";
import { writeFileSync } from "fs";
import { resolve, dirname } from "path";
import { version } from "../package.json";
import { locate } from "./Locate";
import {
printGratsSDL,
printExecutableSchema,
printEnumsModule,
} from "./printSchema";
import * as ts from "typescript";
grats/examples/next-js/tsconfig.json (line 8):
"allowJs": true,
Runtime Environment Support
Node.js:
-
✅ Explicitly Supported: All official examples are Node.js environment
-
examples/apollo-server/server.tsuses@apollo/server -
examples/yoga/server.tsusesgraphql-yogaand Node.js HTTP server -
examples/express-graphql-http/server.tsuses Express -
examples/production-app/server.tsusesgraphql-yoga
-
-
Official Documentation Explicitly States:
website/docs/01-getting-started/01-quick-start.mdxline 51 usesimport { createServer } from "node:http" -
package.json Engine Requirements:
grats/package.jsonlines 46-48 specify"engines": { "node": ">=20 <=24" }
Bun:
-
⚠️ Theoretically Supported but Not Verified:
- Generated Schema code is standard TypeScript/JavaScript, theoretically can run in Bun
- But no Bun-related documentation, examples, or configuration
- All examples use Node.js runtime
Deno:
-
⚠️ Theoretically Supported but Not Verified:
- Generated Schema code is standard TypeScript/JavaScript, theoretically can run in Deno
- But no Deno-related documentation, examples, or configuration
- Grats CLI uses Node.js-specific APIs (
fs,path), needs Node.js environment to run
Cloudflare Workers:
-
⚠️ Theoretically Supported but Not Verified:
- Generated Schema code is standard TypeScript/JavaScript, theoretically can run in Cloudflare Workers
- But no Cloudflare Workers-related documentation, examples, or configuration
- All examples are Node.js server-side code
Browser:
-
⚠️ Only for Playground:
-
website/src/pages/exec/index.tsxuses@typescript/vfsto run TypeScript compiler in browser, but this is for online Playground, not actual usage scenario -
website/src/pages/exec/index.tsxlines 15-24 simulateprocess.cwd(), indicating special handling is needed to run in browser - All actual usage examples are server-side code
-
Code Evidence:
grats/website/docs/01-getting-started/01-quick-start.mdx (lines 50-66):
import { createServer } from "node:http";
import { createYoga } from "graphql-yoga";
import { getSchema } from "./schema.js"; // Will be generated by Grats
/** @gqlQueryField */
export function hello(): string {
return "Hello world!";
}
const yoga = createYoga({ schema: getSchema() });
const server = createServer(yoga);
server.listen(4000, () => {
console.log("Running a GraphQL API server at http://localhost:4000/graphql");
});
grats/package.json (lines 46-48):
"engines": {
"node": ">=20 <=24"
},
Build Tool Support
Next.js:
-
✅ Official Example:
examples/next-js/provides complete Next.js App Router integration example -
Next.js Configuration:
examples/next-js/next.config.jsuses default configuration, no special build tool configuration needed -
TypeScript Configuration:
examples/next-js/tsconfig.jsonuses Next.js recommended TypeScript configuration (moduleResolution: "bundler",allowJs: true) -
Official Documentation:
website/docs/05-examples/08-next-js.mdprovides Next.js integration documentation
TypeScript Compiler (tsc):
-
✅ Core Dependency: All examples use
tscto compile TypeScript code -
Build Scripts:
-
examples/apollo-server/package.jsonline 7:"start": "tsc && node dist/server.js" -
examples/yoga/package.jsonline 7:"start": "tsc && node dist/server.js" -
examples/express-graphql-http/package.jsonline 7:"start": "tsc && node dist/server.js"
-
-
TypeScript Configuration Examples:
-
examples/apollo-server/tsconfig.jsonusesmodule: "NodeNext",moduleResolution: "NodeNext" -
examples/yoga/tsconfig.jsonusesmodule: "NodeNext",moduleResolution: "NodeNext" -
examples/production-app/tsconfig.jsonusesmodule: "NodeNext",moduleResolution: "NodeNext"
-
Webpack:
-
⚠️ No Official Configuration Example:
- Documentation and examples do not provide webpack configuration examples
-
website/package.jsonline 45 contains"webpack": "^5.78.0", but this is for website build, not framework's build tool integration - Theoretically can integrate through TypeScript compiler, but requires user to configure themselves
Vite:
-
⚠️ No Official Configuration Example:
- Documentation and examples do not provide vite configuration examples
- No vite-related configuration files or documentation
- Theoretically can integrate through TypeScript compiler, but requires user to configure themselves
Rspack:
-
⚠️ No Official Configuration Example:
- Documentation and examples do not provide rspack configuration examples
- No rspack-related configuration files or documentation
- Theoretically can integrate through TypeScript compiler, but requires user to configure themselves
Grats CLI as Build Step:
-
✅ Independent CLI Tool:
grats/src/cli.tsimplements independent command-line tool, does not depend on specific build tools -
Workflow:
website/docs/05-guides/01-workflows.mdrecommends running Grats as build step (npm run grats), then using TypeScript compiler to compile -
Watch Mode: Supports
--watchoption, can automatically regenerate Schema during development
Code Evidence:
grats/examples/next-js/next.config.js (lines 1-5):
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = nextConfig;
grats/examples/apollo-server/tsconfig.json (lines 6-14):
"compilerOptions": {
"outDir": "dist",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "esnext",
"lib": ["esnext"],
"strict": true
}
grats/examples/yoga/tsconfig.json (lines 6-15):
"compilerOptions": {
"outDir": "dist",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "esnext",
"lib": ["esnext"],
"strict": true,
"skipLibCheck": true
}
Analysis:
- ✅ Native TypeScript Support: Completely based on TypeScript compiler, provides complete TypeScript support
- ✅ Node.js Explicitly Supported: All official examples are Node.js environment, documentation explicitly states
- ⚠️ JavaScript Support Limited: Although supports
allowJs, requires TypeScript compiler, not true native JavaScript support - ⚠️ Other Runtime Environments Not Verified: Bun, Deno, Cloudflare Workers theoretically supported, but no documentation or examples
- ⚠️ Browser Not Supported: Only for Playground, not actual usage scenario
- ✅ Next.js Official Example: Provides complete Next.js integration example
- ✅ TypeScript Compiler Integration: All examples use
tsc, integration is simple - ⚠️ Other Build Tools No Official Support: webpack, vite, rspack have no official configuration examples, requires user to configure themselves
- ✅ Independent CLI Tool: Does not depend on specific build tools, can be flexibly integrated as build step
Score Details
5.1 ORM Integration Depth
Score: <-To Be Scored->
Reasons:
- No official plugins: Does not provide official plugins for Prisma, Drizzle, TypeORM, and other ORMs
- Requires lots of glue code: Must manually write all database query logic
- Type sync requires manual maintenance: ORM model definitions are separated from GraphQL Schema definitions, need to manually maintain synchronization
- Can manually integrate: Although can manually integrate ORM, requires lots of boilerplate code
5.2 Validation Library Integration
Score: <-To Be Scored->
Reasons:
- No official plugins: Does not provide official plugins for Zod, Yup, Valibot, and other validation libraries
- Requires manual integration: All validation logic needs to be manually written in Resolvers
- Validation logic separated from Schema definition: Validation code is scattered across various Resolvers, difficult to reuse
- Requires lots of boilerplate code: Each validation scenario needs to be manually written
5.3 GraphQL Server Compatibility
Score: <-To Be Scored->
Reasons:
- Fully compatible: Fully compatible with all mainstream GraphQL Servers
- Zero configuration: Just pass the generated Schema to the Server
- Official examples are complete: Provides multiple mainstream Server integration examples
- Type-safe: Schema configuration interface is type-safe
5.4 Toolchain Integration
Score: <-To Be Scored->
Reasons:
- Native TypeScript Support: Completely based on TypeScript compiler, provides complete TypeScript support, all examples are TypeScript
-
JavaScript Support Limited: Although supports
allowJs, requires TypeScript compiler, not true native JavaScript support -
Node.js Explicitly Supported: All official examples are Node.js environment, documentation explicitly states,
package.jsonspecifies Node.js engine requirements - Other Runtime Environments Not Verified: Bun, Deno, Cloudflare Workers theoretically supported (generated code is standard TypeScript/JavaScript), but no documentation, examples, or configuration
-
Browser Not Supported: Only for Playground (through
@typescript/vfs), not actual usage scenario - Next.js Official Example: Provides complete Next.js App Router integration example and documentation
-
TypeScript Compiler Integration: All examples use
tsc, integration is simple, runs as build step - Other Build Tools No Official Support: webpack, vite, rspack have no official configuration examples, requires user to configure themselves
- Independent CLI Tool: Does not depend on specific build tools, can be flexibly integrated as build step, supports watch mode
Overall Score
Ecosystem Integration Total Score: 3.0
Advantages:
- Excellent GraphQL Server compatibility, fully compatible with all mainstream Servers
- Next.js official examples are complete
- Standard GraphQL Schema output, integration is simple
Disadvantages:
- ORM integration requires manual implementation, no official plugins
- Validation library integration requires manual implementation, no official plugins
- Requires lots of boilerplate code, type sync needs to be manually maintained
📝 Summary
Overall Score: 3.1/5.0
| Dimension | Score | Description |
|---|---|---|
| Architecture | 3.5 | Lightweight dependencies, uses standard JSDoc comments, but requires build step, configuration is slightly complex |
| Type Definition | 4.5 | Fully supports native TypeScript syntax, powerful type inference, automatic interface field inheritance |
| Resolvers & Validation | 2.8 | Functional API is concise, parameter types are automatically inferred, but no built-in validation and DataLoader |
| Built-in Features | 2.4 | Core features are complete (Directives, Scalars, Subscriptions), but advanced features are missing |
| Ecosystem Integration | 3.0 | Excellent GraphQL Server compatibility, but ORM and validation libraries require manual integration |
Overall Evaluation
Grats adopts the Static Analysis architecture pattern, analyzing JSDoc comments and type definitions in source code through the TypeScript compiler API to generate GraphQL Schema at compile time. This is an Implementation-First approach, using TypeScript code as the single source of truth. Type inference is strong, fully supports native TypeScript syntax, interface fields are automatically inherited. But requires build step, validation and DataLoader need to be manually implemented, advanced feature support is limited.
Core Advantages
- Uses Standard JSDoc: Conforms to TypeScript best practices, does not depend on decorators or reflection metadata
- Powerful Type Inference: Fully supports native TypeScript syntax, automatically analyzes complex type structures
- Automatic Interface Field Inheritance: No need to redeclare common fields when implementing interfaces
- Zero Configuration Enums: Supports TypeScript Enum and string union types, completely zero configuration
- Type Definitions and Schema Definitions Completely Synchronized: TypeScript code is the single source of truth
Main Disadvantages
-
Requires Build Step: Must run
gratsCLI command to generate Schema, cannot "write and use immediately" - Requires Many JSDoc Comments: Each Resolver requires JSDoc comments, code is slightly verbose
- No Built-in Validation: All validation logic needs to be manually written, validation code is repeated
- No DataLoader Support: Need to manually implement, requires lots of boilerplate code
- Advanced Features Missing: Middleware, Query Complexity, Depth Limiting are not supported
Use Cases
Recommended For
- Teams that prefer Implementation-First approach
- Projects that need powerful type inference
- Projects that don't mind build steps
- Projects that need automatic interface field inheritance
Not Recommended For
- Projects that need "write and use immediately"
- Projects that need validation or DataLoader
- Projects that need advanced features (Middleware, query complexity)
- Projects that want to reduce JSDoc comments
Improvement Suggestions
- Provide Validation and DataLoader Support: Reduce manual implementation, improve development efficiency
- Reduce JSDoc Comment Requirements: Reduce comments through better type inference
- Provide Middleware and Query Complexity Support: Meet advanced feature needs
- Support Runtime Build: Provide optional runtime build mode, reduce build step dependency
Top comments (0)