swlib

module
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: AGPL-3.0

README

SwLib - SwayRider Shared Library

SwLib is a comprehensive Go library providing reusable components for building microservices in the SwayRider platform. It offers a complete toolkit for service lifecycle management, security, logging, configuration, and common utilities.

Table of Contents

Installation

go get github.com/swayrider/swayrider/backend/swlib

Quick Start

Here's a minimal example of creating a microservice using swlib:

package main

import (
    "github.com/swayrider/swayrider/backend/swlib/app"
)

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        WithGrpc(grpcSetup).
        Run()
}

func grpcSetup(a *app.App, grpcServer *grpc.Server, mux *runtime.ServeMux) {
    // Register your gRPC service handlers here
}

Packages

app - Service Bootstrap Framework

The app package is the core of swlib, providing a fluent builder pattern for configuring and running microservices. It handles service lifecycle, configuration, database connections, gRPC/HTTP servers, and graceful shutdown.

Basic Usage
package main

import (
    "github.com/swayrider/swayrider/backend/swlib/app"
)

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields | app.DatabaseConnectionFields).
        Run()
}
Configuration Field Groups

The app package provides pre-defined configuration field groups that can be combined using bitwise OR:

Field Group Description Environment Variables
BackendServiceFields Basic service configuration LOG_LEVEL, HTTP_PORT, GRPC_PORT
DatabaseConnectionFields PostgreSQL connection DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
MinioConnectionFields MinIO object storage MINIO_HOST, MINIO_PORT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY, MINIO_SECURE
WebServiceFields Web server configuration WEB_PORT, WEB_ROOT
ClientCredentialsFields OAuth client credentials CLIENT_ID, CLIENT_SECRET
HTMXServiceFields HTMX-specific configuration Various HTMX settings
Custom Configuration Fields
func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        WithConfigFields(
            app.ConfigField{
                Name:        "api-key",
                Description: "External API key",
                EnvName:     "API_KEY",
                Required:    true,
            },
            app.ConfigField{
                Name:        "cache-ttl",
                Description: "Cache time-to-live in seconds",
                EnvName:     "CACHE_TTL",
                Default:     "300",
            },
        ).
        Run()

    // Access configuration values
    apiKey := application.GetConfig("api-key")
    cacheTTL := application.GetConfigAsInt("cache-ttl")
}
Database Integration
import (
    "database/sql"
    _ "github.com/lib/pq"
)

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields | app.DatabaseConnectionFields).
        WithDatabase(
            // Database constructor
            func(a *app.App) (*sql.DB, error) {
                connStr := fmt.Sprintf(
                    "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
                    a.GetConfig("db-host"),
                    a.GetConfig("db-port"),
                    a.GetConfig("db-user"),
                    a.GetConfig("db-password"),
                    a.GetConfig("db-name"),
                )
                return sql.Open("postgres", connStr)
            },
            // Bootstrap function (optional migrations, etc.)
            func(a *app.App, db *sql.DB) error {
                // Run migrations or initial setup
                return nil
            },
        ).
        Run()

    // Access database
    db := app.GetDatabase[*sql.DB](application)
}
Object Store (MinIO) Integration
import "github.com/minio/minio-go/v7"

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields | app.MinioConnectionFields).
        WithObjectStore(
            func(a *app.App) (*minio.Client, error) {
                return minio.New(
                    fmt.Sprintf("%s:%s", a.GetConfig("minio-host"), a.GetConfig("minio-port")),
                    &minio.Options{
                        Creds:  credentials.NewStaticV4(a.GetConfig("minio-access-key"), a.GetConfig("minio-secret-key"), ""),
                        Secure: a.GetConfigAsBool("minio-secure"),
                    },
                )
            },
            func(a *app.App, client *minio.Client) error {
                // Initialize buckets, etc.
                return nil
            },
        ).
        Run()

    // Access object store
    minioClient := app.GetObjectStore[*minio.Client](application)
}
gRPC and HTTP Gateway
import (
    "google.golang.org/grpc"
    "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
)

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        WithGrpc(setupGrpc).
        Run()
}

