Current section
Files
Jump to
Current section
Files
atproto_client
README.md
README.md
# atproto_client
> **Alpha, pre-release.** Pre-1.0, evolving alongside
> [at-record](https://tangled.org/@mokkenstorm.dev/at-record); expect breaking
> changes between 0.x releases.
A small, transport-agnostic [atproto](https://atproto.com/) client for Gleam.
No app or lexicon knowledge: XRPC, identity resolution, OAuth discovery,
blobs, and generic repo CRUD. Dual-target (erlang + javascript); browser-only
consumers want the sibling [`atproto_browser`](https://hex.pm/packages/atproto_browser) instead,
which owns the `atproto/browser/*` namespace within the shared `atproto/`
prefix.
## Installation
```sh
gleam add atproto_client
```
## Usage
The client carries a `send` function instead of a hardcoded HTTP backend, so
you choose the transport per target. On Erlang, `gleam_httpc`'s `send_bits`
maps onto the client's `TransportError` channel:
```gleam
import atproto/xrpc
import atproto_core/xrpc as core_xrpc
import gleam/httpc
import gleam/result
import gleam/string
fn client() -> xrpc.Client {
xrpc.Client(send: fn(req) {
httpc.send_bits(req)
|> result.map_error(fn(error) {
case error {
httpc.ResponseTimeout -> core_xrpc.Timeout
httpc.FailedToConnect(_, _) ->
core_xrpc.ConnectionFailed(string.inspect(error))
_ -> core_xrpc.Other(string.inspect(error))
}
})
})
}
```
Resolve an identity, then read its records:
```gleam
import atproto/identity
import atproto/repo
import gleam/dynamic/decode
let assert Ok(pds) =
identity.resolve_pds(client(), identity.default_resolver, "someone.bsky.social")
let assert Ok(rows) =
repo.list_records(
client(),
pds,
access_token,
did,
"app.bsky.feed.post",
decode.dynamic,
)
```
For the full walkthrough, including both authentication routes, see the
[getting started guide](https://hexdocs.pm/atproto_client/getting-started.html).
## Architecture
### Core
| Module | What it does |
| ----------------------- | -------------------------------------------------------------------------------------- |
| `atproto/xrpc` | `Client`, `TransportError`, `XrpcError`, `get`/`post_json`, `parse` |
| `atproto/identity` | `resolve_pds` (handle/DID -> PDS) via Slingshot `resolveMiniDoc` |
| `atproto/auth` | `create_session`/`refresh_session` (`com.atproto.server.createSession`) |
| `atproto/repo` | `list_records`/`create_record`/`get_record`/`put_record`/`delete_record`/`upload_blob` |
| `atproto/blob` | `Blob` type, its codec, and `public_url` for refs returned by `repo.upload_blob` |
| `atproto/uri` | at-uri helpers (`rkey`) |
| `atproto/constellation` | `get_backlinks` against the microcosm backlink index |
### OAuth
The full atproto OAuth client flow for a confidential client, one module per
step:
| Module | What it does |
| ------------------------ | ------------------------------------------------------------------------------ |
| `atproto/oauth/metadata` | Discover the authorization server and its endpoints from a PDS |
| `atproto/oauth/flow` | `start`: PAR + PKCE + DPoP, returns the authorization URL and a pending `Flow` |
| `atproto/oauth/tokens` | `exchange_code`/`refresh`/`revoke` against the token endpoint |
| `atproto/oauth/resource` | Wraps a `Client` so every request carries the DPoP-bound access token |
| `atproto/oauth/runner` | Interprets the sans-io effect kernel against a synchronous `Client` |
The modules above are sync wrappers over the sans-io effect kernel in
[`atproto_core`](https://hex.pm/packages/atproto_core). Its
`TransportError`/`XrpcError` types are aliased through `atproto/xrpc`;
constructing the variants needs `atproto_core/xrpc`.
## Development
```sh
gleam test
```