Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
gorest
https://github.com/pilinux/gorest
Admin
gorest is a RESTful API starter kit written in Golang with the Gin framework, designed for rapid
...
Tokens:
60,726
Snippets:
347
Trust Score:
8.7
Update:
1 month ago
Context
Skills
Chat
Benchmark
92.5
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# gorest gorest is a production-ready Go RESTful API starter kit built with Gin framework, GORM ORM, JWT authentication, and support for multiple databases including MySQL, PostgreSQL, SQLite, Redis, and MongoDB. It provides a complete authentication system with user registration, login, JWT token management, two-factor authentication (2FA), email verification, password recovery, and security features like CORS, rate limiting, and firewall protection. The framework follows a layered architecture pattern with controllers for HTTP request handling, handlers for business logic, services for shared utilities, and repositories for data access. It supports multiple JWT signing algorithms (HMAC, ECDSA, EdDSA, RSA), password hashing with Argon2id, optional encryption at rest for user data, and integrates with Sentry for error logging. The codebase includes two example applications demonstrating different architectural approaches: a simpler rapid-development style and an interface-driven design focused on modularity and testability. ## Configuration Loading Load application configuration from environment variables using the `config` package. The `Config()` function reads from a `.env` file and populates a global configuration struct accessible via `GetConfig()`. ```go package main import ( "fmt" gconfig "github.com/pilinux/gorest/config" ) func main() { // Load configuration from .env file if err := gconfig.Config(); err != nil { fmt.Println("Failed to load config:", err) return } // Access configuration values configure := gconfig.GetConfig() fmt.Printf("Server: %s:%s\n", configure.Server.ServerHost, configure.Server.ServerPort) fmt.Printf("Environment: %s\n", configure.Server.ServerEnv) // Check feature flags if gconfig.IsRDBMS() { fmt.Println("RDBMS is enabled") } if gconfig.IsJWT() { fmt.Println("JWT authentication is enabled") } if gconfig.Is2FA() { fmt.Println("Two-factor authentication is enabled") } if gconfig.IsRedis() { fmt.Println("Redis is enabled") } if gconfig.IsMongo() { fmt.Println("MongoDB is enabled") } } ``` ## Database Initialization - RDBMS Initialize connections to MySQL, PostgreSQL, or SQLite databases using GORM. The `InitDB()` function automatically configures the connection based on environment variables. ```go package main import ( "fmt" gconfig "github.com/pilinux/gorest/config" gdb "github.com/pilinux/gorest/database" ) func main() { // Load configuration first if err := gconfig.Config(); err != nil { panic(err) } // Initialize RDBMS connection with retry loop if gconfig.IsRDBMS() { for { if err := gdb.InitDB().Error; err != nil { fmt.Println("DB connection failed, retrying:", err) continue } break } fmt.Println("Database connected successfully") } // Get the GORM database instance db := gdb.GetDB() // Use db for queries var count int64 db.Table("users").Count(&count) fmt.Printf("Total users: %d\n", count) // Close connection when done defer gdb.CloseSQL() } ``` ## Database Initialization - Redis Initialize Redis connection pool for caching, session storage, and JWT token blacklisting. Returns a radix v4 client for executing Redis commands. ```go package main import ( "context" "fmt" "time" "github.com/mediocregopher/radix/v4" gconfig "github.com/pilinux/gorest/config" gdb "github.com/pilinux/gorest/database" ) func main() { if err := gconfig.Config(); err != nil { panic(err) } if gconfig.IsRedis() { if _, err := gdb.InitRedis(); err != nil { panic(err) } fmt.Println("Redis connected successfully") } // Get Redis client client := gdb.GetRedis() // Set a value with expiration ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() var result string err := client.Do(ctx, radix.FlatCmd(&result, "SET", "mykey", "myvalue", "EX", 3600)) if err != nil { fmt.Println("SET failed:", err) return } fmt.Println("SET result:", result) // Output: OK // Get the value var value string err = client.Do(ctx, radix.FlatCmd(&value, "GET", "mykey")) if err != nil { fmt.Println("GET failed:", err) return } fmt.Println("GET result:", value) // Output: myvalue defer gdb.CloseRedis() } ``` ## Database Initialization - MongoDB Initialize MongoDB connection with connection pooling and automatic ping validation. Supports MongoDB Atlas and standard deployments. ```go package main import ( "context" "fmt" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" gconfig "github.com/pilinux/gorest/config" gdb "github.com/pilinux/gorest/database" ) func main() { if err := gconfig.Config(); err != nil { panic(err) } if gconfig.IsMongo() { if _, err := gdb.InitMongo(); err != nil { panic(err) } fmt.Println("MongoDB connected successfully") } // Get MongoDB client client := gdb.GetMongo() // Create an index index := mongo.IndexModel{Keys: bson.D{{Key: "email", Value: 1}}} if err := gdb.MongoCreateIndex("mydb", "users", index); err != nil { fmt.Println("Index creation failed:", err) } // Insert a document ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() collection := client.Database("mydb").Collection("users") doc := bson.D{ {Key: "name", Value: "John Doe"}, {Key: "email", Value: "john@example.com"}, } result, err := collection.InsertOne(ctx, doc) if err != nil { fmt.Println("Insert failed:", err) return } fmt.Printf("Inserted document ID: %v\n", result.InsertedID) defer gdb.CloseMongo() } ``` ## User Registration API Register new users with email and password. Passwords are automatically hashed using Argon2id before storage. The endpoint validates password length against configured minimum. ```go // Handler setup in router v1.POST("register", gcontroller.CreateUserAuth) // Request: // POST /api/v1/register // Content-Type: application/json // { // "email": "user@example.com", // "password": "securepassword123" // } // Response (201 Created): // { // "authID": 1, // "email": "user@example.com" // } // cURL example: // curl -X POST http://localhost:8999/api/v1/register \ // -H "Content-Type: application/json" \ // -d '{"email":"user@example.com","password":"securepassword123"}' ``` ## User Login API Authenticate users and issue JWT access and refresh tokens. Supports cookie-based token storage and optional 2FA verification status in response. ```go // Handler setup in router v1.POST("login", gcontroller.Login) // Request: // POST /api/v1/login // Content-Type: application/json // { // "email": "user@example.com", // "password": "securepassword123" // } // Response (200 OK): // { // "accessJWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // "refreshJWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // "twoFA": "off" // } // cURL example: // curl -X POST http://localhost:8999/api/v1/login \ // -H "Content-Type: application/json" \ // -d '{"email":"user@example.com","password":"securepassword123"}' ``` ## JWT Token Refresh API Exchange a valid refresh token for new access and refresh token pairs. Requires the RefreshJWT middleware. ```go // Handler setup in router rJWT := v1.Group("refresh") rJWT.Use(gmiddleware.RefreshJWT()) rJWT.Use(gservice.JWTBlacklistChecker()) rJWT.POST("", gcontroller.Refresh) // Request with Authorization header: // POST /api/v1/refresh // Authorization: Bearer <refreshToken> // Or with request body: // POST /api/v1/refresh // Content-Type: application/json // { // "refreshJWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." // } // Response (200 OK): // { // "accessJWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // "refreshJWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." // } // cURL example: // curl -X POST http://localhost:8999/api/v1/refresh \ // -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` ## JWT Middleware Protect routes with JWT authentication. The middleware validates tokens from cookies or Authorization headers and sets user claims in the Gin context. ```go package main import ( "net/http" "github.com/gin-gonic/gin" gmiddleware "github.com/pilinux/gorest/lib/middleware" gservice "github.com/pilinux/gorest/service" ) func main() { r := gin.Default() // Protected routes group protected := r.Group("/api/v1") protected.Use(gmiddleware.JWT()) protected.Use(gservice.JWTBlacklistChecker()) protected.GET("/profile", func(c *gin.Context) { // Access user claims from context claims := gservice.GetClaims(c) c.JSON(http.StatusOK, gin.H{ "authID": claims.AuthID, "email": claims.Email, "role": claims.Role, }) }) // For external auth providers (e.g., Clerk) with custom cookie clerkRoutes := r.Group("/api/v1/clerk") clerkRoutes.Use(gmiddleware.JWT("__session")) clerkRoutes.GET("/user", func(c *gin.Context) { // Use "sub" claim for external user ID sub := c.GetString("sub") c.JSON(http.StatusOK, gin.H{"userID": sub}) }) r.Run(":8999") } ``` ## Issue JWT Tokens Programmatically Generate JWT tokens with custom claims for access and refresh token pairs. Supports multiple signing algorithms. ```go package main import ( "fmt" gmiddleware "github.com/pilinux/gorest/lib/middleware" ) func main() { // Configure JWT parameters (normally loaded from config) gmiddleware.JWTParams = gmiddleware.JWTParameters{ Algorithm: "HS256", AccessKey: []byte("your-secret-access-key"), AccessKeyTTL: 5, // 5 minutes RefreshKey: []byte("your-secret-refresh-key"), RefreshKeyTTL: 60, // 60 minutes Issuer: "myapp", Audience: "myapp-users", } // Create custom claims claims := gmiddleware.MyCustomClaims{ AuthID: 123, Email: "user@example.com", Role: "admin", Scope: "read write", TwoFA: "verified", SiteLan: "en", Custom1: "custom-value-1", } // Generate access token accessToken, jtiAccess, err := gmiddleware.GetJWT(claims, "access") if err != nil { fmt.Println("Access token error:", err) return } fmt.Printf("Access Token: %s\nJTI: %s\n", accessToken, jtiAccess) // Generate refresh token refreshToken, jtiRefresh, err := gmiddleware.GetJWT(claims, "refresh") if err != nil { fmt.Println("Refresh token error:", err) return } fmt.Printf("Refresh Token: %s\nJTI: %s\n", refreshToken, jtiRefresh) } ``` ## Two-Factor Authentication Setup Enable TOTP-based 2FA for users. Generates a QR code for authenticator apps like Google Authenticator or Authy. ```go // Router setup r2FA := v1.Group("2fa") r2FA.Use(gmiddleware.JWT()) r2FA.Use(gservice.JWTBlacklistChecker()) r2FA.POST("setup", gcontroller.Setup2FA) r2FA.POST("activate", gcontroller.Activate2FA) r2FA.POST("validate", gcontroller.Validate2FA) r2FA.POST("validate-backup-code", gcontroller.ValidateBackup2FA) // Step 1: Setup 2FA (requires password confirmation) // POST /api/v1/2fa/setup // Authorization: Bearer <accessToken> // Content-Type: application/json // { // "password": "userpassword" // } // Response: QR code image path for scanning // Step 2: Activate 2FA with OTP from authenticator app // POST /api/v1/2fa/activate // Authorization: Bearer <accessToken> // Content-Type: application/json // { // "otp": "123456" // } // Step 3: Validate OTP during login (when 2FA is enabled) // POST /api/v1/2fa/validate // Authorization: Bearer <accessToken> // Content-Type: application/json // { // "otp": "123456" // } // cURL example for setup: // curl -X POST http://localhost:8999/api/v1/2fa/setup \ // -H "Authorization: Bearer <accessToken>" \ // -H "Content-Type: application/json" \ // -d '{"password":"userpassword"}' ``` ## Password Recovery API Send password recovery emails and reset forgotten passwords using verification codes. ```go // Router setup rPass := v1.Group("password") rPass.POST("forgot", gcontroller.PasswordForgot) // Public rPass.POST("reset", gcontroller.PasswordRecover) // Public // Protected password change rPass.Use(gmiddleware.JWT()) rPass.Use(gservice.JWTBlacklistChecker()) rPass.POST("edit", gcontroller.PasswordUpdate) // Step 1: Request password recovery email // POST /api/v1/password/forgot // Content-Type: application/json // { // "email": "user@example.com" // } // Step 2: Reset password with verification code // POST /api/v1/password/reset // Content-Type: application/json // { // "email": "user@example.com", // "verificationCode": "abc123", // "passNew": "newSecurePassword", // "passRepeat": "newSecurePassword" // } // Change password while logged in // POST /api/v1/password/edit // Authorization: Bearer <accessToken> // Content-Type: application/json // { // "password": "currentPassword", // "passNew": "newSecurePassword", // "passRepeat": "newSecurePassword" // } // cURL example: // curl -X POST http://localhost:8999/api/v1/password/forgot \ // -H "Content-Type: application/json" \ // -d '{"email":"user@example.com"}' ``` ## CORS Middleware Configure Cross-Origin Resource Sharing policies for the API. Supports comprehensive security headers. ```go package main import ( "github.com/gin-gonic/gin" gmiddleware "github.com/pilinux/gorest/lib/middleware" ) func main() { r := gin.Default() // Define CORS policies corsPolicy := []gmiddleware.CORSPolicy{ {Key: "Access-Control-Allow-Origin", Value: "https://example.com"}, {Key: "Access-Control-Allow-Methods", Value: "GET, POST, PUT, DELETE, OPTIONS"}, {Key: "Access-Control-Allow-Headers", Value: "Content-Type, Authorization"}, {Key: "Access-Control-Allow-Credentials", Value: "true"}, {Key: "Access-Control-Max-Age", Value: "3600"}, {Key: "X-Content-Type-Options", Value: "nosniff"}, {Key: "X-Frame-Options", Value: "DENY"}, {Key: "Referrer-Policy", Value: "strict-origin-when-cross-origin"}, {Key: "Content-Security-Policy", Value: "default-src 'self'"}, } r.Use(gmiddleware.CORS(corsPolicy)) r.GET("/api/data", func(c *gin.Context) { c.JSON(200, gin.H{"message": "CORS enabled"}) }) r.Run(":8999") } ``` ## Rate Limiting Middleware Implement IP-based rate limiting to protect against abuse. Supports per-second, minute, hour, or day limits. ```go package main import ( "github.com/gin-gonic/gin" glib "github.com/pilinux/gorest/lib" gmiddleware "github.com/pilinux/gorest/lib/middleware" ) func main() { r := gin.Default() // Create rate limiter: 100 requests per minute // Format: "count-period" where period is S, M, H, D limiter, err := glib.InitRateLimiter("100-M", "X-Real-Ip") if err != nil { panic(err) } // Apply rate limiting globally r.Use(gmiddleware.RateLimit(limiter)) r.GET("/api/data", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Rate limited endpoint"}) }) r.Run(":8999") } // Example rate limit configurations: // "10-S" - 10 requests per second // "100-M" - 100 requests per minute // "1000-H" - 1000 requests per hour // "10000-D" - 10000 requests per day ``` ## Firewall Middleware Implement IP-based whitelist or blacklist firewall protection. Supports individual IPs, CIDR ranges, and IPv6. ```go package main import ( "github.com/gin-gonic/gin" gmiddleware "github.com/pilinux/gorest/lib/middleware" ) func main() { r := gin.Default() // Whitelist mode - only allow specified IPs r.Use(gmiddleware.Firewall("whitelist", "192.168.1.0/24, 10.0.0.1")) // Or blacklist mode - block specified IPs // r.Use(gmiddleware.Firewall("blacklist", "192.168.100.0/24")) // Allow all (useful for development) // r.Use(gmiddleware.Firewall("whitelist", "*")) r.GET("/api/secure", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Firewall protected"}) }) r.Run(":8999") } // IP formats supported: // Single IP: "192.168.1.1" // CIDR range: "192.168.0.0/16" // IPv6: "2001:db8::/32" // Multiple: "192.168.1.1, 10.0.0.0/8, 2001:db8::/32" // Wildcard: "*" (allow/block all) ``` ## Password Hashing with Argon2id Hash passwords securely using Argon2id algorithm with configurable parameters. Supports optional secret pepper for additional security. ```go package main import ( "fmt" glib "github.com/pilinux/gorest/lib" ) func main() { // Configure Argon2id parameters config := glib.HashPassConfig{ Memory: 64, // 64 * 1024 = 64 MiB Iterations: 2, // Number of passes Parallelism: 2, // Number of threads SaltLength: 16, // 16 bytes salt KeyLength: 32, // 32 bytes output } password := "userSecretPassword" pepper := "optional-secret-pepper" // Can be empty string // Hash the password hash, err := glib.HashPass(config, password, pepper) if err != nil { fmt.Println("Hashing failed:", err) return } fmt.Printf("Password hash: %s\n", hash) // Output format: $argon2id$v=19$m=65536,t=2,p=2$<salt>$<hash> } ``` ## AES-GCM Encryption Encrypt and decrypt sensitive data using AES-GCM authenticated encryption for data at rest. ```go package main import ( "fmt" glib "github.com/pilinux/gorest/lib" ) func main() { // Key must be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256 key := []byte("32-byte-long-secret-key-here!!") plaintext := []byte("Sensitive user data to encrypt") // Encrypt ciphertext, err := glib.Encrypt(plaintext, key) if err != nil { fmt.Println("Encryption failed:", err) return } fmt.Printf("Encrypted (base64): %x\n", ciphertext) // Decrypt decrypted, err := glib.Decrypt(ciphertext, key) if err != nil { fmt.Println("Decryption failed:", err) return } fmt.Printf("Decrypted: %s\n", decrypted) } ``` ## Email Validation Validate email addresses using pattern matching and MX record lookup to ensure deliverability. ```go package main import ( "fmt" glib "github.com/pilinux/gorest/lib" ) func main() { emails := []string{ "valid@example.com", "invalid-email", "no-mx@nonexistent-domain-xyz.com", } for _, email := range emails { if glib.ValidateEmail(email) { fmt.Printf("%s - Valid (has MX records)\n", email) } else { fmt.Printf("%s - Invalid\n", email) } } } ``` ## TOTP Two-Factor Authentication Library Generate and validate Time-based One-Time Passwords (TOTP) for two-factor authentication. ```go package main import ( "crypto" "fmt" glib "github.com/pilinux/gorest/lib" ) func main() { userEmail := "user@example.com" issuer := "MyApp" // Create new TOTP secret otpBytes, err := glib.NewTOTP(userEmail, issuer, crypto.SHA1, 6) if err != nil { fmt.Println("TOTP creation failed:", err) return } // Generate QR code for authenticator app qrPNG, err := glib.NewQR(otpBytes, issuer) if err != nil { fmt.Println("QR generation failed:", err) return } // Save QR code to file filename, err := glib.ByteToPNG(qrPNG, "/tmp/qrcodes") if err != nil { fmt.Println("QR save failed:", err) return } fmt.Printf("QR code saved to: %s\n", filename) // Validate OTP from user (example code) userOTP := "123456" updatedOTP, err := glib.ValidateTOTP(otpBytes, issuer, userOTP) if err != nil { fmt.Println("OTP validation failed:", err) return } fmt.Println("OTP validated successfully") _ = updatedOTP // Use updated bytes for subsequent validations } ``` ## HTTP Response Renderer Standardized HTTP response rendering supporting JSON and HTML template responses. ```go package main import ( "net/http" "github.com/gin-gonic/gin" gmodel "github.com/pilinux/gorest/database/model" grenderer "github.com/pilinux/gorest/lib/renderer" ) func main() { r := gin.Default() // JSON response r.GET("/api/users/:id", func(c *gin.Context) { user := map[string]any{ "id": c.Param("id"), "name": "John Doe", "email": "john@example.com", } response := gmodel.HTTPResponse{Message: user} grenderer.Render(c, response, http.StatusOK) }) // Error response r.GET("/api/error", func(c *gin.Context) { response := gmodel.HTTPResponse{Message: "Resource not found"} grenderer.Render(c, response, http.StatusNotFound) }) // HTML template response (requires Pongo2 middleware) r.GET("/page", func(c *gin.Context) { data := gmodel.HTTPResponse{ Message: map[string]any{ "title": "Welcome", "user": "John", }, } grenderer.Render(c, data, http.StatusOK, "welcome.html") }) r.Run(":8999") } ``` ## Graceful Server Shutdown Implement graceful shutdown with configurable timeout and database connection cleanup. ```go package main import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" gdb "github.com/pilinux/gorest/database" gserver "github.com/pilinux/gorest/lib/server" ) func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{"status": "running"}) }) srv := &http.Server{ Addr: ":8999", Handler: r, ReadTimeout: 30 * time.Second, ReadHeaderTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 60 * time.Second, } // Channel to signal shutdown completion done := make(chan struct{}) // Start graceful shutdown watcher in background go func() { err := gserver.GracefulShutdown( srv, 30*time.Second, // Shutdown timeout done, gdb.CloseAllDB, // Cleanup function ) if err != nil { fmt.Println("Shutdown error:", err) } }() // Start server fmt.Println("Server starting on :8999") if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { fmt.Printf("Server error: %v\n", err) } // Wait for graceful shutdown to complete <-done fmt.Println("Server shutdown complete") } ``` ## Complete Application Example Full application setup demonstrating router configuration, middleware stack, database initialization, and route definitions. ```go package main import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" gconfig "github.com/pilinux/gorest/config" gcontroller "github.com/pilinux/gorest/controller" gdb "github.com/pilinux/gorest/database" glib "github.com/pilinux/gorest/lib" gmiddleware "github.com/pilinux/gorest/lib/middleware" gserver "github.com/pilinux/gorest/lib/server" gservice "github.com/pilinux/gorest/service" ) func main() { // Load configuration if err := gconfig.Config(); err != nil { panic(err) } configure := gconfig.GetConfig() // Initialize databases if gconfig.IsRDBMS() { if err := gdb.InitDB().Error; err != nil { panic(err) } } if gconfig.IsRedis() { if _, err := gdb.InitRedis(); err != nil { panic(err) } } // Setup router if gconfig.IsProd() { gin.SetMode(gin.ReleaseMode) } r := gin.Default() r.SetTrustedProxies(nil) // Apply middleware if gconfig.IsCORS() { r.Use(gmiddleware.CORS(configure.Security.CORS)) } if gconfig.IsWAF() { r.Use(gmiddleware.Firewall( configure.Security.Firewall.ListType, configure.Security.Firewall.IP, )) } if gconfig.IsRateLimit() { limiter, _ := glib.InitRateLimiter(configure.Security.RateLimit, "X-Real-Ip") r.Use(gmiddleware.RateLimit(limiter)) } // API routes v1 := r.Group("/api/v1") // Public routes v1.POST("register", gcontroller.CreateUserAuth) v1.POST("login", gcontroller.Login) // Protected routes protected := v1.Group("") protected.Use(gmiddleware.JWT()) protected.Use(gservice.JWTBlacklistChecker()) protected.GET("profile", func(c *gin.Context) { claims := gservice.GetClaims(c) c.JSON(http.StatusOK, gin.H{ "authID": claims.AuthID, "email": claims.Email, }) }) // Logout (requires both access and refresh tokens) logout := v1.Group("logout") logout.Use(gmiddleware.JWT()) logout.Use(gmiddleware.RefreshJWT()) logout.Use(gservice.JWTBlacklistChecker()) logout.POST("", gcontroller.Logout) // Token refresh refresh := v1.Group("refresh") refresh.Use(gmiddleware.RefreshJWT()) refresh.Use(gservice.JWTBlacklistChecker()) refresh.POST("", gcontroller.Refresh) // Start server with graceful shutdown srv := &http.Server{ Addr: fmt.Sprintf("%s:%s", configure.Server.ServerHost, configure.Server.ServerPort), Handler: r, } done := make(chan struct{}) go gserver.GracefulShutdown(srv, 30*time.Second, done, gdb.CloseAllDB) fmt.Printf("Server starting on %s\n", srv.Addr) srv.ListenAndServe() <-done } ``` ## Repository Pattern Example Implement the repository pattern for clean data access layer separation with GORM. ```go package repo import ( "context" "gorm.io/gorm" ) // Post model type Post struct { PostID uint64 `gorm:"primaryKey" json:"postID"` CreatedAt int64 `json:"createdAt"` UpdatedAt int64 `json:"updatedAt"` Title string `json:"title"` Body string `json:"body"` IDAuth uint64 `gorm:"index" json:"-"` IDUser uint64 `gorm:"index" json:"-"` } // PostRepository interface type PostRepository interface { GetPosts(ctx context.Context) ([]Post, error) GetPost(ctx context.Context, id uint64) (*Post, error) CreatePost(ctx context.Context, post *Post) error UpdatePost(ctx context.Context, post *Post) error DeletePost(ctx context.Context, id uint64) error ListPosts(ctx context.Context, limit, offset int) ([]Post, error) CountPosts(ctx context.Context) (int64, error) } // PostRepo implements PostRepository type PostRepo struct { db *gorm.DB } func NewPostRepo(db *gorm.DB) *PostRepo { return &PostRepo{db: db} } func (r *PostRepo) GetPosts(ctx context.Context) ([]Post, error) { var posts []Post err := r.db.WithContext(ctx).Find(&posts).Error return posts, err } func (r *PostRepo) GetPost(ctx context.Context, id uint64) (*Post, error) { var post Post err := r.db.WithContext(ctx).Where("post_id = ?", id).First(&post).Error return &post, err } func (r *PostRepo) CreatePost(ctx context.Context, post *Post) error { return r.db.WithContext(ctx).Create(post).Error } func (r *PostRepo) UpdatePost(ctx context.Context, post *Post) error { return r.db.WithContext(ctx).Save(post).Error } func (r *PostRepo) DeletePost(ctx context.Context, id uint64) error { return r.db.WithContext(ctx).Where("post_id = ?", id).Delete(&Post{}).Error } func (r *PostRepo) ListPosts(ctx context.Context, limit, offset int) ([]Post, error) { var posts []Post err := r.db.WithContext(ctx). Order("post_id desc"). Limit(limit). Offset(offset). Find(&posts).Error return posts, err } func (r *PostRepo) CountPosts(ctx context.Context) (int64, error) { var count int64 err := r.db.WithContext(ctx).Model(&Post{}).Count(&count).Error return count, err } ``` ## Service Layer Pattern Implement business logic in a service layer with proper error handling and HTTP status code mapping. ```go package service import ( "context" "errors" "net/http" gmodel "github.com/pilinux/gorest/database/model" log "github.com/sirupsen/logrus" "gorm.io/gorm" ) type PostService struct { postRepo PostRepository } func NewPostService(repo PostRepository) *PostService { return &PostService{postRepo: repo} } func (s *PostService) GetPosts(ctx context.Context, page, pageSize int) (gmodel.HTTPResponse, int) { // Normalize pagination if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } if pageSize > 100 { pageSize = 100 } offset := (page - 1) * pageSize // Get total count total, err := s.postRepo.CountPosts(ctx) if err != nil { if errors.Is(err, context.Canceled) { return gmodel.HTTPResponse{Message: "request canceled"}, http.StatusRequestTimeout } log.WithError(err).Error("GetPosts.CountPosts") return gmodel.HTTPResponse{Message: "internal server error"}, http.StatusInternalServerError } if total == 0 { return gmodel.HTTPResponse{Message: "no posts found"}, http.StatusNotFound } // Get paginated posts posts, err := s.postRepo.ListPosts(ctx, pageSize, offset) if err != nil { log.WithError(err).Error("GetPosts.ListPosts") return gmodel.HTTPResponse{Message: "internal server error"}, http.StatusInternalServerError } result := map[string]any{ "posts": posts, "page": page, "pageSize": pageSize, "total": total, "hasNext": int64(page*pageSize) < total, "hasPrevious": page > 1, } return gmodel.HTTPResponse{Message: result}, http.StatusOK } func (s *PostService) GetPost(ctx context.Context, id uint64) (gmodel.HTTPResponse, int) { post, err := s.postRepo.GetPost(ctx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return gmodel.HTTPResponse{Message: "post not found"}, http.StatusNotFound } log.WithError(err).Error("GetPost") return gmodel.HTTPResponse{Message: "internal server error"}, http.StatusInternalServerError } return gmodel.HTTPResponse{Message: post}, http.StatusOK } func (s *PostService) CreatePost(ctx context.Context, post *Post) (gmodel.HTTPResponse, int) { if err := s.postRepo.CreatePost(ctx, post); err != nil { log.WithError(err).Error("CreatePost") return gmodel.HTTPResponse{Message: "internal server error"}, http.StatusInternalServerError } return gmodel.HTTPResponse{Message: post}, http.StatusCreated } ``` ## Environment Variables Reference Essential environment variables for configuring gorest applications. ```bash # .env file example # Application APP_HOST=localhost APP_PORT=8999 APP_ENV=development # JWT Authentication ACTIVATE_JWT=yes JWT_ALG=HS256 ACCESS_KEY=your-strong-secret-key-for-access-tokens REFRESH_KEY=your-strong-secret-key-for-refresh-tokens ACCESS_KEY_TTL=5 REFRESH_KEY_TTL=60 ISSUER=myapp # Password Policy MIN_PASS_LENGTH=8 ACTIVATE_HASHING=yes HASHPASSMEMORY=64 HASHPASSITERATIONS=2 HASHPASSPARALLELISM=2 HASHPASSSALTLENGTH=16 HASHPASSKEYLENGTH=32 # Database - MySQL ACTIVATE_RDBMS=yes DBDRIVER=mysql DBHOST=localhost DBPORT=3306 DBNAME=myapp DBUSER=root DBPASS=password DBTIMEZONE=UTC # Database - Redis ACTIVATE_REDIS=yes REDISHOST=127.0.0.1 REDISPORT=6379 POOLSIZE=10 CONNTTL=5 # Security ACTIVATE_CORS=yes CORS_ORIGIN=* CORS_METHODS=GET, POST, PUT, DELETE, OPTIONS ACTIVATE_FIREWALL=yes LISTTYPE=whitelist IP=* RATE_LIMIT=100-M # Two-Factor Authentication ACTIVATE_2FA=no TWO_FA_ISSUER=MyApp TWO_FA_CRYPTO=1 TWO_FA_DIGITS=6 ``` gorest provides a comprehensive foundation for building secure, production-ready RESTful APIs in Go. Its modular architecture separates concerns across configuration, database, controller, handler, and service layers, making it easy to extend and customize. The framework handles common concerns like authentication, authorization, rate limiting, and database connections out of the box, allowing developers to focus on business logic. The two included example applications demonstrate different architectural approaches: `example` for rapid prototyping with simpler patterns, and `example2` for larger applications using interface-driven design with repositories and services. Both examples show how to integrate with the gorest core packages using the standard import aliases (`gconfig`, `gdb`, `gmiddleware`, `gservice`, etc.) and can serve as templates for new projects. The framework's environment-based configuration makes it straightforward to deploy across development, staging, and production environments with different database backends and security settings.