Atex.XRPC.Router
(atex v0.9.1)
View Source
Routing utilities for building ATProto XRPC server endpoints.
Provides the query/3 and procedure/3 macros that expand to
Plug.Router.get/3 and Plug.Router.post/3 respectively, with built-in
handling for:
- NSID-prefixed route paths (
/xrpc/<nsid>) - Service auth validation via
Atex.ServiceAuth - Query param and body validation for lexicon modules generated with
Atex.Lexicon.deflexicon/1.
Usage
defmodule MyAPI do
use Plug.Router
use Atex.XRPC.Router
plug :match
plug :dispatch
# Matches GET /xrpc/com.example.getProfile
query "com.example.getProfile" do
send_resp(conn, 200, "ok")
end
# Matches POST /xrpc/com.example.createPost, enforces auth
procedure Com.Example.CreatePost, require_auth: true do
# conn.assigns[:params] and conn.assigns[:body] are populated
# when the lexicon module defines Params/Input submodules
send_resp(conn, 200, "created")
end
endAuthentication
Authentication uses Atex.ServiceAuth.validate_conn/2. The audience (aud)
is read from conn.private[:xrpc_aud], which is populated automatically by
Atex.XRPC.Router.AudPlug that reads :service_did from app config. To
disable automatic plug injection:
use Atex.XRPC.Router, plug_aud: falseWhen require_auth: true is passed to a route macro, a missing or invalid
token halts with a 401 response. Otherwise auth is attempted softly -
on success the decoded JWT is placed at conn.assigns[:current_jwt], on
failure the conn is left untouched.
Validation
When a lexicon module atom is passed, the macro checks at compile time whether
<Module>.Params and/or <Module>.Input exist. If they do, their
from_json/1 is called at request time:
- Valid params →
conn.assigns[:params] - Valid body →
conn.assigns[:body] - Either failing → halts with a
400response
Summary
Functions
Defines a POST route for an XRPC procedure.
The first argument is either:
- A plain string NSID (e.g.
"com.example.createPost") - validated at compile time. - A lexicon module atom (e.g.
Com.Example.CreatePost) - the NSID is fetched frommodule.id()at compile time.
Options
:require_auth- whentrue, requests without a valid service auth token are rejected with a401. Defaults tofalse.
Assigns
:current_jwt- the decodedJOSE.JWTstruct, set on successful auth.:params- validated params struct, set when the lexicon module defines aParamssubmodule.:body- validated input struct, set when the lexicon module defines anInputsubmodule.
Non-JSON payloads
If a lexicon procedure defines an input with an encoding without an object
schema, this will simply validate the incoming Content-Type header against the
requested encoding. Nothing happens on success, you will need to read conn's
body as usual and do extra validation yourself, as clients may lie about their content.
Wildcards are handled correctly as per the atproto documentation.
Examples
procedure "com.example.createPost", require_auth: true do
send_resp(conn, 200, "created")
end
procedure Com.Example.CreatePost, require_auth: true do
# conn.assigns[:body] contains the validated Input struct
send_resp(conn, 200, "created")
end
Defines a GET route for an XRPC query.
The first argument is either:
- A plain string NSID (e.g.
"com.example.getProfile") - validated at compile time. - A lexicon module atom (e.g.
Com.Example.GetProfile) - the NSID is fetched frommodule.id()at compile time.
Options
:require_auth- whentrue, requests without a valid service auth token are rejected with a401. Defaults tofalse.
Assigns
:current_jwt- the decodedJOSE.JWTstruct, set on successful auth.:params- validated params struct, set when the lexicon module defines aParamssubmodule.
Examples
query "com.example.getTimeline", require_auth: true do
send_resp(conn, 200, "ok")
end
query Com.Example.GetTimeline do
send_resp(conn, 200, "ok")
end