diff --git a/graphql/codegen/examples/orm-sdk.ts b/graphql/codegen/examples/orm-sdk.ts
index d36de4fe8..27a216780 100644
--- a/graphql/codegen/examples/orm-sdk.ts
+++ b/graphql/codegen/examples/orm-sdk.ts
@@ -5,7 +5,7 @@
import {
createClient,
GraphQLRequestError,
-} from '../examples/output/generated-orm';
+} from './output/generated-orm';
const ENDPOINT = 'http://api.localhost:3000/graphql';
let db = createClient({ endpoint: ENDPOINT });
@@ -17,23 +17,23 @@ async function main() {
console.log('ORM SDK Demo\n');
// ─────────────────────────────────────────────────────────────────────────────
- // 1. Login & Auth
+ // 1. SignIn & Auth
// ─────────────────────────────────────────────────────────────────────────────
- section('1. Login Mutation');
- const loginResult = await db.mutation
- .login(
+ section('1. SignIn Mutation');
+ const signInResult = await db.mutation
+ .signIn(
{ input: { email: 'admin@gmail.com', password: 'password1111!@#$' } },
{ select: { apiToken: { select: { accessToken: true } } } }
)
.execute();
- const token = loginResult.data?.login?.apiToken?.accessToken;
+ const token = signInResult.data?.signIn?.apiToken?.accessToken;
if (token) {
db = createClient({
endpoint: ENDPOINT,
headers: { Authorization: `Bearer ${token}` },
});
- console.log('✓ Logged in, token:', token.slice(0, 30) + '...');
+ console.log('✓ Signed in, token:', token.slice(0, 30) + '...');
}
// ─────────────────────────────────────────────────────────────────────────────
@@ -169,11 +169,11 @@ async function main() {
// ─────────────────────────────────────────────────────────────────────────────
section('6. Custom Queries');
const currentUser = await db.query
- .getCurrentUser({
+ .currentUser({
select: { id: true, username: true, displayName: true },
})
.execute();
- console.log('Current user:', currentUser.data?.getCurrentUser?.username);
+ console.log('Current user:', currentUser.data?.currentUser?.username);
// ─────────────────────────────────────────────────────────────────────────────
// 7. Error Handling: execute(), unwrap(), unwrapOr()
@@ -223,7 +223,7 @@ async function main() {
select: {
id: true,
username: true,
- userProfile: { select: { displayName: true } },
+ roleTypeByType: { select: { name: true } },
},
first: 5,
where: { username: { isNull: false } },
diff --git a/graphql/codegen/examples/react-hooks-test.tsx b/graphql/codegen/examples/react-hooks-test.tsx
new file mode 100644
index 000000000..379cd5b28
--- /dev/null
+++ b/graphql/codegen/examples/react-hooks-test.tsx
@@ -0,0 +1,398 @@
+/**
+ * React Query Hooks Test File
+ * Tests that the generated hooks work correctly with React + TypeScript
+ *
+ * This file is for type-checking purposes. Run: npx tsc --noEmit examples/react-hooks-test.tsx
+ */
+import React from 'react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+
+// Import generated hooks
+import {
+ useUsersQuery,
+ usersQueryKey,
+ fetchUsersQuery,
+ prefetchUsersQuery,
+} from './output/generated-sdk/queries/useUsersQuery';
+import {
+ useUserQuery,
+ userQueryKey,
+} from './output/generated-sdk/queries/useUserQuery';
+import {
+ useDatabasesQuery,
+ databasesQueryKey,
+} from './output/generated-sdk/queries/useDatabasesQuery';
+import {
+ useTablesQuery,
+ tablesQueryKey,
+} from './output/generated-sdk/queries/useTablesQuery';
+import {
+ useCurrentUserQuery,
+ currentUserQueryKey,
+} from './output/generated-sdk/queries/useCurrentUserQuery';
+
+// Import mutation hooks
+import { useCreateUserMutation } from './output/generated-sdk/mutations/useCreateUserMutation';
+import { useUpdateUserMutation } from './output/generated-sdk/mutations/useUpdateUserMutation';
+import { useDeleteUserMutation } from './output/generated-sdk/mutations/useDeleteUserMutation';
+import { useSignInMutation } from './output/generated-sdk/mutations/useSignInMutation';
+import { useSignOutMutation } from './output/generated-sdk/mutations/useSignOutMutation';
+import { useSignUpMutation } from './output/generated-sdk/mutations/useSignUpMutation';
+
+// Import types
+import type { UserFilter, DatabaseFilter, TableFilter } from './output/generated-sdk/schema-types';
+import type { User, Database, Table } from './output/generated-sdk/types';
+
+// Import client configuration
+import { configure } from './output/generated-sdk/client';
+
+// Import query/mutation keys for cache invalidation
+import { queryKeys, userKeys, customQueryKeys } from './output/generated-sdk/query-keys';
+import { mutationKeys, customMutationKeys } from './output/generated-sdk/mutation-keys';
+import { invalidate } from './output/generated-sdk/invalidation';
+
+const queryClient = new QueryClient();
+
+/**
+ * Test: List query hook with pagination and filtering
+ */
+function UsersListComponent() {
+ const filter: UserFilter = {
+ username: { isNull: false },
+ and: [{ type: { equalTo: 0 } }, { type: { greaterThan: 5 } }],
+ };
+
+ const { data, isLoading, error, refetch } = useUsersQuery({
+ first: 10,
+ orderBy: ['USERNAME_ASC'],
+ filter,
+ });
+
+ // Type assertions to verify return types
+ const users: User[] | undefined = data?.users?.nodes;
+ const totalCount: number | undefined = data?.users?.totalCount;
+ const hasNextPage: boolean | undefined = data?.users?.pageInfo.hasNextPage;
+ const hasPreviousPage: boolean | undefined = data?.users?.pageInfo.hasPreviousPage;
+ const startCursor: string | null | undefined = data?.users?.pageInfo.startCursor;
+ const endCursor: string | null | undefined = data?.users?.pageInfo.endCursor;
+
+ if (isLoading) return
Loading...
;
+ if (error) return Error: {error.message}
;
+
+ return (
+
+
Users ({totalCount})
+
+ {users?.map((user) => (
+ -
+ {user.username} - {user.displayName}
+
+ ))}
+
+
+
+ Page: {hasNextPage ? 'Has more' : 'Last page'} |{' '}
+ {hasPreviousPage ? 'Has previous' : 'First page'}
+
+
+ );
+}
+
+/**
+ * Test: Single item query hook
+ */
+function UserDetailComponent({ userId }: { userId: string }) {
+ const { data, isLoading, error } = useUserQuery({ id: userId });
+
+ // Type assertion
+ const user: User | null | undefined = data?.user;
+
+ if (isLoading) return Loading...
;
+ if (error) return Error: {error.message}
;
+ if (!user) return User not found
;
+
+ return (
+
+
{user.username}
+
Display name: {user.displayName}
+
Type: {user.type}
+
Created: {user.createdAt}
+
+ );
+}
+
+/**
+ * Test: Custom query hook (currentUser)
+ */
+function CurrentUserComponent() {
+ const { data, isLoading, error } = useCurrentUserQuery();
+
+ const currentUser = data?.currentUser;
+
+ if (isLoading) return Loading...
;
+ if (error) return Error: {error.message}
;
+ if (!currentUser) return Not logged in
;
+
+ return (
+
+
Welcome, {currentUser.username}
+
ID: {currentUser.id}
+
+ );
+}
+
+/**
+ * Test: Create mutation hook
+ */
+function CreateUserForm() {
+ const createMutation = useCreateUserMutation({
+ onSuccess: (data) => {
+ console.log('Created user:', data.createUser?.user?.id);
+ },
+ onError: (error) => {
+ console.error('Failed to create user:', error.message);
+ },
+ });
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ createMutation.mutate({
+ input: {
+ user: {
+ username: 'newuser',
+ displayName: 'New User',
+ },
+ },
+ });
+ };
+
+ return (
+
+ );
+}
+
+/**
+ * Test: Update mutation hook
+ */
+function UpdateUserForm({ userId }: { userId: string }) {
+ const updateMutation = useUpdateUserMutation({
+ onSuccess: (data) => {
+ console.log('Updated user:', data.updateUser?.user?.username);
+ },
+ });
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ updateMutation.mutate({
+ input: {
+ id: userId,
+ patch: {
+ displayName: 'Updated Display Name',
+ },
+ },
+ });
+ };
+
+ return (
+
+ );
+}
+
+/**
+ * Test: Delete mutation hook
+ */
+function DeleteUserButton({ userId }: { userId: string }) {
+ const deleteMutation = useDeleteUserMutation({
+ onSuccess: () => {
+ console.log('User deleted');
+ },
+ });
+
+ return (
+
+ );
+}
+
+/**
+ * Test: Auth mutations (signIn, signOut, signUp)
+ */
+function AuthComponent() {
+ const signInMutation = useSignInMutation({
+ onSuccess: (data) => {
+ const token = data.signIn?.apiToken?.accessToken;
+ if (token) {
+ console.log('Signed in with token:', token);
+ }
+ },
+ });
+
+ const signOutMutation = useSignOutMutation({
+ onSuccess: () => {
+ console.log('Signed out');
+ },
+ });
+
+ const signUpMutation = useSignUpMutation({
+ onSuccess: (data) => {
+ console.log('Signed up:', data.signUp?.apiToken?.accessToken);
+ },
+ });
+
+ const handleSignIn = () => {
+ signInMutation.mutate({
+ input: {
+ email: 'test@example.com',
+ password: 'password123',
+ },
+ });
+ };
+
+ const handleSignOut = () => {
+ signOutMutation.mutate({ input: {} });
+ };
+
+ const handleSignUp = () => {
+ signUpMutation.mutate({
+ input: {
+ email: 'newuser@example.com',
+ password: 'password123',
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+
+/**
+ * Test: Query keys and cache invalidation
+ */
+function CacheInvalidationTest() {
+ // Test query key factories
+ const usersKey = userKeys.list({ first: 10 });
+ const userKey = userKeys.detail('123');
+ const currentUserKey = customQueryKeys.currentUser();
+
+ // Test centralized query keys
+ const allUserKeys = queryKeys.user;
+ const allDatabaseKeys = queryKeys.database;
+
+ // Test invalidation helper
+ const handleInvalidate = async () => {
+ await invalidate.user.all(queryClient);
+ };
+
+ return (
+
+
Users key: {JSON.stringify(usersKey)}
+
User key: {JSON.stringify(userKey)}
+
+
+ );
+}
+
+/**
+ * Test: Prefetch and fetch functions
+ */
+async function testPrefetchAndFetch() {
+ // Configure the client
+ configure({
+ endpoint: 'http://api.localhost:3000/graphql',
+ headers: { Authorization: 'Bearer token' },
+ });
+
+ // Test fetch function (for SSR/server components)
+ const users = await fetchUsersQuery({ first: 10 });
+ console.log('Fetched users:', users.users?.nodes?.length);
+
+ // Test prefetch function (for cache warming)
+ await prefetchUsersQuery(queryClient, { first: 10 });
+ console.log('Prefetched users query');
+}
+
+/**
+ * Test: Relation queries with filters
+ */
+function RelationQueriesComponent() {
+ const { data: dbData } = useDatabasesQuery({ first: 1 });
+ const databaseId = dbData?.databases?.nodes?.[0]?.id;
+
+ // Filter tables by database ID (foreign key filter)
+ const tableFilter: TableFilter | undefined = databaseId
+ ? { databaseId: { equalTo: databaseId } }
+ : undefined;
+
+ const { data: tablesData } = useTablesQuery(
+ {
+ first: 10,
+ filter: tableFilter,
+ orderBy: ['NAME_ASC'],
+ },
+ {
+ enabled: !!databaseId,
+ }
+ );
+
+ const tables: Table[] | undefined = tablesData?.tables?.nodes;
+
+ return (
+
+
Tables in Database
+
+ {tables?.map((table) => (
+ - {table.name}
+ ))}
+
+
+ );
+}
+
+/**
+ * Main App component to wrap everything
+ */
+export function App() {
+ return (
+
+
+
React Query Hooks Test
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/graphql/codegen/examples/react-query-sdk.ts b/graphql/codegen/examples/react-query-sdk.ts
index e13a7ee07..4c1d265d8 100644
--- a/graphql/codegen/examples/react-query-sdk.ts
+++ b/graphql/codegen/examples/react-query-sdk.ts
@@ -8,45 +8,45 @@ import {
execute,
executeWithErrors,
GraphQLClientError,
-} from '../examples/output/generated-sdk/client';
+} from './output/generated-sdk/client';
import {
usersQueryDocument,
type UsersQueryResult,
type UsersQueryVariables,
-} from '../examples/output/generated-sdk/queries/useUsersQuery';
+} from './output/generated-sdk/queries/useUsersQuery';
import {
userQueryDocument,
type UserQueryResult,
type UserQueryVariables,
-} from '../examples/output/generated-sdk/queries/useUserQuery';
+} from './output/generated-sdk/queries/useUserQuery';
import {
databasesQueryDocument,
type DatabasesQueryResult,
type DatabasesQueryVariables,
-} from '../examples/output/generated-sdk/queries/useDatabasesQuery';
+} from './output/generated-sdk/queries/useDatabasesQuery';
import {
tablesQueryDocument,
type TablesQueryResult,
type TablesQueryVariables,
-} from '../examples/output/generated-sdk/queries/useTablesQuery';
+} from './output/generated-sdk/queries/useTablesQuery';
import {
- getCurrentUserQueryDocument,
- type GetCurrentUserQueryResult,
-} from '../examples/output/generated-sdk/queries/useGetCurrentUserQuery';
+ currentUserQueryDocument,
+ type CurrentUserQueryResult,
+} from './output/generated-sdk/queries/useCurrentUserQuery';
import {
userByUsernameQueryDocument,
type UserByUsernameQueryResult,
type UserByUsernameQueryVariables,
-} from '../examples/output/generated-sdk/queries/useUserByUsernameQuery';
+} from './output/generated-sdk/queries/useUserByUsernameQuery';
import {
- loginMutationDocument,
- type LoginMutationResult,
- type LoginMutationVariables,
-} from '../examples/output/generated-sdk/mutations/useLoginMutation';
+ signInMutationDocument,
+ type SignInMutationResult,
+ type SignInMutationVariables,
+} from './output/generated-sdk/mutations/useSignInMutation';
import type {
UserFilter,
TableFilter,
-} from '../examples/output/generated-sdk/schema-types';
+} from './output/generated-sdk/schema-types';
const ENDPOINT = 'http://api.localhost:3000/graphql';
const section = (title: string) =>
@@ -66,25 +66,25 @@ async function main() {
console.log('✓ Client configured');
// ─────────────────────────────────────────────────────────────────────────────
- // 2. Login Mutation
+ // 2. SignIn Mutation
// ─────────────────────────────────────────────────────────────────────────────
- section('2. Login Mutation');
+ section('2. SignIn Mutation');
try {
- const loginResult = await execute<
- LoginMutationResult,
- LoginMutationVariables
- >(loginMutationDocument, {
+ const signInResult = await execute<
+ SignInMutationResult,
+ SignInMutationVariables
+ >(signInMutationDocument, {
input: { email: 'admin@gmail.com', password: 'password1111!@#$' },
});
- const token = loginResult.login?.apiToken?.accessToken;
+ const token = signInResult.signIn?.apiToken?.accessToken;
if (token) {
// Use setHeader() to update auth without re-configuring
setHeader('Authorization', `Bearer ${token}`);
- console.log('✓ Logged in, token:', token.slice(0, 30) + '...');
+ console.log('✓ Signed in, token:', token.slice(0, 30) + '...');
}
} catch (e) {
if (e instanceof GraphQLClientError)
- console.log('Login failed:', e.errors[0]?.message);
+ console.log('SignIn failed:', e.errors[0]?.message);
else throw e;
}
@@ -162,11 +162,10 @@ async function main() {
// 6. Custom Queries
// ─────────────────────────────────────────────────────────────────────────────
section('6. Custom Queries');
- const { data: currentUser } =
- await executeWithErrors(
- getCurrentUserQueryDocument
- );
- console.log('Current user:', currentUser?.getCurrentUser?.username);
+ const { data: currentUser } = await executeWithErrors(
+ currentUserQueryDocument
+ );
+ console.log('Current user:', currentUser?.currentUser?.username);
// ─────────────────────────────────────────────────────────────────────────────
// 7. Relation Queries (Foreign Key Filter)
@@ -212,8 +211,8 @@ async function main() {
// execute - throws on error
try {
- await execute(
- loginMutationDocument,
+ await execute(
+ signInMutationDocument,
{ input: { email: 'invalid@x.com', password: 'wrong' } }
);
} catch (e) {
diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap
index f0d630e95..9fa5733d5 100644
--- a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap
+++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap
@@ -166,6 +166,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface User {
id: string;
@@ -578,6 +638,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface User {
id: string;
@@ -834,6 +954,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface User {
id: string;
@@ -1102,6 +1282,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface User {
id: string;
@@ -1375,6 +1615,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface User {
id: string;
@@ -1700,6 +2000,66 @@ export interface InternetAddressFilter {
export interface FullTextFilter {
matches?: string;
}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
// ============ Entity Types ============
export interface Post {
id: string;
@@ -2017,5 +2377,65 @@ export interface InternetAddressFilter {
}
export interface FullTextFilter {
matches?: string;
+}
+export interface StringListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
+}
+export interface IntListFilter {
+ isNull?: boolean;
+ equalTo?: number[];
+ notEqualTo?: number[];
+ distinctFrom?: number[];
+ notDistinctFrom?: number[];
+ lessThan?: number[];
+ lessThanOrEqualTo?: number[];
+ greaterThan?: number[];
+ greaterThanOrEqualTo?: number[];
+ contains?: number[];
+ containedBy?: number[];
+ overlaps?: number[];
+ anyEqualTo?: number;
+ anyNotEqualTo?: number;
+ anyLessThan?: number;
+ anyLessThanOrEqualTo?: number;
+ anyGreaterThan?: number;
+ anyGreaterThanOrEqualTo?: number;
+}
+export interface UUIDListFilter {
+ isNull?: boolean;
+ equalTo?: string[];
+ notEqualTo?: string[];
+ distinctFrom?: string[];
+ notDistinctFrom?: string[];
+ lessThan?: string[];
+ lessThanOrEqualTo?: string[];
+ greaterThan?: string[];
+ greaterThanOrEqualTo?: string[];
+ contains?: string[];
+ containedBy?: string[];
+ overlaps?: string[];
+ anyEqualTo?: string;
+ anyNotEqualTo?: string;
+ anyLessThan?: string;
+ anyLessThanOrEqualTo?: string;
+ anyGreaterThan?: string;
+ anyGreaterThanOrEqualTo?: string;
}"
`;
diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap
index b5b055b2d..1fa921534 100644
--- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap
+++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap
@@ -215,7 +215,7 @@ export interface LoginMutationResult {
export function useLoginMutation(options?: Omit, 'mutationFn'>) {
return useMutation({
mutationKey: customMutationKeys.login(),
- mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables),
+ mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables),
...options
});
}"
@@ -250,7 +250,7 @@ export interface RegisterMutationResult {
export function useRegisterMutation(options?: Omit, 'mutationFn'>) {
return useMutation({
mutationKey: customMutationKeys.register(),
- mutationFn: (variables: RegisterMutationVariables) => execute(registerMutationDocument, variables),
+ mutationFn: (variables: RegisterMutationVariables) => execute(registerMutationDocument, variables),
...options
});
}"
@@ -282,7 +282,7 @@ export interface LogoutMutationResult {
export function useLogoutMutation(options?: Omit, 'mutationFn'>) {
return useMutation({
mutationKey: customMutationKeys.logout(),
- mutationFn: () => execute(logoutMutationDocument),
+ mutationFn: () => execute(logoutMutationDocument),
...options
});
}"
@@ -316,7 +316,7 @@ export interface LoginMutationResult {
}
export function useLoginMutation(options?: Omit, 'mutationFn'>) {
return useMutation({
- mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables),
+ mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables),
...options
});
}"
@@ -365,7 +365,7 @@ export const searchUsersQueryKey = customQueryKeys.searchUsers;
export function useSearchUsersQuery(variables: SearchUsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: searchUsersQueryKey(variables),
- queryFn: () => execute(searchUsersQueryDocument, variables),
+ queryFn: () => execute(searchUsersQueryDocument, variables),
enabled: !!variables && options?.enabled !== false,
...options
});
@@ -379,7 +379,7 @@ export function useSearchUsersQuery(variables: SearchUsersQueryVariables, option
* \`\`\`
*/
export async function fetchSearchUsersQuery(variables: SearchUsersQueryVariables, options?: ExecuteOptions): Promise {
- return execute(searchUsersQueryDocument, variables, options);
+ return execute(searchUsersQueryDocument, variables, options);
}
/**
* Prefetch searchUsers for SSR or cache warming
@@ -392,7 +392,7 @@ export async function fetchSearchUsersQuery(variables: SearchUsersQueryVariables
export async function prefetchSearchUsersQuery(queryClient: QueryClient, variables: SearchUsersQueryVariables, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: searchUsersQueryKey(variables),
- queryFn: () => execute(searchUsersQueryDocument, variables, options)
+ queryFn: () => execute(searchUsersQueryDocument, variables, options)
});
}"
`;
@@ -436,7 +436,7 @@ export const currentUserQueryKey = customQueryKeys.currentUser;
export function useCurrentUserQuery(options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: currentUserQueryKey(),
- queryFn: () => execute(currentUserQueryDocument),
+ queryFn: () => execute(currentUserQueryDocument),
...options
});
}
@@ -449,7 +449,7 @@ export function useCurrentUserQuery(options?: Omit {
- return execute(currentUserQueryDocument, undefined, options);
+ return execute(currentUserQueryDocument, undefined, options);
}
/**
* Prefetch currentUser for SSR or cache warming
@@ -462,7 +462,7 @@ export async function fetchCurrentUserQuery(options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: currentUserQueryKey(),
- queryFn: () => execute(currentUserQueryDocument, undefined, options)
+ queryFn: () => execute(currentUserQueryDocument, undefined, options)
});
}"
`;
@@ -505,7 +505,7 @@ export const currentUserQueryKey = () => ["currentUser"] as const;
export function useCurrentUserQuery(options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: currentUserQueryKey(),
- queryFn: () => execute(currentUserQueryDocument),
+ queryFn: () => execute(currentUserQueryDocument),
...options
});
}
@@ -518,7 +518,7 @@ export function useCurrentUserQuery(options?: Omit {
- return execute(currentUserQueryDocument, undefined, options);
+ return execute(currentUserQueryDocument, undefined, options);
}
/**
* Prefetch currentUser for SSR or cache warming
@@ -531,7 +531,7 @@ export async function fetchCurrentUserQuery(options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: currentUserQueryKey(),
- queryFn: () => execute(currentUserQueryDocument, undefined, options)
+ queryFn: () => execute(currentUserQueryDocument, undefined, options)
});
}"
`;
@@ -597,7 +597,7 @@ export function useCreateUserMutation(options?: Omit execute(createUserMutationDocument, variables),
+ mutationFn: (variables: CreateUserMutationVariables) => execute(createUserMutationDocument, variables),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: userKeys.lists()
@@ -674,7 +674,7 @@ export function useCreatePostMutation(options?: Omit execute(createPostMutationDocument, variables),
+ mutationFn: (variables: CreatePostMutationVariables) => execute(createPostMutationDocument, variables),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: postKeys.lists()
@@ -743,7 +743,7 @@ export interface CreateUserMutationResult {
export function useCreateUserMutation(options?: Omit, 'mutationFn'>) {
const queryClient = useQueryClient();
return useMutation({
- mutationFn: (variables: CreateUserMutationVariables) => execute(createUserMutationDocument, variables),
+ mutationFn: (variables: CreateUserMutationVariables) => execute(createUserMutationDocument, variables),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["user", "list"]
@@ -803,7 +803,7 @@ export function useDeleteUserMutation(options?: Omit execute(deleteUserMutationDocument, variables),
+ mutationFn: (variables: DeleteUserMutationVariables) => execute(deleteUserMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.removeQueries({
queryKey: userKeys.detail(variables.input.id)
@@ -867,7 +867,7 @@ export function useDeletePostMutation(options?: Omit execute(deletePostMutationDocument, variables),
+ mutationFn: (variables: DeletePostMutationVariables) => execute(deletePostMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.removeQueries({
queryKey: postKeys.detail(variables.input.id)
@@ -927,7 +927,7 @@ export interface DeleteUserMutationResult {
export function useDeleteUserMutation(options?: Omit, 'mutationFn'>) {
const queryClient = useQueryClient();
return useMutation({
- mutationFn: (variables: DeleteUserMutationVariables) => execute(deleteUserMutationDocument, variables),
+ mutationFn: (variables: DeleteUserMutationVariables) => execute(deleteUserMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.removeQueries({
queryKey: ["user", "detail", variables.input.id]
@@ -1005,7 +1005,7 @@ export function useUpdateUserMutation(options?: Omit execute(updateUserMutationDocument, variables),
+ mutationFn: (variables: UpdateUserMutationVariables) => execute(updateUserMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: userKeys.detail(variables.input.id)
@@ -1088,7 +1088,7 @@ export function useUpdatePostMutation(options?: Omit execute(updatePostMutationDocument, variables),
+ mutationFn: (variables: UpdatePostMutationVariables) => execute(updatePostMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: postKeys.detail(variables.input.id)
@@ -1163,7 +1163,7 @@ export interface UpdateUserMutationResult {
export function useUpdateUserMutation(options?: Omit, 'mutationFn'>) {
const queryClient = useQueryClient();
return useMutation({
- mutationFn: (variables: UpdateUserMutationVariables) => execute(updateUserMutationDocument, variables),
+ mutationFn: (variables: UpdateUserMutationVariables) => execute(updateUserMutationDocument, variables),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: ["user", "detail", variables.input.id]
@@ -1192,8 +1192,17 @@ import type { User, UUIDFilter, StringFilter, DatetimeFilter } from "../types";
import { userKeys } from "../query-keys";
export type { User } from "../types";
export const usersQueryDocument = \`
-query UsersQuery($first: Int, $offset: Int, $filter: UserFilter, $orderBy: [UsersOrderBy!]) {
- users(first: $first, offset: $offset, filter: $filter, orderBy: $orderBy) {
+query UsersQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: UserFilter, $condition: UserCondition, $orderBy: [UsersOrderBy!]) {
+ users(
+ first: $first
+ last: $last
+ offset: $offset
+ before: $before
+ after: $after
+ filter: $filter
+ condition: $condition
+ orderBy: $orderBy
+ ) {
totalCount
nodes {
id
@@ -1219,11 +1228,21 @@ interface UserFilter {
or?: UserFilter[];
not?: UserFilter;
}
+interface UserCondition {
+ id?: string;
+ email?: string;
+ name?: string;
+ createdAt?: string;
+}
type UsersOrderBy = "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC";
export interface UsersQueryVariables {
first?: number;
+ last?: number;
offset?: number;
+ before?: string;
+ after?: string;
filter?: UserFilter;
+ condition?: UserCondition;
orderBy?: UsersOrderBy[];
}
export interface UsersQueryResult {
@@ -1255,7 +1274,7 @@ export const usersQueryKey = userKeys.list;
export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: userKeys.list(variables),
- queryFn: () => execute(usersQueryDocument, variables),
+ queryFn: () => execute(usersQueryDocument, variables),
...options
});
}
@@ -1275,7 +1294,7 @@ export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit {
- return execute(usersQueryDocument, variables, options);
+ return execute(usersQueryDocument, variables, options);
}
/**
* Prefetch User list for SSR or cache warming
@@ -1288,7 +1307,7 @@ export async function fetchUsersQuery(variables?: UsersQueryVariables, options?:
export async function prefetchUsersQuery(queryClient: QueryClient, variables?: UsersQueryVariables, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: userKeys.list(variables),
- queryFn: () => execute(usersQueryDocument, variables, options)
+ queryFn: () => execute(usersQueryDocument, variables, options)
});
}"
`;
@@ -1309,8 +1328,17 @@ import { postKeys } from "../query-keys";
import type { PostScope } from "../query-keys";
export type { Post } from "../types";
export const postsQueryDocument = \`
-query PostsQuery($first: Int, $offset: Int, $filter: PostFilter, $orderBy: [PostsOrderBy!]) {
- posts(first: $first, offset: $offset, filter: $filter, orderBy: $orderBy) {
+query PostsQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: PostFilter, $condition: PostCondition, $orderBy: [PostsOrderBy!]) {
+ posts(
+ first: $first
+ last: $last
+ offset: $offset
+ before: $before
+ after: $after
+ filter: $filter
+ condition: $condition
+ orderBy: $orderBy
+ ) {
totalCount
nodes {
id
@@ -1340,11 +1368,23 @@ interface PostFilter {
or?: PostFilter[];
not?: PostFilter;
}
+interface PostCondition {
+ id?: string;
+ title?: string;
+ content?: string;
+ authorId?: string;
+ published?: boolean;
+ createdAt?: string;
+}
type PostsOrderBy = "ID_ASC" | "ID_DESC" | "TITLE_ASC" | "TITLE_DESC" | "CONTENT_ASC" | "CONTENT_DESC" | "AUTHOR_ID_ASC" | "AUTHOR_ID_DESC" | "PUBLISHED_ASC" | "PUBLISHED_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC";
export interface PostsQueryVariables {
first?: number;
+ last?: number;
offset?: number;
+ before?: string;
+ after?: string;
filter?: PostFilter;
+ condition?: PostCondition;
orderBy?: PostsOrderBy[];
}
export interface PostsQueryResult {
@@ -1388,7 +1428,7 @@ export function usePostsQuery(variables?: PostsQueryVariables, options?: Omit execute(postsQueryDocument, variables),
+ queryFn: () => execute(postsQueryDocument, variables),
...queryOptions
});
}
@@ -1408,7 +1448,7 @@ export function usePostsQuery(variables?: PostsQueryVariables, options?: Omit {
- return execute(postsQueryDocument, variables, options);
+ return execute(postsQueryDocument, variables, options);
}
/**
* Prefetch Post list for SSR or cache warming
@@ -1421,7 +1461,7 @@ export async function fetchPostsQuery(variables?: PostsQueryVariables, options?:
export async function prefetchPostsQuery(queryClient: QueryClient, variables?: PostsQueryVariables, scope?: PostScope, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: postKeys.list(variables, scope),
- queryFn: () => execute(postsQueryDocument, variables, options)
+ queryFn: () => execute(postsQueryDocument, variables, options)
});
}"
`;
@@ -1440,8 +1480,17 @@ import type { ExecuteOptions } from "../client";
import type { User, UUIDFilter, StringFilter, DatetimeFilter } from "../types";
export type { User } from "../types";
export const usersQueryDocument = \`
-query UsersQuery($first: Int, $offset: Int, $filter: UserFilter, $orderBy: [UsersOrderBy!]) {
- users(first: $first, offset: $offset, filter: $filter, orderBy: $orderBy) {
+query UsersQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: UserFilter, $condition: UserCondition, $orderBy: [UsersOrderBy!]) {
+ users(
+ first: $first
+ last: $last
+ offset: $offset
+ before: $before
+ after: $after
+ filter: $filter
+ condition: $condition
+ orderBy: $orderBy
+ ) {
totalCount
nodes {
id
@@ -1467,11 +1516,21 @@ interface UserFilter {
or?: UserFilter[];
not?: UserFilter;
}
+interface UserCondition {
+ id?: string;
+ email?: string;
+ name?: string;
+ createdAt?: string;
+}
type UsersOrderBy = "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC";
export interface UsersQueryVariables {
first?: number;
+ last?: number;
offset?: number;
+ before?: string;
+ after?: string;
filter?: UserFilter;
+ condition?: UserCondition;
orderBy?: UsersOrderBy[];
}
export interface UsersQueryResult {
@@ -1502,7 +1561,7 @@ export const usersQueryKey = (variables?: UsersQueryVariables) => ["user", "list
export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: usersQueryKey(variables),
- queryFn: () => execute(usersQueryDocument, variables),
+ queryFn: () => execute(usersQueryDocument, variables),
...options
});
}
@@ -1522,7 +1581,7 @@ export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit {
- return execute(usersQueryDocument, variables, options);
+ return execute(usersQueryDocument, variables, options);
}
/**
* Prefetch User list for SSR or cache warming
@@ -1535,7 +1594,7 @@ export async function fetchUsersQuery(variables?: UsersQueryVariables, options?:
export async function prefetchUsersQuery(queryClient: QueryClient, variables?: UsersQueryVariables, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: usersQueryKey(variables),
- queryFn: () => execute(usersQueryDocument, variables, options)
+ queryFn: () => execute(usersQueryDocument, variables, options)
});
}"
`;
@@ -1583,7 +1642,7 @@ export const userQueryKey = userKeys.detail;
export function useUserQuery(variables: UserQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: userKeys.detail(variables.id),
- queryFn: () => execute(userQueryDocument, variables),
+ queryFn: () => execute(userQueryDocument, variables),
...options
});
}
@@ -1596,7 +1655,7 @@ export function useUserQuery(variables: UserQueryVariables, options?: Omit {
- return execute(userQueryDocument, variables, options);
+ return execute(userQueryDocument, variables, options);
}
/**
* Prefetch a single User for SSR or cache warming
@@ -1609,7 +1668,7 @@ export async function fetchUserQuery(variables: UserQueryVariables, options?: Ex
export async function prefetchUserQuery(queryClient: QueryClient, variables: UserQueryVariables, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: userKeys.detail(variables.id),
- queryFn: () => execute(userQueryDocument, variables, options)
+ queryFn: () => execute(userQueryDocument, variables, options)
});
}"
`;
@@ -1672,7 +1731,7 @@ export function usePostQuery(variables: PostQueryVariables, options?: Omit execute(postQueryDocument, variables),
+ queryFn: () => execute(postQueryDocument, variables),
...queryOptions
});
}
@@ -1685,7 +1744,7 @@ export function usePostQuery(variables: PostQueryVariables, options?: Omit {
- return execute(postQueryDocument, variables, options);
+ return execute(postQueryDocument, variables, options);
}
/**
* Prefetch a single Post for SSR or cache warming
@@ -1698,7 +1757,7 @@ export async function fetchPostQuery(variables: PostQueryVariables, options?: Ex
export async function prefetchPostQuery(queryClient: QueryClient, variables: PostQueryVariables, scope?: PostScope, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: postKeys.detail(variables.id, scope),
- queryFn: () => execute(postQueryDocument, variables, options)
+ queryFn: () => execute(postQueryDocument, variables, options)
});
}"
`;
@@ -1744,7 +1803,7 @@ export const userQueryKey = (id: string) => ["user", "detail", id] as const;
export function useUserQuery(variables: UserQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) {
return useQuery({
queryKey: userQueryKey(variables.id),
- queryFn: () => execute(userQueryDocument, variables),
+ queryFn: () => execute(userQueryDocument, variables),
...options
});
}
@@ -1757,7 +1816,7 @@ export function useUserQuery(variables: UserQueryVariables, options?: Omit {
- return execute(userQueryDocument, variables, options);
+ return execute(userQueryDocument, variables, options);
}
/**
* Prefetch a single User for SSR or cache warming
@@ -1770,7 +1829,7 @@ export async function fetchUserQuery(variables: UserQueryVariables, options?: Ex
export async function prefetchUserQuery(queryClient: QueryClient, variables: UserQueryVariables, options?: ExecuteOptions): Promise {
await queryClient.prefetchQuery({
queryKey: userQueryKey(variables.id),
- queryFn: () => execute(userQueryDocument, variables, options)
+ queryFn: () => execute(userQueryDocument, variables, options)
});
}"
`;
diff --git a/graphql/codegen/src/cli/codegen/babel-ast.ts b/graphql/codegen/src/cli/codegen/babel-ast.ts
index ccdae6ede..9a189b2f7 100644
--- a/graphql/codegen/src/cli/codegen/babel-ast.ts
+++ b/graphql/codegen/src/cli/codegen/babel-ast.ts
@@ -109,9 +109,28 @@ export function typedParam(
export function keyofTypeof(name: string): t.TSTypeOperator {
const typeofOp = t.tsTypeOperator(t.tsTypeReference(t.identifier(name)));
typeofOp.operator = 'typeof';
-
+
const keyofOp = t.tsTypeOperator(typeofOp);
keyofOp.operator = 'keyof';
-
+
return keyofOp;
}
+
+/**
+ * Create a call expression with TypeScript type parameters
+ *
+ * This is used to generate typed function calls like:
+ * execute(document, variables)
+ */
+export function createTypedCallExpression(
+ callee: t.Expression,
+ args: (t.Expression | t.SpreadElement)[],
+ typeParams: t.TSType[]
+): t.CallExpression {
+ const call = t.callExpression(callee, args);
+ if (typeParams.length > 0) {
+ // @ts-ignore - Babel types support typeParameters on CallExpression for TS
+ call.typeParameters = t.tsTypeParameterInstantiation(typeParams);
+ }
+ return call;
+}
diff --git a/graphql/codegen/src/cli/codegen/barrel.ts b/graphql/codegen/src/cli/codegen/barrel.ts
index b5d89b591..90a722b1a 100644
--- a/graphql/codegen/src/cli/codegen/barrel.ts
+++ b/graphql/codegen/src/cli/codegen/barrel.ts
@@ -12,6 +12,7 @@ import {
getCreateMutationHookName,
getUpdateMutationHookName,
getDeleteMutationHookName,
+ hasValidPrimaryKey,
} from './utils';
import { getOperationHookName } from './type-resolver';
@@ -31,10 +32,13 @@ export function generateQueriesBarrel(tables: CleanTable[]): string {
// Export all query hooks
for (const table of tables) {
const listHookName = getListQueryHookName(table);
- const singleHookName = getSingleQueryHookName(table);
-
statements.push(exportAllFrom(`./${listHookName}`));
- statements.push(exportAllFrom(`./${singleHookName}`));
+
+ // Only export single query hook if table has valid primary key
+ if (hasValidPrimaryKey(table)) {
+ const singleHookName = getSingleQueryHookName(table);
+ statements.push(exportAllFrom(`./${singleHookName}`));
+ }
}
// Add file header as leading comment on first statement
@@ -204,20 +208,33 @@ export function generateCustomQueriesBarrel(
customQueryNames: string[]
): string {
const statements: t.Statement[] = [];
+ const exportedHooks = new Set();
// Export all table query hooks
for (const table of tables) {
const listHookName = getListQueryHookName(table);
- const singleHookName = getSingleQueryHookName(table);
+ if (!exportedHooks.has(listHookName)) {
+ statements.push(exportAllFrom(`./${listHookName}`));
+ exportedHooks.add(listHookName);
+ }
- statements.push(exportAllFrom(`./${listHookName}`));
- statements.push(exportAllFrom(`./${singleHookName}`));
+ // Only export single query hook if table has valid primary key
+ if (hasValidPrimaryKey(table)) {
+ const singleHookName = getSingleQueryHookName(table);
+ if (!exportedHooks.has(singleHookName)) {
+ statements.push(exportAllFrom(`./${singleHookName}`));
+ exportedHooks.add(singleHookName);
+ }
+ }
}
- // Add custom query hooks
+ // Add custom query hooks (skip if already exported from table hooks)
for (const name of customQueryNames) {
const hookName = getOperationHookName(name, 'query');
- statements.push(exportAllFrom(`./${hookName}`));
+ if (!exportedHooks.has(hookName)) {
+ statements.push(exportAllFrom(`./${hookName}`));
+ exportedHooks.add(hookName);
+ }
}
// Add file header as leading comment on first statement
@@ -240,28 +257,40 @@ export function generateCustomMutationsBarrel(
customMutationNames: string[]
): string {
const statements: t.Statement[] = [];
+ const exportedHooks = new Set();
// Export all table mutation hooks
for (const table of tables) {
const createHookName = getCreateMutationHookName(table);
- const updateHookName = getUpdateMutationHookName(table);
- const deleteHookName = getDeleteMutationHookName(table);
-
- statements.push(exportAllFrom(`./${createHookName}`));
+ if (!exportedHooks.has(createHookName)) {
+ statements.push(exportAllFrom(`./${createHookName}`));
+ exportedHooks.add(createHookName);
+ }
// Only add update/delete if they exist
if (table.query?.update !== null) {
- statements.push(exportAllFrom(`./${updateHookName}`));
+ const updateHookName = getUpdateMutationHookName(table);
+ if (!exportedHooks.has(updateHookName)) {
+ statements.push(exportAllFrom(`./${updateHookName}`));
+ exportedHooks.add(updateHookName);
+ }
}
if (table.query?.delete !== null) {
- statements.push(exportAllFrom(`./${deleteHookName}`));
+ const deleteHookName = getDeleteMutationHookName(table);
+ if (!exportedHooks.has(deleteHookName)) {
+ statements.push(exportAllFrom(`./${deleteHookName}`));
+ exportedHooks.add(deleteHookName);
+ }
}
}
- // Add custom mutation hooks
+ // Add custom mutation hooks (skip if already exported from table hooks)
for (const name of customMutationNames) {
const hookName = getOperationHookName(name, 'mutation');
- statements.push(exportAllFrom(`./${hookName}`));
+ if (!exportedHooks.has(hookName)) {
+ statements.push(exportAllFrom(`./${hookName}`));
+ exportedHooks.add(hookName);
+ }
}
// Add file header as leading comment on first statement
diff --git a/graphql/codegen/src/cli/codegen/custom-mutations.ts b/graphql/codegen/src/cli/codegen/custom-mutations.ts
index f9156cadb..2d9a10aef 100644
--- a/graphql/codegen/src/cli/codegen/custom-mutations.ts
+++ b/graphql/codegen/src/cli/codegen/custom-mutations.ts
@@ -16,7 +16,7 @@ import type {
TypeRegistry,
} from '../../types/schema';
import * as t from '@babel/types';
-import { generateCode, addJSDocComment, typedParam } from './babel-ast';
+import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
import { buildCustomMutationString } from './schema-gql-ast';
import {
typeRefToTsType,
@@ -236,10 +236,14 @@ function generateCustomMutationHookInternal(
t.identifier('mutationFn'),
t.arrowFunctionExpression(
[typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)))],
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(resultTypeName)),
+ t.tsTypeReference(t.identifier(variablesTypeName)),
+ ]
+ )
)
)
);
@@ -249,7 +253,11 @@ function generateCustomMutationHookInternal(
t.identifier('mutationFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [t.identifier(documentConstName)])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName)],
+ [t.tsTypeReference(t.identifier(resultTypeName))]
+ )
)
)
);
diff --git a/graphql/codegen/src/cli/codegen/custom-queries.ts b/graphql/codegen/src/cli/codegen/custom-queries.ts
index 4bcbd2d9f..daefb0eb0 100644
--- a/graphql/codegen/src/cli/codegen/custom-queries.ts
+++ b/graphql/codegen/src/cli/codegen/custom-queries.ts
@@ -16,7 +16,7 @@ import type {
TypeRegistry,
} from '../../types/schema';
import * as t from '@babel/types';
-import { generateCode, addJSDocComment, typedParam } from './babel-ast';
+import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
import { buildCustomQueryString } from './schema-gql-ast';
import {
typeRefToTsType,
@@ -267,10 +267,14 @@ export function generateCustomQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(resultTypeName)),
+ t.tsTypeReference(t.identifier(variablesTypeName)),
+ ]
+ )
)
)
);
@@ -307,7 +311,11 @@ export function generateCustomQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [t.identifier(documentConstName)])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName)],
+ [t.tsTypeReference(t.identifier(resultTypeName))]
+ )
)
)
);
@@ -365,21 +373,24 @@ export function generateCustomQueryHook(
if (hasArgs) {
fetchBodyStatements.push(
t.returnStatement(
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(resultTypeName)),
+ t.tsTypeReference(t.identifier(variablesTypeName)),
+ ]
+ )
)
);
} else {
fetchBodyStatements.push(
t.returnStatement(
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('undefined'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')],
+ [t.tsTypeReference(t.identifier(resultTypeName))]
+ )
)
);
}
@@ -436,11 +447,14 @@ export function generateCustomQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(resultTypeName)),
+ t.tsTypeReference(t.identifier(variablesTypeName)),
+ ]
+ )
)
)
);
@@ -456,11 +470,11 @@ export function generateCustomQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(documentConstName),
- t.identifier('undefined'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')],
+ [t.tsTypeReference(t.identifier(resultTypeName))]
+ )
)
)
);
diff --git a/graphql/codegen/src/cli/codegen/gql-ast.ts b/graphql/codegen/src/cli/codegen/gql-ast.ts
index 7e1353ce3..ec2c13aac 100644
--- a/graphql/codegen/src/cli/codegen/gql-ast.ts
+++ b/graphql/codegen/src/cli/codegen/gql-ast.ts
@@ -21,6 +21,7 @@ import {
getUpdateMutationName,
getDeleteMutationName,
getFilterTypeName,
+ getConditionTypeName,
getOrderByTypeName,
getScalarFields,
getPrimaryKeyInfo,
@@ -72,23 +73,40 @@ export function buildListQueryAST(config: ListQueryConfig): DocumentNode {
const { table } = config;
const queryName = getAllRowsQueryName(table);
const filterType = getFilterTypeName(table);
+ const conditionType = getConditionTypeName(table);
const orderByType = getOrderByTypeName(table);
const scalarFields = getScalarFields(table);
- // Variable definitions
+ // Variable definitions - all pagination arguments from PostGraphile
const variableDefinitions: VariableDefinitionNode[] = [
t.variableDefinition({
variable: t.variable({ name: 'first' }),
type: t.namedType({ type: 'Int' }),
}),
+ t.variableDefinition({
+ variable: t.variable({ name: 'last' }),
+ type: t.namedType({ type: 'Int' }),
+ }),
t.variableDefinition({
variable: t.variable({ name: 'offset' }),
type: t.namedType({ type: 'Int' }),
}),
+ t.variableDefinition({
+ variable: t.variable({ name: 'before' }),
+ type: t.namedType({ type: 'Cursor' }),
+ }),
+ t.variableDefinition({
+ variable: t.variable({ name: 'after' }),
+ type: t.namedType({ type: 'Cursor' }),
+ }),
t.variableDefinition({
variable: t.variable({ name: 'filter' }),
type: t.namedType({ type: filterType }),
}),
+ t.variableDefinition({
+ variable: t.variable({ name: 'condition' }),
+ type: t.namedType({ type: conditionType }),
+ }),
t.variableDefinition({
variable: t.variable({ name: 'orderBy' }),
type: t.listType({
@@ -100,8 +118,12 @@ export function buildListQueryAST(config: ListQueryConfig): DocumentNode {
// Query arguments
const args: ArgumentNode[] = [
t.argument({ name: 'first', value: t.variable({ name: 'first' }) }),
+ t.argument({ name: 'last', value: t.variable({ name: 'last' }) }),
t.argument({ name: 'offset', value: t.variable({ name: 'offset' }) }),
+ t.argument({ name: 'before', value: t.variable({ name: 'before' }) }),
+ t.argument({ name: 'after', value: t.variable({ name: 'after' }) }),
t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }),
+ t.argument({ name: 'condition', value: t.variable({ name: 'condition' }) }),
t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }),
];
diff --git a/graphql/codegen/src/cli/codegen/index.ts b/graphql/codegen/src/cli/codegen/index.ts
index 163a5acd8..8778b7406 100644
--- a/graphql/codegen/src/cli/codegen/index.ts
+++ b/graphql/codegen/src/cli/codegen/index.ts
@@ -255,6 +255,7 @@ export function generate(options: GenerateOptions): GenerateResult {
enumsFromSchemaTypes: generatedEnumNames,
useCentralizedKeys,
hasRelationships,
+ tableTypeNames,
});
for (const hook of mutationHooks) {
files.push({
diff --git a/graphql/codegen/src/cli/codegen/mutations.ts b/graphql/codegen/src/cli/codegen/mutations.ts
index d0f5f4fce..11596b3fe 100644
--- a/graphql/codegen/src/cli/codegen/mutations.ts
+++ b/graphql/codegen/src/cli/codegen/mutations.ts
@@ -9,7 +9,7 @@
*/
import type { CleanTable } from '../../types/schema';
import * as t from '@babel/types';
-import { generateCode, addJSDocComment, typedParam } from './babel-ast';
+import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
import {
buildCreateMutationAST,
buildUpdateMutationAST,
@@ -56,6 +56,8 @@ export interface MutationGeneratorOptions {
enumsFromSchemaTypes?: string[];
useCentralizedKeys?: boolean;
hasRelationships?: boolean;
+ /** All table type names for determining which types to import from types.ts vs schema-types.ts */
+ tableTypeNames?: Set;
}
export function generateCreateMutationHook(
@@ -67,6 +69,7 @@ export function generateCreateMutationHook(
enumsFromSchemaTypes = [],
useCentralizedKeys = true,
hasRelationships = false,
+ tableTypeNames = new Set(),
} = options;
if (!reactQueryEnabled) {
@@ -85,10 +88,14 @@ export function generateCreateMutationHook(
const pkFieldNames = new Set(getPrimaryKeyInfo(table).map((pk) => pk.name));
const usedEnums = new Set();
+ const usedTableTypes = new Set();
for (const field of scalarFields) {
const cleanType = field.type.gqlType.replace(/!/g, '');
if (enumSet.has(cleanType)) {
usedEnums.add(cleanType);
+ } else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
+ // Track table types used in scalar fields (excluding the main type which is already imported)
+ usedTableTypes.add(cleanType);
}
}
@@ -119,8 +126,10 @@ export function generateCreateMutationHook(
);
statements.push(clientImport);
+ // Import the main type and any other table types used in scalar fields
+ const allTypesToImport = [typeName, ...Array.from(usedTableTypes)].sort();
const typesImport = t.importDeclaration(
- [t.importSpecifier(t.identifier(typeName), t.identifier(typeName))],
+ allTypesToImport.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))),
t.stringLiteral('../types')
);
typesImport.importKind = 'type';
@@ -269,10 +278,14 @@ export function generateCreateMutationHook(
t.identifier('mutationFn'),
t.arrowFunctionExpression(
[typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${mutationName}MutationDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
+ ]
+ )
)
)
);
@@ -352,6 +365,7 @@ export function generateUpdateMutationHook(
enumsFromSchemaTypes = [],
useCentralizedKeys = true,
hasRelationships = false,
+ tableTypeNames = new Set(),
} = options;
if (!reactQueryEnabled) {
@@ -376,10 +390,13 @@ export function generateUpdateMutationHook(
const pkFieldNames = new Set(pkFields.map((pk) => pk.name));
const usedEnums = new Set();
+ const usedTableTypes = new Set();
for (const field of scalarFields) {
const cleanType = field.type.gqlType.replace(/!/g, '');
if (enumSet.has(cleanType)) {
usedEnums.add(cleanType);
+ } else if (tableTypeNames.has(cleanType) && cleanType !== typeName) {
+ usedTableTypes.add(cleanType);
}
}
@@ -410,8 +427,10 @@ export function generateUpdateMutationHook(
);
statements.push(clientImport);
+ // Import the main type and any other table types used in scalar fields
+ const allTypesToImportUpdate = [typeName, ...Array.from(usedTableTypes)].sort();
const typesImport = t.importDeclaration(
- [t.importSpecifier(t.identifier(typeName), t.identifier(typeName))],
+ allTypesToImportUpdate.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))),
t.stringLiteral('../types')
);
typesImport.importKind = 'type';
@@ -565,10 +584,14 @@ export function generateUpdateMutationHook(
t.identifier('mutationFn'),
t.arrowFunctionExpression(
[typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${mutationName}MutationDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
+ ]
+ )
)
)
);
@@ -816,10 +839,14 @@ export function generateDeleteMutationHook(
t.identifier('mutationFn'),
t.arrowFunctionExpression(
[typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${mutationName}MutationDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)),
+ ]
+ )
)
)
);
diff --git a/graphql/codegen/src/cli/codegen/orm/input-types-generator.ts b/graphql/codegen/src/cli/codegen/orm/input-types-generator.ts
index f5d0246c3..ca7154c57 100644
--- a/graphql/codegen/src/cli/codegen/orm/input-types-generator.ts
+++ b/graphql/codegen/src/cli/codegen/orm/input-types-generator.ts
@@ -242,7 +242,8 @@ type FilterOperators =
| 'string'
| 'json'
| 'inet'
- | 'fulltext';
+ | 'fulltext'
+ | 'listArray';
interface ScalarFilterConfig {
name: string;
@@ -305,6 +306,22 @@ const SCALAR_FILTER_CONFIGS: ScalarFilterConfig[] = [
operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'],
},
{ name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] },
+ // List filters (for array fields like string[], int[], uuid[])
+ {
+ name: 'StringListFilter',
+ tsType: 'string[]',
+ operators: ['equality', 'distinct', 'comparison', 'listArray'],
+ },
+ {
+ name: 'IntListFilter',
+ tsType: 'number[]',
+ operators: ['equality', 'distinct', 'comparison', 'listArray'],
+ },
+ {
+ name: 'UUIDListFilter',
+ tsType: 'string[]',
+ operators: ['equality', 'distinct', 'comparison', 'listArray'],
+ },
];
/**
@@ -400,6 +417,23 @@ function buildScalarFilterProperties(
props.push({ name: 'matches', type: 'string', optional: true });
}
+ // List/Array operators (contains, overlaps, anyEqualTo, etc.)
+ if (operators.includes('listArray')) {
+ // Extract base type from array type (e.g., 'string[]' -> 'string')
+ const baseType = tsType.replace('[]', '');
+ props.push(
+ { name: 'contains', type: tsType, optional: true },
+ { name: 'containedBy', type: tsType, optional: true },
+ { name: 'overlaps', type: tsType, optional: true },
+ { name: 'anyEqualTo', type: baseType, optional: true },
+ { name: 'anyNotEqualTo', type: baseType, optional: true },
+ { name: 'anyLessThan', type: baseType, optional: true },
+ { name: 'anyLessThanOrEqualTo', type: baseType, optional: true },
+ { name: 'anyGreaterThan', type: baseType, optional: true },
+ { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true }
+ );
+ }
+
return props;
}
diff --git a/graphql/codegen/src/cli/codegen/queries.ts b/graphql/codegen/src/cli/codegen/queries.ts
index 3e4ae2775..55852c339 100644
--- a/graphql/codegen/src/cli/codegen/queries.ts
+++ b/graphql/codegen/src/cli/codegen/queries.ts
@@ -8,7 +8,7 @@
*/
import type { CleanTable } from '../../types/schema';
import * as t from '@babel/types';
-import { generateCode, addJSDocComment, typedParam } from './babel-ast';
+import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast';
import {
buildListQueryAST,
buildSingleQueryAST,
@@ -23,10 +23,13 @@ import {
getAllRowsQueryName,
getSingleRowQueryName,
getFilterTypeName,
+ getConditionTypeName,
getOrderByTypeName,
getScalarFields,
getScalarFilterType,
getPrimaryKeyInfo,
+ hasValidPrimaryKey,
+ fieldTypeToTs,
toScreamingSnake,
ucFirst,
lcFirst,
@@ -106,6 +109,7 @@ export function generateListQueryHook(
const hookName = getListQueryHookName(table);
const queryName = getAllRowsQueryName(table);
const filterTypeName = getFilterTypeName(table);
+ const conditionTypeName = getConditionTypeName(table);
const orderByTypeName = getOrderByTypeName(table);
const scalarFields = getScalarFields(table);
const keysName = `${lcFirst(typeName)}Keys`;
@@ -232,6 +236,80 @@ export function generateListQueryHook(
createFilterInterfaceDeclaration(filterTypeName, fieldFilters, false)
);
+ // Generate Condition interface (simple equality filter with scalar types)
+ // Track non-primitive types (enums) that need to be imported
+ const enumTypesUsed = new Set();
+ const conditionProperties: t.TSPropertySignature[] = scalarFields.map(
+ (field) => {
+ const tsType = fieldTypeToTs(field.type);
+ const isPrimitive =
+ tsType === 'string' ||
+ tsType === 'number' ||
+ tsType === 'boolean' ||
+ tsType === 'unknown' ||
+ tsType.endsWith('[]');
+ let typeAnnotation: t.TSType;
+ if (field.type.isArray) {
+ const baseType = tsType.replace('[]', '');
+ const isBasePrimitive =
+ baseType === 'string' ||
+ baseType === 'number' ||
+ baseType === 'boolean' ||
+ baseType === 'unknown';
+ if (!isBasePrimitive) {
+ enumTypesUsed.add(baseType);
+ }
+ typeAnnotation = t.tsArrayType(
+ baseType === 'string'
+ ? t.tsStringKeyword()
+ : baseType === 'number'
+ ? t.tsNumberKeyword()
+ : baseType === 'boolean'
+ ? t.tsBooleanKeyword()
+ : t.tsTypeReference(t.identifier(baseType))
+ );
+ } else {
+ if (!isPrimitive) {
+ enumTypesUsed.add(tsType);
+ }
+ typeAnnotation =
+ tsType === 'string'
+ ? t.tsStringKeyword()
+ : tsType === 'number'
+ ? t.tsNumberKeyword()
+ : tsType === 'boolean'
+ ? t.tsBooleanKeyword()
+ : t.tsTypeReference(t.identifier(tsType));
+ }
+ const prop = t.tsPropertySignature(
+ t.identifier(field.name),
+ t.tsTypeAnnotation(typeAnnotation)
+ );
+ prop.optional = true;
+ return prop;
+ }
+ );
+
+ // Add import for enum types if any are used
+ if (enumTypesUsed.size > 0) {
+ const schemaTypesImport = t.importDeclaration(
+ Array.from(enumTypesUsed).map((et) =>
+ t.importSpecifier(t.identifier(et), t.identifier(et))
+ ),
+ t.stringLiteral('../schema-types')
+ );
+ schemaTypesImport.importKind = 'type';
+ statements.push(schemaTypesImport);
+ }
+
+ const conditionInterface = t.tsInterfaceDeclaration(
+ t.identifier(conditionTypeName),
+ null,
+ null,
+ t.tsInterfaceBody(conditionProperties)
+ );
+ statements.push(conditionInterface);
+
const orderByValues = [
...scalarFields.flatMap((f) => [
`${toScreamingSnake(f.name)}_ASC`,
@@ -257,6 +335,14 @@ export function generateListQueryHook(
p.optional = true;
return p;
})(),
+ (() => {
+ const p = t.tsPropertySignature(
+ t.identifier('last'),
+ t.tsTypeAnnotation(t.tsNumberKeyword())
+ );
+ p.optional = true;
+ return p;
+ })(),
(() => {
const p = t.tsPropertySignature(
t.identifier('offset'),
@@ -265,6 +351,22 @@ export function generateListQueryHook(
p.optional = true;
return p;
})(),
+ (() => {
+ const p = t.tsPropertySignature(
+ t.identifier('before'),
+ t.tsTypeAnnotation(t.tsStringKeyword())
+ );
+ p.optional = true;
+ return p;
+ })(),
+ (() => {
+ const p = t.tsPropertySignature(
+ t.identifier('after'),
+ t.tsTypeAnnotation(t.tsStringKeyword())
+ );
+ p.optional = true;
+ return p;
+ })(),
(() => {
const p = t.tsPropertySignature(
t.identifier('filter'),
@@ -273,6 +375,14 @@ export function generateListQueryHook(
p.optional = true;
return p;
})(),
+ (() => {
+ const p = t.tsPropertySignature(
+ t.identifier('condition'),
+ t.tsTypeAnnotation(t.tsTypeReference(t.identifier(conditionTypeName)))
+ );
+ p.optional = true;
+ return p;
+ })(),
(() => {
const p = t.tsPropertySignature(
t.identifier('orderBy'),
@@ -426,10 +536,14 @@ export function generateListQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('queryOptions')),
@@ -456,10 +570,14 @@ export function generateListQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('options')),
@@ -482,10 +600,14 @@ export function generateListQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('options')),
@@ -549,11 +671,14 @@ export function generateListQueryHook(
const fetchFuncBody = t.blockStatement([
t.returnStatement(
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
+ ]
+ )
),
]);
const fetchFunc = t.functionDeclaration(
@@ -664,11 +789,14 @@ export function generateListQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)),
+ ]
+ )
)
),
]),
@@ -717,7 +845,12 @@ export function generateListQueryHook(
export function generateSingleQueryHook(
table: CleanTable,
options: QueryGeneratorOptions = {}
-): GeneratedQueryFile {
+): GeneratedQueryFile | null {
+ // Skip tables with composite keys - they are handled as custom queries
+ if (!hasValidPrimaryKey(table)) {
+ return null;
+ }
+
const {
reactQueryEnabled = true,
useCentralizedKeys = true,
@@ -951,10 +1084,14 @@ export function generateSingleQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('queryOptions')),
@@ -986,10 +1123,14 @@ export function generateSingleQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('options')),
@@ -1015,10 +1156,14 @@ export function generateSingleQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
+ ]
+ )
)
),
t.spreadElement(t.identifier('options')),
@@ -1077,11 +1222,14 @@ export function generateSingleQueryHook(
const fetchFuncBody = t.blockStatement([
t.returnStatement(
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
+ ]
+ )
),
]);
const fetchFunc = t.functionDeclaration(
@@ -1186,11 +1334,14 @@ export function generateSingleQueryHook(
t.identifier('queryFn'),
t.arrowFunctionExpression(
[],
- t.callExpression(t.identifier('execute'), [
- t.identifier(`${queryName}QueryDocument`),
- t.identifier('variables'),
- t.identifier('options'),
- ])
+ createTypedCallExpression(
+ t.identifier('execute'),
+ [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')],
+ [
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)),
+ t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)),
+ ]
+ )
)
),
]),
@@ -1243,7 +1394,10 @@ export function generateAllQueryHooks(
const files: GeneratedQueryFile[] = [];
for (const table of tables) {
files.push(generateListQueryHook(table, options));
- files.push(generateSingleQueryHook(table, options));
+ const singleHook = generateSingleQueryHook(table, options);
+ if (singleHook) {
+ files.push(singleHook);
+ }
}
return files;
}
diff --git a/graphql/codegen/src/cli/codegen/utils.ts b/graphql/codegen/src/cli/codegen/utils.ts
index 075e0bad2..dc46b8703 100644
--- a/graphql/codegen/src/cli/codegen/utils.ts
+++ b/graphql/codegen/src/cli/codegen/utils.ts
@@ -381,6 +381,25 @@ export function getPrimaryKeyFields(table: CleanTable): string[] {
return getPrimaryKeyInfo(table).map((pk) => pk.name);
}
+/**
+ * Check if table has a valid single-field primary key
+ * Used to determine if a single query hook can be generated
+ * Tables with composite keys return false (handled as custom queries)
+ */
+export function hasValidPrimaryKey(table: CleanTable): boolean {
+ // Check for explicit primary key constraint with single field
+ const pk = table.constraints?.primaryKey?.[0];
+ if (pk && pk.fields.length === 1) {
+ return true;
+ }
+ // Check for 'id' field as fallback
+ const idField = table.fields.find((f) => f.name.toLowerCase() === 'id');
+ if (idField) {
+ return true;
+ }
+ return false;
+}
+
// ============================================================================
// Query key generation
// ============================================================================