CRUD APIs
Build data APIs quickly from Resource and Store
The crud package builds standard data APIs from resource.Resource and crud.Store. It is not a Provider and does not need runa.Install; it is usually used directly during route registration.
CRUD fits admin panels, data management APIs, and repeated list/detail/save/delete endpoints. If an endpoint is highly custom, use Resource and write the handler yourself.
Install
go get github.com/duxweb/runa/crud
If you use Oro as the storage adapter:
go get github.com/duxweb/runa/crud/orostore
Basic flow
The CRUD flow is:
route.Group -> resource.New -> crud.New -> configure pagination, sorting, validation, and output transform
Minimal structure:
users := resource.New(route.Default().Group("/admin"), "/users").
Name("user").
Summary("User").
Tags("User")
crud.New[User, UserQuery](users, userStore)
userStore must implement crud.Store[User, UserQuery].
Store interface
type Store[Model any, Query any] interface {
Query(ctx *crud.Context[Model]) (Query, error)
List(ctx *crud.Context[Model], query Query) ([]*Model, core.ListMeta, error)
Show(ctx *crud.Context[Model], query Query) (*Model, error)
Create(ctx *crud.Context[Model]) (*Model, error)
Edit(ctx *crud.Context[Model], query Query) (*Model, error)
Store(ctx *crud.Context[Model], query Query, fields []string) (*Model, error)
Delete(ctx *crud.Context[Model], query Query) error
Tx(ctx *crud.Context[Model], fn func(ctx *crud.Context[Model]) error) error
}
For beginners, Store is the part that actually reads and writes data. CRUD connects HTTP actions to Store methods.
Choose enabled actions
crud.New[User, UserQuery](users, userStore).
Actions(crud.ListAction, crud.ShowAction, crud.CreateAction, crud.EditAction, crud.DeleteAction)
By default, CRUD registers list, show, create, edit, and delete. Enable store, batch, import, export, or soft delete only when needed.
Pagination, sorting, and filters
crud.New[User, UserQuery](users, userStore).
Page(20, 100).
Sort("id", "desc").
SortFields(crud.SortField{Name: "name"}).
Filters(
filter.Eq[int]("status"),
filter.Like("name"),
)
This means: default page size 20, maximum 100, default sort by id desc, allow sorting by name, and allow filtering by status and name.
Output transform
Database models are not always safe or suitable as API responses. Use Transform to return output structs.
type UserOutput struct {
ID int `json:"id"`
Name string `json:"name"`
}
crud.New[User, UserQuery](users, userStore).
Transform[UserOutput](func(c *crud.Context[User], model *User) UserOutput {
return UserOutput{ID: model.ID, Name: model.Name}
})
Validate input
crud.New[User, UserQuery](users, userStore).
Validate(func(c *crud.Context[User], v *validate.Validator) {
v.Field("Name").Required("Name is required")
}, crud.CreateAction, crud.EditAction)
The optional action arguments limit validation to selected actions.
Import and export
CRUD includes CSV import and export by default. XLSX support is optional and lives in the crud/excelize module, so projects that only use CRUD do not pull in Excel dependencies.
go get github.com/duxweb/runa/crud/excelize
Register the XLSX format with a blank import in your application:
import _ "github.com/duxweb/runa/crud/excelize"
Then allow the format on import/export actions:
crud.New[User, UserQuery](users, userStore).
Export[UserOutput](exportUser, func(e *crud.Exporter[User, UserOutput]) error {
e.Name("users").Formats("xlsx", "csv")
return nil
})
Import and export are advanced features. First get list, show, create, edit, and delete working.
Oro Store
If the project uses Oro ORM, use crud/orostore:
crud.New[User, *oro.ModelQuery[User]](users, orostore.Store[User](db))
crud/orostore adapts github.com/duxweb/oro and fits projects already using Oro ORM. See CRUD Oro Store for the full setup.
Common mistakes
Treating CRUD as a Provider
crud does not need app.Install(crud.Provider()). It is a builder called during route registration.
Mixing Store responsibilities
CRUD only orchestrates HTTP to Store. Real query, save, transaction, and soft-delete logic still belongs in Store.