func setupGrpc(a *app.App, grpcServer *grpc.Server, mux *runtime.ServeMux) {
    // Register gRPC service
    pb.RegisterMyServiceServer(grpcServer, &MyServiceServer{app: a})

    // Register HTTP gateway (optional)
    pb.RegisterMyServiceHandlerServer(context.Background(), mux, &MyServiceServer{app: a})
}
Service Clients

For inter-service communication, use the service client pattern:

import "github.com/swayrider/swayrider/backend/grpcclients/authclient"

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        WithServiceClients(
            app.NewServiceClient("authservice", func(host, port string) (*authclient.Client, error) {
                return authclient.New(host, port)
            }),
        ).
        Run()

    // Access service client
    authClient := app.GetServiceClient[*authclient.Client](application, "authservice")
}
Background Routines
func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        WithBackgroundRoutines(
            func(a *app.App) {
                ticker := time.NewTicker(5 * time.Minute)
                defer ticker.Stop()

                for {
                    select {
                    case <-a.Done():
                        return
                    case <-ticker.C:
                        // Periodic task
                        cleanupExpiredSessions(a)
                    }
                }
            },
        ).
        Run()
}
Application Data Store

Thread-safe key-value store for sharing data across components:

func main() {
    application := app.New("myservice").
        WithDefaultConfigFields(app.BackendServiceFields).
        Run()

    // Store data
    application.SetData("cache-manager", cacheManager)

    // Retrieve data
    cm := app.GetData[*CacheManager](application, "cache-manager")
}

cache - In-Memory Caching

Simple thread-safe local cache for storing arbitrary values.

Usage
import "github.com/swayrider/swayrider/backend/swlib/cache"

// Define cache keys
const (
    UserCacheKey    cache.LocalCacheKey = "user"
    SessionCacheKey cache.LocalCacheKey = "session"
)

func main() {
    // Store a value
    cache.LCSet(UserCacheKey, &User{ID: "123", Name: "John"})

    // Retrieve a value
    if value, ok := cache.LCGet(UserCacheKey); ok {
        user := value.(*User)
        fmt.Println(user.Name)
    }

    // Check existence
    if cache.LCHas(UserCacheKey) {
        // Key exists
    }

    // Delete a value
    cache.LCDel(UserCacheKey)
}

compression - Data Compression

Utilities for working with compressed files.

Extracting ZIP Archives
import "github.com/swayrider/swayrider/backend/swlib/compression"

func extractData() error {
    err := compression.UnZip("/path/to/archive.zip", "/path/to/destination")
    if err != nil {
        return fmt.Errorf("failed to extract archive: %w", err)
    }
    return nil
}

crypto - Cryptographic Utilities

Secure password hashing, random string generation, and RSA keypair management.

Password Hashing

Uses Argon2id, the winner of the Password Hashing Competition:

import "github.com/swayrider/swayrider/backend/swlib/crypto"

func registerUser(password string) (string, error) {
    // Hash password for storage
    hash, err := crypto.CalculatePasswordHash(password)
    if err != nil {
        return "", fmt.Errorf("failed to hash password: %w", err)
    }
    return hash, nil
}

func authenticateUser(storedHash, password string) bool {
    // Verify password against stored hash
    return crypto.VerifyPassword(storedHash, password)
}
Secure Random Strings
import "github.com/swayrider/swayrider/backend/swlib/crypto"

func generateToken() (string, error) {
    // Generate a 32-character cryptographically secure random string
    token, err := crypto.GenerateSecureRandomString(32)
    if err != nil {
        return "", err
    }
    return token, nil
}
RSA Keypair Generation
import "github.com/swayrider/swayrider/backend/swlib/crypto"

func rotateKeys() error {
    privateKeyPEM, publicKeyPEM, expiresAt, err := crypto.CreateKeypair()
    if err != nil {
        return fmt.Errorf("failed to create keypair: %w", err)
    }

    // Store keys securely
    // privateKeyPEM: PEM-encoded private key
    // publicKeyPEM: PEM-encoded public key
    // expiresAt: Recommended expiration (30 days from now)

    return nil
}

env - Environment Variables

Simplified environment variable access with type conversion and fallbacks.

Usage
import "github.com/swayrider/swayrider/backend/swlib/env"

