Skip to content

tahiee/BackendEngineeringAssessment-FXTradingApp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FX Trading App – Backend API

A production-grade backend for an FX Trading application built with NestJS, TypeORM, and PostgreSQL. Users can register, verify their email, fund multi-currency wallets, and trade/convert currencies using real-time FX rates.


Tech Stack

Layer Technology
Framework NestJS (Node.js)
ORM TypeORM
Database PostgreSQL
Auth JWT + Passport
Email Nodemailer (Gmail SMTP)
FX Rates ExchangeRate-API v6
Docs Swagger (OpenAPI)
Testing Jest

Setup Instructions

Prerequisites

  • Node.js >= 18
  • PostgreSQL >= 14
  • A free API key from exchangerate-api.com
  • Gmail account with App Password enabled

1. Clone & Install

git clone https://github.com/YOUR_USERNAME/fx-trading-app.git
cd fx-trading-app
npm install

2. Configure Environment

cp .env.example .env

Edit .env with your values:

DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=yourpassword
DB_NAME=fx_trading

JWT_SECRET=your_super_secret_key
JWT_EXPIRES_IN=7d

MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USER=your_email@gmail.com
MAIL_PASS=your_gmail_app_password
MAIL_FROM=your_email@gmail.com

FX_API_KEY=your_exchangerate_api_key
FX_API_BASE_URL=https://v6.exchangerate-api.com/v6

PORT=3000
NODE_ENV=development

3. Create Database

psql -U postgres -c "CREATE DATABASE fx_trading;"

4. Run the App

# Development (auto-sync DB schema)
npm run start:dev

# Production
npm run build && npm run start:prod

5. Access Swagger Docs

http://localhost:3000/api/docs

6. Run Tests

npm test
npm run test:cov

API Documentation

Authentication

POST /auth/register

Register a new user. Sends a 6-digit OTP to the provided email.

Request:

{ "email": "user@example.com", "password": "MyPass123!" }

Response:

{ "message": "Registration successful. Please verify your email with the OTP sent.", "email": "user@example.com" }

POST /auth/verify

Verify OTP to activate account and receive JWT. Also initializes the user's multi-currency wallet.

Request:

{ "email": "user@example.com", "otp": "482910" }

Response:

{ "message": "Email verified successfully. Wallet initialized.", "access_token": "eyJhbG..." }

POST /auth/login

Login with email + password.

Request:

{ "email": "user@example.com", "password": "MyPass123!" }

All endpoints below require Authorization: Bearer <token> header.

Wallet

GET /wallet

Returns all currency balances for the authenticated user.

Response:

[
  { "currency": "NGN", "balance": "50000.00000000" },
  { "currency": "USD", "balance": "32.50000000" }
]

POST /wallet/fund

Fund wallet in any supported currency.

Request:

{ "amount": 50000, "currency": "NGN", "idempotencyKey": "fund-001" }

POST /wallet/convert

Convert between any two supported currencies using real-time FX rates.

Request:

{ "fromCurrency": "NGN", "toCurrency": "USD", "amount": 1000 }

Response:

{
  "message": "Conversion successful",
  "from": { "currency": "NGN", "amount": 1000 },
  "to": { "currency": "USD", "amount": 0.65 },
  "rate": 0.00065,
  "transactionId": "uuid-here"
}

POST /wallet/trade

Trade Naira against foreign currencies (NGN must be one side of the trade).

Request:

{ "fromCurrency": "NGN", "toCurrency": "GBP", "amount": 100000 }

FX Rates

GET /fx/rates?base=NGN

Returns real-time FX rates for all supported currencies relative to the base.

GET /fx/currencies

Returns list of supported currencies: NGN, USD, EUR, GBP, CAD, AUD, JPY

Transactions

GET /transactions

Returns full transaction history for the authenticated user, newest first.


Supported Currencies

NGN USD EUR GBP CAD AUD JPY


Architectural Decisions

Multi-Currency Wallet Design

Each user has a separate wallet_balances row per currency (e.g., one row for NGN, one for USD). This allows:

  • Efficient indexed lookups by (userId, currency)
  • Simple atomic balance updates without JSON column parsing
  • Easy extension to new currencies (just insert a new row)

Atomic Transactions with Pessimistic Locking

All balance mutations (fund, convert, trade) use PostgreSQL transactions with FOR UPDATE row-level locks. This prevents:

  • Double-spending — two concurrent requests cannot both read the same balance and spend it
  • Race conditions — all reads and writes within a transaction see consistent data

FX Rate Fetching Strategy

  • Rates are fetched from ExchangeRate-API before acquiring DB locks (to minimize lock hold time)
  • Results are cached in-memory with a 5-minute TTL
  • On API failure, a fallback uses the inverted cached rate if available
  • If no fallback exists, a 503 Service Unavailable is returned

Idempotency Keys

All mutating wallet endpoints accept an optional idempotencyKey. If a key is reused, the API returns 409 Conflict instead of processing the transaction twice. This is critical for retry safety in network failures.

Trade vs Convert

  • /wallet/convert — general-purpose currency conversion between any two supported currencies
  • /wallet/trade — explicitly for NGN ↔ foreign currency trades, as per the FX trading context. Non-NGN pairs are rejected with a clear error message.

Assumptions

  1. Funding is direct — no payment gateway integration (assumed to be handled externally or mocked)
  2. No spread/fee — conversions use raw FX rates. A fee mechanism can be layered in WalletService
  3. Wallet initialization — all currency rows are created with 0 balance upon email verification
  4. OTP expiry — 10 minutes. A resend-OTP endpoint can be added for production
  5. Scalability — For millions of users, introduce Redis for rate caching, a queue (BullMQ) for async transaction processing, and horizontal scaling behind a load balancer

Bonus Features Implemented

  • ✅ Idempotency keys (duplicate transaction prevention)
  • ✅ In-memory FX rate caching (5-min TTL)
  • ✅ Pessimistic row locking (race condition prevention)
  • ✅ Global exception filter with structured error responses
  • ✅ Swagger/OpenAPI documentation
  • ✅ Unit tests for wallet and conversion logic
  • ✅ Role field on User entity (ready for RBAC extension)

Project Structure

src/
├── auth/               # Registration, OTP, JWT login
│   ├── dto/
│   ├── guards/
│   └── strategies/
├── users/              # User entity + service
├── wallet/             # Fund, convert, trade logic
│   ├── dto/
│   └── entities/
├── fx/                 # FX rate fetching + caching
├── transactions/       # Transaction history
└── common/             # Filters, decorators

About

Backend Engineering Assessment – FX Trading App

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors