store

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 20, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

GoFr Store Generator

A CLI tool that generates GoFr store layer code from YAML configuration files, creating type-safe database access methods with proper context support.

Quick Start

Installation

The store generator is included with gofr-cli. Ensure you have GoFr CLI installed:

go install gofr.dev/cli/gofr@latest
Your First Store
  1. Initialize a store configuration:

    gofr store init -name=user
    

    This creates:

    • stores/store.yaml — central configuration template
    • stores/all.go — store registry factory (auto-generated)
    • stores/user/interface.go — initial interface stub
    • stores/user/user.go — initial implementation stub (editable)
  2. Edit stores/store.yaml with your models and queries (see Configuration Reference below).

  3. Generate the store code:

    gofr store generate
    
  4. Use in your application:

    import "your-project/stores/user"
    
    userStore := user.NewUserStore()
    result, err := userStore.GetUserByID(ctx, 123)
    

Basic Usage

Commands
# Initialize a new store directory and store.yaml configuration file
# The -name flag is required
gofr store init -name=<store-name>

# Generate store code from a store.yaml
# Default config path: stores/store.yaml
gofr store generate

# Generate store code from a specific config file
gofr store generate -config=custom-store.yaml
Project Structure

After gofr store init -name=user:

stores/
├── store.yaml          # Central configuration template (edit this)
├── all.go              # Store registry factory (auto-generated, DO NOT EDIT)
└── user/
    ├── interface.go    # UserStore interface stub (DO NOT EDIT — regenerated by generate)
    └── user.go         # userStore implementation stub (editable — add your SQL logic here)

After gofr store generate:

stores/
├── store.yaml          # Central configuration (source of truth)
├── all.go              # Store registry factory (DO NOT EDIT)
└── user/
    ├── interface.go    # UserStore interface (DO NOT EDIT — regenerated each time)
    ├── userStore.go    # userStore implementation boilerplate (add SQL logic here)
    └── user.go         # User model (if defined in YAML, DO NOT EDIT)
Using Generated Stores

Option 1: Direct initialization

import "your-project/stores/user"

userStore := user.NewUserStore()
result, err := userStore.GetUserByID(ctx, id)

Option 2: Using the registry

import (
    "your-project/stores"
    "your-project/stores/user"
)

// GetStore returns a factory-created instance; cast to the correct interface
userStore := stores.GetStore("user").(user.UserStore)
result, err := userStore.GetUserByID(ctx, id)

💡 Note: stores.All() returns a map[string]func() any — a map of factory functions, not active instances. Use stores.GetStore(name) for convenient access.

Integration Example
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.Run()
}

Configuration Reference

Basic YAML Structure
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"

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"'
Store Configuration
Field Description Required
name Store identifier (used in registry) Yes
package Go package name Yes
output_dir Directory for generated files Optional (defaults to stores/<name>)
interface Interface name — recommended: <Name>Store (e.g., UserStore) Optional (defaults to <Name>Store)
implementation Implementation struct name (e.g., userStore) Optional (defaults to <name>Store)
queries Array of database queries Optional

⚠️ Naming Convention: The registry (stores/all.go) automatically appends "Store" when building constructor calls. To avoid compilation errors, always name your interface as <Name>Store (e.g., UserStore) and the generated constructor will be New<Name>Store().

Models

Generate a new model:

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"'

Reference an existing model:

models:
  - name: "User"
    path: "../models/user.go"
    package: "your-project/models"
Queries

Query Types:

  • select - SELECT queries
  • insert - INSERT queries
  • update - UPDATE queries
  • delete - DELETE queries

Return Types:

  • single - Returns (Model, error)
  • multiple - Returns ([]Model, error)
  • count - Returns (int64, error)
  • custom - Returns (any, error)

Example Query:

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 ID"
Multiple Stores

You can define multiple stores in a single YAML file. Each store gets its own directory and the registry (stores/all.go) tracks all of them.

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: [...]

Accessing multiple stores from the registry:

userStore    := stores.GetStore("user").(user.UserStore)
productStore := stores.GetStore("product").(product.ProductStore)

Generated Code Examples

Interface
// 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)
}
Implementation
// 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 query using ctx.SQL()
    var result User
    // err := ctx.SQL().QueryRowContext(ctx, sql, id).Scan(&result.ID, ...)
    return result, nil
}

💡 Note: The generator creates method signatures and boilerplate only. You must implement the actual SQL execution in the // TODO sections using ctx.SQL() methods.

Model
// Code generated by gofr.dev/cli/gofr. DO NOT EDIT.
package user

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"
}

