Stellar - A library for API Programming

Stellar is a set of Idris 2 libraries for building interactive programs by programming declaratively with APIs using dependent types. API-programming a new programming paradigm where the unit of program manipulation isn’t an object, a function, or a type, but an API.

When working with complex software involving multiple APIs it is easy to lose track of how the different systems interact. Stellar alleviates this by building programs as a pipeline of API-transformers that translate between layers of APIs of different kinds.

Every API is a Request/Response Pair

Every API in Stellar is described by the same data structure:

record API where
    request : Type
    response : request -> Type

The request and the response are types that describe what counts as a valid value for a given API. This single definition a surprising range of systems:

  • HTTP — requests are HTTP requests, responses are HTTP responses.
  • A command line interface — requests are List String, responses are String.
  • A database — requests are queries, responses are tables, confirmations or errors.

Because all of these share the same request/response building block, we can combine complex programs out of API-transformers.

Mapping across APIs with API-transformers

Say you want to write an HTTP server for a web service. You are dealing with at least three interfaces:

  • HTTP itself
  • The parsed endpoints of the server
  • A database storing your data

Stellar lets you write the server as a pipeline:

-- HTTP ~> API ~> Database
app = router |> queries |> execDB

router is an API transformer translating bare HTTP requests into API endpoints, queries turns each endpoint into a database query, and execDB runs database queries and provides a result.

The database result is then translated back into endpoint responses and, in turn, encoded in HTTP responses.

This example is pseudocode, simplified for the purpose of explanation. The full story is theoretically involved but explains with absolute precision what is happening and why. For the theoretical explanation, see the container-compendium

What are API-transformers Concretely

An API-transformer is a type that is parameterised over two APIs. The most natural way of interpreting it is as a pair of methods, one to convert the “request” part of the APIs and one to convert the “response”.

At a high level, if we have two APIs A1 and A2 with their own request/response which we call A1.request, A1.response, A1.request, and A2.response, then an API-transformer must convert from A1.request to A2.request, and also convert back from responses of A2.response to responses A1.response.

Conceptually, it can help to think of A1 as the “high-level, main API” and A2 as the “lower-level, delegated API”. In this way, an API transformer delegates tasks to a lower-level api dealing with more details, from an API that is maybe more abstract, or closer to the user than the bytes.

There is a nice illustration we can draw from this structure, by laying our request/response pairs vertically, and drawing the maps that make up the API-transformer we see that mapping the request goes forward but mapping the response goes backward:

The corresponding type in Idris is a record with two fields:

record (=&>) (source, target : API) where
  mapRequest : source.request -> target.request
  mapResponse : (orig : source.request) ->
                target.response (mapRequest orig) -> source.response org

Mapping requests is straightforward, but mapping responses is a bit more involved. Let’s first look a the last line and ignore the arguments, we have target.response -> source.response . This is what converts responses from the delegated API to the main API. We notice that this type depends on orig, stating that any reinterpretation of a response must be done with regards to a fixed request. Finally, we notice that target.response takes mapRequest orig in argument ensuring that the response we obtain comes from the message we translated. This removes any possible doubt as to the origin of a response. A response is always refering to a previous message, and we keep track of any translation we have done previously to obtain that response.

This covers the main definition of API and API-transformer, the actual implementation is different for performance reasons, but they are equivalent.

API substrates

When you are designing your application, you assume both client and server use the same API directly. But the reality of deploying both programs assumes that they communicate via a common protocol through which you serialise and deserialise the data that makes up your API. We call this third protocol the substrate.

Client
Client
Server
Server
HTTP
HTTP
substrate
substrate
API
API
Text is not SVG - cannot display
The application is modelled as if the connection between the server and the client is transparent. But in reality, any communication between the two sides is mediated by a common substrate, HTTP in that case

The substrate explains what happens in the real world: each party will lose structural information in order to communicate via the common protocol. But the data is present while designing your system. This guarantees that both client and server do not go out of sync and this synchronisation can be typechecked and versionned before you perform any deployment, detecting mismatches ahead of time. Whenever we are deploying we are relying on the common substrate which cannot detect API mismatches, only protocol errors

Stellar currently provides only two substrates, CLI and HTTP, but more are on the way.

Modules

Stellar is split into focused packages, each sharing the same API abstraction. Browse the source and per-module README on GitLab:

Module Description
stellar-api Core API / container-morphism definitions
stellar-http HTTP backend (Node and Chez Scheme)
stellar-sql SQLite-backed database API
stellar-fs File-system API
stellar-optics Optics / lens utilities
stellar-browser Browser backend
stellar-mcp Model Context Protocol server

Getting the source

Everything lives in a single repository:

git clone https://gitlab.com/avidela/stellar.git
cd stellar

The project is built with pack. For example, to build the API package:

cd api && pack build stellar-api.ipkg

A note on usability

This repository is a prototype of an idea that can blossom into the future of programming. Current tools aren’t quite good enough to provide the optimal user experience we can expect from programming at the level of APIs, but we are working on it. This project is not the final form of the API-programing idea, but a prototype to validate the idea. We’re developping the technology to make API-programming a completely natural part way of programming at glaive.