diff --git a/graphql/codegen/.gitignore b/graphql/codegen/.gitignore index 3046b9746..4dcf19c7e 100644 --- a/graphql/codegen/.gitignore +++ b/graphql/codegen/.gitignore @@ -1,14 +1,8 @@ # Build output dist/ -# Generated SDK output (for testing) -output-rq -output-orm/ - -examples/output/generated-sdk/ -examples/output/generated-orm/ -examples/output/generated-sdk-schema/ -examples/output/generated-orm-schema/ +# Artifacts of codegen for testing +examples/output/ # Dependencies node_modules/ diff --git a/graphql/codegen/README.md b/graphql/codegen/README.md index d88cf7ad1..100495d74 100644 --- a/graphql/codegen/README.md +++ b/graphql/codegen/README.md @@ -117,6 +117,7 @@ Generate React Query hooks from a PostGraphile endpoint. ```bash Options: -e, --endpoint GraphQL endpoint URL (overrides config) + -t, --target Target name in config file -o, --output Output directory (default: ./generated/graphql) -c, --config Path to config file -a, --authorization Authorization header value @@ -132,6 +133,7 @@ Generate Prisma-like ORM client from a PostGraphile endpoint. ```bash Options: -e, --endpoint GraphQL endpoint URL + -t, --target Target name in config file -o, --output Output directory (default: ./generated/orm) -c, --config Path to config file -a, --authorization Authorization header value @@ -164,9 +166,10 @@ Options: ## Configuration ```typescript -interface GraphQLSDKConfig { - // Required - endpoint: string; +interface GraphQLSDKConfigTarget { + // Required (choose one) + endpoint?: string; + schema?: string; // Output output?: string; // default: './generated/graphql' @@ -194,25 +197,62 @@ interface GraphQLSDKConfig { // Code generation options codegen?: { - maxFieldDepth?: number; // default: 2 - skipQueryField?: boolean; // default: true + maxFieldDepth?: number; // default: 2 + skipQueryField?: boolean; // default: true }; // ORM-specific config orm?: { - output?: string; // default: './generated/orm' - useSharedTypes?: boolean; // default: true + output?: string; // default: './generated/orm' + useSharedTypes?: boolean; // default: true }; } + +interface GraphQLSDKMultiConfig { + defaults?: GraphQLSDKConfigTarget; + targets: Record; +} + +type GraphQLSDKConfig = GraphQLSDKConfigTarget | GraphQLSDKMultiConfig; +``` + +### Multi-target Configuration + +Configure multiple schema sources and outputs in one file: + +```typescript +export default defineConfig({ + defaults: { + headers: { Authorization: 'Bearer ' }, + }, + targets: { + public: { + endpoint: 'https://api.example.com/graphql', + output: './generated/public', + }, + admin: { + schema: './admin.schema.graphql', + output: './generated/admin', + }, + }, +}); ``` +CLI behavior: + +- `graphql-codegen generate` runs all targets +- `graphql-codegen generate --target admin` runs a single target +- `--output` requires `--target` when multiple targets exist + ### Glob Patterns Filter patterns support wildcards: + - `*` - matches any string - `?` - matches single character Examples: + ```typescript { tables: { @@ -308,30 +348,25 @@ Fetches multiple records with pagination, filtering, and ordering: import { useCarsQuery } from './generated/hooks'; function CarList() { - const { - data, - isLoading, - isError, - error, - refetch, - isFetching, - } = useCarsQuery({ - // Pagination - first: 10, // First N records - // last: 10, // Last N records - // after: 'cursor', // Cursor-based pagination - // before: 'cursor', - // offset: 20, // Offset pagination - - // Filtering - filter: { - brand: { equalTo: 'Tesla' }, - price: { greaterThan: 50000 }, - }, - - // Ordering - orderBy: ['CREATED_AT_DESC', 'NAME_ASC'], - }); + const { data, isLoading, isError, error, refetch, isFetching } = useCarsQuery( + { + // Pagination + first: 10, // First N records + // last: 10, // Last N records + // after: 'cursor', // Cursor-based pagination + // before: 'cursor', + // offset: 20, // Offset pagination + + // Filtering + filter: { + brand: { equalTo: 'Tesla' }, + price: { greaterThan: 50000 }, + }, + + // Ordering + orderBy: ['CREATED_AT_DESC', 'NAME_ASC'], + } + ); if (isLoading) return
Loading...
; if (isError) return
Error: {error.message}
; @@ -340,11 +375,13 @@ function CarList() {

Total: {data?.cars.totalCount}

    - {data?.cars.nodes.map(car => ( -
  • {car.brand} - ${car.price}
  • + {data?.cars.nodes.map((car) => ( +
  • + {car.brand} - ${car.price} +
  • ))}
- + {/* Pagination info */} {data?.cars.pageInfo.hasNextPage && ( @@ -411,7 +448,12 @@ function CreateCarForm() { }; return ( -
{ e.preventDefault(); handleSubmit({ brand: 'Tesla', price: 80000 }); }}> + { + e.preventDefault(); + handleSubmit({ brand: 'Tesla', price: 80000 }); + }} + > {/* form fields */} ); @@ -593,11 +658,7 @@ function LogoutButton() { }, }); - return ( - - ); + return ; } // Forgot Password @@ -609,7 +670,11 @@ function ForgotPasswordForm() { }); return ( - ); @@ -629,10 +694,10 @@ useCarsQuery({ notEqualTo: 'Ford', in: ['Tesla', 'BMW', 'Mercedes'], notIn: ['Unknown'], - contains: 'es', // LIKE '%es%' - startsWith: 'Tes', // LIKE 'Tes%' - endsWith: 'la', // LIKE '%la' - includesInsensitive: 'TESLA', // Case-insensitive + contains: 'es', // LIKE '%es%' + startsWith: 'Tes', // LIKE 'Tes%' + endsWith: 'la', // LIKE '%la' + includesInsensitive: 'TESLA', // Case-insensitive }, }, }); @@ -671,7 +736,7 @@ useOrdersQuery({ // Null checks useUsersQuery({ filter: { - deletedAt: { isNull: true }, // Only non-deleted + deletedAt: { isNull: true }, // Only non-deleted }, }); @@ -687,10 +752,7 @@ useUsersQuery({ useUsersQuery({ filter: { // OR - or: [ - { role: { equalTo: 'ADMIN' } }, - { role: { equalTo: 'MODERATOR' } }, - ], + or: [{ role: { equalTo: 'ADMIN' } }, { role: { equalTo: 'MODERATOR' } }], }, }); @@ -746,12 +808,12 @@ useCarsQuery({ first: 10 }); useCarsQuery({ last: 10 }); // Offset pagination -useCarsQuery({ first: 10, offset: 20 }); // Skip 20, take 10 +useCarsQuery({ first: 10, offset: 20 }); // Skip 20, take 10 // Cursor-based pagination function PaginatedList() { const [cursor, setCursor] = useState(null); - + const { data } = useCarsQuery({ first: 10, after: cursor, @@ -759,8 +821,10 @@ function PaginatedList() { return (
- {data?.cars.nodes.map(car =>
{car.brand}
)} - + {data?.cars.nodes.map((car) => ( +
{car.brand}
+ ))} + {data?.cars.pageInfo.hasNextPage && (