func loadConfig() {
    // String with fallback
    apiHost := env.Get("API_HOST", "localhost")

    // Integer with fallback
    port := env.GetAsInt("PORT", 8080)

    // Float with fallback
    timeout := env.GetAsFloat64("TIMEOUT_SECONDS", 30.0)

    // Boolean with fallback
    debug := env.GetAsBool("DEBUG", false)

    // String array (comma-separated)
    allowedOrigins := env.GetAsStringArr("ALLOWED_ORIGINS", []string{"http://localhost:3000"})

    // Integer array
    retryDelays := env.GetAsIntArr("RETRY_DELAYS", []int{1, 2, 5, 10})
}

flag - CLI Flag Parsing

Custom flag types for parsing array values from command-line arguments.

String Array Flags
import (
    "flag"
    "github.com/swayrider/swayrider/backend/swlib/swflag"
)

func main() {
    var hosts swflag.StringArr

    flag.Var(&hosts, "host", "Host addresses (comma-separated or multiple flags)")
    flag.Parse()

    // Usage:
    // ./myapp -host "host1,host2,host3"
    // ./myapp -host host1 -host host2 -host host3

    for _, host := range hosts {
        fmt.Println("Host:", host)
    }
}
With Custom FlagSet
import "github.com/swayrider/swayrider/backend/swlib/swflag"

func parseFlags() {
    fs := flag.NewFlagSet("myapp", flag.ExitOnError)

    // Get parser function
    parseStringArr := swflag.StringArrayParser()

    // Define and parse flags
    hosts := parseStringArr(fs, "hosts", "Host addresses")

    fs.Parse(os.Args[1:])

    for _, host := range *hosts {
        fmt.Println(host)
    }
}
Other Array Types
import "github.com/swayrider/swayrider/backend/swlib/swflag"

func main() {
    var ports swflag.IntArr
    var weights swflag.Float64Arr
    var features swflag.BoolArr

    flag.Var(&ports, "port", "Port numbers")
    flag.Var(&weights, "weight", "Weight values")
    flag.Var(&features, "feature", "Feature flags")
    flag.Parse()
}

grpc - gRPC Utilities

Interceptors and helpers for gRPC service communication.

Authentication Interceptor
import (
    "google.golang.org/grpc"
    "github.com/swayrider/swayrider/backend/swlib/grpc/interceptors"
)

func setupGrpcServer() *grpc.Server {
    // Get public keys function
    getPublicKeys := func() ([]string, error) {
        // Return list of valid public keys for JWT verification
        return []string{publicKeyPEM}, nil
    }

    server := grpc.NewServer(
        grpc.UnaryInterceptor(interceptors.UnaryAuthInterceptor(getPublicKeys)),
        grpc.StreamInterceptor(interceptors.StreamAuthInterceptor(getPublicKeys)),
    )

    return server
}
Client Info Interceptor

Extracts and propagates client information through the request chain:

import "github.com/swayrider/swayrider/backend/swlib/grpc/interceptors"

func setupGrpcServer() *grpc.Server {
    server := grpc.NewServer(
        grpc.ChainUnaryInterceptor(
            interceptors.ClientInfoInterceptor(),
            // ... other interceptors
        ),
    )
    return server
}
Service-to-Service Calls with Auto-Retry
import "github.com/swayrider/swayrider/backend/swlib/grpc/s2s"

func callOtherService(ctx context.Context, authClient *authclient.Client) (*pb.Response, error) {
    // Automatically retries with fresh token on Unauthenticated error
    response, err := s2s.Call(ctx, func(ctx context.Context) (*pb.Response, error) {
        return authClient.SomeMethod(ctx, &pb.Request{})
    }, func() error {
        // Token refresh function
        return authClient.RefreshToken()
    })

    return response, err
}

http - HTTP Middleware

HTTP middleware components for authentication, content validation, and request context.

JWT Authentication Middleware
import (
    "net/http"
    "github.com/swayrider/swayrider/backend/swlib/http/middlewares"
)

func setupRoutes() http.Handler {
    mux := http.NewServeMux()

    // Get public keys function
    getPublicKeys := func() ([]string, error) {
        return []string{publicKeyPEM}, nil
    }

    // Protected route
    mux.Handle("/api/protected", middlewares.Auth(getPublicKeys)(protectedHandler))

    return mux
}
Web Authentication with Redirects

For web applications that need to redirect to login pages:

import "github.com/swayrider/swayrider/backend/swlib/http/middlewares"

func setupWebRoutes() http.Handler {
    mux := http.NewServeMux()

    getPublicKeys := func() ([]string, error) {
        return []string{publicKeyPEM}, nil
    }

    // Redirects to /login if not authenticated
    // Redirects to /verify if email not verified
    mux.Handle("/dashboard", middlewares.WebAuth(getPublicKeys)(dashboardHandler))

    return mux
}
Content-Type Validation
import "github.com/swayrider/swayrider/backend/swlib/http/middlewares"

func setupRoutes() http.Handler {
    mux := http.NewServeMux()

    // Only allow JSON content
    jsonOnly := middlewares.RequireMimeType("application/json")
    mux.Handle("/api/data", jsonOnly(dataHandler))

    return mux
}
Source Info Middleware

Extracts client IP, user agent, and other request metadata:

import "github.com/swayrider/swayrider/backend/swlib/http/middlewares"

func setupRoutes() http.Handler {
    mux := http.NewServeMux()

    // Adds client info to request context
    mux.Handle("/api/", middlewares.SourceInfo()(apiHandler))

    return mux
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    // Access source info from context
    clientIP := security.GetOrigIp(r.Context())
    userAgent := security.GetUserAgent(r.Context())
}
Secure File Serving

Prevents directory traversal attacks when serving static files:

import "github.com/swayrider/swayrider/backend/swlib/http/middlewares"

func setupStaticFiles() http.Handler {
    fs := http.Dir("./static")
    fileServer := http.FileServer(middlewares.NeuterFS{Fs: fs})
    return fileServer
}
import "github.com/swayrider/swayrider/backend/swlib/http/cookies"

func setUserCookie(w http.ResponseWriter, userData map[string]string) error {
    // Encode data into cookie
    encoded, err := cookies.Encode("user_data", userData)
    if err != nil {
        return err
    }

    http.SetCookie(w, &http.Cookie{
        Name:     "user_data",
        Value:    encoded,
        HttpOnly: true,
        Secure:   true,
    })
    return nil
}

func getUserCookie(r *http.Request) (map[string]string, error) {
    cookie, err := r.Cookie("user_data")
    if err != nil {
        return nil, err
    }

    var userData map[string]string
    err = cookies.Decode("user_data", cookie.Value, &userData)
    return userData, err
}

jwt - JWT Token Management

Comprehensive JWT handling with custom claims for user and service authentication.

Configuration
import "github.com/swayrider/swayrider/backend/swlib/jwt"

func init() {
    // Configure JWT issuer and audience (typically done once at startup)
    jwt.Configure("https://auth.swayrider.com", "swayrider-services")
}
Generating Tokens
import "github.com/swayrider/swayrider/backend/swlib/jwt"

func createUserToken(userID string, email string, isAdmin bool) (*jwt.AccessToken, error) {
    // OpenID Connect claims
    openIDClaims := &jwt.OpenIDClaims{
        Email:         email,
        EmailVerified: true,
        Name:          "John Doe",
    }

    // SwayRider-specific claims
    swayRiderClaims := &jwt.SwayRiderUserClaims{
        IsAdmin:      isAdmin,
        AccountLevel: "premium",
    }

    // Generate token with 1-hour TTL
    tokenID, accessToken, expiresAt, err := jwt.GenerateToken(
        userID,
        openIDClaims,
        swayRiderClaims,
        privateKeyPEM,
        time.Hour,
    )
    if err != nil {
        return nil, err
    }

    return accessToken, nil
}
Generating Service Client Tokens
import "github.com/swayrider/swayrider/backend/swlib/jwt"

func createServiceToken(clientID string, scopes []string) (*jwt.AccessToken, error) {
    serviceClaims := &jwt.SwayRiderServiceClaims{
        Scopes: scopes,
    }

    _, accessToken, _, err := jwt.GenerateToken(
        clientID,
        nil, // No OpenID claims for service clients
        serviceClaims,
        privateKeyPEM,
        24*time.Hour,
    )

    return accessToken, err
}
Verifying Tokens
import "github.com/swayrider/swayrider/backend/swlib/jwt"

