Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/bluesky-social/atproto/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The @atproto/lexicon package provides TypeScript implementation of the Lexicon schema language for AT Protocol. Lexicon is used to define and validate data schemas for records, XRPC endpoints, and other protocol elements.

Installation

npm install @atproto/lexicon

Core Concepts

Lexicon schemas define:
  • Record types: Structure of user data (posts, profiles, likes, etc.)
  • XRPC procedures: Request/response schemas for API methods
  • XRPC queries: Parameter and response schemas for API queries
  • Data types: Primitives, objects, arrays, unions, and references
Schemas use familiar concepts like types, required/optional fields, string formats, and validation constraints.

Main Classes

Lexicons

The main class for managing and validating against Lexicon schemas.
import { Lexicons } from '@atproto/lexicon'

// Create a lexicon collection
const lexicons = new Lexicons()

// Add schema definitions
lexicons.add({
  lexicon: 1,
  id: 'app.bsky.feed.post',
  defs: {
    main: {
      type: 'record',
      key: 'tid',
      record: {
        type: 'object',
        required: ['text', 'createdAt'],
        properties: {
          text: { type: 'string', maxLength: 300 },
          createdAt: { type: 'string', format: 'datetime' },
          reply: { type: 'ref', ref: '#replyRef' }
        }
      }
    },
    replyRef: {
      type: 'object',
      required: ['root', 'parent'],
      properties: {
        root: { type: 'ref', ref: 'com.atproto.repo.strongRef' },
        parent: { type: 'ref', ref: 'com.atproto.repo.strongRef' }
      }
    }
  }
})

// Validate a record
try {
  lexicons.assertValidRecord('app.bsky.feed.post', {
    $type: 'app.bsky.feed.post',
    text: 'Hello, world!',
    createdAt: new Date().toISOString()
  })
  console.log('Record is valid')
} catch (err) {
  console.error('Validation failed:', err.message)
}
constructor
Lexicons
constructor(docs?: Iterable<LexiconDoc>)
Creates a new Lexicon collection. Optionally initializes with an iterable of schema documents.
docs
Iterable<LexiconDoc>
Initial schema documents to add
Methods:
add
void
add(doc: LexiconDoc): void
Adds a Lexicon schema document to the collection.
doc
LexiconDoc
required
Schema document to add
remove
void
remove(uri: string): void
Removes a schema document by its NSID.
uri
string
required
Schema NSID to remove
get
LexiconDoc | undefined
get(uri: string): LexiconDoc | undefined
Retrieves a schema document by NSID.
getDef
LexUserType | undefined
getDef(uri: string): LexUserType | undefined
Retrieves a specific definition from a schema. Can reference nested definitions using # notation.
const def = lexicons.getDef('app.bsky.feed.post#replyRef')

Record Validation

assertValidRecord
void
assertValidRecord(nsid: string, value: unknown): void
Validates that a value conforms to a record schema. Throws ValidationError on failure.
nsid
string
required
Record type NSID
value
unknown
required
Record data to validate
lexicons.assertValidRecord('app.bsky.feed.post', {
  $type: 'app.bsky.feed.post',
  text: 'Hello',
  createdAt: new Date().toISOString()
})
validateRecord
ValidationResult
validateRecord(nsid: string, value: unknown): ValidationResult
Validates a record and returns a result object instead of throwing.
const result = lexicons.validateRecord('app.bsky.feed.post', record)
if (!result.success) {
  console.error('Validation errors:', result.error)
}

XRPC Validation

assertValidXrpcParams
void
assertValidXrpcParams(nsid: string, params: unknown): void
Validates XRPC query parameters.
lexicons.assertValidXrpcParams('app.bsky.feed.getTimeline', {
  limit: 50,
  cursor: 'abc123'
})
assertValidXrpcInput
void
assertValidXrpcInput(nsid: string, value: unknown): void
Validates XRPC procedure input body.
lexicons.assertValidXrpcInput('com.atproto.repo.createRecord', {
  repo: 'did:plc:...',
  collection: 'app.bsky.feed.post',
  record: { text: 'Hello', createdAt: new Date().toISOString() }
})
assertValidXrpcOutput
void
assertValidXrpcOutput(nsid: string, value: unknown): void
Validates XRPC response body.
assertValidXrpcMessage
void
assertValidXrpcMessage<T = unknown>(nsid: string, value: unknown): void
Validates XRPC subscription message.

Schema Types

Primitive Types

Lexicon supports several primitive types:
// Boolean
{ type: 'boolean', default: false }

// Integer
{ 
  type: 'integer',
  minimum: 1,
  maximum: 100,
  default: 50
}

// String
{
  type: 'string',
  minLength: 1,
  maxLength: 300,
  format: 'datetime' // or 'uri', 'at-uri', 'did', 'handle', etc.
}

