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.