func verifyUserToken(tokenString string) (*jwt.Claims, error) {
    claims, err := jwt.VerifyToken(tokenString, publicKeyPEM, jwt.VerifyOptions{
        ValidateExpiration: true,
    })
    if err != nil {
        return nil, fmt.Errorf("invalid token: %w", err)
    }

    // Access claims
    fmt.Println("User ID:", claims.Subject)
    fmt.Println("Email:", claims.OpenID.Email)
    fmt.Println("Is Admin:", claims.SwayRider.User.IsAdmin)

    return claims, nil
}
Working with Claims
import "github.com/swayrider/swayrider/backend/swlib/jwt"

// Serialize claims to map
func claimsToMap(claims *jwt.Claims) map[string]interface{} {
    return claims.MapClaims()
}

// Deserialize claims from map
func mapToClaims(m map[string]interface{}) (*jwt.OpenIDClaims, error) {
    var claims jwt.OpenIDClaims
    err := claims.FromMapClaims(m)
    return &claims, err
}

logger - Structured Logging

Context-aware logging with component and function tracking.

Basic Usage
import "github.com/swayrider/swayrider/backend/swlib/logger"

func main() {
    // Package-level logging
    logger.Infof("Application starting on port %d", 8080)
    logger.Debugf("Debug mode enabled")
    logger.Warnf("Cache size approaching limit: %d%%", 85)
    logger.Errorf("Failed to connect to database: %v", err)
    logger.Successf("Migration completed successfully")
}
Component-Scoped Logging
import "github.com/swayrider/swayrider/backend/swlib/logger"

type UserService struct {
    log *logger.Logger
}

func NewUserService() *UserService {
    return &UserService{
        log: logger.New("user-service"),
    }
}

func (s *UserService) CreateUser(email string) error {
    // Create function-scoped logger
    log := s.log.Derive("CreateUser")

    log.Infof("Creating user with email: %s", email)

    // ... user creation logic

    log.Successf("User created successfully")
    return nil
}
Log Levels
Level Method Icon Use Case
Info Infof, Infoln info General information
Debug Debugf, Debugln bug Debugging details
Warn Warnf, Warnln warning Warnings
Error Errorf, Errorln x Errors
Fatal Fatalf, Fatalln x Fatal errors (exits program)
Success Successf, Successln check Success messages

math - Mathematical Utilities

Geometric calculations and floating-point utilities.

Zoom Level to Radius Conversion
import "github.com/swayrider/swayrider/backend/swlib/math/geo"

func calculateSearchArea(zoomLevel int) {
    // Get search radius in meters
    radiusMeters := geo.Zoom2Radius(zoomLevel)

    // Get search radius in kilometers
    radiusKm := geo.Zoom2RadiusKm(zoomLevel)

    fmt.Printf("Zoom %d: %.0fm / %.2fkm radius\n", zoomLevel, radiusMeters, radiusKm)
}

// Zoom level reference:
// Zoom 0:  20000km (world view)
// Zoom 5:  1000km  (country)
// Zoom 10: 50km    (city)
// Zoom 15: 1.5km   (neighborhood)
// Zoom 18: 200m    (street)
Float Comparisons
import "github.com/swayrider/swayrider/backend/swlib/math/floats"

func compareFloats() {
    a := 0.1 + 0.2
    b := 0.3

    // Direct comparison fails due to floating-point precision
    fmt.Println(a == b) // false

    // Use epsilon comparison
    fmt.Println(floats.Equal(a, b)) // true

    // Round to specific precision
    rounded := floats.Round(3.14159, 2) // 3.14
}

security - Authorization Framework

Endpoint-based authorization and JWT context extraction.

Defining Endpoint Profiles
import "github.com/swayrider/swayrider/backend/swlib/security"

