docs/references/gofrcli/store/page.md
Available since:
gofr-cliv0.8.1
The gofr store command is a code generator that creates type-safe data access layers from YAML configuration files. It eliminates boilerplate code while maintaining GoFr's best practices for observability and context management.
*gofr.Context for built-in observability.stores/all.go.Create a new store directory and a store.yaml configuration template. The -name flag is required.
gofr store init -name=<store-name>
Example:
gofr store init -name=user
This creates the following structure:
stores/store.yaml — Configuration file template (shared across all stores).stores/all.go — Store registry factory (auto-generated, DO NOT EDIT).stores/user/interface.go — Initial interface stub (DO NOT EDIT — regenerated by generate).stores/user/user.go — Initial implementation stub (editable — add your SQL logic here).Generate or update Go code from your store configuration file.
gofr store generate
💡 Note: By default, this command looks for the configuration at
stores/store.yaml. To use a different path, use the-configflag:bashgofr store generate -config=path/to/store.yaml
Step 1: Initialize Configuration
gofr store init -name=user
Step 2: Define Your Store in stores/store.yaml
version: "1.0"
stores:
- name: "user"
package: "user"
output_dir: "stores/user"
interface: "UserStore"
implementation: "userStore"
queries:
- name: "GetUserByID"
sql: "SELECT id, name, email FROM users WHERE id = ?"
type: "select"
model: "User"
returns: "single"
params:
- name: "id"
type: "int64"
description: "Retrieves a user by their ID"
- name: "GetAllUsers"
sql: "SELECT id, name, email FROM users"
type: "select"
model: "User"
returns: "multiple"
description: "Retrieves all users"
models:
- name: "User"
fields:
- name: "ID"
type: "int64"
tag: 'db:"id" json:"id"'
- name: "Name"
type: "string"
tag: 'db:"name" json:"name"'
- name: "Email"
type: "string"
tag: 'db:"email" json:"email"'
Step 3: Generate Store Code
gofr store generate
This generates:
stores/
├── store.yaml # Central Configuration
├── all.go # Store registry factory (auto-generated)
└── user/
├── interface.go # UserStore interface definition
├── userStore.go # userStore implementation boilerplate
└── user.go # User model struct
Step 4: Use in Your Application
package main
import (
"gofr.dev/pkg/gofr"
"your-project/stores/user"
)
func main() {
app := gofr.New()
userStore := user.NewUserStore()
app.GET("/users/{id}", func(ctx *gofr.Context) (interface{}, error) {
id, _ := strconv.ParseInt(ctx.PathParam("id"), 10, 64)
return userStore.GetUserByID(ctx, id)
})
app.GET("/users", func(ctx *gofr.Context) (interface{}, error) {
return userStore.GetAllUsers(ctx)
})
app.Run()
}
You can define all stores in a single YAML file. Each store gets its own output directory and all are registered into the same stores/all.go registry.
version: "1.0"
stores:
- name: "user"
package: "user"
output_dir: "stores/user"
interface: "UserStore"
implementation: "userStore"
queries: [...]
- name: "product"
package: "product"
output_dir: "stores/product"
interface: "ProductStore"
implementation: "productStore"
queries: [...]
models:
- name: "User"
fields: [...]
- name: "Product"
fields: [...]
Generated structure:
stores/
├── all.go
├── user/
│ ├── interface.go
│ ├── userStore.go
│ └── user.go
└── product/
├── interface.go
├── productStore.go
└── product.go
Using the registry with multiple stores:
import (
"your-project/stores"
"your-project/stores/user"
"your-project/stores/product"
)
// stores.GetStore returns a factory-created instance
userStore := stores.GetStore("user").(user.UserStore)
productStore := stores.GetStore("product").(product.ProductStore)
💡 Note:
stores.All()returns amap[string]func() any— a map of factory functions, not active instances.stores.GetStore(name)calls the factory for you and returns the instance.
| Field | Description | Required |
|---|---|---|
name | Store identifier used in the registry key. | Yes |
package | Go package name for generated code. | Yes |
output_dir | Directory path where files will be generated. | Optional (defaults to stores/<name>) |
interface | Interface name — recommended: <Name>Store (e.g., UserStore). | Optional (defaults to <Name>Store) |
implementation | Private struct name for the implementation (e.g., userStore). | Optional (defaults to <name>Store) |
queries | List of database queries. | Optional |
⚠️ Naming Convention: The registry (
stores/all.go) uses a hardcoded<Name>Storepattern when generating constructor calls (e.g.,NewUserStore()). Always name your interface as<Name>Storeto avoid compilation errors.
select — SELECT queries.insert — INSERT queries.update — UPDATE queries.delete — DELETE queries.single — Returns (Model, error).multiple — Returns ([]Model, error).count — Returns (int64, error).custom — Returns (any, error).params:
- name: "id"
type: "int64"
- name: "email"
type: "string"
Supported parameter types include all Go primitive types, time.Time, and pointer types (e.g., *int64).
models:
- name: "User"
fields:
- name: "ID"
type: "int64"
tag: 'db:"id" json:"id"'
- name: "Name"
type: "string"
tag: 'db:"name" json:"name"'
- name: "CreatedAt"
type: "time.Time"
tag: 'db:"created_at" json:"created_at"'
This generates:
type User struct {
ID int64 `db:"id" json:"id"`
Name string `db:"name" json:"name"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
func (User) TableName() string {
return "user"
}
If you already have models defined elsewhere:
models:
- name: "User"
path: "../models/user.go"
package: "your-project/models"
interface.go)// Code generated by gofr.dev/cli/gofr. DO NOT EDIT.
package user
import "gofr.dev/pkg/gofr"
type UserStore interface {
GetUserByID(ctx *gofr.Context, id int64) (User, error)
GetAllUsers(ctx *gofr.Context) ([]User, error)
}
userStore.go)// Code generated by gofr.dev/cli/gofr. DO NOT EDIT.
package user
type userStore struct{}
func NewUserStore() UserStore {
return &userStore{}
}
func (s *userStore) GetUserByID(ctx *gofr.Context, id int64) (User, error) {
// TODO: Implement using ctx.SQL()
var result User
// err := ctx.SQL().QueryRowContext(ctx, sql, id).Scan(&result.ID, ...)
return result, nil
}
func (s *userStore) GetAllUsers(ctx *gofr.Context) ([]User, error) {
// TODO: Implement using ctx.SQL()
return []User{}, nil
}
// TODO: Implement sections with actual SQL execution using ctx.SQL() methods.<Name>Store Interface Names: The registry assumes this convention. E.g., interface: "UserStore" results in the constructor NewUserStore() and type assertion .(user.UserStore).store.yaml to keep your data access layer centrally configured.interface.go and all.go are marked DO NOT EDIT and are overwritten on every gofr store generate. The implementation stub (<name>.go) created by gofr store init is editable — this is where you add your SQL logic. The userStore.go generated by gofr store generate is also editable boilerplate.store.yaml. Re-run gofr store generate after any configuration change to sync the generated interfaces.For a complete working example of the store generator, see the store example in the gofr-cli repository.
For detailed configuration options and advanced usage, refer to the Store Generator README.