in a world where no one cares

Building the Hasura GraphQL Engine

$WORKPLACE is using Hasura for a project. The core server component of Hasura – the “GraphQL Engine” – is distributed only as a Docker container. This works well enough on my Linux machine, but I have a strong aversion to running Docker on non-Linux machines. I find idea that containers can still be considered “lightweight” when they’re running inside a full-fat VM to be a bit laughable. I prefer to run things on “bare metal” in my development environment where I can. And while Hasura don’t distribute first-party native binaries, there’s hypothetically nothing stopping us from building our own; it is, after all, open source (APL 2.0). So let’s do that.

The Engine is written in Haskell, so as a prerequisite, install ghcup. If it’s been added to your PATH correctly, you should be able to run ghcup list and get a list of available Haskell compiler (GHC) and build/package manager (Cabal) versions.

Check out the GraphQL Engine Git repository:

$ git clone https://github.com/hasura/graphql-engine.git
# Make sure we're building a specific release version, not just the master branch.
$ git checkout v1.3.3
# The server-side source code is all in the server/ directory.
$ cd graphql-engine/server

The Haskell build manager, Cabal, is almost capable of building the project without intervention. The fly in the ointment is that Hasura seems to be quite picky about the GHC version it’s built with, and Cabal isn’t high enough up the dependency food chain to pick which compiler version gets used. So instead, we’ll need to pull desired GHC and Cabal versions out of the freeze file, and install/set those as the defaults with ghcup:

# Install the GHC version corresponding to the required Haskell language base version.
$ grep 'any.base ==' cabal.project.freeze
any.base ==4.14.0.0,
$ ghcup install ghc base-4.14.0.0
[ Info ] downloading: https://downloads.haskell.org/~ghc/8.10.1/ghc-8.10.1-x86_64-apple-darwin.tar.xz
...
[ Info ] GHC installation successful
$ ghcup set ghc base-4.14.0.0
[ Info ] GHC 8.10.1 successfully set as default version
$ grep 'any.Cabal ==' cabal.project.freeze
constraints: any.Cabal ==3.2.0.0,
$ ghcup install cabal 3.2.0.0
[ Info ] downloading: https://downloads.haskell.org/~cabal/cabal-install-3.2.0.0/cabal-install-3.2.0.0-x86_64-apple-darwin17.7.0.tar.xz
...
[ Info ] Cabal installation successful
$ ghcup set cabal 3.2.0.0
[ Info ] Cabal 3.2.0.0 successfully set as default version

Before you can off and run make, you’ll also need a couple libraries installed: unixODBC and libpq are the important ones. On a Mac, you can brew install unixodbc libqp.

Then you can kick Cabal into action:

$ cabal v2-update
(git junk happens)
Downloading the latest package list from hackage.haskell.org
To revert to previous state run:
cabal v2-update 'hackage.haskell.org,...'
$ cabal v2-build
(git junk happens)
Resolving dependencies...
Build profile: -w ghc-8.10.1 -O1
In order, the following will be built (use -v for more details):
(looong build process happens)
Building executable 'graphql-engine' for graphql-engine-1.0.0..
Linking /path/to/graphql-engine/server/dist-newstyle/build/x86_64-osx/ghc-8.10.1/graphql-engine-1.0.0/x/graphql-engine/opt/build/graphql-engine/graphql-engine ...

And that’s it. The long path given by the last line (“Linking…”) is the final executable. It’ll be about 64MB. You can run it as-is and it’ll complain about missing arguments. The documented configuration arguments and environment variables are really just passed through to the binary running in the container, so they’ll work just fine with the binary running out of the container, too. For example, to connect to the local database testdb as the user testuser, and enable the console:

/path/to/graphql-engine \
--database-url postgres://testuser@localhost/testdb \
serve \
--enable-console

Then point a browser at http://localhost:8080/console.

Note that the graphql-engine executable is dynamically linked against a number of other libraries:

$ otool -L /path/to/graphql-engine
/path/to/graphql-engine:
/usr/local/opt/postgresql/lib/libpq.5.dylib (compatibility version 5.0.0, current version 5.13.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
/usr/lib/libcharset.1.dylib (compatibility version 2.0.0, current version 2.0.0)

Not very portable. To physically relocate these libraries and modify the linking of the graphql-engine executable for easier distribution, you can either do this yourself with install_name_tool, or use something like locallink to do it for you.

Write a Comment

Comment