What is Lokstra?

A versatile Go web framework that works in two ways: as a Router or as a complete Business Application Framework


🎯 Two Ways to Use Lokstra

Lokstra is designed to meet you where you are:

🎯 Track 1: As a Router (Like Echo, Gin, Chi)

Quick, simple HTTP routing without framework complexity

Use Lokstra as a pure HTTP router for:

  • Quick prototypes and MVPs
  • Simple REST APIs
  • Learning HTTP routing fundamentals
  • Projects without DI needs
r := lokstra.NewRouter("api")
r.GET("/users", getUsersHandler)
r.Use(cors.Middleware("*"))

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

Compare with: Echo, Gin, Chi, Fiber
Learn more: Router Guide


�️ Track 2: As a Business Application Framework (Like NestJS, Spring Boot)

Enterprise features with DI, auto-router, and configuration-driven deployment

Use Lokstra as a complete framework for:

  • Enterprise applications
  • Microservices architectures
  • Dependency injection and service layer
  • Configuration-driven deployment
  • Annotation-driven development (like NestJS decorators)

With Lokstra Annotations (Recommended) - Zero boilerplate!

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

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

// Auto-generates: factory, remote proxy, routes, DI wiring!

Or traditional approach (YAML/Code):

# Define services in YAML
service-definitions:
  user-service:
    type: user-service-factory
    depends-on: [database]
// Type-safe lazy loading
var userService = service.LazyLoad[*UserService]("user-service")

func handler() {
    users := userService.MustGet().GetAll()
}

// Auto-router generates REST endpoints from service methods!

Compare with: NestJS, Spring Boot, Uber Fx, Buffalo
Learn more: Framework Guide


πŸš€ The Big Idea

Start simple, scale when needed:

  1. Start with routing (Track 1) for quick results
  2. Add services and DI when complexity grows (Track 2)
  3. Enable auto-router to reduce boilerplate
  4. Switch deployment topology without code changes

Lokstra grows with your needs!


πŸ“– Core Concepts (5-Minute Overview)

Lokstra has 6 core components that work together:

1. Router - HTTP Routing

r := lokstra.NewRouter("api")
r.GET("/users", func() ([]User, error) {
    return db.GetAllUsers()
})

Key Feature: Flexible handler signatures - many forms supported!


2. Service - Business Logic

type UserService struct {}

func (s *UserService) GetAll() ([]User, error) {
    return db.Query("SELECT * FROM users")
}

// Register service type (factory creates instances)
lokstra_registry.RegisterServiceType("user-service", func() any {
    return &UserService{}
}, nil)

Key Feature: Service methods can become HTTP endpoints automatically!


3. Middleware - Request Processing

r.Use(recovery.Middleware(nil), cors.Middleware("*"))

Key Feature: Apply middleware globally or per-route


4. Configuration - YAML or Code

deployments:
  production:
    servers:
      web-server:
        base-url: http://localhost
        addr: ":8080"
        published-services: [user-service]

Key Feature: One config file for multiple deployments


5. App - Application Container

app := lokstra.NewApp("my-app", ":8080", router)

Key Feature: Combine multiple routers into one app


6. Server - Server Manager

server := lokstra.NewServer("main", app)
server.Run(30 * time.Second) // Graceful shutdown

Key Feature: Manage multiple apps, automatic graceful shutdown


πŸš€ Your First Lokstra App

Complete working example in 20 lines:

package main

import (
    "github.com/primadi/lokstra"
    "time"
)

func main() {
    // 1. Create router
    r := lokstra.NewRouter("api")
    
    // 2. Register routes
    r.GET("/ping", func() string {
        return "pong"
    })
    
    r.GET("/users", func() []string {
        return []string{"Alice", "Bob", "Charlie"}
    })
    
    // 3. Create app & run
    app := lokstra.NewApp("demo", ":8080", r)
    app.PrintStartInfo()
    if err := app.Run(30 * time.Second); err != nil {
      log.Fatal(err)
    }
}

Test it:

curl http://localhost:8080/ping    # β†’ "pong"
curl http://localhost:8080/users   # β†’ ["Alice","Bob","Charlie"]

πŸ’­ Note: This is a simplified introduction example. For complete runnable examples with proper project structure, see:

  • Quick Start - Your first working API in 5 minutes
  • Examples - 4 progressive examples from basics to production

πŸ’‘ What Makes Lokstra Different?

1. Flexible Handler Signatures

You’re not locked into one pattern. Write handlers that make sense:

