Getting started

atproto_codegen is a CLI. You point it at a directory of lexicon JSON files and it writes Gleam modules with types, JSON codecs, and (optionally) a typed XRPC client into your src/.

1. Vendor your lexicons

Codegen reads <lexicons_dir>/**/*.json. Vendor every lexicon you reference, including external ones (e.g. com.atproto.repo.strongRef), into that tree; refs that point at defs codegen never sees are reported as warnings and would not compile.

lexicons/
  com/example/myapp/shelf/entry.json
  com/atproto/repo/strongRef.json

2. Run codegen

Add the package as a dev dependency and run its main module:

gleam add --dev atproto_codegen
gleam run -m atproto_codegen -- ./lexicons ./src/my_app/gen my_app/gen com.example.myapp.,com.atproto.

The arguments: lexicons dir, output dir, the module prefix generated code imports itself under (it must match where the output dir sits in src/), and comma-separated NSID prefixes stripped when deriving module paths, so com.example.myapp.shelf.entry emits src/my_app/gen/shelf/entry.gleam.

3. Use the generated modules

Each lexicon with a record or object def becomes a module with a type per def, an encoder to Json, and a gleam/dynamic/decode decoder. Unions generate a named type with one variant per ref plus an Other passthrough that round-trips unknown $types. Unsupported defs are reported and skipped, never emitted wrong.

4. Optional: generate a typed XRPC client

Pass a fifth argument to also emit one module of typed functions for every query and procedure:

gleam run -m atproto_codegen -- ./lexicons ./src/my_app/gen my_app/gen com.example.myapp. client

This writes src/my_app/gen/client.gleam with typed params, input and output per method, and a per-method error union (one variant per lexicon-declared error plus transport and unknown catch-alls). The generated client is built on atproto_client’s atproto/xrpc primitives, so add that as a regular dependency (atproto_client >= 0.2.0). It also constructs xrpc error variants through atproto_core/xrpc, so add atproto_core as a direct dependency too. Generated blob fields use atproto/blob from atproto_client.

Regenerating

Output is deterministic: the same lexicon corpus always produces byte-identical files, so generated code diffs cleanly in review. Rerun the command whenever the lexicons change; every generated file carries a banner and should not be edited by hand.

Search Document