Skip to content

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Jan 23, 2026

refactor(graphql-codegen): separate concerns by moving business logic to core

Summary

Refactors the @constructive-io/graphql-codegen package to properly separate concerns by moving all business logic from the cli/ directory into a core/ directory structure. The CLI commands are now thin wrappers that handle user interaction while delegating to core modules.

Structural changes:

  • cli/codegen/*core/codegen/* (code generation)
  • cli/introspect/*core/introspect/* (schema introspection)
  • cli/watch/*core/watch/* (watch mode)
  • cli/commands/shared.tscore/pipeline/index.ts

New core modules:

  • core/config/ - Config loading (loader.ts) and resolution (resolver.ts)
  • core/output/ - File writing utilities (writer.ts)
  • core/database/ - Database schema utilities (buildSchemaFromDatabase, buildSchemaSDLFromDatabase)
  • core/introspect/source/database.ts - DatabaseSchemaSource for in-memory database introspection
  • core/introspect/source/pgpm-module.ts - PgpmModuleSchemaSource for PGPM module introspection
  • core/introspect/source/api-schemas.ts - Utilities for resolving schemas from API names
  • core/codegen/shared/ - Shared types generator for unified output

Key pattern change: The watch orchestrator now accepts generate functions via dependency injection to avoid circular dependencies between CLI commands and core modules.

Updates since last revision

Phase 11: Database/PGPM config restructuring

Restructured the configuration to use a cleaner nested db object pattern:

  • Renamed schemaschemaFile throughout codebase
  • Consolidated pgpmModulePath, pgpmWorkspacePath, pgpmModuleName into nested db.pgpm object
  • Moved schemas, apiNames, keepDb into the db object
  • Added db.config?: Partial<PgConfig> with fallback to environment variables via getEnvOptions().pg
  • Updated CLI to use --schema-file flag and new db structure
  • Updated README with new config structure examples and programmatic API documentation
  • Updated all tests for new config structure

New config structure:

await generate({
  db: {
    config: { host: 'localhost', database: 'mydb' }, // optional, falls back to env vars
    pgpm: { modulePath: './packages/my-module' },    // or workspacePath + moduleName
    schemas: ['public'],
    apiNames: ['my_api'],  // mutually exclusive with schemas
    keepDb: true,          // for debugging
  },
  output: './generated',
  reactQuery: true,
  orm: true,
});

Review & Testing Checklist for Human

  • Test the new db config structure: Verify that db.pgpm.modulePath, db.schemas, db.apiNames, and db.config all work correctly at runtime
  • Verify CLI flag changes: Test --schema-file (renamed from --schema), --pgpm-module-path, --schemas, --api-names flags
  • Test environment variable fallback: When db.config is not provided, verify it correctly falls back to PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE environment variables
  • Verify README examples: The README was heavily rewritten - spot check that the code examples actually work
  • Test multi-target config: Verify resolveConfigTargets() correctly handles the new nested db structure in defaults and targets

Notes

  • All 193 unit tests pass in graphql-codegen, all 10 tests pass in packages/cli
  • Build succeeds with TypeScript compilation
  • All 48 CI checks pass
  • Breaking changes:
    • schema config field renamed to schemaFile
    • database, pgpmModulePath, pgpmWorkspacePath, pgpmModuleName, schemas, apiNames, keepDb moved into nested db object
    • CLI flag --schema renamed to --schema-file
    • generateReactQuery and generateOrm are no longer exported - use generate({ reactQuery: true }) or generate({ orm: true })
    • ResolvedConfig, ResolvedWatchConfig, ResolvedQueryKeyConfig types are deleted - use GraphQLSDKConfigTarget, WatchConfig, QueryKeyConfig
  • The generated ORM code in graphql-server is now static (committed) and no longer regenerated via devDependency

Link to Devin run: https://app.devin.ai/sessions/52a59f63cea74493bcb0d97f67422e8d
Requested by: Dan Lynch (@pyramation)

… to core

Move all business logic from cli/ to core/ directory structure:
- Move cli/codegen/* to core/codegen/* (code generation)
- Move cli/introspect/* to core/introspect/* (schema introspection)
- Move cli/watch/* to core/watch/* (watch mode)
- Move cli/commands/shared.ts to core/pipeline/ (codegen pipeline)
- Create core/config/ with loader.ts and resolver.ts (config utilities)
- Create core/output/ with writer.ts (file writing utilities)

CLI commands are now thin wrappers that:
- Handle user prompts and CLI arguments
- Call core functions for business logic
- Format and display output

This improves separation of concerns and makes the core logic
reusable outside of the CLI context.

Update all test imports to reference new core/ paths.
Update package.json copy:ts script for new file locations.
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

… CLI

Phase 2 of separation of concerns:
- Add @constructive-io/graphql-server as dependency to graphql-codegen
- Remove @constructive-io/graphql-codegen devDependency from graphql-server (generated code is committed)
- Create core/database module with buildSchemaFromDatabase function
- Clean up packages/cli codegen.ts to use inquirerer pattern and import from graphql-codegen
- Export buildSchemaFromDatabase and buildSchemaSDLFromDatabase from graphql-codegen

This breaks the circular dependency by making it a one-way dependency:
graphql-codegen -> graphql-server (no reverse dependency)
The codegen command now uses prompter.prompt() for input handling,
so tests need to provide a mock prompter that returns the argv values.
…ements

- Rename 'generate' to 'react-query' for generator type (more specific naming)
- Rename 'generate-orm' to 'orm' for generator type
- Replace ternary operators with switch statements that throw on unknown cases
- Improves code clarity and type safety
…gen command

- Convert orm boolean check to explicit generatorType variable
- Use switch statement with 'orm' and 'react-query' cases
- Add throw for unknown generator type (defensive programming)
- Extract output directory calculation to separate variable
Extend generateReactQuery and generateOrm to accept database options directly,
eliminating the need to call buildSchemaFromDatabase first.

New usage:
  await generateReactQuery({ database: 'mydb', schemas: ['public'] });
  await generateOrm({ database: 'mydb', schemas: ['public'] });

Changes:
- Add DatabaseSchemaSource class for in-memory database introspection
- Update createSchemaSource to detect and handle database mode
- Update ConfigOverrideOptions to include database and schemas options
- Update generateReactQuery and generateOrm to pass database options through
- Add validateSourceOptions to check for exactly one source type
- Add ephemeral database utilities to pgsql-client (createEphemeralDb)
- Add PgpmModuleSchemaSource class for introspecting PGPM modules
- Support two input modes: pgpmModulePath and pgpmWorkspacePath + pgpmModuleName
- Add keepDb option for debugging ephemeral databases
- Update config resolver and generate commands to handle PGPM options
- Add @pgpmjs/core, pgsql-client, and pgsql-seed dependencies to graphql-codegen
…overy

- Add apiNames option to schema sources (mutually exclusive with schemas)
- Create api-schemas.ts with utilities for resolving schemas from API names
- Query services_public.api_schemas joined with services_public.apis and metaschema_public.schema
- Validate services_public/metaschema_public schemas exist:
  - For database mode: validate at the beginning
  - For PGPM modes: validate after module deployment
- Update config resolver to handle apiNames in ConfigOverrideOptions
- Update generate.ts and generate-orm.ts to extract and pass apiNames
- Add pg-cache and pg-env as dependencies
…y and --orm flags

- Create unified generate() function that respects config.reactQuery.enabled and orm.enabled
- Simplify CLI to use single 'generate' command with --reactquery and --orm flags
- Remove old 'generate-orm' command (now unified into 'generate')
- Add CLI support for new source options: --database, --pgpm-module-path, --pgpm-workspace-path, --pgpm-module-name
- Add CLI support for schema options: --schemas, --api-names
- Update help text with comprehensive documentation
- Update README with new CLI usage examples
- Update package.json example scripts
…ctQuery/generateOrm private

- Create core/codegen/shared/ module for shared types generation
- Update unified generate() to use shared types when both React Query and ORM are enabled
- Add sharedTypesPath option to React Query SDK and ORM generators
- Make generateReactQuery() and generateOrm() internal (not exported from package)
- Update packages/cli to use unified generate() function
- Export only generate() as the public API
…nd use Babel AST for barrel exports

- Add database, pgpmModulePath, pgpmWorkspacePath, pgpmModuleName, schemas, apiNames, keepDb fields to GraphQLSDKConfigTarget and ResolvedConfig types
- Remove all (config as any) type casts now that fields are first-class citizens
- Simplify buildTargetOverrides by removing redundant undefined assignments (validation happens in loadAndResolveConfig)
- Update generateSharedBarrel in shared/index.ts to use Babel AST instead of string concatenation
- Update generateUnifiedBarrel in generate-unified.ts to use Babel AST instead of string concatenation
- Update resolveMultiTargetConfig to check for all source types (database, pgpm, etc.)
…ttern

- Delete ResolvedConfig, ResolvedWatchConfig, ResolvedQueryKeyConfig types
- Use GraphQLSDKConfigTarget everywhere (single type with optional fields)
- Delete buildTargetOverrides function - use destructuring inline
- Create getConfigOptions function following pgpm/env pattern
- Make ConfigOverrideOptions extend GraphQLSDKConfigTarget
- Rename ResolvedTargetConfig to TargetConfig
- Update all imports and usages to use new types
- Remove (config as any) casts since config now has all fields
- Rename reactQuery/orm options to enableReactQuery/enableOrm in GenerateOptions
- Update tests to use QueryKeyConfig instead of ResolvedQueryKeyConfig
…ndpoints during introspection

This fixes DNS resolution issues on macOS where subdomains like api.localhost
don't resolve automatically to 127.0.0.1 (unlike browsers which handle *.localhost).

Uses undici Agent with custom DNS lookup to resolve *.localhost to 127.0.0.1
at codegen time only. The generated client code remains browser-compatible.
Use kebab-case for CLI flag consistency with other flags like --dry-run, --keep-db, etc.
…y interface

- Move programmatic API documentation to top (after installation)
- Move CLI documentation to bottom as secondary interface
- Remove legacy/backwards compatibility mentions and exports
- Remove deprecation comments from code
- Simplify barrel.ts by removing legacy boolean signature support
- Clean up client-generator.ts comments
- Update single-target.config.ts example comment
Remove GeneratorType, GenerateOrmOptions, GenerateOrmResult, and
GenerateOrmTargetResult exports that were removed during legacy cleanup
…ed db object

- Rename 'schema' to 'schemaFile' throughout codebase
- Consolidate pgpmModulePath, pgpmWorkspacePath, pgpmModuleName into db.pgpm object
- Move schemas, apiNames, keepDb into db object
- Add db.config?: Partial<PgConfig> with fallback to environment variables
- Update CLI to use --schema-file flag and new db structure
- Update all generate commands, introspection sources, and config resolver
- Update README with new config structure examples
- Update tests for new config structure
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