Rails with GraphQL

Rails with GraphQL cover image

Rails is still one of the fastest ways to get an API on disk. This note is the small setup I used for a Rails API app with GraphQL and PostgreSQL.

It is not a manifesto for GraphQL. It is the shortest path I used when I wanted Rails to handle the app shell and GraphQL to own the client-facing query shape.

Install Ruby and Rails

I used rbenv for Ruby versions:

brew install rbenv
rbenv install 3.0.1

Install Rails and Bundler:

gem install rails
gem install bundler

You can list available Ruby versions if you want a different one:

rbenv install --list

Create an API-only app

Start Rails without the default frontend stack and use PostgreSQL:

rails new app --api -d postgresql
cd app

Add the GraphQL gem:

bundle add graphql

Generate the GraphQL skeleton:

rails generate graphql:install

Rails puts the GraphQL code under app/graphql. I inspect that directory before adding any real models, because the generator creates a lot of decisions that are easy to ignore at first.

Why this shape works

Rails handles the application shell: routing, database config, models, migrations, validations, and environment wiring. GraphQL gives clients a typed query surface without creating a custom REST endpoint for every screen.

The setup is small enough that you can inspect all of it. That matters more than the generator output. I usually check the generated schema, the base query type, and how errors are returned before building features. If those pieces are muddy, the app will stay muddy.

What to inspect next

After the generator runs, I would check these files before building features:

  • app/graphql/types/query_type.rb
  • app/graphql/types/mutation_type.rb
  • app/graphql/app_schema.rb
  • the generated controller route for GraphQL requests

The point is to understand the boundary before adding models. Which errors are returned to clients? Are exceptions hidden or exposed? Where does authorization belong? Are mutations named around domain actions, or are they just CRUD wrappers around tables?

Rails makes it easy to move quickly, but GraphQL schemas age badly when every screen gets its own shape. I prefer starting with a small query surface and a few intentional mutations. The schema should describe product operations, not leak the database one table at a time.