Helper Packages

Lokstra provides a comprehensive set of helper utilities in the common package to simplify common programming tasks like type conversion, validation, data manipulation, and more.

Table of Contents

Overview

The common package contains utility functions and types that are used throughout Lokstra and are also available for your application code. These helpers are designed to be:

✓ Type-Safe         - Extensive use of generics
✓ Performance       - Optimized with caching where applicable
✓ Well-Tested       - Comprehensive test coverage
✓ Easy to Use       - Simple, intuitive APIs
✓ Documented        - Clear documentation and examples

Available Packages

Package Purpose Key Features
cast Type conversion utilities ToInt, ToFloat64, ToTime, ToType[T], ToStruct
utils General utilities Map helpers, slice helpers, hash, parsing
validator Struct validation Tag-based validation, custom validators, caching
customtype Custom types DateTime, Date, Decimal with JSON support
json JSON utilities Parse with error recovery
response_writer HTTP response helpers JSON responses, error handling

Quick Start

Type Conversion

import "github.com/primadi/lokstra/common/cast"

// Convert any type to int
age, err := cast.ToInt(userData["age"])

// Convert any type to time.Time
createdAt, err := cast.ToTime(userData["created_at"])

// Convert any type to struct
var user User
err := cast.ToStruct(userData, &user, false)

// Generic type conversion
count, err := cast.ToType[int](rawValue, false)

Validation

import "github.com/primadi/lokstra/common/validator"

type CreateUserRequest struct {
    Username string `json:"username" validate:"required,min=3,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"required,gte=18,lte=100"`
}

// Validate struct
fieldErrors, err := validator.ValidateStruct(&request)
if len(fieldErrors) > 0 {
    // Return validation errors
    return api_formatter.ValidationError(fieldErrors)
}

Map Helpers

import "github.com/primadi/lokstra/common/utils"

// Extract value from map with default
host := utils.GetValueFromMap(config, "host", "localhost")
port := utils.GetValueFromMap(config, "port", 8080)
timeout := utils.GetDurationFromMap(config, "timeout", 30*time.Second)

// Clone map
cloned := utils.CloneMap(original)

Custom Types

import "github.com/primadi/lokstra/common/customtype"

type User struct {
    ID        string              `json:"id"`
    Name      string              `json:"name"`
    BirthDate customtype.Date     `json:"birth_date"`
    CreatedAt customtype.DateTime `json:"created_at"`
    Balance   customtype.Decimal  `json:"balance"`
}

// Custom types handle JSON marshaling/unmarshaling automatically

Package Details

cast Package

Purpose: Safe type conversion with error handling

Key Functions:

// Primitive conversions
ToInt(val any) (int, error)
ToFloat64(val any) (float64, error)
ToTime(val any) (time.Time, error)

// Generic conversion
ToType[T any](val any, strict bool) (T, error)

// Struct conversion
ToStruct(source any, dest any, strict bool) error

// Slice conversion
SliceConvert[T any](slice any) (T, error)

// Check emptiness
IsEmpty(val any) bool

Use Cases:

Full documentation →

utils Package

Purpose: General utility functions for common tasks

Key Functions:

// Map operations
GetValueFromMap[T any](map[string]any, string, T) T
GetDurationFromMap(map[string]any, string, any) time.Duration
CloneMap[K comparable, V any](map[K]V) map[K]V

// Slice operations
ToAnySlice[T any]([]T) []any
SlicesConcat[T any](...[]T) []T
AppendSorted[T any]([]T, T, func(a, b T) bool) []T

// String operations
CamelToSnake(string) string
ParseClientIP(*http.Request) string

// Security
HashPassword(string) (string, error)

// Type checking
IsNil(any) bool

Full documentation →

validator Package

Purpose: Struct validation with tag-based rules

Key Features:

Built-in Validators:

