Skip to main content

2 posts tagged with "railway"

View All Tags

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.


What's new in Exograph 0.4

· 5 min read
Ramnivas Laddad
Co-founder @ Exograph
Shadaj Laddad
Co-founder @ Exograph

We are excited to announce the release of Exograph 0.4! This release introduces several new features and enhancements, many in response to our users' feedback (thank you!). While we will explore some of these features in future blogs, here is a quick summary of what's new since the 0.3 version.

NPM modules support

Exograph offers a Deno integration to write custom business logic in TypeScript or JavaScript. Until now, you could only use Deno modules. While Deno modules ecosystem continues to expand, it is not as rich as the Node ecosystem. Recognizing this, Deno added support for NPM modules to tap into the vast range of NPM packages, and Exograph 0.4 extends this support through our integration. You can now harness NPM modules to checkout with Stripe, send messages with Slack, interact with AWS, and so on.

As an illustration, here is how you would send emails using Resend. First, you would define a mutation:

module Email {
mutation sendAnnouncement(): Boolean

And implement it using the resend NPM module:

import { Resend } from "npm:resend";

const resend = new Resend("re_...");

export async function sendAnnouncement(): Promise<boolean> {
await resend.emails.send({
from: "...",
to: "...",
subject: "Exograph 0.4 is out!",
html: "<p>Exograph <strong>0.4</strong> is out with support for npm modules, playground mode, and more!</p>",

return true;

Compared to the example shown on the Resend site, the difference is the npm: prefix in the import statement. This prefix tells Exograph to look for the module in the npm registry.

Railway integration

Exograph's deployment has always been easy since the server is just a simple binary with everything needed to run. However, we strive to make it easier for specific platforms. Exograph 0.4 now supports Railway as a deployment target! Here is a quick video where we create an Exograph project from scratch and deploy it to Railway in under three minutes.

To support this kind of integration, where the cloud platform can also run the build step, we now publish two Docker images: cli with the exo binary and server with the exo-server binary.

exo playground

A recommended practice for GraphQL deployments is to turn off introspection in production, which is the default in Exograph. However, exploring the GraphQL API with such a server becomes difficult without code completion and other goodies. Exograph 0.4 introduces a new exo playground command that uses the schema from the local server and executes GraphQL operations against the specified remote endpoint.

exo playground --endpoint https://<server-url>/graphql
Starting playground server connected to the endpoint at: https://<server-url>/graphql
- Playground hosted at:

You will see a UI element in the playground showing the specified endpoint. Besides this difference, the playground will behave identically to the local playground, including autocomplete, schema documentation, query history, and integrated authentication.


This doesn't bypass the recommended practice of turning off introspection in production. The exo playground is useful only if you have access to the server's source code, in which case, you would know the schema, anyway!

See the video with the Railway integration above for a quick demo of the exo playground command.

Access control improvements

Exograph offers to express access control rules precisely. In version 0.4, we enhanced this expressive power through higher-order functions. Consider a document management system where users can read documents if they have read permissions and mutate if they have written permissions. The following access control rules express this requirement.

context AuthContext {
@jwt("sub") id: Int

module DocsDatabase {
query = self.permissions.some(permission => == &&,
mutation = self.permissions.some(permission => == && permission.write)
type Document {
@pk id: Int = autoIncrement()
permissions: Set<Permission>

type Permission {
@pk id: Int = autoIncrement()
document: Document
user: User
read: Boolean
write: Boolean

type User {
@pk id: Int = autoIncrement()
permissions: Set<Permission>

With this setup, no user can read or write a document without the appropriate permission. The some function allows you to express this requirement in a single line. Internally, it lowers it down to an SQL predicate for efficient execution.

Currently, we support the some higher-order function, which matches the Array.prototype.some function in Javascript. This function takes a predicate function and returns true if the predicate function returns true for any element in the array.

Other improvements

Besides these major features, we continue to improve Exograph to fit more use cases and simplify the developer experience.

For the Postgres plugin, for example, you can now specify that the tables could be in a non-public schema and specify deeply nested creating and updates in a single mutation. It also allows clients to supply the primary key value for UUID fields when creating an entity.

In version 0.3, we introduced a friction-free integration with Clerk for authentication. Since then, we have extended this support to Auth0 as an authentication provider! While Exograph generically supports all compliant OIDC providers, the playground integration for Clerk and Auth0 makes it easy to try out APIs that require authentication. Along the way, we updated key rotation for the OIDC authentication mechanism.

Let us know what you think of these new features and what you would like to see in the future. You can reach us on Twitter or Discord.