// Simple
r.GET("/ping", func() string { return "pong" })

// With error
r.GET("/users", func() ([]User, error) { 
    return db.GetUsers() 
})

// With context
r.POST("/users", func(ctx *request.Context, user *User) error {
    result, err := db.Save(user)
    if err != nil {
      return ctx.Api.InternalError(err.Error())
    }
    return ctx.Api.Ok(result)
})

// With response.ApiHelper
r.GET("/complex", func(user *User) (*response.ApiHelper, error) {
    result, err := db.Save(user)
    if err != nil {
      return nil, err
    }
    return response.NewApiOk(result), nil
})

29+ different handler forms supported!


2. Service as Router (Game Changer!)

Your service methods automatically become HTTP endpoints:

// Step 1: Define service with methods
type UserService struct {}

type GetAllParams struct {}
type GetByIDParams struct {
    ID string `path:"id"`
}
type CreateUserParams struct {
    User *User `json:"user"`
}

func (s *UserService) GetAll(p *GetAllParams) ([]User, error) { ... }
func (s *UserService) GetByID(p *GetByIDParams) (*User, error) { ... }
func (s *UserService) Create(p *CreateUserParams) error { ... }

func NewUserService() *UserService {
    return &UserService{}
}

// Step 2: Register service factory with metadata
lokstra_registry.RegisterServiceType(
    "user-service-factory",
    NewUserService,
    nil,
    deploy.WithResource("user", "users"),
    deploy.WithConvention("rest"),
)

// Step 3: Auto-generate router from service!
userRouter := lokstra_registry.NewRouterFromServiceType("user-service-factory")

// Automatically creates:
// GET  /users       β†’ GetAll() method
// GET  /users/{id}  β†’ GetByID() method  
// POST /users       β†’ Create() method

Zero boilerplate routing code!


3. One Binary, Multiple Deployments

Configure once, deploy anywhere:

deployments:
  monolith:
    servers:
      web-server:
        addr: ":8080"
        published-services: [users, orders, payments]
  
  multi-server:
    servers:
      user-service:
        base-url: http://user-service
        addr: ":8001"
        published-services: [users]
      
      order-payment-service:
        base-url: http://order-payment-service
        addr: ":8002"
        published-services: [orders, payments]

Same code, different architectures - just change config!

# Run as monolith
./app --server=monolith

# Run as microservice
./app --server=user-service

4. Built-in Dependency Injection

No external DI framework needed:

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

type UserService struct {
    DB *Database
}

// Register factories
lokstra_registry.RegisterServiceType("db-factory", createDBFactory, nil)

lokstra_registry.RegisterServiceType("users-factory", 
    func(deps map[string]any, config map[string]any) any {
        return &UserService{
            DB: deps["db"].(*Database),
        }
    }, nil)

// Service-level lazy loading - service created on first access
users := lokstra_registry.GetService[*UserService]("users")

// Inside service method - dependencies already injected
func (u *UserService) GetUsers() ([]User, error) {
    return u.DB.Query("SELECT * FROM users")
}

Key Feature: Service-level lazy loading - services created only when needed, dependencies eagerly resolved!


πŸ—οΈ Architecture at a Glance

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           HTTP Request                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          APP (with Listener)            β”‚
β”‚   (ServeMux / FastHTTP / HTTP2 / etc)   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚       ROUTER (http.Handler)        β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β”‚  β”‚       ROUTE                  β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”‚ MidWare 1 (before) ───┼───┼──┼─┼──┐
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”‚ MidWare 2 (before) ───┼───┼──┼─┼───
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”‚    HANDLER        ────┼───┼──┼─┼───
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”‚ MidWare 2 (after)  ───┼───┼──┼─┼───
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β”‚  β”‚ MidWare 1 (after)  ───┼───┼──┼─┼───
β”‚  β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚  β”‚ β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                  β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚     β”‚    SERVICES        │◄───┐
                  β”‚     β”‚  (business logic)  β”‚    β”‚
                  β”‚     β”‚                    β”‚    β”‚
                  β”‚     β”‚  Service can call: |    β”‚
                  β”‚     β”‚  - Other Services β”€β”Όβ”€β”€β”€β”€β”˜
                  β”‚     β”‚  - Database        |    
                  β”‚     β”‚  - External APIs   |    
                  β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚ RESPONSE OBJECT  β”‚
         β”‚  (formatting)    β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Request Flow:

  1. HTTP request arrives at App’s listener
  2. Listener routes to Router (http.Handler)
  3. Router finds matching Route
  4. Route executes Middleware chain (via ctx.Next())
    • Middleware can call Services (e.g., auth checks)
  5. Handler executes
    • Handler can call Services
  6. Services contain business logic
    • Services can call other Services
    • Services can call databases, external APIs, etc.
  7. Response Object formats the response
  8. Response flows back to client

