v0.2.1A generator for Go services · MIT

Rails conventions, laid on Go track.

Go like Rails for one API service.

Go like Rails for scaffolding an API — plain Go on Gin, sqlc, and Postgres, beside your Rails app, not a rewrite. Scaffold an API service in minutes, and get plain Go code you can read and own. It never imports Bogie.

Install · new · run
$ go install github.com/bogie-go/bogie@latest
$ bogie new blog
$ cd blog && make up && bogie db:prepare && bogie server
The Bogie mascot: a gopher riding a mine cart on rails, with a ruby beside it
Go up front. Ruby’s best idea along for the ride.

Under the car

Built from tools Go already ships.

No new runtime to learn. Bogie picks one tool per job, wires them the same way in every app, and leaves the code where you can read it.

  • Gin
  • sqlc + pgx
  • goose
  • River
  • Air
  • Kamal
  • Postgres
YOUR SERVICE app/ · db/ · config/ · bin/ River · jobs sqlc + pgx Gin · routing goose · migrate Postgres, the only database Kamal ships it
Fig. 1. A bogie is the wheeled frame under a rail car. The car is yours; Bogie carries it.

Convention without magic

Nothing exists until it is written down and registered.

What you keep from Rails

  • Generators: new, g scaffold, d to undo
  • A layout you already know: app/controllers, db/migrate
  • The vocabulary: db:migrate, credentials:edit
  • One way to do it, so reviews talk about the work

What Go keeps honest

  • Generated code is yours: read it, change it, own it
  • Registration lands above marker comments you can see
  • Interfaces sit next to the code that uses them
  • Generators are idempotent; bogie d removes exactly what g made

Out of scope, on purpose

  • Templates, sessions, CSRF: it serves JSON
  • Databases other than Postgres
  • An ORM, fixtures, factories or a console
  • Replacing Rails: it runs beside it just fine

The line

Every app rides the same four stops.

Rails gives you Active Record, Action Controller and friends. Bogie gives you the same separation, spelled out in plain Go. Here is what bogie g scaffold post lays down, plus a job from bogie g job.

  1. 01 · app/domain · db/queries · app/models

    Models keep the SQL honest

    A domain type with no third-party imports. Named queries in plain SQL, compiled by sqlc onto pgx. A store maps one to the other, and runs the model's validations on save. When you want to know what hits the database, you open a .sql file.

    // app/domain/post.go
    type Post struct {
    	ID          string
    	Title       string
    	Body        string
    	Score       int32
    	PublishedAt time.Time
    	AuthorID    string
    	CreatedAt   time.Time
    	UpdatedAt   time.Time
    }
    -- db/queries/posts.sql
    -- name: CreatePost :one
    INSERT INTO posts (title, body, score, published_at, author_id)
    VALUES ($1, $2, $3, $4, $5) RETURNING *;
  2. 02 · app/controllers

    Controllers convert and call

    A handler converts HTTP into domain values, calls one method on a store interface declared next to it holding only what that controller needs, and converts the result back. *gin.Context never leaves this folder.

    // app/controllers/posts_controller.go
    type postsStore interface {
    	CreatePost(ctx context.Context, p domain.Post) (domain.Post, error)
    	GetPost(ctx context.Context, id string) (domain.Post, error)
    	ListPosts(ctx context.Context, limit int32) ([]domain.Post, error)
    	UpdatePost(ctx context.Context, p domain.Post) (domain.Post, error)
    	DeletePost(ctx context.Context, id string) error
    }
  3. 03 · app/views

    Views are the wire contract

    A domain type never doubles as a wire type, so a field added to the database does not silently appear on the wire. Requests and responses are their own structs.

    // app/views/posts.go
    type PostResponse struct {
    	ID          string    `json:"id"`
    	Title       string    `json:"title"`
    	Body        string    `json:"body"`
    	PublishedAt time.Time `json:"published_at"`
    }
  4. 04 · app/jobs · bogie g job

    Jobs can commit with the row

    River runs on the same Postgres as the app: no Redis, no second system to keep up. Enqueue with InsertTx inside a transaction and the job commits with the row or not at all. bogie g job registers the worker above the bogie:jobs marker.

    type SendWelcomeArgs struct{}
    
    func (SendWelcomeArgs) Kind() string { return "send_welcome" }
    
    type SendWelcomeWorker struct {
    	river.WorkerDefaults[SendWelcomeArgs]
    	Log *slog.Logger
    }
    
    func (w *SendWelcomeWorker) Work(ctx context.Context,
    	job *river.Job[SendWelcomeArgs]) error {
    	w.Log.InfoContext(ctx, "send_welcome: not implemented yet", "job", job.ID)
    	return nil
    }

The rulebook

Six rules. Every app.

Small enough to memorise, strict enough that two Bogie apps read the same. bogie doctor checks the wiring.

  1. 1ctx comes first on anything that does I/O.
  2. 2Interfaces are declared where they are consumed.
  3. 3The domain layer imports nothing third-party.
  4. 4Constructors return (T, error).
  5. 5The HTTP context never leaves the controllers.
  6. 6No utils, helpers or common packages.

