A robust, scalable backend for a multi-currency FX trading application built with NestJS, TypeORM, and PostgreSQL.
- User Management: Registration, Login, and Email Verification (OTP).
- Multi-Currency Wallet: Support for NGN, USD, EUR, GBP, and more.
- Real-Time FX Rates: Integration with external exchange rate providers (with Redis caching).
- Trading Engine: Secure currency conversion and trading with slippage protection via time-limited quotes.
- Transaction History: Comprehensive audit trail of all financial activities.
- Admin Dashboard: Dedicated module for administrators to view users and transactions.
- Security: JWT Authentication, Role-Based Access Control (RBAC), and Idempotency.
- Framework: NestJS (Node.js)
- Database: PostgreSQL (via TypeORM)
- Caching: Redis
- Testing: Jest (Unit & E2E)
- Documentation: Swagger (OpenAPI)
The application is structured into feature-based modules (Auth, User, Wallet, Exchange, Transaction) to ensure separation of concerns and maintainability.
To prevent data inconsistency and ensure auditability, all wallet balance changes are recorded in a ledger_entries table.
- Every financial action (credit/debit) creates a
Transactionrecord and a correspondingLedgerEntry. - Wallet balances are calculated/verified by aggregating ledger entries (or updating a snapshot).
- Pessimistic Locking:
SELECT ... FOR UPDATEis used during fund transfers to prevent race conditions and double-spending.
- Atomic Transactions: All financial operations obey ACID properties using
QueryRunnertransactions. - Idempotency: Critical endpoints (like
POST /wallet/trade) support anIdempotency-Keyheader to safely retry requests without duplicating trades.
- Rates are fetched from an external provider (
ExchangeRate-APIor similar). - Caching: Rates are cached in Redis for 60 seconds to reduce external API calls and improve latency.
- Quotes: Trades require a 2-step process (Get Quote -> Execute Trade) to lock in a rate for a short window (e.g., 60s), protecting users from price slippage.
Overview of the system's modular structure and external integrations.
Sequence diagram of the Double-Entry Ledger trade execution with Pessimistic Locking.
Once the application is running, full interactive API documentation is available at: http://localhost:5001/api/v1/docs
| Module | Method | Endpoint | Description |
|---|---|---|---|
| Auth | POST |
/auth/register |
Register new user (sends OTP) |
POST |
/auth/login |
Login and get JWT | |
POST |
/auth/verify |
Verify email with OTP | |
| Wallet | GET |
/wallet |
Get all currency balances |
POST |
/wallet/fund |
Fund wallet (e.g., in NGN) | |
POST |
/wallet/trade |
Execute a trade using a quote | |
| FX | GET |
/fx/rates |
Get live exchange rates |
GET |
/fx/quote |
Get a guaranteed rate quote | |
| History | GET |
/transactions |
View transaction history |
| Admin | POST |
/admin/login |
Admin login |
GET |
/admin/users |
(Admin) List all users | |
GET |
/admin/transactions |
(Admin) List all transactions |
- Node.js (v18+)
- PostgreSQL
- Redis
git clone https://github.com/ChuloWay/fx_trading_platform`
cd fx_trading_platformnpm installCreate a .env file in the root directory:
# Server
PORT=5001
NODE_ENV=development
# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=postgres
DATABASE_NAME=fx_trading
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# JWT
JWT_SECRET=your_super_secret_key
JWT_EXPIRATION=1d
# Admin Seeding
ADMIN_EMAIL=admin@fxapp.com
ADMIN_PASSWORD=Admin@123
# FX Provider
FX_RATE_API_KEY=your_api_key_here# Development
npm run start:dev
# Production
npm run start:prod# Unit tests
npm run test
# E2E tests
npm run test:e2e- Unit Tests: Cover individual services (
AuthService,WalletService,ExchangeService) using mocks. - E2E Tests: Validate the entire request flow from Controller to Database using a test database.
- User flows: Register -> Verify -> Login -> Fund -> Trade.
- Error handling: Invalid inputs, unauthorized access, insufficient funds.
- Base Currency: The system treats NGN as the primary funding currency for this assessment, but the architecture supports any base currency.
- Email Delivery: For development, emails are logged to the console (
ConsoleEmailProvider). In production, this can be switched toGmailSmtpEmailProvidervia env vars. - Authentication: Users must verify their email before they can login or trade.
- Transaction Limits: No specific limits (min/max) were enforced for this MVP, but logic exists to prevent negative balances.
- Redis Caching: Implemented for FX rates.
- Idempotency: Guarded against duplicate trade requests.
- Role-Based Access: Infrastructure for Admin vs. User roles (
RolesGuard). - Swagger Docs: Auto-generated API documentation.

