Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions graphql/codegen/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ export class UserModel {
}

findFirst<const S extends UserSelect>(
args?: FindFirstArgs<DeepExact<S, UserSelect>, UserFilter>
): QueryBuilder<{ users: { nodes: InferSelectResult<UserWithRelations, S>[] } }> {
args?: FindFirstArgs<DeepExact<S, UserSelect>, UserFilter, UsersOrderBy>
): QueryBuilder<{ user: InferSelectResult<UserWithRelations, S> | null }> {
// ...
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,10 @@ export interface FindManyArgs<TSelect, TWhere, TOrderBy = never> {
offset?: number;
}

export interface FindFirstArgs<TSelect, TWhere> {
export interface FindFirstArgs<TSelect, TWhere, TOrderBy = never> {
select?: TSelect;
where?: TWhere;
orderBy?: TOrderBy[];
}

export interface CreateArgs<TSelect, TData> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,32 @@ export class UserModel {
variables
});
}
findFirst<S extends UserSelect>(args: FindFirstArgs<S, UserFilter> & {
findFirst<S extends UserSelect>(args: FindFirstArgs<S, UserFilter, UsersOrderBy> & {
select: S;
} & StrictSelect<S, UserSelect>): QueryBuilder<{
users: {
nodes: InferSelectResult<UserWithRelations, S>[];
};
user: InferSelectResult<UserWithRelations, S> | null;
}> {
const {
document,
variables
} = buildFindFirstDocument("User", "users", args.select, {
where: args?.where
}, "UserFilter", connectionFieldsMap);
where: args?.where,
orderBy: args?.orderBy as string[] | undefined
}, "UserFilter", "UsersOrderBy", connectionFieldsMap);
return new QueryBuilder({
client: this.client,
operation: "query",
operationName: "User",
fieldName: "users",
fieldName: "user",
document,
variables
variables,
transform: (data: {
users?: {
nodes?: InferSelectResult<UserWithRelations, S>[];
};
}) => ({
"user": data.users?.nodes?.[0] ?? null
})
});
}
findOne<S extends UserSelect>(args: {
Expand Down Expand Up @@ -188,26 +194,32 @@ export class AuditLogModel {
variables
});
}
findFirst<S extends AuditLogSelect>(args: FindFirstArgs<S, AuditLogFilter> & {
findFirst<S extends AuditLogSelect>(args: FindFirstArgs<S, AuditLogFilter, AuditLogsOrderBy> & {
select: S;
} & StrictSelect<S, AuditLogSelect>): QueryBuilder<{
auditLogs: {
nodes: InferSelectResult<AuditLogWithRelations, S>[];
};
auditLog: InferSelectResult<AuditLogWithRelations, S> | null;
}> {
const {
document,
variables
} = buildFindFirstDocument("AuditLog", "auditLogs", args.select, {
where: args?.where
}, "AuditLogFilter", connectionFieldsMap);
where: args?.where,
orderBy: args?.orderBy as string[] | undefined
}, "AuditLogFilter", "AuditLogsOrderBy", connectionFieldsMap);
return new QueryBuilder({
client: this.client,
operation: "query",
operationName: "AuditLog",
fieldName: "auditLogs",
fieldName: "auditLog",
document,
variables
variables,
transform: (data: {
auditLogs?: {
nodes?: InferSelectResult<AuditLogWithRelations, S>[];
};
}) => ({
"auditLog": data.auditLogs?.nodes?.[0] ?? null
})
});
}
findOne<S extends AuditLogSelect>(args: {
Expand Down Expand Up @@ -291,33 +303,39 @@ export class OrganizationModel {
variables
});
}
findFirst<S extends OrganizationSelect>(args: FindFirstArgs<S, OrganizationFilter> & {
findFirst<S extends OrganizationSelect>(args: FindFirstArgs<S, OrganizationFilter, OrganizationsOrderBy> & {
select: S;
} & StrictSelect<S, OrganizationSelect>): QueryBuilder<{
allOrganizations: {
nodes: InferSelectResult<OrganizationWithRelations, S>[];
};
organization: InferSelectResult<OrganizationWithRelations, S> | null;
}> {
const {
document,
variables
} = buildFindFirstDocument("Organization", "allOrganizations", args.select, {
where: args?.where
}, "OrganizationFilter", connectionFieldsMap);
where: args?.where,
orderBy: args?.orderBy as string[] | undefined
}, "OrganizationFilter", "OrganizationsOrderBy", connectionFieldsMap);
return new QueryBuilder({
client: this.client,
operation: "query",
operationName: "Organization",
fieldName: "allOrganizations",
document,
variables
fieldName: "organization",
document,
variables,
transform: (data: {
allOrganizations?: {
nodes?: InferSelectResult<OrganizationWithRelations, S>[];
};
}) => ({
"organization": data.allOrganizations?.nodes?.[0] ?? null
})
});
}
findOne<S extends OrganizationSelect>(args: {
id: string;
select: S;
} & StrictSelect<S, OrganizationSelect>): QueryBuilder<{
organizationById: InferSelectResult<OrganizationWithRelations, S> | null;
organization: InferSelectResult<OrganizationWithRelations, S> | null;
}> {
const {
document,
Expand All @@ -327,7 +345,7 @@ export class OrganizationModel {
client: this.client,
operation: "query",
operationName: "Organization",
fieldName: "organizationById",
fieldName: "organization",
document,
variables
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1843,7 +1843,7 @@ export async function fetchUserQuery<S extends UserSelect>(params: {
export async function fetchUserQuery(params: {
id: string;
selection: SelectionConfig<UserSelect>;
}) {
}): Promise<any> {
const args = buildSelectionArgs<UserSelect>(params.selection);
return getClient().user.findOne({
id: params.id,
Expand Down Expand Up @@ -1975,7 +1975,7 @@ export async function fetchPostQuery<S extends PostSelect>(params: {
export async function fetchPostQuery(params: {
id: string;
selection: SelectionConfig<PostSelect>;
}) {
}): Promise<any> {
const args = buildSelectionArgs<PostSelect>(params.selection);
return getClient().post.findOne({
id: params.id,
Expand Down Expand Up @@ -2094,7 +2094,7 @@ export async function fetchUserQuery<S extends UserSelect>(params: {
export async function fetchUserQuery(params: {
id: string;
selection: SelectionConfig<UserSelect>;
}) {
}): Promise<any> {
const args = buildSelectionArgs<UserSelect>(params.selection);
return getClient().user.findOne({
id: params.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ function buildFindManyArgsType(table: Table): t.TSType {

/**
* Build the FindFirstArgs type instantiation for a table:
* FindFirstArgs<SelectType, FilterType> & { select: SelectType }
* FindFirstArgs<SelectType, FilterType, OrderByType> & { select: SelectType }
*
* The intersection with { select: SelectType } makes select required,
* matching what the ORM's findFirst method expects.
Expand All @@ -529,11 +529,13 @@ function buildFindFirstArgsType(table: Table): t.TSType {
const { typeName } = getTableNames(table);
const selectTypeName = `${typeName}Select`;
const whereTypeName = getFilterTypeName(table);
const orderByTypeName = getOrderByTypeName(table);
const findFirstType = t.tsTypeReference(
t.identifier('FindFirstArgs'),
t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(selectTypeName)),
t.tsTypeReference(t.identifier(whereTypeName)),
t.tsTypeReference(t.identifier(orderByTypeName)),
]),
);
// Intersect with { select: SelectType } to make select required
Expand Down Expand Up @@ -1766,6 +1768,7 @@ export function generateTableCommand(table: Table, options?: TableCommandOptions
' --select <fields> Comma-separated list of fields to return',
' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)',
' --condition.<f>.<op> Condition filter (dot-notation)',
' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)',
'',
);
if (hasSearchFields) {
Expand Down
16 changes: 0 additions & 16 deletions graphql/codegen/src/core/codegen/orm/client-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,6 @@ export function generateCreateClientFile(
// Re-export all models
statements.push(t.exportAllDeclaration(t.stringLiteral('./models')));

// Re-export NodeHttpAdapter when enabled (for use in any Node.js application)
if (options?.nodeHttpAdapter) {
statements.push(
t.exportNamedDeclaration(
null,
[
t.exportSpecifier(
t.identifier('NodeHttpAdapter'),
t.identifier('NodeHttpAdapter'),
),
],
t.stringLiteral('./node-fetch'),
),
);
}

// Re-export custom operations
if (hasCustomQueries) {
statements.push(
Expand Down
94 changes: 76 additions & 18 deletions graphql/codegen/src/core/codegen/orm/model-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
getGeneratedFileHeader,
getOrderByTypeName,
getPrimaryKeyInfo,
getSingleRowQueryName,
getTableNames,
hasValidPrimaryKey,
lcFirst,
Expand Down Expand Up @@ -193,7 +192,10 @@ export function generateModelFile(
const pkField = pkFields[0];
const pluralQueryName = table.query?.all ?? pluralName;
const singleQueryName = table.query?.one;
const singleResultFieldName = getSingleRowQueryName(table);
// The unwrapped result key for findFirst/findOne — must be the friendly
// singular noun (e.g. "animal"), NOT the GraphQL by-id query name (e.g.
// "animalById"), so the surface aligns with the rest of the SDK.
const singleResultFieldName = singularName;
const createMutationName = table.query?.create ?? `create${typeName}`;
const updateMutationName = table.query?.update;
const deleteMutationName = table.query?.delete;
Expand Down Expand Up @@ -452,6 +454,7 @@ export function generateModelFile(
const findFirstTypeArgs: Array<(sel: t.TSType) => t.TSType> = [
(sel: t.TSType) => sel,
() => t.tsTypeReference(t.identifier(whereTypeName)),
() => t.tsTypeReference(t.identifier(orderByTypeName)),
];
const argsType = (sel: t.TSType) =>
t.tsTypeReference(
Expand All @@ -467,23 +470,17 @@ export function generateModelFile(
t.tsTypeParameterInstantiation([
t.tsTypeLiteral([
t.tsPropertySignature(
t.identifier(pluralQueryName),
t.identifier(singleResultFieldName),
t.tsTypeAnnotation(
t.tsTypeLiteral([
t.tsPropertySignature(
t.identifier('nodes'),
t.tsTypeAnnotation(
t.tsArrayType(
t.tsTypeReference(
t.identifier('InferSelectResult'),
t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(relationTypeName)),
sel,
]),
),
),
),
t.tsUnionType([
t.tsTypeReference(
t.identifier('InferSelectResult'),
t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(relationTypeName)),
sel,
]),
),
t.tsNullKeyword(),
]),
),
),
Expand Down Expand Up @@ -514,15 +511,75 @@ export function generateModelFile(
true,
),
),
t.objectProperty(
t.identifier('orderBy'),
t.tsAsExpression(
t.optionalMemberExpression(
t.identifier('args'),
t.identifier('orderBy'),
false,
true,
),
t.tsUnionType([
t.tsArrayType(t.tsStringKeyword()),
t.tsUndefinedKeyword(),
]),
),
),
];
const bodyArgs = [
t.stringLiteral(typeName),
t.stringLiteral(pluralQueryName),
selectExpr,
t.objectExpression(findFirstObjProps),
t.stringLiteral(whereTypeName),
t.stringLiteral(orderByTypeName),
t.identifier('connectionFieldsMap'),
];
const transformDataParam = t.identifier('data');
const transformedNodesProp = t.tsPropertySignature(
t.identifier('nodes'),
t.tsTypeAnnotation(
t.tsArrayType(
t.tsTypeReference(
t.identifier('InferSelectResult'),
t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(relationTypeName)),
sRef(),
]),
),
),
),
);
transformedNodesProp.optional = true;
const transformedCollectionProp = t.tsPropertySignature(
t.identifier(pluralQueryName),
t.tsTypeAnnotation(t.tsTypeLiteral([transformedNodesProp])),
);
transformedCollectionProp.optional = true;
transformDataParam.typeAnnotation = t.tsTypeAnnotation(
t.tsTypeLiteral([transformedCollectionProp]),
);
const firstNodeExpr = t.optionalMemberExpression(
t.optionalMemberExpression(
t.memberExpression(t.identifier('data'), t.identifier(pluralQueryName)),
t.identifier('nodes'),
false,
true,
),
t.numericLiteral(0),
true,
true,
);
const transformFn = t.arrowFunctionExpression(
[transformDataParam],
t.objectExpression([
t.objectProperty(
t.stringLiteral(singleResultFieldName),
t.logicalExpression('??', firstNodeExpr, t.nullLiteral()),
),
]),
);
classBody.push(
createClassMethod(
'findFirst',
Expand All @@ -534,7 +591,8 @@ export function generateModelFile(
bodyArgs,
'query',
typeName,
pluralQueryName,
singleResultFieldName,
[t.objectProperty(t.identifier('transform'), transformFn)],
),
),
);
Expand Down
Loading
Loading