func init() {
    // Public endpoint - no authentication required
    security.SetEndpointProfile("/api/health", security.PublicEndpoint())

    // Protected endpoint - requires valid JWT
    security.SetEndpointProfile("/api/users", &security.EndpointProfile{})

    // Admin-only endpoint
    security.SetEndpointProfile("/api/admin", security.AdminEndpoint())

    // Endpoint for unverified users (e.g., email verification)
    security.SetEndpointProfile("/api/verify-email", security.UnverifiedEndpoint())

    // Service-to-service endpoint
    security.SetEndpointProfile("/api/internal", security.ServiceClientEndpoint("read:users", "write:users"))

    // Custom profile
    security.SetEndpointProfile("/api/premium", &security.EndpointProfile{
        AllowedAccountTypes: []string{"premium", "enterprise"},
    })

    // Method-specific profiles
    security.SetEndpointProfile("/api/posts", &security.EndpointProfile{
        AllowPublic: true,
    }, "GET")
    security.SetEndpointProfile("/api/posts", &security.EndpointProfile{}, "POST", "PUT", "DELETE")
}
Endpoint Profile Options
Option Type Description
AllowPublic bool Allow unauthenticated access
AllowUnverified bool Allow users with unverified email
DenyVerified bool Deny users with verified email (for verification endpoints)
AllowExpiredJwt bool Accept expired tokens (for refresh endpoints)
RequiresAdmin bool Require admin privileges
AllowedAccountTypes []string Whitelist specific account levels
AllowService bool Allow service client tokens
AllowedScopes []string Required scopes for service clients
Extracting JWT Data from Context
import "github.com/swayrider/swayrider/backend/swlib/security"

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // Get JWT claims
    claims := security.GetClaims(ctx)
    userID := claims.Subject
    email := claims.OpenID.Email

    // Get raw JWT token
    token := security.GetJwt(ctx)

    // Get refresh token (if present)
    refreshToken := security.GetRefreshToken(ctx)

    // Get client information
    clientIP := security.GetOrigIp(ctx)
    userAgent := security.GetUserAgent(ctx)
    host := security.GetHost(ctx)
    isSecure := security.GetSecure(ctx)
}
Evaluating Endpoint Profiles
import "github.com/swayrider/swayrider/backend/swlib/security"

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        profile := security.GetEndpointProfileForMethod(r.URL.Path, r.Method)
        if profile == nil {
            // No profile defined - deny by default
            http.Error(w, "Forbidden", http.StatusForbidden)
            return
        }

        token := extractToken(r)

        result := profile.Evaluate(token, getPublicKeys, logger)
        if !result.Allowed {
            http.Error(w, result.Reason, http.StatusUnauthorized)
            return
        }

        // Add claims to context
        ctx := context.WithValue(r.Context(), security.ClaimsKey, result.Claims)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

str - String Utilities

Simple string manipulation helpers.

import "github.com/swayrider/swayrider/backend/swlib/str"

func processString() {
    // Remove null terminators from byte slice
    data := []byte("hello\x00\x00\x00")
    cleaned := str.NullTerm(data) // []byte("hello")

    // Convert string to pointer (nil for empty strings)
    name := "John"
    namePtr := str.ToPtr(name) // *string pointing to "John"

    empty := ""
    emptyPtr := str.ToPtr(empty) // nil
}

Architecture Overview

+-------------------------------------------------------------+
|                        Application                          |
|  +-----------------------------------------------------+    |
|  |                    app.App                          |    |
|  |  +----------+ +----------+ +--------------------+   |    |
|  |  |  Config  | | Database | |  Service Clients   |   |    |
|  |  +----------+ +----------+ +--------------------+   |    |
|  |  +----------+ +----------+ +--------------------+   |    |
|  |  |   gRPC   | |   HTTP   | | Background Routines|   |    |
|  |  +----------+ +----------+ +--------------------+   |    |
|  +-----------------------------------------------------+    |
+-------------------------------------------------------------+
           |              |              |
           v              v              v
+---------------+ +-------------+ +-------------+
|   security/   | |    jwt/     | |   logger/   |
| Authorization | |   Tokens    | |   Logging   |
+---------------+ +-------------+ +-------------+
           |              |              |
           v              v              v
+---------------+ +-------------+ +-------------+
|    crypto/    | |    http/    | |    grpc/    |
|   Security    | |  Middleware | | Interceptors|
+---------------+ +-------------+ +-------------+
           |              |              |
           v              v              v
+---------------+ +-------------+ +-------------+
|     env/      | |    flag/    | |   cache/    |
|  Environment  | |  CLI Flags  | |   Caching   |
+---------------+ +-------------+ +-------------+
Request Flow
HTTP Request
     |
     v
+-------------+
| SourceInfo  |  <- Extracts client IP, user agent
| Middleware  |
+-------------+
     |
     v
+-------------+
|    Auth     |  <- Validates JWT, checks endpoint profile
| Middleware  |
+-------------+
     |
     v
+-------------+
|   Handler   |  <- Access claims via security.GetClaims(ctx)
+-------------+

Best Practices

1. Service Initialization

Always use the builder pattern with app.New():

app.New("servicename").
    WithDefaultConfigFields(app.BackendServiceFields | app.DatabaseConnectionFields).
    WithServiceClients(...).
    WithDatabase(...).
    WithGrpc(...).
    Run()
2. Configuration
  • Use environment variables for all configuration
  • Define required fields explicitly
  • Provide sensible defaults where appropriate
  • Use the pre-defined field groups for consistency
3. Security
  • Always define endpoint profiles for authorization
  • Use security.GetClaims() to access user information
  • Never store passwords - use crypto.CalculatePasswordHash()
  • Rotate JWT keys regularly using crypto.CreateKeypair()
4. Logging
  • Create component-scoped loggers for traceability
  • Use Derive() for function-level context
  • Use appropriate log levels consistently
5. Error Handling
  • Return errors up the call stack
  • Log errors at the appropriate level
  • Use logger.Fatalf() only for unrecoverable errors
6. Inter-Service Communication
  • Use typed service clients from grpcclients/
  • Handle authentication failures with s2s.Call()
  • Configure service endpoints via environment variables

Directories

Path Synopsis
Package app provides a service bootstrap framework for building microservices.
Package app provides a service bootstrap framework for building microservices.
Package cache provides a simple thread-safe in-memory cache for storing arbitrary values.
Package cache provides a simple thread-safe in-memory cache for storing arbitrary values.
Package compression provides utilities for working with compressed archives.
Package compression provides utilities for working with compressed archives.
Package crypto provides cryptographic utilities for password hashing, secure random generation, and RSA keypair management.
Package crypto provides cryptographic utilities for password hashing, secure random generation, and RSA keypair management.
Package env provides utilities for reading environment variables with type conversion and fallback values.
Package env provides utilities for reading environment variables with type conversion and fallback values.
Package flag provides custom flag types for parsing array values from command-line arguments.
Package flag provides custom flag types for parsing array values from command-line arguments.
grpc
interceptors
Package interceptors provides gRPC server interceptors for authentication and client information extraction.
Package interceptors provides gRPC server interceptors for authentication and client information extraction.
s2s
Package s2s provides utilities for service-to-service gRPC communication with automatic token refresh on authentication failures.
Package s2s provides utilities for service-to-service gRPC communication with automatic token refresh on authentication failures.
http
compression
Package compression provides HTTP compression utilities.
Package compression provides HTTP compression utilities.
cookies
Package cookies provides HTTP cookie utilities for secure cookie management.
Package cookies provides HTTP cookie utilities for secure cookie management.
middlewares
Package middlewares provides HTTP middleware functions for authentication, content-type handling, file serving security, and request metadata extraction.
Package middlewares provides HTTP middleware functions for authentication, content-type handling, file serving security, and request metadata extraction.
Package jwt provides JWT token generation and verification using RS256 signing.
Package jwt provides JWT token generation and verification using RS256 signing.
Package logger provides context-aware logging with emoji-based log levels.
Package logger provides context-aware logging with emoji-based log levels.
math
floats
Package floats provides utilities for floating-point comparisons with epsilon tolerance to handle precision issues.
Package floats provides utilities for floating-point comparisons with epsilon tolerance to handle precision issues.
geo
Package geo provides geographic utility functions for map-related calculations.
Package geo provides geographic utility functions for map-related calculations.
Package security provides authentication context utilities and endpoint authorization profiles for SwayRider services.
Package security provides authentication context utilities and endpoint authorization profiles for SwayRider services.
Package str provides simple string manipulation utilities.
Package str provides simple string manipulation utilities.
Package svcreg provides service registry functionality using Consul.
Package svcreg provides service registry functionality using Consul.

Jump to

Keyboard shortcuts

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