This is my first experience with Golang, and I used it to build a backend service for a multi-day diet planning system. It exposes REST endpoints for CRUD operations and gRPC services tailored for optimization clients.
diet-backend is the data and API layer of a larger personal diet-planning project.
Its responsibilities are:
- manage domain data (
categories,ingredients,dishes,meals), - provide public REST endpoints for management and inspection,
- provide gRPC endpoints consumed by optimization workflows,
- persist all data in PostgreSQL with explicit SQL migrations,
- bootstrap a realistic dataset for local development and demos.
This backend is designed to support the companion project diet-dietitian, where optimization logic generates diet plans from nutritional constraints.
The intended product flow is:
- users can generate a diet for a configurable number of days,
- daily and global requirements can be enforced (e.g., protein/fat targets, vegetarian constraints, dish preferences),
- users can inspect and modify generated diets,
- both built-in and user-defined dishes/ingredients can be part of the process.
This repository focuses on the backend foundation required for those capabilities.
The codebase follows a layered, ports-and-adapters style:
-
Domain (
internal/domain)- Core entities (
Category,Ingredient,Dish,Meal,Quantity) and invariants. - Validation rules live here (e.g., non-empty names, non-negative quantities, valid hex colors).
- Explicit quantity types following the Quantity pattern: composition (
g,units), nutrient (g/100g,g/unit,kcal/100g), and currency (euro/100g,euro/unit).
- Core entities (
-
Application (
internal/application)- Use-case interfaces (
in/) and service implementations (services/). - Repository and ID generation interfaces (
out/) for dependency inversion.
- Use-case interfaces (
-
Adapters In (
internal/adapters/in)- REST API (Gin + Swagger annotations).
- gRPC API (protobuf contracts + handlers).
-
Adapters Out (
internal/adapters/out)- Persistence via GORM + PostgreSQL models/repositories.
- Migration handling with
golang-migrate. - UUID ID generator.
-
Bootstrap / Runtime
main.gowires all dependencies manually.- Runs REST and gRPC servers concurrently.
.
├── main.go
├── Makefile
├── docker-compose.yml
├── internal/
│ ├── adapters/
│ │ ├── in/
│ │ │ ├── grpc/
│ │ │ └── rest/
│ │ └── out/
│ │ └── persistence/
│ ├── application/
│ │ ├── in/
│ │ ├── out/
│ │ ├── services/
│ │ └── testutil/
│ ├── domain/
│ ├── config/
│ └── seed/
└── README.md
Swagger docs are exposed at:
GET /docs/index.html
Current routes:
POST /categories
GET /ingredientsPOST /ingredientsGET /ingredients/:idPATCH /ingredients/:idDELETE /ingredients/:id
GET /dishesPOST /dishesGET /dishes/:idPATCH /dishes/:idDELETE /dishes/:id
GET /mealsPOST /mealsDELETE /meals/:id
Proto contracts live in:
internal/adapters/in/grpc/proto/diet/v1/dish.protointernal/adapters/in/grpc/proto/diet/v1/meal.proto
Services:
DishService.ListDishesMealService.ListMeals
Seeding uses embedded JSON files (internal/seed/data) and is designed to be idempotent:
- creates missing entities,
- updates existing ingredients/dishes by name,
- ensures referential consistency across meals, categories, ingredients, and dish recipes.
Current dataset size:
categories.json: 10ingredients.json: 61dish_recipes.json: 75dishes.json: 75meals.json: 3
Run seed in Docker:
make seedOr (after services are up):
docker compose run --rm backend ./main seed- Go
1.25.5 - Docker + Docker Compose
- Optional local tools for full workflow:
swagbufgolangci-lintmigrate
Example .env values:
DB_HOST=localhost
DB_PORT=5432
DB_USER=dietuser
DB_PASSWORD=test
DB_NAME=dietdb
DB_SSLMODE=disable
SERVER_REST_PORT=8080
SERVER_GRPC_PORT=9090make up-buildThis will:
- build and start backend + database,
- run migrations,
- seed data.
Ports:
- REST:
8080 - gRPC:
9090 - PostgreSQL host mapping:
5433 -> 5432by default
Stop everything:
make downmake fmt
make lint
make swagger
make grpc-gen
make build
make run
make test
make test-coverage
make cimake migration name=add_new_table
make migration-up
make migration-down
make migration-version- Domain tests for entity invariants and value objects.
- Application service tests with repository and ID generator mocks.
- Focus on behavior and error propagation.
Test files currently cover:
internal/domain/{category,ingredient,dish,meal,quantity}internal/application/services/{category,ingredient,dish,meal}
- Linting configured with
golangci-lint(formatting, vet/static checks, complexity and duplication checks). - Swagger and protobuf generation are part of the build workflow.
- Startup fails fast on invalid config or DB connection issues.
- REST and gRPC servers start in one process and shut down gracefully.
- HTTP error mapping is still coarse in several handlers; many failures currently map to
500. - Dietary flags (
vegetarian,vegan) in gRPC are inferred heuristically from ingredient names, not modeled explicitly in domain entities. - No authentication/authorization yet.