# simple_bank simple_bank is a small example backend service in Go that demonstrates a banking-style API with: - HTTP gateway (grpc-gateway) and gRPC server - PostgreSQL for persistent storage (SQLC generated queries) - Redis + Asynq for background task queue / worker processing - Migrations via golang-migrate - Email verification task example This repository is intended as a reference/demo for building a production-ish Go service with background jobs. --- ## Features - RESTful HTTP gateway (default `:8080`) backed by gRPC (default `:9090`) - gRPC API and generated protobufs (uses grpc-gateway) - Database operations generated with `sqlc` - Background job dispatching and processing with `asynq` (Redis) - Config-driven via environment variables - Example Send Verification Email background task --- ## Prerequisites - Go 1.20+ (or compatible) - PostgreSQL - Redis - make/migrate/other CLIs (optional for migrations) - Docker & Docker Compose (recommended for local dev) --- ## Quickstart (development) 1. Copy environment example (or use `api.env`): - Main variables you will want to set: - `DB_ADDR` (Postgres connection string) - `REDIS_ADDR` (Redis host:port) - `HTTP_SERVER_ADDR` (default `:8080`) - `GRPC_SERVER_ADDR` (default `:9090`) - `ENVIRONMENT` (e.g. `development` to enable console logs) 2. Start local infra with Docker Compose (if you want local Postgres + Redis): - (This repo includes a `docker-compose.yml` with Postgres and Redis) - Example: - docker-compose up -d - Confirm Postgres and Redis are reachable at the addresses in your env. 3. Run DB migrations: - Using golang-migrate CLI (example): - migrate -path db/migrations -database "postgres://postgres:postgres@localhost:5432/simple_bank?sslmode=disable" up - Adjust the `database` URL to match your `DB_ADDR` environment variable. 4. Download dependencies and build: - go mod download - go build -o simple_bank ./... 5. Run the service: - With the environment loaded (example): - export $(cat api.env | xargs) # or use your preferred env loader - ./simple_bank - Running the binary will: - start the task processor (asynq server) that consumes background jobs - start gRPC server (on `GRPC_SERVER_ADDR`) - start the HTTP gateway (on `HTTP_SERVER_ADDR`) in a goroutine/handler - Alternatively, run directly with `go run`: - go run ./main.go 6. Verify: - HTTP gateway: http://localhost:8080 (or whatever `HTTP_SERVER_ADDR` you configure) - gRPC: connect at the configured `GRPC_SERVER_ADDR` - Redis dashboard / Redis Commander: if using docker-compose, a redis-commander service may be available --- ## Background Worker (Asynq) - Tasks are enqueued by the app and processed by an asynq server embedded in the application. - The worker configuration (queues and handlers) lives under `worker/`: - `worker.NewRedisTaskProcessor` creates the asynq server (`asynq.Server`) with queue weights and error handler. - `worker.NewRedisTaskDistributor` creates a client to enqueue tasks. - Example queue names: `CriticalQueue`, `DefaultQueue`. Customizing logs and formatting for asynq: - The asynq server can be given a custom logger via `asynq.Config.Logger`. This project includes `worker/logger.go` which adapts logs to `zerolog`. Edit `worker/logger.go` if you want to change asynq log formatting/fields. - If you also want to capture/format other libraries' logs (for example libraries that use the standard `log` package), redirect stdlib logs to your `zerolog` instance at startup. --- ## Environment Variables Important variables (see `api.env` for full list): - Server: - `HTTP_SERVER_ADDR` (default `:8080`) - `GRPC_SERVER_ADDR` (default `:9090`) - `ENVIRONMENT` (`development` for console logs) - Database: - `DB_DRIVER` (e.g. `pgx`) - `DB_ADDR` (postgres connection string) - `MIGRATIONS_URL` (e.g. `file://db/migrations`) - Redis: - `REDIS_ADDR` (e.g. `localhost:6379`) - `REDIS_PASSWORD` - `REDIS_DB` - Auth / Tokens: - `AUTH_TOKEN_SECRET`, `AUTH_TOKEN_EXP`, `REFRESH_TOKEN_EXP` - Email: - `EMAIL_FROM`, `EMAIL_SMTP_PASSWORD` (used by the worker email task) - Rate limiter: - `RATE_LIMITER_REQS_COUNT`, `RATE_LIMITER_TIME_FRAME`, `RATE_LIMITER_ENABLED` --- ## Testing - Unit tests: - go test ./... - Integration tests: - Ensure Postgres and Redis are running and configured as in `api.env` before running tests that depend on external services. --- ## Development tips - To change queue weights, error handling or task handlers, edit `worker/processor.go`. - To change how tasks are enqueued, review `worker/distributer.go`. - To adjust logging format globally, modify `main.go` startup logger configuration and `worker/logger.go`. - Protobuf / gRPC: regenerate stubs if you change proto files (this project uses `pb/` and generated code). --- ## Project layout (high level) - `main.go` — application entrypoint - `api/` — Gin HTTP handlers (HTTP server) - `gapi/` — gRPC server, grpc-gateway wiring - `worker/` — asynq task distributor & processor, custom logger - `db/` — SQLC generated code and migrations - `pb/` — generated protobuf/gRPC code - `docs/statik` — static files (swagger UI) bundled with the binary --- ## Troubleshooting - If migrations fail: - Confirm `DB_ADDR` is correct and database is reachable. - Check that `db/migrations` contains the migration files and paths are correct. - If Redis jobs don't process: - Confirm the app can reach Redis (`REDIS_ADDR`). - Ensure the process that runs the asynq server is running (the app starts it by default). - If logs appear in different formats: - Redirect stdlib logs into `zerolog` or update `worker/logger.go` to include more structured context. --- ## Contributing - Fork and open pull requests. - Keep changes small and focused; add/update unit tests where appropriate. --- If you'd like, I can: - Add a short example curl snippet for the HTTP API endpoints. - Add a sample `docker-compose` snippet and explicit commands for local development. - Add a "Deploy" section with production tips (env secrets, TLS, metrics, etc.).