@flatfile/implementation-utils-zod-adapter
v0.0.9
Published
Provides a mechanism for passing Zod schemas to a bulkRecordHook for transformation and validation as well as general purpose utilities for working with Zod schemas.
Readme
@flatfile/implementation-utils-zod-adapter
Provides a mechanism for passing Zod schemas to a bulkRecordHook for transformation and validation as well as general purpose utilities for working with Zod schemas.
Why Zod?
Zod provides a composable, type-safe, runtime schema system that maps cleanly onto TypeScript types. We can use Zod to:
- Coerce and transform raw Flatfile record data into strongly-typed objects.
- Validate business rules (length, pattern, enum membership, cross-field constraints).
- Report errors/warnings back to Flatfile so end-users receive immediate feedback.
- Keep compile-time types (
z.infer<Schema>) in sync with runtime validation logic.
Features
The zodRecordHookAdapter
Location: src/utils/zod/adapter.ts
export const zodRecordHookAdapter = (
transformSchema: z.ZodObject<…>,
validationSchema: z.ZodObject,
): ((records: FlatfileRecord[]) => FlatfileRecord[]) => { … }Responsibilities
| Stage | Responsibility |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Transform | • partialSafeParse runs against transformSchema.• Valid fields are immediately written back to the Flatfile record via record.set(key, value).• Invalid fields are collected but do not halt processing. |
| Validate | • A second pass executes validationSchema.safeParse(record.obj).• All issues are converted into record.addError or record.addWarning calls. |
Issue Handling
adapter.ts inspects every ZodIssue:
issue.code === "custom" && issue.params?.isWarning→addWarningissue.code === "custom" && issue.params?.isSilent→ skipped (useful for silent refinements)- otherwise →
addError
This allows blueprints to downgrade non-blocking rules to warnings without losing the rich error metadata Zod provides.
Partial-success Strategy
partialSafeParse categorises results as:
full– every field validpartial– some fields valid; invalid ones returned separatelynone– fatal object-level issues
For full | partial, valid values are persisted so downstream hooks always operate on the cleanest possible record shape.
Helper: partial-safe-parse.ts
partialSafeParse is a small wrapper around Zod’s safeParse that lets us retrieve only the good fields when a record is partially invalid. This enables the transform-then-validate pattern described above.
Key utilities exported:
omit/pick– thin Object helpers used internally.safeParseResult– ergonomic result-shaping helper.partialSafeParse– the workhorse that returns{ successType, validData, invalidData }.
Usage
Blueprint Conventions
Every sheet defines two schemas:
| File | Variable | Purpose |
| ------------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| *.sheet.ts | XYZSheetTransformSchema | Lightweight transformations (trimming, coercion, defaulting). Must never reject on its own unless absolutely necessary. |
| *.sheet.ts | XYZSheetValidationSchema | Strict field-level & cross-field rules. Rejects invalid data and surfaces meaningful messages. |
These schemas are then passed into the adapter inside the sheet’s recordHook:
space.sheet("Prescriber Information", {
…,
recordHook: zodRecordHookAdapter(
prescriberInformationSheetTransformSchema,
prescriberInformationSheetValidationSchema,
),
});Example snippet (Prescriber)
const prescriberInformationSheetValidationSchema = z.object({
[NPI_NUMBER]: z.string().length(10),
[START_DATE]: z.string().pipe(dateTransform),
[END_DATE]: z.string().pipe(dateTransform).nullable(),
[DEA_NUMBER]: recommendedSchema(
z
.string()
.length(9)
.regex(/^[A-Z]{2}\d{7}$/i)
.nullable(),
"DEA number is recommended"
),
});Notes:
recommendedSchemais a thin wrapper that attaches{ isWarning: true }so the adapter logs warnings instead of errors.dateTransformis applied in validation to guarantee canonicalDateobjects for downstream jobs.
Adding a New Sheet / Schema
- Create
XYZSheetTransformSchema&XYZSheetValidationSchemain the sheet file. - Pipe any common transforms (trim, upper-case, etc.) before validation.
- Prefer literal enums (
z.enum([...])) over regex where possible for maintainability. - Pass both schemas into
zodRecordHookAdapter.
Future Enhancements
- Implement checks that rely on additional context/data from other sheets by dynamically constructing schemas.
- Implement checks that rely on metadata and additional FlatfileRecord context via
checkon the outermost object schema.
