Lokstra Quick Reference

Fast lookup for common patterns, imports, and configurations.


Installation

# Install CLI
go install github.com/primadi/lokstra/cmd/lokstra@latest

# Install framework
go get github.com/primadi/lokstra

# Create new project
lokstra new myapp
lokstra new myapp -template 02_app_framework/01_medium_system

# Generate code from annotations
lokstra autogen .           # Manual generation
go run . --generate-only    # Force rebuild all

# Recommended: Use lokstra.Bootstrap() in main() for auto-generation

Common Imports

// Core framework
import "github.com/primadi/lokstra"
import "github.com/primadi/lokstra/core/request"
import "github.com/primadi/lokstra/core/service"
import "github.com/primadi/lokstra/core/deploy"
import "github.com/primadi/lokstra/lokstra_registry"

// Middleware
import "github.com/primadi/lokstra/middleware/recovery"
import "github.com/primadi/lokstra/middleware/request_logger"
import "github.com/primadi/lokstra/middleware/cors"
import "github.com/primadi/lokstra/middleware/body_limit"

// Built-in services
import "github.com/primadi/lokstra/services/dbpool_pg"
import "github.com/primadi/lokstra/services/redis"

Router Basics

Simple Router

r := lokstra.NewRouter("api")

// Basic routes
r.GET("/", func() string { return "Hello" })
r.POST("/users", createUser)
r.PUT("/users/{id}", updateUser)
r.DELETE("/users/{id}", deleteUser)

// Groups
v1 := r.Group("/v1")
v1.GET("/users", listUsers)

// Middleware
r.Use(recovery.Middleware(nil))
r.Use(cors.Middleware([]string{"*"}))

// Run
app := lokstra.NewApp("myapp", ":8080", r)
app.Run(30 * time.Second)

Handler Signatures

// 1. Simple return
func() string { return "Hello" }

// 2. With error
func(id string) (string, error) { return "User", nil }

// 3. Struct response
func(id string) (*User, error) { return &User{}, nil }

// 4. Context access
func(ctx *request.Context) error { return ctx.Api.Ok("data") }

// 5. Request body
func(ctx *request.Context, params *CreateUserParams) error {
    return ctx.Api.Created(params)
}

// 6. Path + body
func(ctx *request.Context, id string, params *UpdateParams) error {
    return ctx.Api.Ok(params)
}

// 7. Query parameters
type SearchParams struct {
    Q     string `query:"q" validate:"required"`
    Page  int    `query:"page" validate:"min=1"`
}
func(params *SearchParams) ([]Result, error) { return results, nil }

// @RouterService name="user-service", prefix="/api/users"
type UserService struct {
    // @Inject "user-repository"
    UserRepo UserRepository
}

// @Route "GET /{id}"
func (s *UserService) GetByID(p *GetUserParams) (*User, error) {
    return s.UserRepo.GetByID(p.ID)
}

// @Route "POST /"
func (s *UserService) Create(p *CreateUserParams) (*User, error) {
    u := &User{Name: p.Name, Email: p.Email}
    return s.UserRepo.Create(u)
}

// Generate code
// lokstra autogen .

Manual Service Factory (Advanced)

type UserService struct {
    UserRepo UserRepository
}

func (s *UserService) GetByID(id string) (*User, error) {
    return s.UserRepo.GetByID(id)
}

// Local factory
func UserServiceFactory(deps map[string]any, config map[string]any) any {
    return &UserService{
        UserRepo: deps["user-repository"].(UserRepository),
    }
}

// Remote factory (microservices)
func UserServiceRemoteFactory(deps map[string]any, config map[string]any) any {
    proxyService := config["remote"].(*proxy.Service)
    return NewUserServiceRemote(proxyService)
}

Service Registration

import "github.com/primadi/lokstra/lokstra_registry"

func registerServiceTypes() {
    lokstra_registry.RegisterServiceType(
        "user-service-factory",      // Type name
        UserServiceFactory,           // Local factory
        UserServiceRemoteFactory,     // Remote factory (optional)
    )
}

Lazy Loading

Services are loaded lazily (created on first access), but their dependencies are eagerly resolved when the service is created:

