lemmy-api
2025-06-22
Most recently generated bindings to the Lemmy API
LEMMY-API
This repository implements bindings to the Lemmy API for Common Lisp.
Lemmy API interfaces, API endpoints, enums, and types are defined according to the openapi specification.
Build Status
Implemented Versions
- 0.18.3
- 0.19.5
- 0.19.6
- 1.0.0-alpha.0
- 1.0.0-media-fixes.1
Information
The main project page is available at https://sr.ht/~yana/cl-lemmy-api/. Bugs may be at https://todo.sr.ht/~yana/cl-lemmy-api, or reported by emailing ~yana/cl-lemmy-api@todo.sr.ht with the issue title as the subject and issue description as the body of the email. The mailing list is where patches may be submitted, as well as for general discussion. The mailing list archives can be browsed at https://lists.sr.ht/~yana/cl-lemmy-api/.
There is an example of how this API may be used in the form of a trivial client implemented using CLOG. This client is trivial and is to be considered a toy example.
Overview
This system defines a set of bindings to the Lemmy API in the form of objects that are sent/recieved and API endpoints they are sent/recieved from. In addition any operations defined by the specification are generated as functions. This system handles all serialization when sending/recieving objects from the API.
The interface objects sent and recieved – as well as the API endpoints and any
relevant operations – are defined in a definitions.lisp
file for the specific
version of the API in use. Endpoints and operations define the specific HTTP
methods and the associated interface objects that may be sent/recieved.
NB: not all API versions implement the same operations, endpoints, interfaces,
or types. If you are uncertain what is implemented please consult the
definitions.lisp
file for the relevant API version, or the official Lemmy API
documentation itself
Generating API Bindings
The API bindings are generated directly from the openapi specification. This allows new versions to be supported with little to no changes in the codebase.
To generate these bindings load the system #:LEMMY-API/GENERATOR
and call the
function READ-OPENAPI-JSON-AND-DUMP-DEFINITIONS
with the openapi specification
file in question. This will create a directory in the versions
directory with
the name being the version string contained in the openapi specification
file. The definition.lisp
and package.lisp
files for this version will be
placed in this subdirectory.
NB: the generation process does not create an associated asdf system.
Quickstart
Start by loading the ASDF system :LEMMY-API
. This will load the most recent
version of the API bindings (v0.19.5 at time of writing).The API can now be
interacted with by using the generic function CALL-API-ENDPOINT
with an
endpoint and an instance of a lemmy interface class, such as GET-POSTS
. The
instances of lemmy interface objects can be validated with the function
VALIDATE-LEMMY-INTERFACE
.
As an example of this, lets take a look at what logging in might look like:
CL-USER> (ql:quickload :lemmy-api)
To load "lemmy-api":
Load 1 ASDF system:
lemmy-api
; Loading "lemmy-api"
..................................................
[package lemmy-api/0.19.5]........................
..................................................
[package lemmy-api].
(:LEMMY-API)
CL-USER> (lemmy-api:call-api-endpoint 'lemmy-api:/user/login
(make-instance 'lemmy-api:login
:username-or-email
"USERNAME"
:password
"PASSWORD")
:server "https://SERVER.com"
:validate-inbound t)
WARNING: Received HTTP response code 200. Expected one of the following:
400, 201
#<LEMMY-INTERFACE "LoginResponse" {1005020BE3}>
200 (8 bits, #xC8, #o310, #b11001000)
CL-USER> (setf lemmy-api:*jwt* (lemmy-api:jwt *))
"long-token"
CL-USER> (lemmy-api:call-api-endpoint 'lemmy-api:/user/list_logins
nil
:server "https://SERVER.com")
(#<LEMMY-INTERFACE "LoginToken" {1005829293}>
#<LEMMY-INTERFACE "LoginToken" {1005829333}>
#<LEMMY-INTERFACE "LoginToken" {10058293D3}>
#<LEMMY-INTERFACE "LoginToken" {1005829473}>
#<LEMMY-INTERFACE "LoginToken" {1005829513}>
#<LEMMY-INTERFACE "LoginToken" {10058295B3}>)
200 (8 bits, #xC8, #o310, #b11001000)
Most API endpoints return a lemmy interface object, however this one (/user/list_logins) returns an array of lemmy interface objects.
In addition to CALL-API-ENDPOINT
there are individual functions for getting,
putting, or posting to an endpoint. These do not take an object but instead take
a set of keys - the function will autoconstruct (with dynamic extent) the
appropriate object and send it. These functions are named METHOD@ENDPOINT
,
e.g. GET@/SITE
or PUT@/POST/SAVE
. These functions are not implemented for
version 0.18.3.
Since version 0.19.5 the way the API is interacted with has changed. Prior to
this API calls were made with the function LEMMY-API-OPERATE
. This is
considered deprecated and is not implemented for version 0.19.5 or later, but
the generic function is still present.
Quirks
As of version 0.18.3: Some slots are specified as required but are not given by
the server. These should be specified when calling VALIDATE-LEMMY-INTERFACE
,
or added to the dynamic variable *SLOTS-TO-IGNORE*
. A non-comprehensive list
is given at the end of this document. If you become aware of such errors, please
submit a patch updating this list.
When parsing a lemmy interface object without being given an explicit type, a
object of type EMPTY-LEMMY-INTERFACE
will be instantiated instead, with its
slot UNKNOWN-SLOTS
propogated with whatever the JSON parsed out to. An example
of this can be found in the API endpoint /USER/VALIDATE_AUTH
in version
0.19.5, which does not expect a JSON response from the server but gets one
anyway.
This system does not bind slots that dont have a value when generating them from
a JSON response. As such, it may be helpful in certain circumstances to define
custom accessors for specific slots, or alternatively to establish a handler for
unbound slot errors that returns NIL
when invoked with a lemmy-interface
object. E.g.
(handler-bind ((unbound-slot
(lambda (c)
(when (typep (unbound-slot-instance c) 'lemmy-interface)
(let ((r (find-restart 'use-value c)))
(when r
(invoke-restart r nil)))))))
(code body))
As of version 0.18.3: This system does not provide complete bindings. The current system of parsing bindings from the javascript sources is error prone; not all interfaces and methods parse correctly. The solution to this would be to generate an API specification from the rust sources using rust macros. While this is currently under consideration at time of writing, it appears there are numerous issues standing in the way of this. If you are interested in contributing to this endeavor, please see this issue as a starting point: https://github.com/LemmyNet/lemmy/issues/2937.
As of version 0.19.5: The unofficial lemmy openapi specification is used to generate the bindings. This eliminates the error prone parsing of javascript sources. However this system should not be assumed to provide all bindings. If you find a binding that is not present or has not been parsed correctly, please open an issue.
Errors
When an error is signalled during a API call, it will be wrapped if
*WRAP-LEMMY-ERRORS*
is true. If wrapping an error, first lemmy-api attempts to
translate the error into a more human readable one based on the error string
returned by the server. If it cannot be found, it is wrapped in a general
LEMMY-API-HTTP-ERROR
.
Errors are one of the places that can lead to undefined behavior; interface
objects being sent are almost always declared to have dynamic extent, and errors
may hold a reference to a dynamic extent interface object. As such, care must be
taken to not return past the point they are established on the stack before
accessing the interface object. In practice this means using handler-bind
instead of handler-case
.
Systems
This project is broken up into three strata. The base system, LEMMY-API/BASE
,
defines the framework that allows interface objects to be implemented. This
includes, among other things, the macros DEFINE-INTERFACE-CLASS
and
DEFINE-API-ENDPOINT
. The top level system LEMMY-API
provides a common
package to expose symbols from specific lemmy API versions. These versioned
packages, such as LEMMY-API/0.18.3
, are USE
d by, and have their symbols
re-exported from, LEMMY-API
.
Using Specific API Versions
The system LEMMY-API
loads the latest version of the API bindings (at the time
of writing this is v0.19.5), however alternate versions can be loaded by asdf by
specifying the version number, e.g. LEMMY-API/0.18.3
. The LEMMY-API
package
does not define or export any additional symbols, it is provided simply for ease
of use, and the underlying versioned packages can be used in its stead.
The specific definition files for individual versions lie in the
version/x.y.z/
folders. In addition, the specific version of the lemmy api
currently loaded is pushed to the *FEATURES*
list, and multiple versions of
the API bindings can be loaded, as they reside in separate packages.
USE
-ing This Package
Do not use the System LEMMY-API
, unless providing a vendored version; the
LEMMY-API
package tracks the latest API version, and its exported symbols are
subject to change. However, LEMMY-API/MAJ.MIN.PAT
packages should be
considered stable and not subject to change.
Defining New API Versions
New API versions can be defined by running the shell script
generate-bindings.sh
. This shell script takes a single .yaml
specification
file as its only argument. This will place the result of the specification into
its own version folder. It will not generate the asd file entry for the
generated version, this must be manually added to the file lemmy-api.asd
using
the macro define-lemmy-version
.
Currently, API versions are taken from this unofficial specification.
Documentation
Base System
FILE: mop.lisp
This file defines the metaobject used for lemmy interface classes. The MOP is used to add additional parameters to slot definitions, which eases the conversion of slot values into and out of the JSON format, and allows for easier validation.
FILE: interfaces.lisp
This file provides the interface definition macro and the validation function for interface objects.
FILE: error-translations.lisp
This file defines the translations used when wrapping errors.
FILE: conditions.lisp
This file defines conditions used in the lemmy api system.
FILE: json-to-interface.lisp
This file defines how interface objects will be parsed from JSON.
FILE: interface-to-json.lisp
This file provides the inverse of json-to-interface.lisp
.
FILE: api-methods.lisp
This file provides the plumbing for sending and receiving information to and
from a server. It defines the macro DEFINE-API-ENDPOINT
, as well as the
deprecated DEFINE-LEMMY-API-METHOD
. It also defines a host of auxillary
functions for making the actual API calls.
Versioned Systems
FILE: versions/major.minor.patch/package.lisp
This file defines the specific versioned package, and registers the feature
:LEMMY-API/MAJOR.MINOR.PATCH
.
FILE: versions/major.minor.patch/definitions.lisp
As of version 0.19.5: This file is generated automatically in two stages from
the unofficial openapi specification. First the specification (a .yaml
file)
is parsed into memory, and then used to generate a lispy specification
file. That specification file is used to generate a definitions file containing
all the type, class, and method definitions.
As of version 0.18.3: This file is generated automatically in two stages from the lemmy javascript client sources. First the javascript client sources are parsed into a specification file. These specifications are then used to generate the types, classes, and methods which reside in this file. In addition all types, class names, and accessors are exported.
License
GPLv3
Slots to Ignore
- v0.18.3
INBOX-URL