// Unknown (any JSON value)
{ type: 'unknown' }
String Formats:
  • datetime - ISO 8601 datetime
  • uri - URI
  • at-uri - AT URI (at://…)
  • did - DID identifier
  • handle - Handle (domain name)
  • at-identifier - DID or handle
  • nsid - Namespaced identifier
  • cid - Content identifier
  • language - BCP-47 language tag
  • tid - Timestamp identifier
  • record-key - Record key

Complex Types

Object

{
  type: 'object',
  required: ['name', 'age'],
  properties: {
    name: { type: 'string', maxLength: 100 },
    age: { type: 'integer', minimum: 0 },
    email: { type: 'string', format: 'email' }
  }
}

Array

{
  type: 'array',
  items: { type: 'string' },
  minLength: 1,
  maxLength: 10
}

Reference

// Reference to another definition
{ type: 'ref', ref: 'com.atproto.repo.strongRef' }

// Reference to definition in same document
{ type: 'ref', ref: '#replyRef' }

Union

{
  type: 'union',
  refs: [
    'app.bsky.embed.images',
    'app.bsky.embed.external',
    'app.bsky.embed.record'
  ]
}

IPLD Types

Bytes

{
  type: 'bytes',
  maxLength: 1000000 // 1MB
}
{
  type: 'cid-link'
}

Blob

{
  type: 'blob',
  accept: ['image/png', 'image/jpeg'],
  maxSize: 1000000 // bytes
}

Blob Utilities

BlobRef

Represents a blob reference in records.
import { BlobRef } from '@atproto/lexicon'

const blob = BlobRef.make(
  'bafyreiabc123...',
  'image/jpeg',
  50000
)

console.log(blob.ref) // CID object
console.log(blob.mimeType) // 'image/jpeg'
console.log(blob.size) // 50000

// Check if value is a blob ref
if (BlobRef.isBlob(value)) {
  console.log('This is a blob reference')
}
BlobRef.make
BlobRef
static make(
  cid: string | CID,
  mimeType: string,
  size: number
): BlobRef
Creates a blob reference.
cid
string | CID
required
Content identifier
mimeType
string
required
MIME type (e.g., ‘image/jpeg’)
size
number
required
Size in bytes
BlobRef.isBlob
boolean
static isBlob(obj: unknown): obj is BlobRef
Type guard to check if a value is a blob reference.

Type Definitions

LexiconDoc

type LexiconDoc = {
  lexicon: 1
  id: string  // NSID
  revision?: number
  description?: string
  defs: Record<string, LexUserType>
}

LexRecord

Type definition for a record schema.
type LexRecord = {
  type: 'record'
  key?: 'tid' | 'literal' | string
  record: LexObject
}

LexXrpcProcedure

type LexXrpcProcedure = {
  type: 'procedure'
  description?: string
  parameters?: LexXrpcParameters
  input?: LexXrpcBody
  output?: LexXrpcBody
  errors?: LexXrpcError[]
}

LexXrpcQuery

type LexXrpcQuery = {
  type: 'query'
  description?: string
  parameters?: LexXrpcParameters
  output?: LexXrpcBody
  errors?: LexXrpcError[]
}

Serialization Utilities

lexToIpld

Converts a value to IPLD format (for CBOR encoding).
import { lexToIpld } from '@atproto/lexicon'

const record = {
  text: 'Hello',
  createdAt: new Date().toISOString(),
  blob: BlobRef.make('bafyreiabc', 'image/jpeg', 1000)
}

const ipldValue = lexToIpld(record)
// Converts BlobRefs and other special types to IPLD representation

ipldToLex

Converts an IPLD value back to Lexicon format.
import { ipldToLex } from '@atproto/lexicon'

const record = ipldToLex(ipldValue)
// Converts IPLD blob refs back to BlobRef instances

Complete Example

import { Lexicons, BlobRef } from '@atproto/lexicon'
import { CID } from 'multiformats/cid'

// Define schemas
const lexicons = new Lexicons()

lexicons.add({
  lexicon: 1,
  id: 'com.example.post',
  defs: {
    main: {
      type: 'record',
      key: 'tid',
      record: {
        type: 'object',
        required: ['text', 'createdAt'],
        properties: {
          text: {
            type: 'string',
            maxLength: 300,
            minLength: 1
          },
          createdAt: {
            type: 'string',
            format: 'datetime'
          },
          images: {
            type: 'array',
            items: { type: 'blob' },
            maxLength: 4
          },
          tags: {
            type: 'array',
            items: { type: 'string', maxLength: 50 },
            maxLength: 10
          },
          lang: {
            type: 'string',
            format: 'language'
          }
        }
      }
    }
  }
})

lexicons.add({
  lexicon: 1,
  id: 'com.example.getPost',
  defs: {
    main: {
      type: 'query',
      parameters: {
        type: 'params',
        required: ['uri'],
        properties: {
          uri: { type: 'string', format: 'at-uri' },
          depth: { type: 'integer', minimum: 0, maximum: 10, default: 1 }
        }
      },
      output: {
        encoding: 'application/json',
        schema: {
          type: 'object',
          required: ['post'],
          properties: {
            post: { type: 'ref', ref: 'com.example.post' }
          }
        }
      }
    }
  }
})

// Validate a record
const post = {
  $type: 'com.example.post',
  text: 'Check out this photo!',
  createdAt: new Date().toISOString(),
  images: [
    BlobRef.make(
      CID.parse('bafyreia...'),
      'image/jpeg',
      50000
    )
  ],
  tags: ['photo', 'nature'],
  lang: 'en'
}

try {
  lexicons.assertValidRecord('com.example.post', post)
  console.log('✓ Record is valid')
} catch (err) {
  console.error('✗ Validation failed:', err.message)
}

// Validate XRPC parameters
const params = {
  uri: 'at://did:plc:abc123/com.example.post/xyz789',
  depth: 2
}

try {
  lexicons.assertValidXrpcParams('com.example.getPost', params)
  console.log('✓ Parameters are valid')
} catch (err) {
  console.error('✗ Parameter validation failed:', err.message)
}

// Validate output
const output = {
  post: post
}

try {
  lexicons.assertValidXrpcOutput('com.example.getPost', output)
  console.log('✓ Output is valid')
} catch (err) {
  console.error('✗ Output validation failed:', err.message)
}

// Get schema definition
const postDef = lexicons.getDef('com.example.post')
if (postDef) {
  console.log('Post schema:', postDef)
}

// Clone lexicons
const clone = new Lexicons(lexicons)
console.log('Cloned lexicons with', Array.from(clone).length, 'schemas')

Additional Resources

Build docs developers (and LLMs) love