Getting started: the production path (Rails)

The setup that ships: queries live as .graphql files, generation writes # typed: strict Ruby you check in, and CI fails when anything drifts. Mostly copy/paste. (Exploring an API from a console instead? Start with dynamic mode — no build step.)

Rails is assumed below; the non-Rails note at the bottom covers the one difference.

1. Install

# Gemfile
gem "graph_weaver"

2. Bootstrap the schema dump

Codegen reads a schema dump at app/graphql/schema.json (GraphWeaver.schema_path). You never write this file by hand — cache: true writes it on first introspection. Bootstrap once from a console:

GraphWeaver.new("https://api.example.com/graphql", auth: ENV["API_TOKEN"], cache: true).schema

Skip this step and the generate task tells you exactly that — the error message is the documentation. Prefer PR-reviewable diffs? cache: :graphql writes SDL instead of introspection JSON; both generate identical code.

Note cache:/ttl: apply only to url clients — a schema source (a live class or a dump) never introspects, so passing them raises.

3. Wire the client

# config/initializers/graph_weaver.rb
GraphWeaver.client = GraphWeaver.new(
  "https://api.example.com/graphql",
  auth: ENV["API_TOKEN"],
  cache: true,   # reuses the committed dump; delete the file to re-introspect
)

# custom scalars/enums/type helpers — register globally, so the rake
# tasks bake them into generated source
GraphWeaver.register_scalar("DateTime", Time, serialize: :iso8601, requires: "time")

GraphWeaver.client = is the load-bearing line: generated modules without a baked transport resolve to it at execute time (the full resolution order). The generated modules themselves load at boot automatically (the Railtie requires everything under generated_path, after your initializers run) — outside Rails, call GraphWeaver.load_generated! wherever your app boots.

4. Rake tasks — nothing to do

In Rails the graph_weaver:* tasks register themselves (a Railtie), and they depend on :environment, so your initializer — and its registrations, which are baked into generated source — runs first. Outside Rails, add require "graph_weaver/tasks" to your Rakefile.

5. Write a query, generate, commit

# app/graphql/queries/person.graphql
query($id: ID!) {
  person(id: $id) {
    name
    birthday
  }
}
rake graph_weaver:generate   # writes app/graphql/generated/person_query.rb

Commit the schema dump and the generated files. Generated code is reviewed like any other code — and never edited by hand.

PersonQuery.execute!(id: "1").person&.name   # typed, via GraphWeaver.client

6. Test against fakes

# spec/support/graph_weaver.rb
require "graph_weaver/rspec"

GraphWeaver::Testing.configure { |config| config.auto_fake = true }

The opt-in is deliberate (no surprise fakes); once on, the schema auto-locates from the committed dump and every query in every example executes against a seeded, schema-correct FakeClient — no server, no stubs, and rspec --seed 1234 reproduces the fake data along with test order. Pin values with overrides:, simulate failures with Failure.* — see testing.

7. Verify in CI

rake graph_weaver:verify          # generated code fresh? fails on any drift
rake graph_weaver:schema:verify   # server drifted? re-introspects and compares

Two different questions. graph_weaver:verify checks that the committed generated files match what the current schema + queries + registrations would produce — run it in every CI build. graph_weaver:schema:verify asks whether the server has moved since the dump was taken — it needs network, a dump with a recorded source url (introspected dumps have one), and GRAPHWEAVER_AUTH for private APIs; run it on a schedule and refresh with rake graph_weaver:schema:refresh.

Sorbet, with or without

sorbet-runtime is a hard dependency, so generated T::Structs and sigs enforce at runtime in every app — no Sorbet setup required on your end. The static layer (srb tc flagging a typo'd field before anything runs) applies only when your app runs Sorbet, and only to checked-in generated files — dynamic parse is invisible to srb tc. Everything works without Sorbet; codegen plus Sorbet is what moves type errors from runtime to CI.

Not Rails?

Everything above works the same, minus the Railtie conveniences: add require "graph_weaver/tasks" to your Rakefile yourself, and — since there's no :environment hook to run your registrations — require the file that does them from the Rakefile too. GraphWeaver.load_generated! goes wherever your app boots instead of an initializer.