`validate:"required"`             // Field must not be empty
`validate:"email"`                // Must be valid email
`validate:"min=5"`                // Min length/value
`validate:"max=100"`              // Max length/value
`validate:"gt=0"`                 // Greater than
`validate:"gte=18"`               // Greater than or equal
`validate:"lt=100"`               // Less than
`validate:"lte=200"`              // Less than or equal
`validate:"oneof=admin user"`     // Must be one of values

Full documentation →

customtype Package

Purpose: Custom types with special marshaling/unmarshaling

Available Types:

customtype.DateTime    // ISO 8601 datetime with timezone
customtype.Date        // Date only (YYYY-MM-DD)
customtype.Decimal     // High-precision decimal numbers

Features:

Full documentation →

json Package

Purpose: Enhanced JSON operations with error recovery

Key Features:

Full documentation →

response_writer Package

Purpose: HTTP response helpers

Key Features:

Full documentation →

Best Practices

Type Conversion

 DO: Always check errors from conversion functions
age, err := cast.ToInt(value)
if err != nil {
    return fmt.Errorf("invalid age: %w", err)
}

 DO: Use strict mode when data integrity is critical
err := cast.ToStruct(data, &user, true)  // Fails on unknown fields

 DON'T: Ignore conversion errors
age, _ := cast.ToInt(value)  // BAD: Silent failure

 DON'T: Use type assertion without checking
age := value.(int)  // BAD: Panics if wrong type

Validation

 DO: Validate at entry points (API handlers, etc.)
fieldErrors, err := validator.ValidateStruct(&request)
if len(fieldErrors) > 0 {
    return ValidationError(fieldErrors)
}

 DO: Use descriptive field names with json tags
type User struct {
    Email string `json:"email" validate:"required,email"`
}

 DON'T: Skip validation for untrusted input
// BAD: No validation
user := &User{Email: request.Email}

 DON'T: Use validation as business logic
// BAD: Validation should be format checking only
`validate:"required,min=18"`  // OK: Format validation
// Business rule checks should be in service layer

Map Operations

 DO: Use GetValueFromMap with appropriate defaults
port := utils.GetValueFromMap(config, "port", 8080)
timeout := utils.GetDurationFromMap(config, "timeout", 30*time.Second)

 DO: Clone maps when passing to untrusted code
safeCopy := utils.CloneMap(sensitiveData)
process(safeCopy)

 DON'T: Access maps directly without checking existence
port := config["port"].(int)  // BAD: Panics if missing or wrong type

 DON'T: Modify original maps unintentionally
// BAD: Modifies original
process(originalMap)

Custom Types

 DO: Use custom types for domain-specific values
type User struct {
    CreatedAt customtype.DateTime `json:"created_at"`
    BirthDate customtype.Date     `json:"birth_date"`
    Balance   customtype.Decimal  `json:"balance"`
}

 DO: Let custom types handle marshaling automatically
json.Marshal(user)  // DateTime/Date/Decimal handled correctly

 DON'T: Use string for dates/times in structs
type User struct {
    CreatedAt string `json:"created_at"`  // BAD: Use customtype.DateTime
}

 DON'T: Use float64 for money
type Product struct {
    Price float64 `json:"price"`  // BAD: Use customtype.Decimal
}

Performance

 DO: Reuse validator instances (cached automatically)
validator.ValidateStruct(&user)  // Metadata cached after first call

 DO: Use CloneMap instead of manual copying
cloned := utils.CloneMap(original)  // Efficient

 DO: Use AppendSorted for small slices
sorted := utils.AppendSorted(slice, value, less)

 DON'T: Build validation metadata repeatedly
// Validator automatically caches, but don't create new validators

 DON'T: Use inefficient algorithms for large slices
for i, item := range large_slice {
    // BAD: O(n²) for insertion sort
}

Import Paths

import (
    "github.com/primadi/lokstra/common/cast"
    "github.com/primadi/lokstra/common/customtype"
    "github.com/primadi/lokstra/common/json"
    "github.com/primadi/lokstra/common/response_writer"
    "github.com/primadi/lokstra/common/utils"
    "github.com/primadi/lokstra/common/validator"
)

Next: Explore individual helper packages for detailed documentation and examples.