hasql: Fast PostgreSQL driver with a flexible mapping API

[ database, hasql, library, mit, postgresql ] [ Propose Tags ] [ Report a vulnerability ]

Root of the "hasql" ecosystem. This library provides connection management, execution of queries and mapping of parameters and results. Extended functionality such as pooling, transactions and compile-time checking of SQL is provided by extension libraries. For more details and tutorials see the readme.

All error-reporting is explicit: database, protocol and connection failures are reported via the Either type instead of being thrown as exceptions.

The transport layer is pluggable via pqi, so "hasql" itself carries no C dependency. To compile an application you need to depend on "hasql" together with one adapter package and pass that adapter to Hasql.Connection.acquire as its first argument. Two adapters are available:

  • pqi-ffi - the stable, production-proven adapter, backed by the C "libpq" library. It requires "libpq" of at least version 14 to be installed to compile. "libpq" comes distributed with PostgreSQL, so typically all you need is just to install the latest PostgreSQL distro. Via this adapter "hasql" is thoroughly tested to be compatible with a wide range of PostgreSQL servers starting from version 9.

  • pqi-native - a pure-Haskell adapter, which speaks the PostgreSQL wire protocol directly and thus requires no C dependency at all. It is thoroughly tested: pqi-conformance runs it side by side with "libpq" on the same inputs and checks that the results agree, and the test-suites of "hasql", "hasql-pool" and "hasql-transaction" run against both adapters. It is still labelled alpha, but is fully interchangeable with "pqi-ffi": switching between the two is a one-line change.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.2.0, 0.2.1, 0.2.2, 0.2.3, 0.3.0, 0.4.0, 0.4.1, 0.5.0, 0.6.0, 0.7.0, 0.7.1, 0.7.2, 0.7.3, 0.7.3.1, 0.7.3.2, 0.7.4, 0.14.0.2, 0.14.0.3, 0.15.0.2, 0.15.1, 0.15.1.1, 0.19.2, 0.19.3, 0.19.3.1, 0.19.3.2, 0.19.3.3, 0.19.4, 0.19.5, 0.19.6, 0.19.7, 0.19.8, 0.19.9, 0.19.10, 0.19.11, 0.19.12, 0.19.13, 0.19.14, 0.19.15, 0.19.15.1, 0.19.15.2, 0.19.16, 0.19.17, 0.19.17.1, 0.19.18, 0.19.18.1, 0.19.18.2, 0.20, 0.20.0.1, 0.20.1, 1, 1.1, 1.1.1, 1.2, 1.3, 1.3.0.1, 1.3.0.2, 1.3.0.3, 1.3.0.5, 1.3.0.6, 1.4, 1.4.0.1, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.4.1, 1.4.4.2, 1.4.4.3, 1.4.5, 1.4.5.1, 1.4.5.2, 1.4.5.3, 1.5, 1.5.0.1, 1.5.0.2, 1.5.0.3, 1.5.0.4, 1.5.0.5, 1.5.1, 1.6, 1.6.0.1, 1.6.1, 1.6.1.1, 1.6.1.2, 1.6.1.3, 1.6.1.4, 1.6.2, 1.6.3, 1.6.3.1, 1.6.3.2, 1.6.3.3, 1.6.3.4, 1.6.4, 1.6.4.1, 1.6.4.2, 1.6.4.3, 1.6.4.4, 1.7, 1.7.0.1, 1.7.0.2, 1.8, 1.8.0.1, 1.8.0.2, 1.8.1, 1.8.1.1, 1.8.1.2, 1.8.1.3, 1.8.1.4, 1.9, 1.9.1, 1.9.1.1, 1.9.1.2, 1.9.2, 1.9.3, 1.9.3.1, 1.9.3.2, 1.10, 1.10.1, 1.10.2, 1.10.2.1, 1.10.2.2, 1.10.2.3, 1.10.2.4, 1.10.3, 1.10.3.1, 1.10.3.2, 1.10.3.3, 1.10.3.4, 1.10.3.5, 1.10.3.6, 1.10.3.7, 2.0.0.0, 2.0.0.1, 2.0.0.2 (info)
