Contributing to Nixopus Backend
This guide provides detailed instructions for contributing to the Nixopus backend codebase.
Setup for Backend Development
Prerequisites
- Go version 1.23.6 or newer
- PostgreSQL
- Docker and Docker Compose (recommended)
Environment Setup
bash# Clone the repository git clone https://github.com/raghavyuva/nixopus.git cd nixopus # Set up PostgreSQL database createdb nixopus -U postgres createdb nixopus_test -U postgres # Copy environment template # Note: Be sure to update the environment variables to suit your setup. cp api/.env.sample api/.env # Install dependencies cd api go mod download
Database Migrations
Currently the migration works automatically when starting the server. However, you can run migrations manually using the following command:
# Run migrations
cd api
go run migrations/main.go
- Loading Development Fixtures
The project includes a comprehensive fixtures system for development and testing. You can load sample data using the following commands:
cd api
# Load fixtures without affecting existing data
make fixtures-load
# Drop and recreate all tables, then load fixtures (clean slate)
make fixtures-recreate
# Truncate all tables, then load fixtures
make fixtures-clean
# Get help on fixtures commands
make fixtures-help
Available Fixture Files:
fixtures/development/complete.yml
- Loads all fixtures (uses imports)fixtures/development/users.yml
- User data onlyfixtures/development/organizations.yml
- Organization data onlyfixtures/development/roles.yml
- Role data onlyfixtures/development/permissions.yml
- Permission data onlyfixtures/development/role_permissions.yml
- Role-permission mappingsfixtures/development/feature_flags.yml
- Feature flagsfixtures/development/organization_users.yml
- User-organization relationships
The complete.yml
file uses import statements to load all individual files, making it easy to get a full development environment set up quickly.
Note: air as a dev-dependency so you can start the backend with the air command.
Project Structure
The backend follows a clean architecture approach:
api/
├── internal/
│ ├── features/ # Feature modules
│ ├── middleware/ # HTTP middleware
│ ├── config/ # Application configuration
│ ├── storage/ # Data storage implementation
│ ├── utils/ # Utility functions
│ └── types/ # Type definitions
├── migrations/ # Database migrations
└── tests/ # Test utilities
Adding a New Feature
Create a New Branch
bashgit checkout -b feature/your-feature-name
Implement Your Feature
Create a new directory under
api/internal/features/
with the following structure:api/internal/features/your-feature/ ├── controller/init.go # HTTP handlers ├── service/service_name.go # Business logic ├── storage/dao_name.go # Data access └── types/type_name.go # Type definitions
Here's a sample implementation:
go// types.go package yourfeature type YourEntity struct { ID string `json:"id" db:"id"` Name string `json:"name" db:"name"` CreatedAt string `json:"created_at" db:"created_at"` UpdatedAt string `json:"updated_at" db:"updated_at"` } // init.go (Controller) package yourfeature import "github.com/go-fuego/fuego" type Controller struct { service *Service } func NewController(s *Service)*Controller { return &Controller{service: s} } func (c *Controller) GetEntities(ctx fuego.Context) error { entities, err := c.service.ListEntities() if err != nil { return ctx.JSON(500, map[string]string{"error": err.Error()}) } return ctx.JSON(200, entities) } func (c *Controller) CreateEntity(ctx fuego.Context) error { var input YourEntity if err := ctx.Bind(&input); err != nil { return ctx.JSON(400, map[string]string{"error": "invalid input"}) } created, err := c.service.CreateEntity(&input) if err != nil { return ctx.JSON(500, map[string]string{"error": err.Error()}) } return ctx.JSON(201, created) } // service.go or service_name.go package yourfeature type Service struct { storage *Storage } func NewService(storage *Storage)*Service { return &Service{storage: storage} } // init.go or storage.go package yourfeature import ( "context" "github.com/uptrace/bun" ) type Storage struct { DB *bun.DB Ctx context.Context } func NewFeatureStorage(db *bun.DB, ctx context.Context)*NewFeatureStorage { return &FeatureStorage{ DB: db, Ctx: ctx } }
Register Routes
Update
api/internal/routes.go
to include your new endpoints:go// Register your feature routes yourFeatureStorage := yourfeature.NewStorage(db) yourFeatureService := yourfeature.NewService(yourFeatureStorage) yourFeatureController := yourfeature.NewController(yourFeatureService) api := router.Group("/api") { // Your feature endpoints featureGroup := api.Group("/your-feature") { featureGroup.GET("/", middleware.Authorize(), yourFeatureController.GetEntities) featureGroup.POST("/", middleware.Authorize(), yourFeatureController.CreateEntity) // Add more routes as needed } }
Add Database Migrations
Create migration files in
api/migrations/your-feature/
:sql-- seq_number_create_your_feature_table.up.sql CREATE TABLE your_feature ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- seq_number_create_your_feature_table.down.sql DROP TABLE IF EXISTS your_feature;
Write Tests
Organize your tests in the
tests/
using a separate folder named after each feature:go// controller_test.go package yourfeature import ( "testing" // Import other necessary packages ) func TestGetEntity(t *testing.T) { // Test implementation } // service_test.go // storage_test.go
Update API Documentation
Note that the docs will be updated automatically; the OpenAPI specification in
api/doc/openapi.json
will be updated automatically.
Testing
Run Unit Tests
bashcd api go test ./internal/features/your-feature/...
Run Integration Tests
bashcd api go test ./api/internal/tests/... -tags=integration
Run All Tests
bashcd api make test
Optimizing Performance
- Use Database Indices for frequently queried columns
- Implement Caching for expensive operations
- Optimize SQL Queries for better performance
- Add Proper Error Handling and logging
Code Style and Guidelines
- Follow Go's Code Review Comments
- Use meaningful variable and function names
- Add comments for complex logic
- Structure code for readability and maintainability
- Follow the existing project patterns
Common Pitfalls
- Forgetting to update migrations
- Not handling database transactions properly
- Missing error handling
- Inadequate test coverage
- Not considering performance implications
Submitting Your Contribution
Commit Changes
bashgit add . git commit -m "feat: add your feature"
Push and Create a Pull Request
bashgit push origin feature/your-feature-name
Follow the PR template and provide detailed information about your changes.
Need Help?
If you need assistance, feel free to:
- Create an issue on GitHub
- Reach out on the project's Discord channel
- Contact the maintainers directly
Thank you for contributing to Nixopus!