Express CSV Logo

Types

Use x.row() to define the types for the schema you pass to CSVImporter. Each field controls:

  • How uploaded values are parsed and validated
  • Labels shown in the importer
  • TypeScript types before rows reach your app

Schema package in a monorepo

In a monorepo:

  • Keep row definitions in a dedicated types or schemas package (for example packages/schemas) that your apps import
  • Use @expresscsv/schemas when you only need schema helpers and do not want the importer code in your dependencies

Basic Usage

import { CSVImporter, x, type Infer } from "@expresscsv/sdk";

const userSchema = x.row({ 
  name: x.string().label("Full Name"), 
  email: x.string().email().label("Email Address"), 
  age: x.number().label("Age").min(18), 
  startDate: x.date().label("Start Date"), 
  active: x.boolean().label("Active").optional(), 
}); 

type UserRow = Infer<typeof userSchema>;

const importer = new CSVImporter({
  schema: userSchema, 
  getSessionToken: async () => fetchSessionToken(),
  importNamespace: "user-import",
});

importer.open({
  onData: async (chunk, next) => {
    const records: UserRow[] = chunk.records;
    await saveImportedUsers(records);
    next();
  },
});

Each key becomes a column the user maps to. The field type determines how values are parsed and validated before your delivery callback runs.

Shared Field Behavior

These methods work the same way across field types:

  • .optional() lets the importer accept an empty cell for that field. Empty optional values are delivered as null, and the row object still includes the schema key.
  • .default(value) is available after .optional(). It is used when the imported value is empty, including missing values, null, an empty string, or whitespace. When a default is used, it is returned directly instead of running the empty value through the rest of the field validators.
  • .columnNameAliases([...]) adds extra header names for column matching. Aliases are used alongside the field key and label by deterministic matching, managed AI matching, and custom matching.
const schema = x.row({
  active: x.boolean().optional(),
  role: x.select(roleOptions).optional().default("viewer"),
});

// Empty "active" cells become:
// { active: null, role: "viewer" }

Field Types

Custom Validation

Use Refine for custom validators with .refine(), .refineBatch(), and suggested fixes.

Dynamic Schemas

You can build the schema conditionally before constructing CSVImporter:

function buildSchema(includeVatId: boolean) {
  return x.row({
    companyName: x.string(),
    email: x.string().email(),
    ...(includeVatId ? { vatId: x.string() } : {}), 
  });
}

const schema = buildSchema(true);

const importer = new CSVImporter({
  schema,
  getSessionToken: async () => fetchSessionToken(),
  importNamespace: "company-import",
});

TypeScript Inference

Use Infer<typeof schema> to extract the validated row type from your schema:

import { x, type Infer } from "@expresscsv/sdk";

const schema = x.row({
  name: x.string(),
  age: x.number(),
  active: x.boolean().optional(),
});

type Row = Infer<typeof schema>; 
// { name: string; age: number; active?: boolean }