Change at this station

Already speak Rails? You speak Bogie.

Rails shops are Bogie’s first passengers: when the team needs one fast, network-facing Go service, the commands and folders are already in their fingers. Go teams get the same thing without ever having written Ruby.

Read the From Rails guide →
RailsBogie
rails new blogbogie new blog
rails serverbogie s
bin/dev, Springbogie server (reload built in)
rails g scaffoldbogie g scaffold
rails g authenticationbogie g authentication
rails g jobbogie g job
rails db:migratebogie db:migrate
rails credentials:editbogie credentials:edit
rails testbogie t
config/routes.rbapp/controllers/routes.go
app/models/post.rbapp/domain/post.go
app/models/posts.go
db/queries/posts.sql

Not the same train

Bogie vs nearby tools.

Three projects get mentioned in the same breath because they all put Rails and Go in one sentence. They solve different problems.

Bogiego-on-rails (gem)Andurel
WhatStandalone Go CLI, Rails-like generators for API servicesRails generator that emitted Go from a Rails appRails-like Go with views
FitsOne Go API beside RailsLegacy generate-from-Rails workflowFull-stack Go wanting views

In the box

From first commit to production, on one track.

AGENTS.md · CLAUDE.md

Written for agents and humans

Every app ships the six rules, every command and a recipe per generator. Because nothing is implicit, an agent’s change is a diff you can actually review.

bin/ci

CI runs on your machine

bin/ci is the whole pipeline: tests with race detection, gofmt, vet and golangci-lint. No hosted runner to wait on.

bogie server · bogie worker

Reload on save

Development runs the server and the worker under Air: save a file and it rebuilds and restarts. Migrations are embedded in the binary and applied at boot, so a new one is picked up on the next reload too. Production runs the compiled binary, untouched.

Kamal

Deploys like your Rails app

A container, a server, one deploy tool. The service migrates at boot, so shipping is one command.

bogie d

Undo is a command

Destroy removes what a generator made, registration included. --pretend shows the change before it happens.

bogie app:update

Upgrades that respect your edits

A three-way merge brings a newer layout into an app you have already changed, instead of overwriting it.

credentials:edit

Secrets, encrypted in the repo

Per-environment encrypted settings, edited and read the way Rails taught you.

One Go service next to your Rails app, not a rewrite.

We moved the parts of our system that needed heavy concurrency and high load to Go, and kept the rest in Rails. Go handles that work well, but it gives you so many choices that building the app itself became the hard part. Bogie brings Rails's convention over configuration to that one service.

Questions

Short answers, before the long guides.

What is Bogie?

A command-line tool that scaffolds an API-only, Postgres-only Go service with Rails-style generators: bogie new, g scaffold, g model, g controller, g job, g authentication. It writes plain Go on top of Gin, sqlc, goose, River and Kamal, and the generated app never imports Bogie.

Is there a Rails for Go?

Bogie gives Rails-style generators for an API-only Go service that sits beside your Rails app. It is not a full-stack Rails clone. For a Rails-like Go framework with views, see Andurel.

How is Bogie different from go-on-rails / Go on Rails?

The go-on-rails gem generated Go code from an existing Rails app, and that project is separate and archived. Bogie is a standalone Go CLI: bogie new / g scaffold with a Rails-like layout, and the generated app never imports Bogie.

Is Bogie a Go web framework?

No. There is no runtime, no base class and no DSL. Bogie renders templates and shells out to tools the Go ecosystem already ships. Delete the tool and the app does not notice. For a Rails-like Go framework with views, see Andurel.

Do I need Rails to use Bogie?

No. It is built for the Rails shop that needs one network-facing Go service beside its app, so the commands, layout and vocabulary follow Rails. A Go team can use it without ever having written Ruby.

Which databases does Bogie support?

Postgres only, by design. Background jobs run on River, which uses the same Postgres, so there is no Redis. SQLite was considered and declined until River's SQLite driver is stable.

How do I install Bogie?

go install github.com/bogie-go/bogie@latest, with Go 1.25 or newer. Postgres runs in Docker from the generated app's own compose file. There is no Homebrew tap and nothing prebuilt.

How does a generated app get upgraded?

bogie app:update is a three-way merge: it renders what the recorded Bogie version generated, what the current version generates, and merges each template-owned file with your edits kept. Only a real overlap leaves conflict markers.

Can an AI agent work in a Bogie app?

Yes. Every generated app ships an AGENTS.md with the six rules, every command and a recipe per task, and the generators register code above visible marker comments, so an agent's change is a diff a person can review.

All aboard.

Four steps to a running JSON API on Postgres: bogie new, make up, bogie db:prepare, bogie server. Migrations, tests and a deploy path come already laid. You need Go 1.26 or newer, and Docker for the local Postgres.

Terminal
$ go install github.com/bogie-go/bogie@latest
$ bogie new blog
$ cd blog && make up && bogie db:prepare && bogie server
# then
$ bogie g scaffold post title:string body:text
$ bin/ci