Capítulo 38 de 61

Chapter 38: Useful Codecs Reference

Core Idea

Zod's docs publish a library of common codec recipes (string↔number, ISO datetime↔Date, bytes↔base64/hex, JSON parsing, etc.) as copy-paste-and-customize snippets rather than first-class APIs, since the "right" behavior (e.g. rounding, error format) is often project-specific.

Key Concepts

  • Not built-in on purpose: these are reference implementations to copy into your own codebase and adapt, not exported functions — Zod intentionally avoids baking in opinions about precision, error format, etc.
  • Numeric conversions: stringToNumber (via parseFloat), stringToInt (via parseInt), stringToBigInt/numberToBigInt (via BigInt()).
  • Date conversions: isoDatetimeToDate, epochSecondsToDate, epochMillisToDate — all wrap new Date(...) on decode and a matching serialization on encode.
  • Binary/text conversions: utf8ToBytes/bytesToUtf8 (via TextEncoder/TextDecoder), base64ToBytes, base64urlToBytes, hexToBytes (via Zod's own z.util.* byte-array helpers).
  • URL & URI: stringToURL/stringToHttpURL (wrap the native URL constructor), uriComponent (wraps encodeURIComponent/decodeURIComponent).
  • Generic jsonCodec(schema): a factory (not a fixed codec) that parses a JSON string, validates the result against a caller-supplied schema, and reports malformed JSON as a proper invalid_format issue instead of an uncaught SyntaxError.

Code Examples

// representative pattern: wrap a native conversion in decode/encode
const stringToInt = z.codec(z.string().regex(z.regexes.integer), z.int(), {
  decode: (str) => Number.parseInt(str, 10),
  encode: (num) => num.toString(),
});

// the generic JSON codec factory — reports parse errors as ZodError issues
const jsonCodec = <T extends z.core.$ZodType>(schema: T) =>
  z.codec(z.string(), schema, {
    decode: (jsonString, ctx) => {
      try {
        return JSON.parse(jsonString);
      } catch (err) {
        ctx.issues.push({ code: "invalid_format", format: "json", input: jsonString, message: err.message });
        return z.NEVER;
      }
    },
    encode: (value) => JSON.stringify(value),
  });

const jsonToUser = jsonCodec(z.object({ name: z.string(), age: z.number() }));
jsonToUser.decode('{"name":"Alice","age":30}'); // => { name: "Alice", age: 30 }
  • What it demonstrates: the common shape of these recipes — a native JS conversion function wrapped as decode, its inverse wrapped as encode — and the ctx.issues/z.NEVER pattern for turning a thrown error into a proper Zod issue.

Reference Tables

CodecInput schemaOutput typeCore mechanism
stringToNumberz.string() (numeric regex)numberparseFloat()
stringToIntz.string() (integer regex)number (int)parseInt(str, 10)
stringToBigIntz.string()bigintBigInt(str)
numberToBigIntz.int()bigintBigInt(num)
isoDatetimeToDatez.iso.datetime()Datenew Date(isoString)
epochSecondsToDatez.int().min(0)Datenew Date(seconds * 1000)
epochMillisToDatez.int().min(0)Datenew Date(millis)
utf8ToBytes / bytesToUtf8string / Uint8Arraythe otherTextEncoder/TextDecoder
base64ToBytes, base64urlToBytes, hexToBytesformat-specific string schemaUint8Arrayz.util.* byte-array helpers
stringToURL / stringToHttpURLz.url() / z.httpUrl()URLnative URL constructor
uriComponentz.string()z.string()encodeURIComponent/decodeURIComponent
jsonCodec(schema)z.string()whatever schema validatesJSON.parse/JSON.stringify, with parse errors reported as invalid_format issues

Key Takeaways

  1. Before hand-writing a codec for a common conversion (string→number, ISO date→Date, bytes↔base64), check this list — the correctness edge cases are already worked out.
  2. These are templates, not APIs — copy them in and adjust validation/rounding/error behavior to your project's needs.
  3. jsonCodec() is the pattern to follow for wrapping any operation that can throw (JSON.parse) into a proper ctx.issues/z.NEVER failure instead of an uncaught exception.

Connects To

  • Codecs — Basics: the z.codec() API these recipes all build on.
  • Transforms: ctx.issues/z.NEVER is the same failure-reporting pattern used inside z.transform().