Skip to content

Commit de11bc5

Browse files
committed
chore: cache schema validators
1 parent 22246e8 commit de11bc5

File tree

4 files changed

+119
-29
lines changed

4 files changed

+119
-29
lines changed

packages/runtime/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"@paralleldrive/cuid2": "^2.2.2",
8888
"decimal.js": "^10.4.3",
8989
"is-plain-object": "^5.0.0",
90+
"json-stable-stringify": "^1.3.0",
9091
"kysely": "^0.27.5",
9192
"nanoid": "^5.0.9",
9293
"pg-connection-string": "^2.9.0",

packages/runtime/src/client/client-impl.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ export class ClientImpl<Schema extends SchemaDef> {
248248
function createClientProxy<Schema extends SchemaDef>(
249249
client: ClientContract<Schema>
250250
): ClientImpl<Schema> {
251+
const inputValidator = new InputValidator(client.$schema);
252+
const resultProcessor = new ResultProcessor(client.$schema);
253+
251254
return new Proxy(client, {
252255
get: (target, prop, receiver) => {
253256
if (typeof prop === 'string' && prop.startsWith('$')) {
@@ -261,7 +264,9 @@ function createClientProxy<Schema extends SchemaDef>(
261264
if (model) {
262265
return createModelCrudHandler(
263266
client,
264-
model as GetModels<Schema>
267+
model as GetModels<Schema>,
268+
inputValidator,
269+
resultProcessor
265270
);
266271
}
267272
}
@@ -276,11 +281,10 @@ function createModelCrudHandler<
276281
Model extends GetModels<Schema>
277282
>(
278283
client: ClientContract<Schema>,
279-
model: Model
284+
model: Model,
285+
inputValidator: InputValidator<Schema>,
286+
resultProcessor: ResultProcessor<Schema>
280287
): ModelOperations<Schema, Model> {
281-
const inputValidator = new InputValidator(client.$schema);
282-
const resultProcessor = new ResultProcessor(client.$schema);
283-
284288
const createPromise = (
285289
operation: CrudOperation,
286290
args: unknown,

packages/runtime/src/client/crud/validator.ts

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Decimal from 'decimal.js';
2+
import stableStringify from 'json-stable-stringify';
23
import { match, P } from 'ts-pattern';
34
import { z, ZodSchema } from 'zod';
45
import type {
@@ -33,104 +34,169 @@ import {
3334
requireModel,
3435
} from '../query-utils';
3536

37+
type GetSchemaFunc<Schema extends SchemaDef, Options> = (
38+
model: GetModels<Schema>,
39+
options: Options
40+
) => ZodSchema;
41+
3642
export class InputValidator<Schema extends SchemaDef> {
43+
private schemaCache = new Map<string, ZodSchema>();
44+
3745
constructor(private readonly schema: Schema) {}
3846

3947
validateFindArgs(model: GetModels<Schema>, unique: boolean, args: unknown) {
40-
return this.validate<FindArgs<Schema, GetModels<Schema>, true>>(
41-
this.makeFindSchema(model, unique, true),
48+
return this.validate<
49+
FindArgs<Schema, GetModels<Schema>, true>,
50+
Parameters<typeof this.makeFindSchema>[1]
51+
>(
52+
model,
4253
'find',
54+
{ unique, collection: true },
55+
(model, options) => this.makeFindSchema(model, options),
4356
args
4457
);
4558
}
4659

4760
validateCreateArgs(model: GetModels<Schema>, args: unknown) {
4861
return this.validate<CreateArgs<Schema, GetModels<Schema>>>(
49-
this.makeCreateSchema(model),
62+
model,
5063
'create',
64+
undefined,
65+
(model) => this.makeCreateSchema(model),
5166
args
5267
);
5368
}
5469

5570
validateCreateManyArgs(model: GetModels<Schema>, args: unknown) {
5671
return this.validate<
57-
CreateManyArgs<Schema, GetModels<Schema>> | undefined
58-
>(this.makeCreateManySchema(model), 'createMany', args);
72+
CreateManyArgs<Schema, GetModels<Schema>>,
73+
undefined
74+
>(
75+
model,
76+
'createMany',
77+
undefined,
78+
(model) => this.makeCreateManySchema(model),
79+
args
80+
);
5981
}
6082

6183
validateCreateManyAndReturnArgs(model: GetModels<Schema>, args: unknown) {
6284
return this.validate<
6385
CreateManyAndReturnArgs<Schema, GetModels<Schema>> | undefined
6486
>(
65-
this.makeCreateManyAndReturnSchema(model),
87+
model,
6688
'createManyAndReturn',
89+
undefined,
90+
(model) => this.makeCreateManyAndReturnSchema(model),
6791
args
6892
);
6993
}
7094

7195
validateUpdateArgs(model: GetModels<Schema>, args: unknown) {
7296
return this.validate<UpdateArgs<Schema, GetModels<Schema>>>(
73-
this.makeUpdateSchema(model),
97+
model,
7498
'update',
99+
undefined,
100+
(model) => this.makeUpdateSchema(model),
75101
args
76102
);
77103
}
78104

79105
validateUpdateManyArgs(model: GetModels<Schema>, args: unknown) {
80106
return this.validate<UpdateManyArgs<Schema, GetModels<Schema>>>(
81-
this.makeUpdateManySchema(model),
107+
model,
82108
'updateMany',
109+
undefined,
110+
(model) => this.makeUpdateManySchema(model),
83111
args
84112
);
85113
}
86114

87115
validateUpsertArgs(model: GetModels<Schema>, args: unknown) {
88116
return this.validate<UpsertArgs<Schema, GetModels<Schema>>>(
89-
this.makeUpsertSchema(model),
117+
model,
90118
'upsert',
119+
undefined,
120+
(model) => this.makeUpsertSchema(model),
91121
args
92122
);
93123
}
94124

95125
validateDeleteArgs(model: GetModels<Schema>, args: unknown) {
96126
return this.validate<DeleteArgs<Schema, GetModels<Schema>>>(
97-
this.makeDeleteSchema(model),
127+
model,
98128
'delete',
129+
undefined,
130+
(model) => this.makeDeleteSchema(model),
99131
args
100132
);
101133
}
102134

103135
validateDeleteManyArgs(model: GetModels<Schema>, args: unknown) {
104136
return this.validate<
105137
DeleteManyArgs<Schema, GetModels<Schema>> | undefined
106-
>(this.makeDeleteManySchema(model), 'deleteMany', args);
138+
>(
139+
model,
140+
'deleteMany',
141+
undefined,
142+
(model) => this.makeDeleteManySchema(model),
143+
args
144+
);
107145
}
108146

109147
validateCountArgs(model: GetModels<Schema>, args: unknown) {
110-
return this.validate<CountArgs<Schema, GetModels<Schema>> | undefined>(
111-
this.makeCountSchema(model),
148+
return this.validate<
149+
CountArgs<Schema, GetModels<Schema>> | undefined,
150+
undefined
151+
>(
152+
model,
112153
'count',
154+
undefined,
155+
(model) => this.makeCountSchema(model),
113156
args
114157
);
115158
}
116159

117160
validateAggregateArgs(model: GetModels<Schema>, args: unknown) {
118-
return this.validate<AggregateArgs<Schema, GetModels<Schema>>>(
119-
this.makeAggregateSchema(model),
161+
return this.validate<
162+
AggregateArgs<Schema, GetModels<Schema>>,
163+
undefined
164+
>(
165+
model,
120166
'aggregate',
167+
undefined,
168+
(model) => this.makeAggregateSchema(model),
121169
args
122170
);
123171
}
124172

125173
validateGroupByArgs(model: GetModels<Schema>, args: unknown) {
126-
return this.validate<GroupByArgs<Schema, GetModels<Schema>>>(
127-
this.makeGroupBySchema(model),
174+
return this.validate<GroupByArgs<Schema, GetModels<Schema>>, undefined>(
175+
model,
128176
'groupBy',
177+
undefined,
178+
(model) => this.makeGroupBySchema(model),
129179
args
130180
);
131181
}
132182

133-
private validate<T>(schema: ZodSchema, operation: string, args: unknown) {
183+
private validate<T, Options = undefined>(
184+
model: GetModels<Schema>,
185+
operation: string,
186+
options: Options,
187+
getSchema: GetSchemaFunc<Schema, Options>,
188+
args: unknown
189+
) {
190+
const cacheKey = stableStringify({
191+
model,
192+
operation,
193+
options,
194+
});
195+
let schema = this.schemaCache.get(cacheKey!);
196+
if (!schema) {
197+
schema = getSchema(model, options);
198+
this.schemaCache.set(cacheKey!, schema);
199+
}
134200
const { error } = schema.safeParse(args);
135201
if (error) {
136202
throw new QueryError(`Invalid ${operation} args: ${error.message}`);
@@ -142,12 +208,11 @@ export class InputValidator<Schema extends SchemaDef> {
142208

143209
private makeFindSchema(
144210
model: string,
145-
unique: boolean,
146-
collection: boolean
211+
options: { unique: boolean; collection: boolean }
147212
) {
148213
const fields: Record<string, z.ZodSchema> = {};
149-
const where = this.makeWhereSchema(model, unique);
150-
if (unique) {
214+
const where = this.makeWhereSchema(model, options.unique);
215+
if (options.unique) {
151216
fields['where'] = where;
152217
} else {
153218
fields['where'] = where.optional();
@@ -159,7 +224,7 @@ export class InputValidator<Schema extends SchemaDef> {
159224
fields['distinct'] = this.makeDistinctSchema(model).optional();
160225
fields['cursor'] = this.makeCursorSchema(model).optional();
161226

162-
if (collection) {
227+
if (options.collection) {
163228
fields['skip'] = z.number().int().nonnegative().optional();
164229
fields['take'] = z.number().int().optional();
165230
fields['orderBy'] = this.orArray(
@@ -172,7 +237,7 @@ export class InputValidator<Schema extends SchemaDef> {
172237
result = this.refineForSelectIncludeMutuallyExclusive(result);
173238
result = this.refineForSelectOmitMutuallyExclusive(result);
174239

175-
if (!unique) {
240+
if (!options.unique) {
176241
result = result.optional();
177242
}
178243
return result;

pnpm-lock.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)