Getting Started with GOE
This guide will walk you through installing GOE, setting up your first project, and running a basic "Hello, World" HTTP server. We'll also explore the modular architecture and show you different ways to structure your application.
Prerequisites
- Go: GOE requires Go version 1.21 or newer (recommended: 1.23+). You can download it from golang.org.
- To check your Go version:
go version
- To check your Go version:
Note: The project currently specifies Go 1.24 in go.mod, but this version doesn't exist yet. The framework should work with Go 1.21+ and has been tested with Go 1.23.
Installation
To add GOE to your Go project, use go get
:
go get go.oease.dev/goe/v2
This command will fetch the latest stable version of the GOE framework.
Core Concepts
Before diving into code, let's understand GOE's key concepts:
- Modular Design: Enable only the components you need (
WithHTTP
,WithCache
,WithDB
) - Contracts: Interface-based design for loose coupling and easy testing
- Dual Access: Use global accessors (
goe.Log()
) or dependency injection - Lifecycle Management: Automatic startup/shutdown handling for all components
Creating Your First Project
Create a project directory:
bashmkdir goe-hello-world cd goe-hello-world
Initialize Go modules:
bashgo mod init example.com/goe-hello-world # Replace example.com/goe-hello-world with your actual module path
Install GOE (if not already done globally or for another project):
bashgo get go.oease.dev/goe/v2
Hello, World! - Your First GOE Application
Let's create a simple HTTP server that responds with "Hello, World!". We'll show you two approaches: using global accessors (simple) and dependency injection (recommended for larger applications).
Approach 1: Simple Global Accessors
Create a file named main.go
with the following content:
package main
import (
"github.com/gofiber/fiber/v3"
"go.oease.dev/goe/v2"
)
func main() {
// Initialize GOE with HTTP module enabled
goe.New(goe.Options{
WithHTTP: true,
})
// Get the HTTP kernel and register routes
app := goe.HTTP().App()
app.Get("/", func(c fiber.Ctx) error {
goe.Log().Info("Received request", "path", c.Path(), "ip", c.IP())
return c.SendString("Hello, World from GOE! 👋")
})
// Start the application
goe.Log().Info("Starting GOE application")
goe.Run()
}
Approach 2: Dependency Injection (Recommended)
For larger applications, use dependency injection for better testability:
package main
import (
"github.com/gofiber/fiber/v3"
"go.oease.dev/goe/v2"
"go.oease.dev/goe/v2/contract"
)
func main() {
// Initialize GOE with HTTP module and route registration
goe.New(goe.Options{
WithHTTP: true,
Invokers: []any{RegisterRoutes},
})
// Start the application
goe.Run()
}
// RegisterRoutes is automatically called by Fx with injected dependencies
func RegisterRoutes(httpKernel contract.HTTPKernel, logger contract.Logger) {
app := httpKernel.App()
// Register routes
app.Get("/", func(c fiber.Ctx) error {
logger.Info("Received request", "path", c.Path(), "ip", c.IP())
return c.SendString("Hello, World from GOE! 👋")
})
app.Get("/health", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{
"status": "healthy",
"service": "goe-app",
})
})
logger.Info("HTTP routes registered successfully")
}
Key Concepts Explained:
Approach 1 (Global Accessors)
goe.New()
: Initializes the application with specified modulesWithHTTP: true
: Enables the HTTP server module (built on GoFiber)goe.HTTP().App()
: Gets the underlying Fiber app instance for route registrationgoe.Log()
: Accesses the global logger instancegoe.Run()
: Starts all modules and blocks until shutdown
Approach 2 (Dependency Injection)
Invokers
: Functions called during startup with automatic dependency injectioncontract.HTTPKernel
: Interface for HTTP server operationscontract.Logger
: Interface for structured logging- Fx Integration: Uber's Fx handles all dependency wiring automatically
Benefits of Each Approach
Global Accessors (Approach 1):
- ✅ Simple and quick for small applications
- ✅ Less boilerplate code
- ✅ Easy to understand for beginners
- ❌ Harder to test (global state)
- ❌ Less explicit dependencies
Dependency Injection (Approach 2):
- ✅ Better for larger applications
- ✅ Easier to test (mockable dependencies)
- ✅ Explicit dependency management
- ✅ Better separation of concerns
- ❌ More initial setup
- ❌ Steeper learning curve
Running the Application
Navigate to your project directory:
bashcd goe-hello-world
Run the application:
bashgo run main.go
Expected output: You should see structured log output similar to this:
INFO Starting GOE application INFO HTTP routes registered successfully INFO HTTP server starting {"host": "0.0.0.0", "port": 8080} INFO Application started successfully
Test your application: Open your browser or use curl to test the endpoints:
bash# Test the main endpoint curl http://localhost:8080/ # Response: Hello, World from GOE! 👋 # Test the health endpoint (if using Approach 2) curl http://localhost:8080/health # Response: {"status":"healthy","service":"goe-app"}
View request logs: Each request will generate structured logs:
INFO Received request {"path": "/", "ip": "127.0.0.1"} INFO HTTP Request {"method": "GET", "path": "/", "status": 200, "duration": "123μs", "request_id": "abc123"}
Graceful shutdown: Press
Ctrl+C
to stop the application. You'll see shutdown logs:INFO Shutting down HTTP server INFO Application stopped gracefully
Configuration and Environment
GOE applications can be configured using environment variables or .env
files:
# .env file
HTTP_HOST=0.0.0.0
HTTP_PORT=8080
LOG_LEVEL=info
APP_NAME=My GOE App
APP_VERSION=1.0.0
Access configuration in your code:
// Using global accessor
port := goe.Config().GetInt("HTTP_PORT")
appName := goe.Config().GetString("APP_NAME")
// Using dependency injection
func MyService(config contract.Config) *Service {
port := config.GetInt("HTTP_PORT")
return &Service{port: port}
}
Adding More Modules
Enable additional modules as needed:
app := goe.New(goe.Options{
WithHTTP: true, // Web server
WithCache: true, // Caching system
WithDB: true, // Database integration
Providers: []any{
NewUserService, // Your custom services
NewOrderService,
},
Invokers: []any{
RegisterRoutes, // Route registration
SetupMiddleware, // Middleware setup
},
})
Testing Your Application
GOE makes testing easy with dependency injection:
func TestUserService(t *testing.T) {
// Create mocks
mockDB := &MockDB{}
mockLogger := &MockLogger{}
// Create service with mocked dependencies
service := NewUserService(mockDB, mockLogger)
// Test your service
user, err := service.GetUser("123")
assert.NoError(t, err)
assert.Equal(t, "John", user.Name)
}
Next Steps
Congratulations! You've successfully created your first GOE application. Here's what to explore next:
Core Concepts
- Project Structure - Organize your GOE projects effectively
- Architecture - Understand GOE's design principles
- Configuration - Master environment and config management
Essential Components
- Logging - Structured logging with Zap
- HTTP Server - Build robust web applications
- Database - GORM integration and best practices
- Caching - Improve performance with caching
Advanced Topics
- Modules - Create custom modules
- Dependency Injection - Master Fx patterns
- Testing - Comprehensive testing strategies
Production Ready
- Best Practices - Production-ready development
- Deployment - Deploy your GOE applications
Ready to dive deeper? Start with Project Structure to learn how to organize larger applications!