Supabase toolkit for Go projects.
go install github.com/01JAMIL/supago.git/cmd/supago@latestsupago init
# configure .env with your database URL (SUPAGO_DATABASE_URL)
supago generate allGiven a database with users and tasks tables, supago generate all produces:
internal/
├── adapters/
│ └── supago/
│ ├── models.go # All Go structs
│ ├── users/
│ │ └── repository.go # Repository implementation
│ └── tasks/
│ └── repository.go # Repository implementation
├── users/
│ ├── service.go # Business logic (starting point)
│ └── handler.go # HTTP handlers (starting point)
├── tasks/
│ ├── service.go # Business logic (starting point)
│ └── handler.go # HTTP handlers (starting point)
└── json/
└── json.go # JSON helpers (generated, DO NOT EDIT)
// internal/adapters/supago/models.go
package supago
type User struct {
ID int64 `json:"id"`
FullName *string `json:"fullName"`
Email *string `json:"email"`
CreatedAt time.Time `json:"createdAt"`
}
type Task struct {
ID int64 `json:"id"`
TaskName *string `json:"taskName"`
IsDone *bool `json:"isDone"`
UserID *int64 `json:"userId"`
CreatedAt time.Time `json:"createdAt"`
}// internal/adapters/supago/users/repository.go
package users
type Repository struct {
pool *pgxpool.Pool
}
func NewRepository(pool *pgxpool.Pool) *Repository { ... }
func (r *Repository) List(ctx context.Context) ([]supago.User, error) { ... }
func (r *Repository) GetByID(ctx context.Context, id int64) (*supago.User, error) { ... }
func (r *Repository) Create(ctx context.Context, user *supago.User) error { ... }
func (r *Repository) Update(ctx context.Context, user *supago.User) error { ... }
func (r *Repository) Delete(ctx context.Context, id int64) error { ... }// internal/users/service.go — starting point, modify as needed
package users
type Service struct {
repo *usersrepo.Repository
}
func (s *Service) List(ctx context.Context) ([]supago.User, error) {
return s.repo.List(ctx)
}// internal/users/handler.go — starting point, modify as needed
func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
items, err := h.svc.List(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.Write(w, http.StatusOK, items)
}| Layer | Path | Ownership |
|---|---|---|
| Infrastructure | internal/adapters/supago/ |
Generated by SupaGo, DO NOT EDIT |
| Application | internal/{table}/ |
Generated as starting point, modify freely |
The infrastructure layer owns the models and repository implementations. The application layer owns the services and handlers — generated once as a starting point, then fully owned by the developer.
supago.yaml is generated by supago init:
version: 1
database:
driver: postgres
url: ${SUPAGO_DATABASE_URL}
generation:
output: internal/adapters/supagoCustom queries let you generate repository methods for queries you define yourself, in addition to the standard CRUD operations.
The generated CRUD repositories cover common operations (List, GetByID, Create, Update, Delete). Real applications also need lookups like "find a user by email" or "list posts by author". Custom queries generate those methods for you, with safe parameterized SQL.
Define queries per table under generation.queries in supago.yaml:
generation:
output: internal/adapters/supago
queries:
users:
- name: FindByEmail
where:
- email = $1
- name: FindByEmailAndName
where:
- email = $1
- full_name = $2
- name: ListByActive
where:
- active = $1
posts:
- name: ListByAuthor
where:
- author_id = $1The query name determines the generated method and its return type:
- Names starting with
FindorGetgenerate a method returning a single item (*Model, usingQueryRow). - Names starting with
Listgenerate a method returning a slice ([]Model, usingQuery).
Each where entry is a column OP $N condition. Column references are validated against the actual database schema.
Generate the repository methods for your configured queries:
supago generate queryThis writes <outputDir>/<table>/queries.go for each configured table, extending the existing generated repository. Run supago generate all (or at least repository) once first so the base repositories exist.
If no queries are configured, the command prints a helpful message instead of failing.
For the configuration above, supago generate query produces:
// internal/adapters/supago/users/queries.go
package users
func (r *Repository) FindByEmail(ctx context.Context, email string) (*supago.User, error) {
row := r.pool.QueryRow(ctx, "SELECT id, full_name, email, created_at FROM users WHERE email = $1", email)
// ...
}
func (r *Repository) ListByActive(ctx context.Context, active bool) ([]supago.User, error) {
// ...
}Generated methods use context.Context, the existing pgxpool.Pool abstraction, the generated models, and parameterized SQL — no string concatenation, so the queries are safe from SQL injection.
Custom queries are intentionally simple in the first iteration:
WHEREconditions only — no joins, subqueries, aggregations, or transactions.- Arguments are strictly positional (
$1,$2, …, used sequentially). - No
ORDER BY,LIMIT, orOFFSET. - Conditions are combined with
AND.
These can be extended in future versions.
# .env
SUPAGO_DATABASE_URL=postgresql://postgres:password@localhost:5432/postgres| Command | Description |
|---|---|
init |
Initialize SupaGo in an existing Go project |
inspect |
Inspect the database schema and display tables, columns, types, and constraints |
generate model |
Generate Go structs from database tables |
generate repository |
Generate Go repositories with full CRUD per table |
generate query |
Generate custom repository queries defined in supago.yaml |
generate crud |
Generate services and HTTP handlers using net/http as a starting point |
generate all |
Run the full generation pipeline (model, repository, crud) |
version |
Print the version of SupaGo |
MIT
