Dataset Item Processing Pipeline#
Dataset items flow from API input through normalization, schema validation, and finally into Prisma/PostgreSQL. The pipeline is shared by the Public API and tRPC (internal UI) and is implemented in two layers: the repository (dataset-items.ts) and the validator (DatasetItemValidator).
Entry Points#
| Caller | Entry function | Notes |
|---|---|---|
Public API POST /dataset-items | upsertDatasetItem | normalizeOpts: { sanitizeControlChars: true } always on; validateOpts.normalizeUndefinedToNull is true for creates, false for updates |
| Bulk / CSV upload | createManyDatasetItems | Compiles one validator per dataset, not per item; supports allowPartialSuccess |
| Single UI create | createDatasetItem | Delegates internally to createManyDatasetItems |
The Public API request body is typed by PostDatasetItemsV1Body, where input, expectedOutput, and metadata are all z.any().nullish() — no coercion at the Zod layer; all normalization happens downstream.
Step 1 — Normalization (DatasetItemValidator.normalize)#
DatasetItemValidator is instantiated once per write operation (or once per dataset in bulk) so Ajv schemas are compiled exactly once . The private normalize method handles three concerns:
-
String coercion — If the incoming value is a
string, it is parsed as JSON viaparseJsonPrioritised. This covers tRPC callers that serialize values to JSON strings before sending. If the string is not valid JSON, it is stored as a plain string. The Public API sends already-parsed objects and takes theelsebranch directly. -
Unsafe integer preservation —
parseJsonPrioritiseduseslossless-jsonto detect numbers with 13+ digits or scientific notation. Safe numbers become JSNumber; unsafe integers (e.g.107505301260286111) are preserved as strings to avoid IEEE-754 precision loss . This behavior was introduced in PR #14119. -
Control character sanitization — When
normalizeOpts.sanitizeControlCharsistrue(always set by the Public API),sanitizeJsonValuerecursively strips C0/C1 control characters from all string leaves . PostgreSQLTEXTcolumns reject\u0000;\n,\t, and\rare preserved.
Empty string maps to null; undefined passes through as undefined .
Step 2 — Schema Validation (DatasetItemValidator.validateAndNormalize)#
After normalization, validateAndNormalize:
- Create guard — When
validateOpts.normalizeUndefinedToNullistrue(create operations),nullorundefinedinput is rejected immediately with aPayloadError. - Schema validation — Delegates to
DatasetSchemaValidatorwhich holds AjvValidateFunctioninstances compiled from the dataset'sinputSchema/expectedOutputSchema. Missing schemas are treated as valid . A singleDatasetSchemaValidatorinstance is reused across all items in a batch, giving a 3800×+ speedup versus per-item Ajv compilation . - Prisma value conversion —
null→Prisma.DbNull(explicit DB NULL);undefined→undefined(field skipped in partial updates) .
On failure, the method returns a PayloadError containing field-level inputErrors and expectedOutputErrors arrays .
Step 3 — Prisma Storage#
Validated payloads are written as Prisma.InputJsonValue | Prisma.NullTypes.DbNull | undefined, matching Prisma's JSON column types . The repository supports two write strategies via executeWithDatasetServiceStrategy:
- STATEFUL — Direct
prisma.datasetItem.create/update. - VERSIONED — Temporal table: invalidates the current row (
validTo = newValidFrom) then inserts a new row . Reads usevalid_to IS NULLor point-in-timevalid_from ≤ T < valid_toqueries .
Bulk inserts under VERSIONED mode run in a single transaction: one updateMany to invalidate, then createMany for all new rows .
Key Files#
| File | Role |
|---|---|
packages/shared/src/server/repositories/dataset-items.ts | Public CRUD API; bulk/single create, upsert, delete, read |
packages/shared/src/server/services/DatasetService/DatasetItemValidator.ts | Normalization + schema validation (internal to DatasetService) |
packages/shared/src/server/services/DatasetService/DatasetSchemaValidator.ts | Compiled Ajv schema validators for input/output fields |
packages/shared/src/utils/json.ts | parseJsonPrioritised — lossless integer-safe JSON parser |
web/src/pages/api/public/dataset-items/index.ts | Public API route handler; sets normalizeOpts / validateOpts |
web/src/features/public-api/types/datasets.ts | Zod schemas for Public API request/response shapes |