import "github.com/primadi/lokstra/core/service"

// Define lazy reference
var userService = service.LazyLoad[*UserService]("user-service")

func handler() {
    // First call loads service (thread-safe)
    users := userService.MustGet().GetAll()
    
    // Cached thereafter
    user := userService.MustGet().GetByID("123")
}

Domain Models

// Entity
type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// Request DTOs
type CreateUserParams struct {
    Name  string `json:"name" validate:"required,min=3,max=50"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"min=18,max=120"`
}

type GetUserParams struct {
    ID string `path:"id" validate:"required"`
}

type ListUsersParams struct {
    Page  int `query:"page" validate:"min=1"`
    Limit int `query:"limit" validate:"min=1,max=100"`
}

// Repository interface
type UserRepository interface {
    GetByID(id string) (*User, error)
    List() ([]*User, error)
    Create(user *User) (*User, error)
    Update(user *User) (*User, error)
    Delete(id string) error
}

// Service interface
type UserService interface {
    GetByID(p *GetUserParams) (*User, error)
    List(p *ListUsersParams) ([]*User, error)
    Create(p *CreateUserParams) (*User, error)
    Update(p *UpdateUserParams) (*User, error)
    Delete(p *DeleteUserParams) error
}

Configuration YAML

Minimal Config

service-definitions:
  user-service:
    type: user-service-factory

deployments:
  development:
    servers:
      api:
        addr: ":8080"
        published-services:
          - user-service

Complete Config

# Middleware
middleware-definitions:
  recovery:
    type: recovery
    config:
      enable_stack_trace: false
  
  request-logger:
    type: request-logger
    config:
      prefix: "API"
      skip_paths: ["/health"]

# Services
service-definitions:
  user-repository:
    type: user-repository-factory
    config:
      dsn: "postgres://localhost/mydb"
  
  user-service:
    type: user-service-factory
    depends-on:
      - user-repository
    router:
      path-prefix: /api
      middlewares:
        - recovery
        - request-logger
      hidden:
        - InternalMethod

# Deployments
deployments:
  development:
    servers:
      api:
        base-url: "http://localhost:8080"
        addr: ":8080"
        published-services:
          - user-service
  
  production:
    servers:
      user-api:
        base-url: "https://user-api.example.com"
        addr: ":8001"
        published-services:
          - user-service

Annotations

// @RouterService name="user-service", prefix="/api", middlewares=["recovery"]
type UserService struct {
    // @Inject "user-repository"
    UserRepo UserRepository
}

// @Route "GET /users/{id}"
func (s *UserService) GetByID(p *GetUserParams) (*User, error) {
    return s.UserRepo.GetByID(p.ID)
}

// @Route "GET /users"
func (s *UserService) List(p *ListUsersParams) ([]*User, error) {
    return s.UserRepo.List()
}

// @Route "POST /users", middlewares=["auth"]
func (s *UserService) Create(p *CreateUserParams) (*User, error) {
    // ...
}

// @Route "PUT /users/{id}", middlewares=["auth", "admin"]
func (s *UserService) Update(p *UpdateUserParams) (*User, error) {
    // ...
}

// @Route "DELETE /users/{id}", middlewares=["auth", "admin"]
func (s *UserService) Delete(p *DeleteUserParams) error {
    // ...
}

Generate code:

// Recommended: Auto-generation in main()
func main() {
    lokstra.Bootstrap() // Detects changes and regenerates
    // ...
}
# Manual generation (before build/deploy)
lokstra autogen ./path/to/service
go run . --generate-only

Per-route middleware:


Middleware

Built-in Middleware

import (
    "github.com/primadi/lokstra/middleware/recovery"
    "github.com/primadi/lokstra/middleware/request_logger"
    "github.com/primadi/lokstra/middleware/slow_request_logger"
    "github.com/primadi/lokstra/middleware/cors"
    "github.com/primadi/lokstra/middleware/body_limit"
    "github.com/primadi/lokstra/middleware/gzipcompression"
)

r := lokstra.NewRouter("api")

// Recommended order
r.Use(recovery.Middleware(nil))
r.Use(request_logger.Middleware(nil))
r.Use(slow_request_logger.Middleware(&slow_request_logger.Config{
    Threshold: 500 * time.Millisecond,
}))
r.Use(cors.Middleware([]string{"*"}))
r.Use(body_limit.Middleware(&body_limit.Config{
    MaxSize: 10 * 1024 * 1024, // 10MB
}))
r.Use(gzipcompression.Middleware(nil))

Custom Middleware

func CustomMiddleware(cfg *Config) request.HandlerFunc {
    return request.HandlerFunc(func(ctx *request.Context) error {
        // Pre-processing
        
        err := ctx.Next() // Call next handler
        
        // Post-processing
        
        return err
    })
}

Response Helpers

func handler(ctx *request.Context) error {
    // Success
    ctx.Api.Ok(data)                    // 200
    ctx.Api.Created(data)               // 201
    ctx.Api.NoContent()                 // 204
    
    // Client errors
    ctx.Api.BadRequest("message")       // 400
    ctx.Api.Unauthorized("message")     // 401
    ctx.Api.Forbidden("message")        // 403
    ctx.Api.NotFound("message")         // 404
    
    // Server errors
    ctx.Api.InternalServerError("msg")  // 500
    
    // Custom
    ctx.Api.ErrorWithCode(422, "message", data)
}

Validation Tags

type Params struct {
    // String validation
    Name  string `validate:"required"`
    Email string `validate:"required,email"`
    URL   string `validate:"url"`
    UUID  string `validate:"uuid"`
    
    // Numeric validation
    Age   int `validate:"min=18,max=120"`
    Price int `validate:"gt=0"`
    
    // String length
    Title string `validate:"min=3,max=100"`
    Code  string `validate:"len=6"`
    
    // Options
    Status string `validate:"oneof=active inactive pending"`
    
    // Nested struct
    Address *Address `validate:"required"`
    
    // Slice
    Tags []string `validate:"min=1,max=10,dive,min=2"`
}

Common validators:


Environment Variables

# Deployment selection
LOKSTRA_DEPLOYMENT=production

# Server selection (multi-server)
LOKSTRA_SERVER=api-server

# Log level
LOKSTRA_LOG_LEVEL=debug  # silent, error, warn, info, debug

# Config file
LOKSTRA_CONFIG=./config.yaml

Project Structure

Simple Router

myapp/
├── main.go
├── handlers.go
└── go.mod

Medium System (DDD)

myapp/
├── main.go
├── register.go
├── config.yaml
├── domain/
│   └── user/
│       ├── models.go
│       ├── repository.go
│       └── service.go
├── repository/
│   └── user_repository.go
└── service/
    └── user_service.go

Enterprise Modular

myapp/
├── main.go
├── register.go
├── config.yaml
└── modules/
    ├── user/
    │   ├── domain/
    │   ├── application/
    │   └── infrastructure/
    └── order/
        ├── domain/
        ├── application/
        └── infrastructure/

CLI Commands

# Create project
lokstra new myapp
lokstra new myapp -template 01_router/01_router_only
lokstra new myapp -template 02_app_framework/01_medium_system

# Generate code
lokstra autogen .
lokstra autogen ./modules/user/application

# List templates
lokstra new --help

Common Issues

Service Not Found

panic: service 'user-service' not found

Fix: Register service type in register.go

Import Cycle

import cycle not allowed

Fix: Use domain interfaces, separate packages properly

Validation Not Working

validation tags ignored

Fix: Use pointer to struct: *CreateUserParams

Handler Not Recognized

unsupported handler signature

Fix: Return error or supported type, use *request.Context


Resources


Templates

Template Use Case Complexity
01_router/01_router_only Learning, simple APIs
01_router/02_single_app Production single app ⭐⭐
01_router/03_multi_app Multi-app servers ⭐⭐⭐
02_app_framework/01_medium_system 2-10 entities, DDD ⭐⭐⭐
02_app_framework/02_enterprise_modular 10+ entities, bounded contexts ⭐⭐⭐⭐⭐
02_app_framework/03_enterprise_router_service Annotation-based, enterprise ⭐⭐⭐⭐⭐