Change log CHANGELOG.md
Dependencies aeson (>=2 && <3), attoparsec (>=0.10 && <0.15), base (>=4.14 && <5), bytestring (>=0.10 && <0.13), bytestring-strict-builder (>=0.4.5.4 && <0.5), comonad (>=5 && <5.1), contravariant (>=1.3 && <2), dlist (>=0.8 && <0.9 || >=1 && <2), hashable (>=1.2 && <2), hasql, iproute (>=1.7 && <1.8), mtl (>=2 && <3), postgresql-binary (>=0.15 && <0.16), postgresql-connection-string (>=0.1 && <0.2), pqi (>=1.0 && <1.2), profunctors (>=5.1 && <6), scientific (>=0.3 && <0.4), text (>=1 && <3), text-builder (>=1 && <1.1), time (>=1.9 && <2), transformers (>=0.5 && <0.7), unordered-containers (>=0.2 && <0.3), uuid (>=1.3 && <2), vector (>=0.10 && <0.14), witherable (>=0.5 && <0.6) [details]
License MIT
Copyright (c) 2014, Nikita Volkov
Author Nikita Volkov <nikita.y.volkov@mail.ru>
Maintainer Nikita Volkov <nikita.y.volkov@mail.ru>
Uploaded by NikitaVolkov at 2026-08-06T14:02:05Z
Category Hasql, Database, PostgreSQL
Home page https://github.com/nikita-volkov/hasql
Bug tracker https://github.com/nikita-volkov/hasql/issues
Source repo head: git clone https://github.com/nikita-volkov/hasql
Distributions Arch:1.6.1.1, LTSHaskell:1.9.3.1, Stackage:1.10.3.7
Reverse Dependencies 80 direct, 19 indirect [details]
Downloads 71479 total (444 in the last 30 days)
Rating 2.5 (votes: 5) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-08-06 [all 1 reports]

Readme for hasql-2.0.0.2

[back to package description]

Hasql

Hackage Continuous Haddock

PostgreSQL driver for Haskell, that prioritizes:

  • Performance
  • Reliability
  • Flexibility

Status

Hasql is production-ready, actively maintained and the API is moderately stable. It's used by many companies and most notably by the Postgrest project.

Pluggable Transport

Hasql's transport is pluggable via pqi. Hasql itself carries no C dependency. It programs against the pqi interface, and you pick the adapter that implements it. That means you depend on two packages, not one:

build-depends:
  hasql,
  pqi-ffi,  -- or pqi-native

Hasql.Connection.acquire then takes the adapter explicitly, as its first argument:

import Pqi.Ffi qualified    -- the C-backed libpq transport
import Pqi.Native qualified -- alpha: pure-Haskell, no C dependency, interchangeable with Pqi.Ffi

connection <- Hasql.Connection.acquire Pqi.Ffi.adapter settings
-- or
connection <- Hasql.Connection.acquire Pqi.Native.adapter settings

pqi-ffi is the stable, production-proven default. It binds the C libpq library, so it requires libpq of at least version 14 to be installed to compile - which typically just means having a recent PostgreSQL distro installed. Through it Hasql is tested against a wide range of PostgreSQL servers, starting from version 9.

pqi-native is a from-scratch, pure-Haskell implementation of the Postgres wire protocol, with no C dependency at all. It is thoroughly tested: pqi-conformance runs it side by side with libpq on the same inputs and checks that the results agree, and the test-suites of hasql, hasql-pool and hasql-transaction now run against both adapters, so the whole stack above the transport is exercised on pqi-native too. It's still labelled alpha - not yet proven at production scale. The two adapters are fully interchangeable: swapping between them is a one-line change (a different Adapter value, nothing else), so you can try pqi-native today with no lock-in and no rewrite to fall back if needed.

Ecosystem

Hasql is not just a single library, it is a granular ecosystem of composable libraries, each isolated to perform its own task and stay simple.

  • "hasql" - the root of the ecosystem, which provides the essential abstraction over the PostgreSQL client functionality and mapping of values. Everything else revolves around that library.

  • "hasql-transaction" - an STM-inspired composable abstraction over database transactions providing automated conflict resolution.

  • "hasql-pool" - a Hasql-specialized abstraction over the connection pool.

  • "hasql-postgresql-types" - integration with the "postgresql-types" library, which is a collection of Haskell types precisely modeling PostgreSQL types without data loss or compromise.

  • "hasql-dynamic-statements" - a toolkit for generating statements based on the parameters.

  • "hasql-th" - Template Haskell utilities, providing compile-time syntax checking and easy statement declaration.

  • "hasql-cursor-query" - a declarative abstraction over cursors.

  • "hasql-cursor-transaction" - a lower-level abstraction over cursors, which however allows to fetch from multiple cursors simultaneously. Generally though "hasql-cursor-query" is the recommended alternative.

  • "hasql-migration" - A port of postgresql-simple-migration for use with hasql.

  • "hasql-listen-notify" / "hasql-notifications" - Support for PostgreSQL asynchronous notifications.

  • "hasql-optparse-applicative" - "optparse-applicative" parsers for Hasql.

  • "hasql-implicits" - implicit definitions, such as default codecs for standard types.

  • "hasql-interpolate" - a QuasiQuoter that supports interpolating Haskell expressions into Hasql queries.

Want to list your package or correct something here? Make a PR.

Transport adapters

Unlike the extension libraries above, which are optional, a transport adapter is mandatory: Hasql needs one to talk to the server at all. See Pluggable Transport for how to pick one.

  • "pqi" - the driver-agnostic interface that Hasql programs against. Pulled in automatically. You don't depend on it directly.

  • "pqi-ffi" - the stable adapter, backed by the C libpq library.

  • "pqi-native" - an alpha pure-Haskell adapter speaking the PostgreSQL wire protocol directly, with no C dependency.

Why make it an ecosystem?

  • Focus. Each library in isolation provides a simple API, which is focused on a specific task or a few related tasks.

  • Flexibility. The user picks and chooses the features, thus precisely matching the level of abstraction that he needs for his task.

  • Much more stable and descriptive semantic versioning. E.g., a change in the API of the "hasql-transaction" library won't affect any of the other libraries and it gives the user a more precise information about which part of his application he needs to update to conform.

  • Interchangeability and competition of the ecosystem components. E.g., not everyone will agree with the restrictive design decisions made in the "hasql-transaction" library. However those decisions are not imposed on the user, and instead of having endless debates about how to abstract over transactions, another extension library can simply be released, which will provide a different interpretation of what the abstraction over transactions should be.

  • Horizontal scalability of the ecosystem. Instead of posting feature- or pull-requests, the users are encouraged to release their own small extension-libraries, with themselves becoming the copyright owners and taking on the maintenance responsibilities. Compare this model to the classical one, where some core-team is responsible for everything. One is scalable, the other is not.

Tutorials

Videos

There's several videos on Hasql done as part of a nice intro-level series of live Haskell+Bazel coding by the "Ants Are Everywhere" YouTube channel:

Articles

Short Example

Following is a complete application, which performs some arithmetic in Postgres using Hasql.

{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}

import Data.Functor.Contravariant
import Data.Int
import Hasql.Session (Session)
import Prelude
import qualified Hasql.Connection as Connection
import qualified Hasql.Connection.Settings as Settings
import qualified Hasql.Decoders as Decoders
import qualified Hasql.Encoders as Encoders
import qualified Hasql.Session as Session
import qualified Hasql.Statement as Statement
import qualified Pqi.Ffi -- from "pqi-ffi" (stable). Swap for "Pqi.Native" from "pqi-native" (alpha, fully interchangeable) to try the pure-Haskell backend

