Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Simple Bank
https://github.com/techschool/simplebank
Admin
Simple Bank is a complete backend web service for a banking system that provides APIs to create and
...
Tokens:
11,813
Snippets:
124
Trust Score:
6.6
Update:
4 days ago
Context
Skills
Chat
Benchmark
72.9
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Simple Bank Simple Bank is a complete backend web service for a banking system built with Go, providing both RESTful HTTP and gRPC APIs. The service handles user authentication with PASETO tokens, bank account management, balance tracking, and secure money transfers between accounts using database transactions. It integrates with PostgreSQL for data persistence and Redis for asynchronous task processing via background workers. The architecture follows a clean separation of concerns with the Gin framework handling HTTP requests, gRPC serving high-performance RPC calls, and gRPC-gateway enabling HTTP/JSON access to gRPC services. The system implements role-based access control (RBAC), email verification workflows, session management with refresh tokens, and is designed for deployment on Kubernetes with AWS EKS support. ## REST API Endpoints ### Create User Creates a new user account with username, password, full name, and email. Passwords are securely hashed using bcrypt before storage. ```bash # Create a new user curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{ "username": "johndoe", "password": "secret123", "full_name": "John Doe", "email": "john@example.com" }' # Response: { "username": "johndoe", "full_name": "John Doe", "email": "john@example.com", "password_changed_at": "0001-01-01T00:00:00Z", "created_at": "2024-01-15T10:30:00Z" } ``` ### Login User Authenticates a user and returns access and refresh tokens for subsequent API calls. Creates a new session stored in the database. ```bash # Login and get tokens curl -X POST http://localhost:8080/users/login \ -H "Content-Type: application/json" \ -d '{ "username": "johndoe", "password": "secret123" }' # Response: { "session_id": "550e8400-e29b-41d4-a716-446655440000", "access_token": "v2.local.abc123...", "access_token_expires_at": "2024-01-15T11:30:00Z", "refresh_token": "v2.local.def456...", "refresh_token_expires_at": "2024-01-16T10:30:00Z", "user": { "username": "johndoe", "full_name": "John Doe", "email": "john@example.com", "password_changed_at": "0001-01-01T00:00:00Z", "created_at": "2024-01-15T10:30:00Z" } } ``` ### Renew Access Token Exchanges a valid refresh token for a new access token without requiring re-authentication. ```bash # Renew access token curl -X POST http://localhost:8080/tokens/renew_access \ -H "Content-Type: application/json" \ -d '{ "refresh_token": "v2.local.def456..." }' # Response: { "access_token": "v2.local.xyz789...", "access_token_expires_at": "2024-01-15T12:30:00Z" } ``` ### Create Account Creates a new bank account for the authenticated user with zero initial balance. Supports multiple currencies (USD, EUR, CAD). Requires authentication. ```bash # Create a new account (requires auth token) curl -X POST http://localhost:8080/accounts \ -H "Content-Type: application/json" \ -H "Authorization: Bearer v2.local.abc123..." \ -d '{ "currency": "USD" }' # Response: { "id": 1, "owner": "johndoe", "balance": 0, "currency": "USD", "created_at": "2024-01-15T10:35:00Z" } ``` ### Get Account Retrieves account details by ID. Users can only access their own accounts due to authorization checks. ```bash # Get account by ID curl -X GET http://localhost:8080/accounts/1 \ -H "Authorization: Bearer v2.local.abc123..." # Response: { "id": 1, "owner": "johndoe", "balance": 1000, "currency": "USD", "created_at": "2024-01-15T10:35:00Z" } ``` ### List Accounts Returns a paginated list of all accounts belonging to the authenticated user. ```bash # List accounts with pagination curl -X GET "http://localhost:8080/accounts?page_id=1&page_size=5" \ -H "Authorization: Bearer v2.local.abc123..." # Response: [ { "id": 1, "owner": "johndoe", "balance": 1000, "currency": "USD", "created_at": "2024-01-15T10:35:00Z" }, { "id": 2, "owner": "johndoe", "balance": 500, "currency": "EUR", "created_at": "2024-01-15T10:40:00Z" } ] ``` ### Create Transfer Transfers money between two accounts within a database transaction. Validates currency match and account ownership. Creates transfer record and entry records for both accounts. ```bash # Transfer money between accounts curl -X POST http://localhost:8080/transfers \ -H "Content-Type: application/json" \ -H "Authorization: Bearer v2.local.abc123..." \ -d '{ "from_account_id": 1, "to_account_id": 3, "amount": 100, "currency": "USD" }' # Response: { "transfer": { "id": 1, "from_account_id": 1, "to_account_id": 3, "amount": 100, "created_at": "2024-01-15T11:00:00Z" }, "from_account": { "id": 1, "owner": "johndoe", "balance": 900, "currency": "USD", "created_at": "2024-01-15T10:35:00Z" }, "to_account": { "id": 3, "owner": "janedoe", "balance": 600, "currency": "USD", "created_at": "2024-01-15T10:36:00Z" }, "from_entry": { "id": 1, "account_id": 1, "amount": -100, "created_at": "2024-01-15T11:00:00Z" }, "to_entry": { "id": 2, "account_id": 3, "amount": 100, "created_at": "2024-01-15T11:00:00Z" } } ``` ## gRPC API Endpoints ### CreateUser (gRPC) Creates a new user via gRPC and triggers an async email verification task via Redis. ```bash # Using grpcurl grpcurl -plaintext -d '{ "username": "johndoe", "password": "secret123", "full_name": "John Doe", "email": "john@example.com" }' localhost:9090 pb.SimpleBank/CreateUser # HTTP via gRPC-gateway curl -X POST http://localhost:8080/v1/create_user \ -H "Content-Type: application/json" \ -d '{ "username": "johndoe", "password": "secret123", "full_name": "John Doe", "email": "john@example.com" }' # Response: { "user": { "username": "johndoe", "full_name": "John Doe", "email": "john@example.com", "password_changed_at": "0001-01-01T00:00:00Z", "created_at": "2024-01-15T10:30:00Z" } } ``` ### LoginUser (gRPC) Authenticates user credentials and returns session tokens. ```bash # Using grpcurl grpcurl -plaintext -d '{ "username": "johndoe", "password": "secret123" }' localhost:9090 pb.SimpleBank/LoginUser # HTTP via gRPC-gateway curl -X POST http://localhost:8080/v1/login_user \ -H "Content-Type: application/json" \ -d '{ "username": "johndoe", "password": "secret123" }' # Response: { "user": { "username": "johndoe", "full_name": "John Doe", "email": "john@example.com" }, "session_id": "550e8400-e29b-41d4-a716-446655440000", "access_token": "v2.local.abc123...", "refresh_token": "v2.local.def456...", "access_token_expires_at": "2024-01-15T11:30:00Z", "refresh_token_expires_at": "2024-01-16T10:30:00Z" } ``` ### UpdateUser (gRPC) Updates user profile with optional fields. Requires authentication and enforces RBAC (users can only update themselves unless they have banker role). ```bash # Using grpcurl with auth metadata grpcurl -plaintext \ -H "authorization: Bearer v2.local.abc123..." \ -d '{ "username": "johndoe", "full_name": "John D. Doe", "email": "johndoe@newmail.com" }' localhost:9090 pb.SimpleBank/UpdateUser # HTTP via gRPC-gateway curl -X PATCH http://localhost:8080/v1/update_user \ -H "Content-Type: application/json" \ -H "Authorization: Bearer v2.local.abc123..." \ -d '{ "username": "johndoe", "full_name": "John D. Doe", "email": "johndoe@newmail.com" }' # Response: { "user": { "username": "johndoe", "full_name": "John D. Doe", "email": "johndoe@newmail.com", "password_changed_at": "0001-01-01T00:00:00Z", "created_at": "2024-01-15T10:30:00Z" } } ``` ### VerifyEmail (gRPC) Verifies user email address using the email ID and secret code sent via email. ```bash # HTTP via gRPC-gateway (typically clicked from email link) curl -X GET "http://localhost:8080/v1/verify_email?email_id=1&secret_code=abc123secretcode456" # Response: { "is_verified": true } ``` ## Database Operations ### Transfer Transaction Performs atomic money transfers using database transactions with deadlock prevention through consistent ordering. ```go package main import ( "context" "log" db "github.com/techschool/simplebank/db/sqlc" ) func transferMoney(store db.Store, fromAccountID, toAccountID, amount int64) { // TransferTx executes a money transfer within a database transaction // It creates transfer record, account entries, and updates balances atomically result, err := store.TransferTx(context.Background(), db.TransferTxParams{ FromAccountID: fromAccountID, ToAccountID: toAccountID, Amount: amount, }) if err != nil { log.Fatal("transfer failed:", err) } log.Printf("Transfer completed: %d from account %d to account %d", result.Transfer.Amount, result.FromAccount.ID, result.ToAccount.ID) log.Printf("New balances - From: %d, To: %d", result.FromAccount.Balance, result.ToAccount.Balance) } ``` ### Account CRUD Operations SQL queries generated by sqlc for type-safe database operations. ```sql -- CreateAccount: Insert new account with owner, balance, currency INSERT INTO accounts (owner, balance, currency) VALUES ($1, $2, $3) RETURNING *; -- GetAccount: Retrieve account by ID SELECT * FROM accounts WHERE id = $1 LIMIT 1; -- ListAccounts: Get paginated accounts for an owner SELECT * FROM accounts WHERE owner = $1 ORDER BY id LIMIT $2 OFFSET $3; -- AddAccountBalance: Atomically update balance UPDATE accounts SET balance = balance + sqlc.arg(amount) WHERE id = sqlc.arg(id) RETURNING *; ``` ## Background Workers ### Send Verification Email Task Asynchronous email verification using Redis and Asynq worker. ```go package main import ( "context" "time" "github.com/hibiken/asynq" "github.com/techschool/simplebank/worker" ) func enqueueVerificationEmail(distributor worker.TaskDistributor, username string) error { // Create task payload taskPayload := &worker.PayloadSendVerifyEmail{ Username: username, } // Configure task options opts := []asynq.Option{ asynq.MaxRetry(10), // Retry up to 10 times on failure asynq.ProcessIn(10 * time.Second), // Delay processing by 10 seconds asynq.Queue(worker.QueueCritical), // Use critical priority queue } // Enqueue task to Redis return distributor.DistributeTaskSendVerifyEmail(context.Background(), taskPayload, opts...) } ``` ## Configuration ### Application Configuration Environment-based configuration using Viper with support for .env files and environment variables. ```go // config.go - Configuration structure type Config struct { Environment string `mapstructure:"ENVIRONMENT"` AllowedOrigins []string `mapstructure:"ALLOWED_ORIGINS"` DBSource string `mapstructure:"DB_SOURCE"` MigrationURL string `mapstructure:"MIGRATION_URL"` RedisAddress string `mapstructure:"REDIS_ADDRESS"` HTTPServerAddress string `mapstructure:"HTTP_SERVER_ADDRESS"` GRPCServerAddress string `mapstructure:"GRPC_SERVER_ADDRESS"` TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"` AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"` RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"` EmailSenderName string `mapstructure:"EMAIL_SENDER_NAME"` EmailSenderAddress string `mapstructure:"EMAIL_SENDER_ADDRESS"` EmailSenderPassword string `mapstructure:"EMAIL_SENDER_PASSWORD"` } ``` ```bash # app.env - Example configuration file ENVIRONMENT=development DB_SOURCE=postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable MIGRATION_URL=file://db/migration REDIS_ADDRESS=0.0.0.0:6379 HTTP_SERVER_ADDRESS=0.0.0.0:8080 GRPC_SERVER_ADDRESS=0.0.0.0:9090 TOKEN_SYMMETRIC_KEY=12345678901234567890123456789012 ACCESS_TOKEN_DURATION=15m REFRESH_TOKEN_DURATION=24h ALLOWED_ORIGINS=http://localhost:5173 ``` ## Docker Deployment ### Docker Compose Setup Complete development environment with PostgreSQL, Redis, and the API service. ```yaml # docker-compose.yaml version: "3.9" services: postgres: image: postgres:14-alpine environment: - POSTGRES_USER=root - POSTGRES_PASSWORD=secret - POSTGRES_DB=simple_bank ports: - "5432:5432" volumes: - data-volume:/var/lib/postgresql/data redis: image: redis:7-alpine api: build: context: . dockerfile: Dockerfile ports: - "8080:8080" # HTTP/gRPC-gateway - "9090:9090" # gRPC environment: - DB_SOURCE=postgresql://root:secret@postgres:5432/simple_bank?sslmode=disable - REDIS_ADDRESS=redis:6379 depends_on: - postgres - redis entrypoint: ["/app/wait-for.sh", "postgres:5432", "--", "/app/start.sh"] command: ["/app/main"] volumes: data-volume: ``` ```bash # Start all services docker-compose up -d # View logs docker-compose logs -f api # Stop all services docker-compose down ``` ## Development Commands ### Makefile Commands Common development tasks available via make. ```bash # Database setup make postgres # Start PostgreSQL container make createdb # Create simple_bank database make dropdb # Drop simple_bank database # Database migrations make migrateup # Apply all migrations make migratedown # Rollback all migrations make migrateup1 # Apply one migration make migratedown1 # Rollback one migration make new_migration name=add_new_table # Create new migration # Code generation make sqlc # Generate Go code from SQL queries make mock # Generate mock interfaces for testing make proto # Generate protobuf, gRPC, and Swagger code # Run application make server # Start the server make test # Run all tests with coverage # Additional services make redis # Start Redis container make evans # Start Evans gRPC client for testing ``` ## Summary Simple Bank serves as a comprehensive example of building production-ready backend services in Go. It demonstrates best practices for API design with both REST and gRPC interfaces, secure authentication using PASETO tokens with refresh token rotation, and transactional money transfers with proper deadlock prevention. The service showcases integration with PostgreSQL using sqlc for type-safe queries, Redis-based async processing with Asynq workers, and automatic Swagger documentation generation. The project is designed for cloud-native deployment with Docker containers and Kubernetes manifests for AWS EKS. Key integration patterns include middleware-based authentication, gRPC interceptors for logging and authorization, database migrations for schema versioning, and comprehensive unit testing with mocked dependencies. Developers can extend this foundation by adding new gRPC services, implementing additional REST endpoints, or integrating with external payment providers while maintaining the established patterns for security, testing, and deployment.