Skip to content

Writing Lexicons

Lexicons are just JSON files, so you can write them manually. However, there are some tools that can help you write them more easily.

@atcute

@atcute/lex-cli combined with @atcute/lexicon-doc allows you to write Lexicons in TypeScript and generate JSON files from them.

ts
import { array, document, object, record, required, string } from '@atcute/lexicon-doc/builder';

export default document({
	id: 'com.example.bookmark',
	defs: {
		main: record({
			key: 'tid',
			description: 'a saved link to come back to later',
			record: object({
				properties: {
					subject: required(string({ format: 'uri' })),
					createdAt: required(string({ format: 'datetime' })),
					tags: array({ items: string(), description: 'tags for organizing bookmarks' }),
				},
			}),
		}),
	},
});

Matt's Lexicon Format

Matt's Lexicon Format is a human-friendly DSL for ATProto Lexicons.

sh
mlf generate lexicon -i thread.mlf -o lexicons/
ts
/// A forum thread
record thread {
    /// Thread title
    title!: string constrained {
        maxLength: 200,
        minLength: 1,
    },
    /// Thread body
    body!: string constrained {
        maxLength: 10000,
    },
    /// Thread creation timestamp
    createdAt!: Datetime,
};
json
{
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1,
  "id": "com.example.thread",
  "defs": {
    "main": {
      "type": "record",
      "description": "A forum thread",
      "key": "tid",
      "record": {
        "type": "object",
        "required": ["title", "body", "createdAt"],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "description": "Thread title"
          },
          "body": {
            "type": "string",
            "maxLength": 10000,
            "description": "Thread body content"
          },
          "createdAt": {
            "type": "string",
            "format": "datetime",
            "description": "Thread creation timestamp"
          }
        }
      }
    }
  }
}

typelex

typelex is a TypeSpec processor for generating ATProto Lexicons using TypeSpec.

ts
import "@typelex/emitter";

namespace app.bsky.actor.profile {
  @rec("self")
  model Main {
    @maxLength(64)
    @maxGraphemes(64)
    displayName?: string;

    @maxLength(256)
    @maxGraphemes(256)
    description?: string;
  }
}
json
{
  "lexicon": 1,
  "id": "app.bsky.actor.profile",
  "defs": {
    "main": {
      "type": "record",
      "key": "self",
      "record": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "maxLength": 64,
            "maxGraphemes": 64
          },
          "description": {
            "type": "string",
            "maxLength": 256,
            "maxGraphemes": 256
          }
        }
      }
    }
  }
}

prototypey

prototypey is a library for generating ATProto Lexicons using TypeScript.

ts
import { lx, type Infer } from "prototypey";

const lex = lx.lexicon("app.bsky.actor.profile", {
  main: lx.record({
    key: "self",
    record: lx.object({
      displayName: lx.string({ maxLength: 64, maxGraphemes: 64 }),
      description: lx.string({ maxLength: 256, maxGraphemes: 256 }),
    }),
  }),
});

type Profile = Infer<typeof lex>;

const aProfile: Profile = {
  $type: "app.bsky.actor.profile",
  displayName: "Benny Harvey"
}
json
{
  "lexicon": 1,
  "id": "app.bsky.actor.profile",
  "defs": {
	"main": {
	  "type": "record",
	  "key": "self",
	  "record": {
		"type": "object",
		"properties": {
		  "displayName": {
			"type": "string",
			"maxLength": 64,
			"maxGraphemes": 64
		  },
		  "description": {
			"type": "string",
			"maxLength": 256,
			"maxGraphemes": 256
		  }
		}
	  }
	}
  }
}