Library / SDK
haskell-beam/beam avatar
haskell-beam/beam

beam: a type-safe SQL interface for Haskell

A type-safe Haskell SQL library

637 stars193 forksHaskellLicense varies

At a glance

What is it?
beam is a Haskell interface to relational databases in which the compiler checks the query. The tables are ordinary Haskell data, so no Template Haskell is needed, the generated SQL is meant to be read by a person, and the library deliberately refuses to manage connections or transactions, leaving that to the interface library of the backend you picked.
Who is it for?
beam fits a Haskell team that wants the compiler to catch a query that references a column that does not exist, and that wants the same query definition to run against several databases without change. It does not fit a team that wants the library to own the connection pool, because that is the decision beam pushes onto you, and it does not fit a project that needs a backend the project does not know about and nobody is willing to write.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Haskell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The compiler checks the query

beam describes itself as a Haskell interface to relational databases, and the design space it occupies is stated as a list of five features.

The first is type-safe queries checked at compile time. The second is predictable query performance by closely matching SQL semantics. The third is support for any relational database via pluggable backends, with the backends adding their own database specific capabilities. The fourth is that database entities, such as tables, are modelled using standard Haskell code, and in particular Template Haskell is not needed. The fifth is that the generated SQL queries are human-readable.

The fourth point deserves more attention than it usually gets, because it is the usual objection to typed query builders in Haskell. The way most such libraries define a table is with a Template Haskell quotation that reads the schema from the database at compile time, which is convenient and which means the build depends on a live database. beam's answer is that a table is a record type with a few type-level annotations, written by hand, and the record fields are the columns. The price is that you write the schema in Haskell, and the benefit is that the build does not need the database and the error messages are the ones the compiler already knows how to write well.

The fifth point is the one that decides whether the library is usable in practice. A query builder that emits a wall of generated SQL is a black box, and a query builder that emits SQL you can read is a debugging tool. The combination of a typed query and a readable rendering is what makes it possible to compare a beam query with the hand-written SQL you would otherwise have written.

On coverage, the readme claims combinators for all of the standard SQL92 features and a significant subset of SQL99, SQL2003, and SQL2008, with a compatibility matrix maintained in the documentation for the details.

What the library deliberately refuses to do

There is a paragraph in the readme that explains the most important design decision in the project, and it is about what beam does not do.

Recognising that over-abstraction frequently means caving in to the lowest common denominator, beam does not do connection or transaction management. The user is free to perform those functions using the appropriate Haskell interface library for the backend of choice.

That is a bold decision for a database library, and most libraries make the opposite one. A library that manages connections gives you a uniform API across backends, and it also imposes a connection model that is the lowest common denominator: no pool tuning, no per-transaction isolation settings, no special handling for a backend that does something unusual, and no way to use the backend's own interface library for the things it does better.

The claim in the readme is that the backends provide a significant portion of backend specific functionality which fits into the beam ecosystem. So the split is that beam owns the query and the update DSLs, and the backend library owns the connection, and the two meet at the point where a query is executed.

The practical consequence is that a project using beam ends up with two libraries, one for the queries and one for the plumbing, and has to learn both. The benefit is that the plumbing is the best available for that database rather than a generic compromise, and that a project that needs to drop to the raw interface for one operation can do so without leaving the ecosystem.

Backends, and the ones maintained elsewhere

The modular design is explicit in the readme. The core package provides the standard functionality, and beam is split into a variety of backends that provide a means of interfacing the data query and update DSLs with particular database management systems.

The point that makes this a real ecosystem rather than a list of adapters is that backends can be written and maintained independently of the repository. The two named examples are the MySQL backend and the Firebird backend, and both are packaged independently, in other people's repositories.

The repository layout shows the backends that live inside it. There is the core package, a PostgreSQL backend, a SQLite backend, a DuckDB backend, a migration package, and a benchmark package. The topics list adds DuckDB and PostgreSQL to the mix, and the compatibility matrix in the documentation is where a reader would go to find out which of the SQL standard each backend actually implements.

The DuckDB backend is the interesting one for a newcomer, because a second in-tree backend shows what the pluggable design is for: the same query definitions can be run against more than one engine without being rewritten. That is the kind of thing the pluggable design is for.

The project also asks for commercial users to consider a donation, with a payment address in the readme, which is an appeal worth taking seriously if the library matters to your work.

Postgres as the worked example

The readme explains the model with the PostgreSQL backend, because the Postgres case is the one where the division of responsibility is most visible.

The backend is built on top of the postgresql-simple interface library. When you use it, you manage connections and transactions with postgresql-simple. You are free to issue queries directly with postgresql-simple, using beam only where you want to.

That last sentence is the one that tells you this is a real design rather than a marketing position. A library that claimed to be the only way to talk to the database, and then made that impossible, would be a cage. Here, the escape hatch is explicit and the recommendation is to use it.

The reason Postgres is the worked example is also the richest part of the story, and it is about types. Postgres offers a number of rich data types on top of the standard SQL ones, and to reflect that, the backend offers pluggable support for the Postgres specific data types and features. In practice that means a beam column can have a Postgres specific type behind it, and the type system carries the information about it.

That is the payoff of the whole approach, and it only works because of the decision not to abstract the connection layer. A generic layer would have had to flatten Postgres specific types into generic ones, and the pluggable backend approach lets the Postgres specific type survive into the Haskell type.

The documentation is executed, not just printed

The most interesting thing about the documentation is that it is a test suite, and the project is candid about the cost of that.

The documentation uses a custom Markdown preprocessor to automatically build the examples against the canonical Chinook database,. The schema is in the repository, in the schema module of the SQLite backend's Chinook example.

The preprocessor and the configuration run every query in the documentation against a live database connection. Any errors in serialisation, any errors in deserialisation, and any invalid syntax are caught while the documentation is being built.

The mechanics are visible in the documentation source itself. A query example is a markdown block marked with a declaration that says it contains beam query code, and inside it there is a line declaring that the example should be built against the applicable backends and included in the code. The template name is either the query template or the statement template, depending on whether the example is a query or a data manipulation statement, and for the query template the included code should produce a query value, while for the statement template it should be a monadic action. The requirements part of the declaration can select which backends the example runs against.

And that is where the cost comes in. By default, beam builds examples for every backend it knows about, including ones that are not in the main source tree, which means you need an instance of all of those database servers running and available. The readme says this is usually not what you want, and shows the fix, which is to set the enabled backends option in the documentation configuration to the one you care about.

yaml
  - docs.markdown.beam_query:
      template_dir: 'docs/beam-templates'
      cache_dir: 'docs/.beam-query-cache'
      conf: 'docs/beam.yaml'
      base_dir: '.'
      enabled_backends:
        - beam-sqlite

The build itself is a Nix flake, and the documentation dependencies are packaged that way, so the build is one command.

console
nix build .#docs

The repository, the packaging, and the benchmarks

The repository is a Haskell project with a long build description that describes it well.

There is a cabal project file, a stack configuration for two stack versions, a Nix flake with a lock file, a directory for the continuous integration configuration, and a script for building the documentation. The environment file at the root is the direnv file, which is the usual way a Nix based project declares its environment for a shell.

The backends sit in their own directories alongside the core package, and the benchmark package is separate, which is worth explaining because the readme says the benchmarks for the backends are collected under a single private package. The word private there means it is not published to the package index, because it depends on every backend, including the ones maintained outside the repository, and a benchmark package that pinned all of them could never be released.

Running one backend's benchmark is a single cabal invocation naming the private benchmark package and the backend, and the readme gives the three forms for the DuckDB, PostgreSQL, and SQLite backends. The verbosity flag is turned down to zero because the benchmark output is noisy.

There is also a release checklist in the repository root and a contributors file, which tells you that releases here are a deliberate process with steps someone wrote down, which is a good sign for a library that a version number means something in.

The project is not archived and the last push was on 2026-10-07, with the 0.11 line released in April and May 2026 and the 0.10 line going back to October 2024. The site is haskell-beam.github.io/beam, the user guide is there, and there is a mailing list for questions.

Editorial conclusion

beam fits a Haskell team that wants the compiler to catch a query that references a column that does not exist, and that wants the same query definition to run against several databases without change. It does not fit a team that wants the library to own the connection pool, because that is the decision beam pushes onto you, and it does not fit a project that needs a backend the project does not know about and nobody is willing to write. Verify first which backend you will use and whether it is packaged with the project or maintained elsewhere, that you are comfortable managing connections and transactions yourself with that backend's interface library, and that the documentation build has a database available, because the examples in the documentation are executed rather than merely displayed.

Frequently asked questions

What is the beam Haskell library?

It is a Haskell interface to relational databases in which queries are type-checked at compile time. The tables are modelled with ordinary Haskell code, so Template Haskell is not needed, the generated SQL is human-readable, and the library supports any relational database through pluggable backends.

Why does beam not manage connections and transactions?

Because the readme argues that over-abstraction means caving in to the lowest common denominator. The user manages connections and transactions with the interface library of the chosen backend, and is free to issue queries with that library directly, using beam only where it is wanted.

Which database backends does beam support?

The repository contains the core package and backends for PostgreSQL, SQLite, and DuckDB, along with a migration package, and other backends such as MySQL and Firebird are packaged independently in other people's repositories. A compatibility matrix in the documentation tracks which parts of the SQL standard each backend implements.

How are the examples in the beam documentation tested?

The documentation uses a custom Markdown preprocessor that runs every query against a live database connection against the canonical Chinook database, so serialisation, deserialisation, and syntax errors are caught while the docs are built. The examples are marked in the markdown source with a declaration naming the template and the backends to run against.

How do I build the beam documentation?

With Nix, using the flake in the repository, and the dependencies for the documentation are packaged that way. The build attempts the examples for every backend it knows about unless the enabled backends option is set in the documentation configuration, which the readme recommends because otherwise you need a running instance of every backend.

Official sources

  1. haskell-beam/beam on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/haskell-beam-beam.svg)](https://hysenlabs.com/projects/haskell-beam-beam)

Was this useful?