Repositories
Learn how to access the Bun ORM using repositories in Caesar.
Overview
Repositories are the recommended pattern for accessing the Bun ORM in Caesar. They provide a clean, organized way to manage your database operations with basic CRUD functionalities. By convention, repositories are located in the ./app/repositories
folder.
Defining a Repository
Here's how you define a repository in Caesar:
package repositories
import (
"myapp/app/models"
"github.com/caesar-rocks/orm"
)
type UsersRepository struct {
*orm.Repository[models.User]
}
func NewUsersRepository(db *orm.Database) *UsersRepository {
return &UsersRepository{Repository: &orm.Repository[models.User]{Database: db}}
}
In the example above, the UsersRepository
is defined to handle CRUD operations for the User
model.
CRUD Queries
The repository pattern in Caesar provides the following CRUD queries:
- Create: Inserts a new record into the database.
- FindAll: Retrieves all records from the database.
- FindOneBy: Finds a single record based on a specified field and value.
- UpdateOneWhere: Updates a record based on a specified field and value.
- DeleteOneWhere: Deletes a record based on a specified field and value.
Custom Queries
While the repository provides basic CRUD operations, you can easily override or add your own custom queries in the repository file. This gives you the flexibility to tailor database interactions to your application's specific needs.
Example of a Custom Query:
func (r *UsersRepository) FindActiveUsers(ctx context.Context) ([]models.User, error) {
var users []models.User
err := r.NewSelect().
Model(&users).
Where("status = ?", "active").
Scan(ctx)
if err != nil {
return nil, err
}
return users, nil
}
In the custom query example above, the FindActiveUsers
method is defined to find all users with an active status.
Usage Example with Controllers
Here's an example of how you might use a repository in your application to manage records within a controller:
User Controller
Define a controller that uses the UsersRepository
:
package controllers
import (
"context"
"log"
"net/http"
"myapp/app/models"
"myapp/app/repositories"
caesar "github.com/caesar-rocks/core"
)
type UsersController struct {
repo *repositories.UsersRepository
}
func NewUsersController(repo *repositories.UsersRepository) *UsersController {
return &UsersController{repo: repo}
}
func (ctr *UsersController) CreateUser(ctx *caesar.CaesarCtx) error {
var data struct {
Email string `json:"email"`
FullName string `json:"full_name"`
Password string `json:"password"`
}
if err := ctx.DecodeJSON(&data); err != nil {
return caesar.NewError(http.StatusBadRequest, "Invalid request payload")
}
hashedPassword := HashPassword(data.Password) // Assume this function is defined elsewhere
user := &models.User{
Email: data.Email,
FullName: data.FullName,
Password: hashedPassword,
}
if err := ctr.repo.Create(ctx.Context(), user); err != nil {
return caesar.NewError(http.StatusInternalServerError, "Failed to create user")
}
return ctx.SendJSON(user, http.StatusCreated)
}
func (ctr *UsersController) GetAllUsers(ctx *caesar.CaesarCtx) error {
users, err := ctr.repo.FindAll(ctx.Context())
if err != nil {
return caesar.NewError(http.StatusInternalServerError, "Failed to fetch users")
}
return ctx.SendJSON(users)
}
func (ctr *UsersController) GetActiveUsers(ctx *caesar.CaesarCtx) error {
users, err := ctr.repo.FindActiveUsers(ctx.Context())
if err != nil {
return caesar.NewError(http.StatusInternalServerError, "Failed to fetch active users")
}
return ctx.SendJSON(users)
}
Registering Routes
Register the controller methods in your application's route configuration:
package config
import (
"myapp/app/controllers"
"myapp/app/repositories"
caesar "github.com/caesar-rocks/core"
)
func RegisterRoutes(usersRepo *repositories.UsersRepository) *caesar.Router {
usersController := controllers.NewUsersController(usersRepo)
router := caesar.NewRouter()
router.Post("/users", usersController.CreateUser)
router.Get("/users", usersController.GetAllUsers)
router.Get("/users/active", usersController.GetActiveUsers)
return router
}
Registering Repositories as Providers
To be able to use the repository in controllers or other parts of your application, you need to register it as a provider in the config/app.go
file:
package config
import (
"myapp/app/repositories"
"github.com/caesar-rocks/core"
)
func ProvideApp() *core.App {
app := core.NewApp(&core.AppConfig{
Addr: ":8080",
})
// Register repositories
app.RegisterProviders(
repositories.NewUsersRepository,
)
// Other providers and invokers
app.RegisterProviders(
// other providers ...
)
app.RegisterInvokers(
// invokers ...
)
return app
}
In this example, the NewUsersRepository
is registered as a provider, which makes it available for dependency injection in other parts of the application, such as controllers.
Summary
Repositories in Caesar provide a structured way to interact with your database using the Bun ORM. They encapsulate basic CRUD operations and allow you to add or override custom queries to fit your specific requirements. By following the convention of placing repositories in the ./app/repositories
folder, you can easily manage and locate your database access logic.
By leveraging controllers, you can cleanly separate your application's business logic and database interactions, ensuring a maintainable and scalable codebase. Don't forget to register your repositories as providers in the config/app.go
file to make them available throughout your application.