fatline

Introduction

Schema-aware, high-performance JSON serialization for TypeScript

fatline is a schema-aware, high-performance JSON serialization library for TypeScript. Named after the FTL communication devices in Dan Simmons' Hyperion Cantos, it transmits your data structures across the wire—intact, minimal, and fast.

The Problem

When you serialize a Date or Set with JSON.stringify(), you lose type information:

JSON.stringify({ createdAt: new Date(), roles: new Set(['admin']) })
// '{"createdAt":"2024-01-01T00:00:00.000Z","roles":{}}'

SuperJSON fixes this by embedding type metadata in every payload:

{
  "json": { "createdAt": "2024-01-01T00:00:00.000Z", "roles": ["admin"] },
  "meta": {
    "values": {
      "createdAt": ["Date"],
      "roles": ["set"]
    }
  }
}

But if you're using tRPC, oRPC, or any typed RPC framework, both sides already know the schema. Why discover types at runtime? Why send type metadata over the wire?

The fatline Approach

// SuperJSON: 18.2 KB, 275ms round-trip
{
  "json": {
    "users": [{ "id": "1", "createdAt": "2024-01-01T00:00:00.000Z", "roles": ["admin"] }]
  },
  "meta": { "values": { "users.0.createdAt": ["Date"], "users.0.roles": ["set"] } }
}

// fatline: 12.4 KB, 83ms round-trip
{
  "users": [{ "id": "1", "createdAt": 1704067200000, "roles": ["admin"] }]
}
// Decoder already knows the types from your schema. Zero metadata.

Design Principles

Zero Config Happy Path

import { serialize, deserialize } from 'fatline'

const data = {
  user: { createdAt: new Date(), roles: new Set(['admin']) }
}

const json = serialize(data)
const back = deserialize(json)

// back.user.createdAt instanceof Date ✓
// back.user.roles instanceof Set ✓

No setup. No registration. It just works.

Types Flow Through

import { createCodec } from 'fatline'
import { z } from 'zod'

const UserCodec = createCodec(z.object({
  id: z.string(),
  createdAt: z.date(),
  tags: z.set(z.string()),
}))

const user = UserCodec.decode(json)
//    ^? { id: string, createdAt: Date, tags: Set<string> }

No manual type assertions. The codec knows.

Errors That Help

serialize(data)
// FatlineError: Cannot serialize value at `user.settings.theme`
//
//   Found: [Function: getTheme]
//
//   Functions cannot be serialized. You can:
//     1. Exclude it: serialize(data, { exclude: ['user.settings.theme'] })
//     2. Transform it first
//     3. Register a custom type handler
//
//   Docs: https://fatline.dev/errors/unserializable

Progressive Disclosure

// Level 1: Just works
import { serialize, deserialize } from 'fatline'

// Level 2: Schema-aware (faster, type-safe)
import { createCodec } from 'fatline'

// Level 3: Framework adapters
import { transformer } from 'fatline/trpc'
import { serializer } from 'fatline/orpc'

// Level 4: Advanced
import { registerType, configure } from 'fatline'

Next Steps

On this page