Goravel evaluation project

Laravel patterns,
written in Go

A SaaS scaffold built with Goravel to answer the question: can Go's Laravel clone handle real SaaS patterns? Auth, CRM, CMS, and a REST API — zero config, SQLite by default.

View on GitHub Quick start
Go 1.22+ Goravel SQLite JWT Auth Tailwind CSS MIT

What's included

Dual-mode Auth

Session-based login for the web UI. JWT Bearer tokens for the REST API. First registered user gets the admin role.

CRM

Client records with status tracking (lead / active / inactive), company, and notes. Full CRUD via both web UI and API.

CMS

Pages with title, slug, Markdown content, and a published toggle. Slug uniqueness enforced at the DB level.

REST API

JSON endpoints under /api/v1/* protected with JWT Bearer. Token auth via /api/auth/*.

Zero-config SQLite

Runs with no database server. WASM-based SQLite driver — no CGO. Swap to Postgres or MySQL by changing two files.

Hot reload

Air configured out of the box. Run air and edits rebuild instantly.

Quick start

Requires Go 1.22+. No database server needed.

bash
# Clone and install
git clone https://github.com/alexramsey92/goravel-test.git
cd goravel-test
go mod tidy

# Set up environment
cp .env.example .env
go run . artisan key:generate
go run . artisan jwt:secret

# Migrate and run
go run . artisan migrate
go run .

Open http://localhost:3000 and register your first account — it automatically receives the admin role.

REST API

Method Path Auth
POST /api/auth/register
POST /api/auth/login
GET /api/v1/me Bearer
GET /api/v1/clients Bearer
POST /api/v1/clients Bearer
GET /api/v1/clients/:id Bearer
DELETE /api/v1/clients/:id Bearer
GET /api/v1/pages Bearer
POST /api/v1/pages Bearer
bash — example
# Login and capture token
TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/login \
  -d "email=you@example.com&password=secret" | jq -r .token)

# Fetch current user
curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/me

# List clients
curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/clients

Laravel → Goravel

Built by a Laravel developer. Here's how the concepts translate.

Laravel Goravel
Auth::user() facades.Auth(ctx).User(&user)
Hash::make($pw) facades.Hash().Make(pw)
Hash::check($pw, $hash) facades.Hash().Check(pw, hash) // bool only
Model::query()->where() facades.Orm().Query().Where()
->count() ->Count() // (int64, error)
redirect('/dashboard') ctx.Response().Redirect(302, "/dashboard")
view('clients.index', [...]) ctx.Response().View().Make("clients/index.tmpl", map[string]any{...})
php artisan migrate go run . artisan migrate

Gotchas

Template naming

Every .tmpl file must be wrapped in {{define "path/name.tmpl"}}...{{end}}. Go's ParseFiles() names templates by base filename only — without the define wrapper, files with the same name (like index.tmpl) silently overwrite each other.

Session middleware

You must apply sessionmiddleware.StartSession() to all web routes or facades.Auth(ctx).Login() will panic with "session driver is not set". It's the equivalent of Laravel's web middleware group.

GroupFunc signature

Route groups take func(router route.Router), not func(). The router parameter is how you register routes inside the group.

Middleware type

Middleware() takes func(http.Context), not strings like "auth:api". You must define a middleware struct and pass its .Handle method.

See the full source

All code is on GitHub — including the README with the complete Laravel→Goravel mapping table, database switching guide, and project structure overview.

alexramsey92/goravel-test