Key Points:

  • Services are accessible everywhere: Middleware, Handlers, and other Services can all call Services
  • Server is just a container for Apps, Services, and configuration - not part of request flow
  • Middleware can use Services for auth, logging, etc.
  • Services can depend on other Services (via Dependency Injection)

πŸŽ“ What You’ll Learn

Choose your learning path:

Track 1: Router Only (2-3 hours)

βœ… Create routers and register routes
βœ… Write handlers in 29+ different styles
βœ… Apply middleware (global, per-route, groups)
βœ… Manage app and server lifecycle
βœ… Build REST APIs without DI

β†’ Router Guide

Track 2: Full Framework (6-8 hours)

βœ… Everything in Track 1, plus:
βœ… Service layer and dependency injection
βœ… Auto-generate routes from services
βœ… Configuration-driven deployment (YAML or Code)
βœ… Monolith β†’ Microservices migration
βœ… External service integration

β†’ Framework Guide


🚦 Where to Go Next?

New to Lokstra? Start Here:

Option 1: Quick Start (Fastest)

Just want to see it work?

Want to understand before coding?

Ready to Learn? Choose Your Track:

Track 1: Router Only

For: Quick APIs, prototypes, simple projects

  1. Examples - Router Track - Hands-on learning (2-3 hours)
  2. Router Guide - Deep dive into routing
  3. API Reference - Router - Complete API

Track 2: Full Framework

For: Enterprise apps, microservices, DI architecture

  1. Examples - Framework Track - Hands-on learning (8-12 hours)
  2. Framework Guide - Services, DI, auto-router
  3. Configuration Reference - YAML schema

Not Sure Which Track?

Start with Track 1 (Router) if:

  • New to Lokstra
  • Building MVP or prototype
  • Want quick results
  • Don’t need DI yet

Start with Track 2 (Framework) if:

  • Need dependency injection
  • Building microservices
  • Want auto-generated routers
  • Familiar with NestJS/Spring Boot

Remember: You can always upgrade from Track 1 to Track 2 later!


🎯 Key Takeaways

Before moving on, remember:

  1. Lokstra is flexible - Use as router-only OR full framework
  2. Start simple, scale up - Begin with Track 1, upgrade to Track 2 when needed
  3. Convention over configuration - Smart defaults, configure when needed
  4. Service-oriented - Services are first-class citizens (Track 2)
  5. Deployment-agnostic - Same code, monolith or microservices (Track 2)
  6. Production-ready - Built for real applications

πŸ“š Learning with Examples

We provide two example tracks based on how you want to use Lokstra:

Track 1: Router-Only Examples

⏱️ 2-3 hours total β€’ Perfect for quick APIs

What you’ll build: REST APIs with routing and middleware (no DI)

πŸ‘‰ Start Router Examples


Track 2: Full Framework Examples

⏱️ 8-12 hours total β€’ For enterprise apps

What you’ll build: Enterprise apps with DI, auto-router, and microservices

πŸ‘‰ Start Framework Examples


πŸ€” Which Track to Choose?

Β  Router Track Framework Track
Time 2-3 hours 8-12 hours
Use Case Quick APIs, prototypes Enterprise, microservices
Features Routing, middleware + DI, auto-router, config
Compare With Echo, Gin, Chi NestJS, Spring Boot

Not sure? Start with Router Track (it’s compatible with Framework features!)


οΏ½πŸš€ Roadmap

Next Release

We’re actively working on:

  • 🎨 HTMX Support - Build modern web apps easier
    • Template rendering integration
    • Partial page updates
    • Form handling patterns
    • Example applications
  • πŸ› οΈ CLI Tools - Developer productivity
    • Project scaffolding (lokstra new)
    • Code generation (lokstra generate)
    • Migration helpers
    • Development server
  • πŸ“¦ Complete Standard Library - Production essentials
    • Middleware: Metrics, Auth (JWT, OAuth), Rate limiting
    • Services: Monitoring, Tracing, Health checks
    • Utilities: Common patterns and helpers

Want to contribute or suggest features? Visit our GitHub repository


Ready? β†’ Continue to Why Lokstra? or jump straight to Quick Start