main :: IO ()
main = do
  Right connection <- Connection.acquire Pqi.Ffi.adapter connectionSettings
  result <- Connection.use connection (sumAndDivModSession 3 8 3)
  print result
  where
    connectionSettings =
      mconcat
        [ Settings.hostAndPort "localhost" 5432,
          Settings.user "postgres",
          Settings.password "postgres",
          Settings.dbname "postgres"
          -- Prepared statements are enabled by default.
          -- To disable them (e.g., for pgbouncer compatibility):
          -- Settings.noPreparedStatements True
        ]

-- * Sessions

-- Session abstracts over the execution of operations on a database connection.
-- It is composable and has a Monad instance.
-------------------------

sumAndDivModSession :: Int64 -> Int64 -> Int64 -> Session (Int64, Int64)
sumAndDivModSession a b c = do
  -- Get the sum of a and b
  sumOfAAndB <- Session.statement (a, b) sumStatement
  -- Divide the sum by c and get the modulo as well
  Session.statement (sumOfAAndB, c) divModStatement

-- * Statements

-- Statement is a definition of an individual SQL-statement,
-- accompanied by a specification of how to encode its parameters and
-- decode its result.
-------------------------

-- | A statement with two integer parameters and an integer result.
sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement = Statement.preparable sql encoder decoder
  where
    -- The SQL of the statement, with $1, $2, ... placeholders for parameters.
    sql =
      "select $1 + $2"
    -- Specification of how to encode the parameters of the statement
    -- where the association with placeholders is achieved by order.
    encoder =
      mconcat
        [ -- Encoder of the first parameter as a non-nullable int8.
          -- It extracts the first element of the tuple using the contravariant functor
          -- instance.
          fst >$< Encoders.param (Encoders.nonNullable Encoders.int8),
          -- Encoder of the second parameter,
          -- which extracts the second element of the tuple.
          snd >$< Encoders.param (Encoders.nonNullable Encoders.int8)
        ]
    -- Specification of how to decode the result of the statement.
    -- States that we expect a single row with a single non-nullable int8 column.
    decoder =
      Decoders.singleRow
        (Decoders.column (Decoders.nonNullable Decoders.int8))

divModStatement :: Statement.Statement (Int64, Int64) (Int64, Int64)
divModStatement = Statement.preparable sql encoder decoder
  where
    sql =
      "select $1 / $2, $1 % $2"
    encoder =
      mconcat
        [ fst >$< Encoders.param (Encoders.nonNullable Encoders.int8),
          snd >$< Encoders.param (Encoders.nonNullable Encoders.int8)
        ]
    -- Decoder that expects a single row with two non-nullable int8 columns,
    -- returning the result as a tuple.
    -- Uses the applicative functor instance to combine two column decoders.
    decoder =
      Decoders.singleRow
        ( (,)
            <$> Decoders.column (Decoders.nonNullable Decoders.int8)
            <*> Decoders.column (Decoders.nonNullable Decoders.int8)
        )

For the general use-case it is advised to prefer declaring statements using the "hasql-th" library, which validates the statements at compile-time and generates codecs automatically. So the above two statements could be implemented the following way:

import qualified Hasql.TH as TH -- from "hasql-th"

sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement =
  [TH.singletonStatement|
    select ($1 :: int8 + $2 :: int8) :: int8
  |]

divModStatement :: Statement.Statement (Int64, Int64) (Int64, Int64)
divModStatement =
  [TH.singletonStatement|
    select
      (($1 :: int8) / ($2 :: int8)) :: int8,
      (($1 :: int8) % ($2 :: int8)) :: int8
  |]

Discussions

Join GitHub Discussions to ask questions, provide feedback, suggest and vote on features, and help shape the future of Hasql.

Support Policy

This policy is intended to balance stability for users with the ability to evolve the library.

Each major release of Hasql is supported for at least one year from the date of its first release. During this period, fixes are backported to the latest minor version of that major release.

After the support period ends, the release may continue to work but is no longer guaranteed to receive fixes.

You're welcome to post requests to change the policy or issues if you believe something is not being addressed.