prisma-nestjs-graphql
Version:
Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module
581 lines (578 loc) • 19.3 kB
TypeScript
import { DMMF, GeneratorOptions } from '@prisma/generator-helper';
import AwaitEventEmitter from 'await-event-emitter';
import { Project, SourceFile } from 'ts-morph';
import { ValueOf, WritableDeep } from 'type-fest';
export type ConfigurationCreateArgs = {
config: {
[k: string]: GeneratorConfigLine;
};
output: string;
sourceFilePath: string;
};
declare class Configuration {
#private;
readonly warnings: Set<string>;
schemaConfig: Record<string, unknown>;
externalConfig?: ExternalConfig;
externalConfigFile?: string;
constructor(args: Pick<Configuration, "schemaConfig" | "externalConfig" | "externalConfigFile"> & {
sourceFilePath: string;
output: string;
warnings?: string[];
});
static create(args: ConfigurationCreateArgs): Promise<Configuration>;
private initializeOutputFilePattern;
get output(): string;
get outputFilePattern(): string;
get importExtension(): string;
get emitCompiled(): boolean;
get tsConfigFilePath(): string | undefined;
get combineScalarFilters(): boolean;
get noTypeId(): boolean;
get noAtomicOperations(): boolean;
get reExport(): ReExportType;
get purgeOutput(): boolean;
get emitSingle(): boolean;
get requireSingleFieldsInWhereUniqueInput(): boolean;
get omitModelsCount(): boolean;
get typeListNullable(): boolean;
get unsafeCompatibleWhereUniqueInput(): boolean;
get prismaClientImport(): string;
get emitBlocksModels(): boolean;
get emitBlocksPrismaEnums(): boolean;
get emitBlocksSchemaEnums(): boolean;
get emitBlocksOutputs(): boolean;
get emitBlocksInputs(): boolean;
get emitBlocksArgs(): boolean;
getGraphqlScalar(type: string): ImportNameSpec | undefined;
getField(namespace?: string): ConfigFieldRecord | undefined;
/**
* Get graphql for input type
*/
getInputType(args: GetInputTypeFunctionArgs): InputTypeRef | undefined;
get customImports(): CustomImportItem[];
shouldHideField(args: {
objectName: string;
propertyName: string;
propertyType: string;
location: FieldLocation;
typeName: string;
/**
* @deprecated Should not use
*/
settings?: ObjectSettings;
/**
* @deprecated Legacy1
*/
output?: boolean;
/**
* @deprecated Legacy
*/
input?: boolean;
}): boolean;
getDecorators(): Generator<DecoratorItem>;
/**
* Get field override arguments for a specific field.
* Returns merged fieldArguments from all matching overrides.
*/
getFieldOverride(args: {
objectName: string;
propertyName: string;
propertyType: string;
location: FieldLocation;
typeName: string;
}): Record<string, unknown> | undefined;
/**
* @deprecated Should be replaced by decorators
*/
get decorate(): LegacyDecorateElement[];
}
declare const ReExport: {
readonly All: "All";
readonly Directories: "Directories";
readonly None: "None";
readonly Single: "Single";
};
export type ReExportType = ValueOf<typeof ReExport>;
export type ObjectSetting = {
/**
* Act as named import or namespaceImport or defaultImport
*/
name: string;
kind: "Decorator" | "Field" | "FieldType" | "PropertyType" | "ObjectType";
arguments?: string[] | Record<string, unknown>;
input: boolean;
output: boolean;
model: boolean;
match?: (test: string) => boolean;
from: string;
namespace?: string;
defaultImport?: string | true;
namespaceImport?: string;
namedImport?: boolean;
};
export interface ObjectSettingsFilterArgs {
name: string;
input?: boolean;
output?: boolean;
}
declare class ObjectSettings extends Array<ObjectSetting> {
shouldHideField({ input, name, output, }: ObjectSettingsFilterArgs): boolean;
getFieldType({ input, name, output, }: ObjectSettingsFilterArgs): ObjectSetting | undefined;
getPropertyType({ input, name, output, }: ObjectSettingsFilterArgs): ObjectSetting | undefined;
getObjectTypeArguments(options: Record<string, any>): string[];
fieldArguments(): Record<string, unknown> | undefined;
}
export type TAwaitEventEmitter = AwaitEventEmitter.default;
export type FieldLocation = DMMF.FieldLocation;
export type Model = WritableDeep<DMMF.Model>;
export type Schema = WritableDeep<DMMF.Schema>;
export type Field = DMMF.Field;
export type InputTypeRef = DMMF.InputTypeRef;
/**
* @deprecated
*/
export type LegacyDecorateElement = {
isMatchField: (s: string) => boolean;
isMatchType: (s: string) => boolean;
from: string;
name: string;
arguments?: string[];
namedImport: boolean;
defaultImport?: string | true;
namespaceImport?: string;
type: string;
field: string;
};
export type FieldInfo = {
/**
* Prisma DMMF field location type
* Can be: 'scalar', 'inputObjectTypes', 'outputObjectTypes', 'enumTypes', 'fieldRefTypes'
*/
location: FieldLocation;
/**
* Class name
*/
objectName: string;
/**
* Property name
*/
propertyName: string;
/**
* Property type (may contain TypeScript elements, like parameters for generics, etc.)
*/
propertyType: string;
/**
* GraphQL/Prisma type name
*/
typeName: string;
};
/**
* Configuration lines defined in schema
*/
export type GeneratorConfigLine = string | string[] | undefined | null;
export type GetInputTypeFunctionArgs = {
inputTypeName: string;
fieldName: string;
fieldInputTypes: InputTypeRef[];
};
export type EventArguments = {
schema: Schema;
models: Map<string, Model>;
modelNames: string[];
modelFields: Map<string, Map<string, Field>>;
fieldSettings: Map<string, Map<string, ObjectSettings>>;
config: Configuration;
project: Project;
output: string;
getSourceFile(args: {
type: string;
name: string;
}): SourceFile;
eventEmitter: TAwaitEventEmitter;
typeNames: Set<string>;
removeTypes: Set<string>;
enums: Record<string, DMMF.DatamodelEnum | undefined>;
getModelName(name: string): string | undefined;
/**
* Input types for this models should be decorated @Type(() => Self)
*/
classTransformerTypeModels: Set<string>;
};
export type ImportNameSpec = {
name: string;
specifier?: string;
};
export type EmitBlocksOption = "enums" | "models" | "inputs" | "args" | "outputs";
export type ConfigFieldRecord = {
/** Arguments passed to the decorator.
* @default [] */
arguments?: string[];
/** Import as default export.
* Use `true` to import by the field namespace name. */
defaultImport?: string | true;
/** Module to import from (e.g. 'class-validator') */
from?: string;
/** Apply decorator on InputType classes.
* @default false */
input?: boolean;
/** Apply decorator only on model ObjectType classes.
* @default false */
model?: boolean;
/** Import entire module under this namespace.
* @default equals field namespace name */
namespaceImport?: string;
/** Apply decorator on ObjectType/output classes.
* @default false */
output?: boolean;
};
/**
* @example
* WhereInput: { '*': 'WhereInput' },
* PostCreateInput: { author: 'UserCreateNestedOneWithoutPostsInput' },
*/
export type ConfigInputTypeMap = Record<string, Record<string, string>>;
export type GetInputTypeRefFunction = (args: GetInputTypeFunctionArgs) => InputTypeRef | undefined;
export type GetInputTypePatternFunction = (args: GetInputTypeFunctionArgs) => string | undefined;
export type GetInputTypeFunction = GetInputTypeRefFunction | GetInputTypePatternFunction;
export type CustomImportItem = {
/** Import as default export.
* Use `true` to import by name. */
defaultImport?: string | true;
/** Module specifier to import from (e.g. 'class-validator') */
from: string;
/** Name to import */
name: string;
/** Import as a named export.
* @default false */
namedImport?: boolean;
/** Import entire module under this namespace */
namespaceImport?: string;
};
/**
* Return `true` to generate `@HideField()` instead of `@Field()` for a field.
*/
export type ShouldHideFieldFunction = (args: FieldInfo) => boolean;
/**
* Modern decorator rule for `ExternalConfig.decorators`.
* Prefer this over legacy `decorate_*` schema keys.
*/
export type DecoratorItem = {
/** Return `true` to apply this decorator to the current field. */
match: (args: FieldInfo) => boolean;
/** Arguments passed to the decorator call.
* Supports templates like `{propertyType.0}`. */
arguments?: string[];
/** Module specifier to import from (e.g. 'class-validator') */
from: string;
/** Decorator name. Can include namespace, e.g. `Transform.Type`. */
name: string;
/** Import as a named export. */
namedImport?: boolean;
/** Import as default export.
* Use `true` to import by decorator name. */
defaultImport?: string | true;
/** Import entire module under this namespace. */
namespaceImport?: string;
};
/**
* Arguments passed to the `@Field()` decorator.
* Follows NestJS GraphQL's FieldOptions structure to avoid conflicts.
* These are merged into the generated `@Field()` options object.
*
* @see https://docs.nestjs.com/graphql/resolvers-map#field-decorator
*/
export type FieldDecoratorArguments = {
/**
* Custom name for the field in GraphQL schema (different from TypeScript property name).
*
* **Important:** When you rename a field (e.g., 'take' → 'first'), you must map the GraphQL
* argument names back to Prisma field names in your resolver, since Prisma expects the
* original field names.
*
* @example
* // Option 1: Manual mapping in resolver
* async findMany(args: FindManyArgs): Promise<Item[]> {
* const { first, ...restArgs } = args as any;
* return this.prisma.item.findMany({
* ...restArgs,
* ...(first !== undefined && { take: first }),
* });
* }
*
* @example
* // Option 2: Use a helper function
* export function mapGraphQLArgsToPrisma(args: any): any {
* const { first, ...restArgs } = args;
* return {
* ...restArgs,
* ...(first !== undefined && { take: first }),
* };
* }
*
* async findMany(args: FindManyArgs): Promise<Item[]> {
* const prismaArgs = mapGraphQLArgsToPrisma(args);
* return this.prisma.item.findMany(prismaArgs);
* }
* export function mapGraphQLArgsToPrisma(args: any): any {
* const { first, ...restArgs } = args;
* return {
* ...restArgs,
* ...(first !== undefined && { take: first }),
* };
* }
*/
name?: string;
/** Description shown in GraphQL schema. */
description?: string;
/** Mark field as deprecated with optional reason. */
deprecationReason?: string;
/** Complexity for query complexity analysis. */
complexity?: unknown;
/**
* Middleware function name(s) to apply to the field.
* These are emitted as identifier references, not strings.
* Make sure to add corresponding customImports for the middleware.
*
* @example
* // Single middleware
* middleware: 'loggerMiddleware'
*
* @example
* // Multiple middleware
* middleware: ['loggerMiddleware', 'authMiddleware']
*/
middleware?: string | string[];
/** Mark field as nullable in GraphQL schema. */
nullable?: boolean;
/** Default value for the field. */
defaultValue?: unknown;
};
/**
* Rule for overriding `@Field()` decorator arguments on generated fields.
* Use this to customize pagination fields (take, skip) or other generated Args fields.
*/
export type FieldDecoratorRule = {
/** Return `true` to apply this override to the current field. */
match: (args: FieldInfo) => boolean;
/**
* Arguments to merge into the `@Field()` decorator options.
* These are merged with existing arguments (nullable, etc.).
*/
decoratorArguments: FieldDecoratorArguments;
};
export type ExternalConfig = Partial<{
/**
* Output folder for generated files.
* If path relative and defined in schema it will be relative to schema,
* if defined in config file it will be relative to this config file.
*/
output: string;
/**
* File path and name pattern for generated files.
* @type {string}
* Available tokens:
* - `{model}` — Model name in dashed-case, or 'prisma' if unknown
* - `{name}` — Dashed-case name of model/input/arg without suffix
* - `{type}` — Short type name (model, input, args, output)
* - `{plural.type}` — Plural short type name (models, inputs, enums)
* @default '{model}/{name}.{type}.ts'
*/
outputFilePattern: string;
/**
* Append an extension to relative import and export module specifiers.
* Useful when your project uses ESM or a custom module resolution.
* @example 'js', 'ts', 'mjs'
*/
importExtension: string;
/**
* Combine nested/nullable scalar filters into a single filter type.
* When enabled, reduces the number of generated filter classes by merging
* e.g. `StringNullableFilter` and `StringFilter` into one.
* @default true
*/
combineScalarFilters: boolean;
/**
* Remove input types for atomic operations.
* When enabled, types like `IntFieldUpdateOperationsInput` are not generated.
* @default true
*/
noAtomicOperations: boolean;
/**
* Emit only selected blocks. Some blocks depend on others
* (e.g. models requires schemaEnums, inputs requires prismaEnums).
* Valid block names: 'args', 'inputs', 'outputs', 'models', 'enums'
* @default All blocks enabled
*/
emitBlocks: EmitBlocksOption[];
/**
* Omit the `_count` field from model output types.
* @default false
*/
omitModelsCount: boolean;
/**
* Emit compiled JavaScript and definition files instead of TypeScript sources.
* @default false
*/
emitCompiled: boolean;
/**
* Path to tsconfig.json (absolute or relative to CWD).
* If not specified, auto-detects `tsconfig.json` if it exists.
*/
tsConfigFilePath: string;
/**
* Re-export strategy for generated files.
* - None — No re-export index files (default)
* - Directories — Index file in each root directory
* - Single — Single index file in the output directory
* - All — All of the above
* @default None
*/
reExport: ReExportType;
/**
* Delete all files in the output folder before each generation.
* @default false
*/
purgeOutput: boolean;
/**
* Mark single-field WhereUniqueInput fields as required (TypeScript)
* and non-nullable (GraphQL).
* NOTE: This will break compatibility between Prisma types and generated classes.
* @default false
*/
requireSingleFieldsInWhereUniqueInput: boolean;
/**
* Generate a single merged file with all classes and enums
* instead of one file per model/input/arg.
* @default false
*/
emitSingle: boolean;
/**
* Make all fields in `*WhereUniqueInput` classes non-optional TypeScript properties.
* @default false
*/
unsafeCompatibleWhereUniqueInput: boolean;
/**
* Add `nullable: true` to relation list properties on output types.
* Changes `[Type!]!` → `[Type!]` in the GraphQL schema.
* @default false
*/
typeListNullable: boolean;
/**
* Import path used for Prisma Client imports in generated files.
* @default '@prisma/client'
*/
prismaClientImport: string;
/**
* Disable GraphQL ID type usage, using Int/Float for @id fields instead.
* @default false
*/
noTypeId: boolean;
/**
* Custom GraphQL scalar type mappings for Prisma scalar types.
* Keyed by Prisma scalar type name (e.g. 'BigInt', 'DateTime').
* @example
* // Override BigInt with graphql-scalars' GraphQLBigInt:
* { BigInt: { name: 'GraphQLBigInt', specifier: 'graphql-scalars' } }
*/
graphqlScalars: Record<string, ImportNameSpec | undefined>;
/**
* Per-field custom decorator configuration, keyed by namespace.
* Enables automatic decorator application from external modules
* on fields annotated with `@{namespace}.XXX` in the Prisma schema.
* Each namespace entry defines:
* - `from` — Module specifier to import (e.g. 'class-validator')
* - `input` — Apply to InputType classes
* - `output` — Apply to ObjectType/output classes
* - `model` — Apply only to model ObjectType classes
* - `defaultImport` — Import as default export (true = use namespace as name)
* - `namespaceImport` — Import entire module under this namespace
* - `namedImport` — Import as named export
*
* @example
* // Schema: /// @Validator.MinLength(3)
* // Config: { from: 'class-validator', input: true }
* // Result: import * as Validator from 'class-validator'; @Validator.MinLength(3)
*/
fields: Record<string, ConfigFieldRecord | undefined>;
/**
* Input type mapping.
* Select which input type should be exposed when multiple candidates exist.
* Since GraphQL does not support input unions, this setting can resolve
* ambiguous fields (e.g. `UserRelationFilter` vs `UserWhereInput`).
*
* Supports two variants:
* - object map: `{ [inputTypeName]: { [fieldName|'*']: pattern } }`
* - function: return either an `InputTypeRef` or a string pattern
* (same matching behavior as map patterns, including `match:` syntax)
* @example
* // Force all WhereInput relation properties to use the plain WhereInput type:
* { WhereInput: { '*': 'WhereInput' } }
* // Or for a specific property in a specific type:
* { PostCreateInput: { author: 'UserCreateNestedOneWithoutPostsInput' } }
* // Function variant returning a pattern:
* ({ inputTypeName, fieldName }) =>
* inputTypeName.includes('CreateOne') && fieldName === 'data'
* ? 'UncheckedCreate'
* : undefined
*/
inputType: GetInputTypeFunction | ConfigInputTypeMap;
/**
* Custom import statements injected into generated files.
* Each element specifies:
* - `from` — Module specifier to import from
* - `name` — Name to import
* - `namedImport` — Import as named export
* - `defaultImport` — Import as default export
* - `namespaceImport` — Import entire module under this namespace
*/
customImports: CustomImportItem[];
/**
* Hook for deciding whether a generated field should be hidden in GraphQL schema.
* Called for each generated input/output/model field.
* Returning `true` adds `@HideField()` and skips `@Field()`.
* When set, this hook overrides hide settings from field comments and legacy `decorate`.
*/
shouldHideField: ShouldHideFieldFunction;
/**
* Modern way to attach decorators to generated fields.
* Each rule is evaluated against generated field metadata (`FieldInfo`) and
* applied when `match` returns `true`.
* Prefer this over legacy `decorate`/`decorate_*` schema configuration.
*/
decorators: DecoratorItem[];
/**
* Override `@Field()` decorator arguments for specific fields.
* Use this to customize pagination fields (take, skip, cursor) or other
* generated Args fields that don't come from your Prisma schema.
*
* Each rule is evaluated against generated field metadata (`FieldInfo`) and
* applied when `match` returns `true`.
*
* @example
* fieldDecoratorArguments: [
* {
* match: ({ objectName, propertyName }) =>
* objectName.endsWith('Args') && propertyName === 'take',
* decoratorArguments: {
* name: 'first',
* defaultValue: 10,
* description: 'Number of records to return',
* },
* },
* ]
*/
fieldDecoratorArguments: FieldDecoratorRule[];
}>;
export declare function generate(args: GeneratorOptions & {
skipAddOutputSourceFiles?: boolean;
connectCallback?: (emitter: TAwaitEventEmitter, eventArguments: EventArguments) => void | Promise<void>;
}): Promise<void>;
export declare const generatorHandlerConfig: {
onGenerate(options: GeneratorOptions): Promise<void>;
onManifest(): {
defaultOutput: string;
prettyName: string;
};
};
export {};