Getting started
This page takes a Go module from nothing to a call that reads a mailbox. A Rust
or TypeScript project follows the same steps with a different -lang; see
Rust and TypeScript for what differs.
Installing jmapc
The client is generated with go generate, so record jmapc as a tool of the
module that uses it:
go get -tool github.com/linyows/jmapc/cmd/jmapcThat pins a version in go.mod, and go tool jmapc runs it. Everyone who
builds the project, and CI, then generates with the same version, which matters
for a tool whose output is committed.
To put it on your PATH instead:
go install github.com/linyows/jmapc/cmd/jmapc@latestA Rust or TypeScript project, having no Go toolchain to run go tool with,
takes a binary from the releases .
Writing a request
A request lives in a file of its own under requests/, and the file name is
the name of the function to generate.
{
"_doc": "ListInboxEmails returns the newest emails in one mailbox.",
"methodCalls": [
["Email/query", {
"_comment": "Find the ids of the matching emails.",
"filter": {"inMailbox": "{{mailboxId}}"},
"sort": [{"property": "receivedAt", "isAscending": false}],
"limit": "{{limit}}"
}, "search"],
["Email/get", {
"_comment": "Fetch the messages from their ids.",
"#ids": {"resultOf": "search", "name": "Email/query", "path": "/ids"},
"properties": ["id", "subject", "from", "receivedAt"]
}, "fetch"]
],
"_returns": "fetch"
}(requests/ListInboxEmails.jmap.json)
Everything without an underscore is the JMAP request RFC 8620 defines. The
{{mailboxId}} and {{limit}} are left for the caller to fill in, _returns
makes the Email/get response the function’s result, and accountId is left
out, so the generated function takes it from the session.
Writing a request describes each of these.
Generating the client
Put the directive beside go.mod:
package mail
//go:generate go tool jmapc generateThen generate:
go generate ./...jmapc checks the request against the specification first, and a request that is wrong about JMAP stops here, with a suggestion where a name is misspelled:
requests/ListInboxEmails.jmap.json: methodCalls[1].arguments.properties[1]: Email has no property "subjct"
did you mean "subject"?A request that checks out becomes client/listinboxemails_gen.go, in a package
named client. The directories are the defaults; a jmapc.json changes them,
as The jmapc command describes. jmapc run sends the
request to a server before any code calls it; see
Sending a request.
Calling it
c := jmapc.New(jmapc.WellKnownURL("example.com"), jmapc.WithBearerToken(token))
res, err := client.ListInboxEmails(ctx, c, client.ListInboxEmailsParams{
MailboxID: inbox,
Limit: 25,
})
if err != nil {
return err
}
for _, email := range res.List {
fmt.Println(email.ReceivedAt, email.From[0].Email, *email.Subject)
}jmapc.New takes the session URL, which WellKnownURL finds from the host
name, and the options that authenticate and configure the client; see
Configuring the client.
res.List is a []client.ListInboxEmailsFetchEmail, holding the four
properties the request asked for and nothing else. Add a property to the
request and generate again, and the struct gains a field. The names of the
generated types come from the file name and the call ids, as
Generated names describes.
err is not only a failed HTTP request. JMAP also reports a failure in a
response that returns 200, and the generated function returns that as an error
too; see Errors.
Keeping the client up to date
The generated files are committed, so they can fall behind a request that was
changed without generating again. generate -check writes nothing and fails
where the files on disk differ from what the requests generate now, which is
what a CI step runs:
- run: go tool jmapc generate -checkWhere to go next
The pages under Requests describe what a request file may hold and what jmapc
checks. The pages under Generated code describe what the generated client does
at run time: errors, push, paging, blobs, the client’s options, and testing
your own code against a JMAP server.
example/requests
holds twenty-five requests, over mail, contacts, calendars, sharing and
filtering.