Skip to main content

Riding on Railway

· 5 min read
Ramnivas Laddad
Co-founder @ Exograph

The driving principle behind Exograph is to make it easy for developers to build their backends by letting them focus only on inherent—not incidental—complexity; Exograph should handle everything else. With Exograph, you can create a GraphQL server with a rich domain model with authorization logic, Postgres persistence, and JavaScript/TypeScript business logic in just a few lines of code. But what about deploying to the cloud?

The new platform-as-a-service offerings make deploying Exograph apps easy, and we make it easier by providing specific integrations. In this blog, I will focus on Railway. With its Postgres support and GitHub integration, you can create an Exograph server from scratch and deploy it in under three minutes!

A quick overview of Exograph

Let's take a quick look at Exograph from a deployment perspective, which will make the Railway integration easy to understand. Exograph separates the build and execution steps and ships two corresponding binaries: exo and exo-server.

The exo binary Exograph's cli tool takes care of everything from building the app, migrating schema, and running integration tests to acting as a development server. It also simplifies deployment to cloud platforms, as we will see shortly.

Running an Exograph application requires that you build the app using exo build and then run the server using exo-server. The exo build command processes the index.exo file (and any files imported from it) and bundles JS/TS files. The result is an intermediate representation file: index.exo_ir.

Building an Exograph app
exo build

The exo-server binary' is the runtime, whose sole purpose is to run Exograph apps. It processes the index.exo_ir file and runs the server.

Running an Exograph app

Please see the Exograph Architecture for more details.

These tools are available as Docker images, making it easy to integrate with cloud platforms and form the core of our Railway integration.

Railway Integration

The exo deploy railway command generates a Docker file (Dockerfile.railway) and railway.toml to point to this file. The generated Docker file contains two stages:

  1. Build stage: Build the app and another to run it. The build stage uses the exo Docker image to build the app and then copies the result to the runtime stage. The stage also runs database migrations.
  2. Runtime stage: Runs the server. It uses the exo-server Docker image.

Deploying to Railway then involves:

  • Pushing all the code to GitHub (or using railway up to push the code directly to Railway)
  • Creating a Railway project
  • Creating a Postgres service (unless you use an external Postgres)
  • Creating a new service pointing to the GitHub repo (unless you use railway up)
  • Binding environment variables for the Postgres database to the service

Let's see how this works in practice.

Creating a new Exograph project

This is easy!

exo new todo
cd todo

This creates a new project with a simple todo app and initializes Git. If you are wondering, this doesn't generate much code. Here is the entire model!

module TodoDatabase {
type Todo {
@pk id: Int = autoIncrement()
title: String
completed: Boolean

If you would like, you can run exo yolo to try it out locally.

Deploying to Railway

We have a choice of using Postgres offered by Railway or an external one. Let's look at both options.

Using its Postgres

Railway offers the Postgres database service, which has the advantage of keeping both the Exograph app and the database on the same platform.

exo deploy railway --use-railway-db=true

This will generate files necessary to deploy to Railway and provide step-by-step instructions. Here is a video of the entire process: we create an Exograph project from scratch and deploy it to Railway in under three minutes.


Currently, the process involves using the dashboard to create the Postgres database and binding it to the service. We will keep an eye on Railway's tooling to make this easier. For example, if Railway supports Infrastructure-as-Code (a requested feature), we can lean on it to make the whole process in a single command.

Using Neon's Postgres

Using an external Postgres database is also easy and has the advantage of specialization offered by database providers. We will use Neon for this example.

Passing --use-railway-db=false to exo deploy railway will generate files for use with an external Postgres database.

exo deploy railway --use-railway-db=false

Other than using an external Postgres, the process is the same as using Railway's Postgres.

Using the playground

Following the best practice, we do not enable introspection and playground in production. But that makes working with the GraphQL server challenging. Exograph's playground solves this problem by using schema from the local model (the app's source code) and sending requests to the remote server. This way, you can explore the API without enabling introspection and playground in production.

Let us know what you think of our Railway integration. You can reach us on Twitter or Discord.