master
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)
-
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.developmentto enable console logs)
- Main variables you will want to set:
-
Start local infra with Docker Compose (if you want local Postgres + Redis):
- (This repo includes a
docker-compose.ymlwith Postgres and Redis) - Example:
- docker-compose up -d
- Confirm Postgres and Redis are reachable at the addresses in your env.
- (This repo includes a
-
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
databaseURL to match yourDB_ADDRenvironment variable.
- Using golang-migrate CLI (example):
-
Download dependencies and build:
- go mod download
- go build -o simple_bank ./...
-
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
-
-
Verify:
- HTTP gateway: http://localhost:8080 (or whatever
HTTP_SERVER_ADDRyou configure) - gRPC: connect at the configured
GRPC_SERVER_ADDR - Redis dashboard / Redis Commander: if using docker-compose, a redis-commander service may be available
- HTTP gateway: http://localhost:8080 (or whatever
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.NewRedisTaskProcessorcreates the asynq server (asynq.Server) with queue weights and error handler.worker.NewRedisTaskDistributorcreates 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 includesworker/logger.gowhich adapts logs tozerolog. Editworker/logger.goif 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
logpackage), redirect stdlib logs to yourzerologinstance at startup.
Environment Variables
Important variables (see api.env for full list):
- Server:
HTTP_SERVER_ADDR(default:8080)GRPC_SERVER_ADDR(default:9090)ENVIRONMENT(developmentfor 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_PASSWORDREDIS_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.envbefore running tests that depend on external services.
- Ensure Postgres and Redis are running and configured as in
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.gostartup logger configuration andworker/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 entrypointapi/— Gin HTTP handlers (HTTP server)gapi/— gRPC server, grpc-gateway wiringworker/— asynq task distributor & processor, custom loggerdb/— SQLC generated code and migrationspb/— generated protobuf/gRPC codedocs/statik— static files (swagger UI) bundled with the binary
Troubleshooting
- If migrations fail:
- Confirm
DB_ADDRis correct and database is reachable. - Check that
db/migrationscontains the migration files and paths are correct.
- Confirm
- 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).
- Confirm the app can reach Redis (
- If logs appear in different formats:
- Redirect stdlib logs into
zerologor updateworker/logger.goto include more structured context.
- Redirect stdlib logs into
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-composesnippet and explicit commands for local development. - Add a "Deploy" section with production tips (env secrets, TLS, metrics, etc.).
Languages
Go
94%
Shell
4%
Makefile
1.7%
Dockerfile
0.3%