| title | Lexicons |
|---|---|
| description | How to use generated lexicon descriptors, records, and raw lexicon documents in consumer code. |
Generated lexicon calls give you typed parameters, typed request bodies, and typed responses for known lexicons. Use the generated package that owns the namespace you are calling.
import 'package:poptart/poptart.dart';
import 'package:bluesky_poptart/app/bsky/actor/search_actors_typeahead.dart'
as search;
Future<void> main() async {
final client = PoptartClient.anonymous();
final response = await client.call(
search.appBskyActorSearchActorsTypeahead,
parameters: const search.ActorSearchActorsTypeaheadInput(
q: 'alice',
limit: 10,
),
);
for (final actor in response.data.actors) {
print('${actor.handle}: ${actor.displayName}');
}
}The generated barrels follow the lexicon ID:
app.bsky.actor.searchActorsTypeahead becomes
package:bluesky_poptart/app/bsky/actor/search_actors_typeahead.dart.
Use poptart_lex for com.atproto.*, bluesky_poptart for app.bsky.* and
chat.bsky.*, ozone_poptart for tools.ozone.*, sprk_poptart for
so.sprk.*, and margin_poptart for at.margin.*.
Every generated XRPC method has two public handles:
await client.call(search.appBskyActorSearchActorsTypeahead);
await client.call(search.methodDescriptor);The generated method value reads well from shallow directory aggregates, like
package:bluesky_poptart/app/bsky/actor.dart as actor. These aggregate files
only include methods whose lexicon leaves live directly in that directory;
broader parent directories do not recursively pull in nested lexicons. The
descriptor is the direct primitive underneath it, and it can be cleaner when a
leaf import alias already names the method, like search.methodDescriptor.
Use whichever makes the call site clearer. Both forms preserve the same type inference for parameters, input, and output.
Records are normal Dart objects with toJson() and fromJson() methods, so
you can validate and serialize them before sending a repo write.
import 'package:poptart/poptart.dart';
import 'package:bluesky_poptart/app/bsky/feed/post.dart' as feed_post;
import 'package:poptart_lex/com/atproto/repo/create_record.dart'
as create_record;
Future<void> publishPost(Session session, String text) async {
final client = PoptartClient.fromSession(session);
final post = feed_post.FeedPostRecord(
text: text,
createdAt: DateTime.now().toUtc(),
);
await client.call(
create_record.comAtprotoRepoCreateRecord,
input: create_record.RepoCreateRecordInput(
repo: session.did,
collection: 'app.bsky.feed.post',
record: post.toJson(),
),
);
}Generated records preserve unknown fields where the schema supports them, which helps consumers survive lexicon changes without immediately losing data.
Use LexiconDoc when your app needs to inspect schemas, build tooling, or load
lexicons dynamically.
import 'package:poptart_lex/docs.dart' as docs;
import 'package:poptart_lexicon/poptart_lexicon.dart';
void main() {
final doc = LexiconDoc.fromJson(docs.comAtprotoRepoCreateRecord);
print(doc.id);
print(doc.description);
print(doc.defs.keys);
}Use generated methods or descriptors for network calls. Use LexiconDoc when
the lexicon itself is your data.
If a lexicon is not generated yet, you can still call it with raw XRPC. Add generated support later when you want typed inputs and outputs.
final client = PoptartClient.fromSession(session);
final response = await client.get<Map<String, dynamic>>(
NSID.parse('com.example.service.getThing'),
parameters: {'id': '123'},
);
print(response.data);Raw calls are useful for experiments and private services. Generated methods and descriptors are better once the method is stable enough to share with other consumers.