Best Practices

  1. Know Which Files Are Auto-Generated: Only interface.go and all.go are marked DO NOT EDIT and are overwritten on every gofr store generate. The implementation stub (<name>.go or <name>Store.go) created by gofr store init is editable — this is where you add your SQL logic.
  2. Use <Name>Store Interface Names: This ensures the registry and constructor align correctly.
  3. Commit your YAML: Treat store.yaml as source of truth. Re-run gofr store generate after every change.
  4. Reference Existing Models: If you already have model structs, use the path + package fields to avoid duplication.

For a complete working example, see store/example.yaml.

Documentation

Index

Constants

View Source
const AllStoresTemplate = `` /* 668-byte string literal not displayed */

AllStoresTemplate is the template for generating all.go registry.

View Source
const ImplementationTemplate = `// Code generated by gofr.dev/cli/gofr. DO NOT EDIT.
package {{ .Store.Package }}

import (
{{- range .Imports }}
    {{- if .Alias }}{{ .Alias }} "{{ .Path }}"{{ else }}"{{ .Path }}"{{ end }}
{{- end }}
)

// {{ .Store.Implementation }} implements {{ .Store.Interface }}
type {{ .Store.Implementation }} struct {
    // Add any dependencies here (e.g., database connection)
}

// New{{ .Store.Interface }} creates a new instance of {{ .Store.Interface }}
func New{{ .Store.Interface }}() {{ .Store.Interface }} {
    return &{{ .Store.Implementation }}{}
}
{{- range .Store.Queries }}

// {{ .Name }} {{ if .Description }}{{ .Description }}{{ else }}executes the {{ .Name }} query{{ end }}
func (s *{{ $.Store.Implementation }}) {{ .Name }}(ctx *gofr.Context{{- range .Params }}, {{ .Name }} {{ .Type }}` +
	`{{- end }}) (
    {{- if eq .Returns "single" }}{{ .Model | getModelType }}
    {{- else if eq .Returns "multiple" }}[]{{ .Model | getModelType }}
    {{- else if eq .Returns "count" }}int64
    {{- else }}any
    {{- end }}, error) {

    // TODO: Implement {{ .Name }} query
    // SQL: {{ .SQL }}
    {{- if eq .Type "select" }}
        {{- if eq .Returns "single" }}
    var result {{ .Model | getModelType }}
    // Implement single row selection using ctx.SQL()
    // Example: err := ctx.SQL().QueryRowContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}}, ` +
	`{{end}}{{ $param.Name }}{{end}}).Scan(&result.Field1, &result.Field2, ...)
    return result, nil
        {{- else if eq .Returns "multiple" }}
    var results []{{ .Model | getModelType }}
    // Implement multiple row selection using ctx.SQL()
    // Example: rows, err := ctx.SQL().QueryContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}},` +
	` {{end}}{{ $param.Name }}{{end}})
    return results, nil
        {{- else if eq .Returns "count" }}
    var count int64
    // Implement count query using ctx.SQL()
    // Example: err := ctx.SQL().QueryRowContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}}, ` +
	`{{end}}{{ $param.Name }}{{end}}).Scan(&count)
    return count, nil
        {{- else }}
    // Implement custom return type
    return nil, nil
        {{- end }}
    {{- else if eq .Type "insert" }}
    // Implement insert operation using ctx.SQL()
    // Example: result, err := ctx.SQL().ExecContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}}, ` +
	`{{end}}{{ $param.Name }}{{end}})
    return {{ if eq .Returns "single" }}{{ .Model }}{}, {{ else if eq .Returns "count" }}int64(0), {{ else }}nil, ` +
	`{{ end }}nil
    {{- else if eq .Type "update" }}
    // Implement update operation using ctx.SQL()
    // Example: result, err := ctx.SQL().ExecContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}},` +
	` {{end}}{{ $param.Name }}{{end}})
    return {{ if eq .Returns "count" }}int64(0), {{ else }}nil, {{ end }}nil
    {{- else if eq .Type "delete" }}
    // Implement delete operation using ctx.SQL()
    // Example: result, err := ctx.SQL().ExecContext(ctx, "{{ .SQL }}", {{ range $i, $param := .Params }}{{if $i}}, ` +
	`{{end}}{{ $param.Name }}{{end}})
    return {{ if eq .Returns "count" }}int64(0), {{ else }}nil, {{ end }}nil
    {{- end }}
}
{{- end }}
`

ImplementationTemplate is the template for generating store implementations.

View Source
const InitialInterfaceTemplate = `` /* 363-byte string literal not displayed */

InitialInterfaceTemplate is the template for initial interface generation.

View Source
const InitialStoreTemplate = `// Code generated by gofr.dev/cli/gofr.
package {{ .PackageName }}

// import (
//     "gofr.dev/pkg/gofr"
// )

// {{ .ImplementationName }} implements the {{ .InterfaceName }} interface
type {{ .ImplementationName }} struct {
    // Add any dependencies here (e.g., database connection)
}

// New{{ .InterfaceName }} creates a new instance of {{ .InterfaceName }}
func New{{ .InterfaceName }}() {{ .InterfaceName }} {
    return &{{ .ImplementationName }}{}
}

// Add your store method implementations here
// Example:
// func (s *{{ .ImplementationName }}) GetUserByID(ctx *gofr.Context, id int64) (User, error) {
//     // TODO: Implement GetUserByID query
//     var result User
//     err := ctx.SQL().QueryRowContext(ctx, "SELECT id, name FROM users WHERE id = ?", id).` +
	`Scan(&result.ID, &result.Name)
//     return result, err
// }
`

InitialStoreTemplate is the template for initial store implementation.

View Source
const InterfaceTemplate = `` /* 688-byte string literal not displayed */

InterfaceTemplate is the template for generating store interfaces.

View Source
const ModelTemplate = `// Code generated by gofr.dev/cli/gofr. DO NOT EDIT.
package {{ .Store.Package }}

import (
    "time"
)

// {{ .Model.Name }} represents the {{ .Model.Name }} model
type {{ .Model.Name }} struct {
{{- range .Model.Fields }}
    {{ .Name }} {{ .Type }} ` + "`{{ .Tag }}`" + `
{{- end }}
}

// TableName returns the table name for {{ .Model.Name }}
func ({{ .Model.Name }}) TableName() string {
    return "{{ .Model.Name | lower }}"
}
`

ModelTemplate is the template for generating data models.

View Source
const StoreConfigTemplate = `` /* 1433-byte string literal not displayed */

StoreConfigTemplate is the template for generating store.yaml configuration.

Variables

This section is empty.

Functions

func GenerateStore

func GenerateStore(ctx *gofr.Context) (any, error)

GenerateStore generates store layer functions based on YAML configuration.

func InitStore

func InitStore(ctx *gofr.Context) (any, error)

InitStore creates the initial store structure and configuration.

Types

type Config

type Config struct {
	Version string  `yaml:"version"`
	Stores  []Info  `yaml:"stores"`
	Models  []Model `yaml:"models"`
}

Config represents the YAML configuration for store generation.

type Entry

type Entry struct {
	Name          string
	PackageName   string
	InterfaceName string
}

Entry represents a store entry for the all.go registry.

type Field

type Field struct {
	Name     string `yaml:"name"`
	Type     string `yaml:"type"`
	Tag      string `yaml:"tag,omitempty"`
	Nullable bool   `yaml:"nullable,omitempty"`
}

Field represents a model field.

type ImportInfo

type ImportInfo struct {
	Path  string
	Alias string
}

ImportInfo represents an import with its path and optional alias.

type Info

type Info struct {
	Name           string  `yaml:"name"`
	Package        string  `yaml:"package"`
	OutputDir      string  `yaml:"output_dir"`
	Interface      string  `yaml:"interface"`
	Implementation string  `yaml:"implementation"`
	Queries        []Query `yaml:"queries"`
}

Info contains store-level configuration.

type Model

type Model struct {
	Name    string  `yaml:"name"`
	Fields  []Field `yaml:"fields,omitempty"`
	Path    string  `yaml:"path,omitempty"`
	Package string  `yaml:"package,omitempty"`
}

Model represents a data model.

type ModelAliasMap

type ModelAliasMap map[string]string

ModelAliasMap maps model names to their import aliases for type resolution.

type Query

type Query struct {
	Name        string            `yaml:"name"`
	SQL         string            `yaml:"sql"`
	Type        string            `yaml:"type"`
	Model       string            `yaml:"model,omitempty"`
	Params      []QueryParam      `yaml:"params,omitempty"`
	Returns     string            `yaml:"returns,omitempty"`
	Description string            `yaml:"description,omitempty"`
	Tags        map[string]string `yaml:"tags,omitempty"`
	UseSelect   bool              `yaml:"use_select,omitempty"`
	Transaction bool              `yaml:"transaction,omitempty"`
}

Query represents a database query.

type QueryParam

type QueryParam struct {
	Name string `yaml:"name"`
	Type string `yaml:"type"`
}

QueryParam represents a query parameter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL