Skip to content

AttiR/leasing-api-azure-functions

Repository files navigation

Leasing API - Azure Functions

A complete leasing (hire-purchase) backend API built with Azure Functions v3, TypeScript, and Prisma.

Note: This project is a technical assessment and is not intended for production use.

Tech Stack

  • Backend: Azure Functions v3, Node.js 14+
  • Language: TypeScript (strict mode)
  • Database: Prisma ORM with SQLite/PostgreSQL
  • Validation: Zod schema validation
  • Architecture: Clean Architecture (Domain → Application → Infrastructure)
  • Testing: Jest (unit + integration tests)
  • Deployment: Azure Functions Core Tools
  • Storage: Azurite (local Azure Storage emulation)

Quick Start

Clone and run immediately

git clone <repository-url>
cd leasing-api-azure-functions
npm test    # Runs tests (auto-setup included)
npm start   # Starts API (auto-setup included)

Alternative: Explicit Setup (Recommended)

# If you prefer to see the setup process
./script.init
./start-dev.sh

Manual Start (After Setup)

# If you want to start manually after setup
npm run build
func start --script-root dist

Important: Make sure you're using Node.js 14+ for Azure Functions v3:

nvm use 14  # Switch to Node.js 14
func start --script-root dist

The API will be running on http://localhost:7071

What happens automatically:

  • Installs dependencies if missing
  • Sets up database and environment files
  • Installs Azure Functions Core Tools if missing
  • Builds the project
  • Starts the API

Getting Started Checklist

  • Clone repository
  • Run ./script.init (creates .env.local and local.settings.json)
  • Edit .env.local with your secure API key
  • Run ./start-dev.sh to start the API
  • Test health endpoint: curl -H "x-api-key: YOUR_API_KEY" http://localhost:7071/api/health

Azure Local Development

For Azure-like local development with storage emulation:

Quick Azure Setup

# One-time setup
./scripts/setup-azurite.sh

# Start with Azure storage emulation
npm run dev:azure

What Azurite Provides

  • Azure Storage Emulation: Blob, Queue, Table services
  • Production-like Environment: Same APIs as Azure
  • Free Development: No Azure subscription needed
  • Offline Development: Works without internet

Azurite Services

  • Blob Storage: http://localhost:10000
  • Queue Storage: http://localhost:10001
  • Table Storage: http://localhost:10002

Development Options

# Regular development (SQLite)
npm start

# Azure-like development (Azurite + SQLite)
npm run dev:azure

# Stop Azurite
npm run dev:azure:stop

API Authentication

All endpoints require the API key header:

-H "x-api-key: YOUR_API_KEY"

API Key Management

For Development:

  • The setup script creates .env.local from template
  • Update .env.local with your secure API key
  • Never commit .env.local to version control

For Production:

  • Use Azure Key Vault for secure secrets management
  • Generate strong, unique API keys
  • Implement key rotation policies
  • Enable audit logging
# Generate a secure API key
openssl rand -hex 32

# Update your .env.local file
API_KEY=your-secure-api-key-here

API Documentation

All endpoints require authentication via the x-api-key header.

Authentication

-H "x-api-key: YOUR_API_KEY"

API Endpoints Overview

Method Endpoint Description Parameters
GET /api/health Health check and API status None
GET /api/quote Calculate lease quote price, termMonths, nominalRatePct, startDate
POST /api/leases Create new lease Request body with lease details
GET /api/leases/{id} Get specific lease id (required UUID)
POST /api/payments Record payment Request body with payment details

Endpoints

Health Check

GET /api/health

Returns the API status and version information.

curl -H "x-api-key: YOUR_API_KEY" http://localhost:7071/api/health

Response:

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2023-01-01T00:00:00Z"
}

Get Quote

GET /api/quote

Calculates lease quote based on provided parameters.

Query Parameters:

  • price (number): Item price
  • termMonths (number): Lease term in months
  • nominalRatePct (number): Annual interest rate percentage
  • startDate (string): Lease start date (ISO 8601)
curl -H "x-api-key: YOUR_API_KEY" "http://localhost:7071/api/quote?price=1000&termMonths=6&nominalRatePct=12&startDate=2023-01-01"

Response:

{
  "monthlyPayment": 175.50,
  "totalAmount": 1053.00,
  "totalInterest": 53.00,
  "paymentSchedule": [...]
}

Create Lease

POST /api/leases

Creates a new lease agreement.

Request Body:

{
  "companyId": "test-company",
  "itemId": "test-item",
  "price": 1000,
  "termMonths": 6,
  "nominalRatePct": 12,
  "startDate": "2023-01-01",
  "upfrontFee": 0,
  "monthlyFee": 50
}
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"companyId":"test-company","itemId":"test-item","price":1000,"termMonths":6,"nominalRatePct":12,"startDate":"2023-01-01","upfrontFee":0,"monthlyFee":50}' \
  http://localhost:7071/api/leases

Response:

{
  "id": "lease-uuid-here",
  "companyId": "test-company",
  "itemId": "test-item",
  "price": 1000,
  "termMonths": 6,
  "nominalRatePct": 12,
  "startDate": "2023-01-01",
  "upfrontFee": 0,
  "monthlyFee": 50,
  "createdAt": "2023-01-01T00:00:00Z",
  "schedule": [...],
  "totals": {...}
}

Get Specific Lease

GET /api/leases/{id}

Retrieves lease information by ID.

Path Parameters:

  • id (required): Lease UUID
curl -H "x-api-key: YOUR_API_KEY" http://localhost:7071/api/leases/lease-uuid-here

Response:

{
  "id": "lease-uuid-here",
  "companyId": "test-company",
  "itemId": "test-item",
  "price": 1000,
  "termMonths": 6,
  "nominalRatePct": 12,
  "startDate": "2023-01-01",
  "upfrontFee": 0,
  "monthlyFee": 50,
  "createdAt": "2023-01-01T00:00:00Z",
  "schedule": [...],
  "totals": {...}
}

Record Payment

POST /api/payments

Records a payment against a lease.

Request Body:

{
  "leaseId": "lease-uuid-here",
  "amount": 200,
  "paidAt": "2023-01-15T10:00:00Z"
}
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"leaseId":"lease-uuid-here","amount":200,"paidAt":"2023-01-15T10:00:00Z"}' \
  http://localhost:7071/api/payments

Response:

{
  "id": "payment-uuid-here",
  "leaseId": "lease-uuid-here",
  "amount": 200,
  "paidAt": "2023-01-15T10:00:00Z"
}

Quick Reference - 4 Required Functional Endpoints

# 1. GET /api/quote - Calculate leasing quote (no persistence)
curl -H "x-api-key: YOUR_API_KEY" "http://localhost:7071/api/quote?price=1000&termMonths=6&nominalRatePct=12&startDate=2023-01-01"

# 2. POST /api/leases - Create new leasing contract for a company
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"companyId":"test-company","itemId":"test-item","price":1000,"termMonths":6,"nominalRatePct":12,"startDate":"2023-01-01","upfrontFee":0,"monthlyFee":50}' \
  http://localhost:7071/api/leases

# 3. GET /api/leases/{id} - Fetch lease details with remaining balance and payment schedule
curl -H "x-api-key: YOUR_API_KEY" http://localhost:7071/api/leases/LEASE_UUID_HERE

# 4. POST /api/payments - Register payment for existing lease, updating remaining balance
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"leaseId":"LEASE_UUID_HERE","amount":200,"paidAt":"2023-01-15T10:00:00Z"}' \
  http://localhost:7071/api/payments

Error Responses

All endpoints return appropriate HTTP status codes:

  • 200 - Success
  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (invalid API key)
  • 404 - Not Found
  • 500 - Internal Server Error

Error Response Format:

{
  "error": "ValidationError",
  "message": "Invalid input parameters",
  "details": {...}
}

Development Commands

After running ./script.init, you can use these commands:

# Start the API (after setup)
./start-dev.sh

# Additional development tools
npm run dev          # Watch TypeScript compilation
npm test             # Run all tests
npm run prisma:studio # Open database GUI

Testing

The project includes comprehensive test coverage across all layers:

Test Categories

Unit Tests

  • Domain Logic Tests: Core business calculations and validations
  • Application Service Tests: Service layer logic with mocked dependencies
  • Test Database: Separate SQLite database for isolated testing

Integration Tests

  • API Workflow Tests: Full end-to-end API workflows
  • Database Integration: Real database operations and updates
  • Cross-Service Integration: Lease creation + payment recording flows

Test Coverage Details

Annuity Calculation Tests (lease.test.ts)

  • Monthly payment calculation using standard annuity formula
  • Interest rate handling (0% and positive rates)
  • Decimal precision and rounding validation
  • Edge cases and boundary conditions

Payment Schedule Generation Tests (lease.test.ts)

  • Correct installment count generation
  • Period numbering and due date calculations
  • Fee allocation across installments
  • Principal balance validation

Payment Allocation Logic Tests (payment.test.ts, payment-simple.test.ts)

  • Allocation order: Fees → Interest → Principal
  • Multi-installment payment distribution
  • Partial payment handling
  • Overpayment scenarios
  • Already paid installment handling

Integration Tests (lease-api.test.ts, payment-api.test.ts)

  • Complete lease creation workflow
  • Payment recording and database updates
  • Multiple payment scenarios
  • Payment allocation across installments
  • Error handling and edge cases

Running Tests

# Run all tests
npm test

# Run specific test categories
npm test -- tests/unit/domain/          # Domain logic tests
npm test -- tests/unit/application/     # Service layer tests
npm test -- tests/integration/          # Integration tests

# Run specific test files
npm test -- tests/unit/domain/lease.test.ts
npm test -- tests/unit/domain/payment.test.ts
npm test -- tests/integration/lease-api.test.ts
npm test -- tests/integration/payment-api.test.ts

Test Structure

tests/
├── unit/
│   ├── domain/
│   │   ├── lease.test.ts           # Annuity & schedule tests
│   │   ├── payment.test.ts         # Payment allocation tests
│   │   └── payment-simple.test.ts  # Simplified payment tests
│   └── application/
│       ├── lease-service.test.ts   # Lease service tests
│       └── payment-service.test.ts # Payment service tests
└── integration/
    ├── lease-api.test.ts           # Lease API integration
    └── payment-api.test.ts         # Payment API integration

Troubleshooting

"Node.js version incompatible"

The setup script automatically handles Node.js 14 installation. If you encounter issues:

# Check Node.js version
node --version  # Should show v14.x.x

# If using nvm
nvm use 14

# Then start manually
func start --script-root dist

"Port 7071 is unavailable"

# Kill any processes using port 7071
lsof -ti:7071 | xargs kill -9

"Functions not found"

# Rebuild the project
npm run build
./start-dev.sh

"Database connection failed"

# Reset database
rm -rf prisma/dev.db
npx prisma migrate dev --name init

Project Structure

leasing-api-azure-functions/
├── 📁 functions/
│   ├── 📁 leases/
│   │   ├── 📄 function.json
│   │   └── 📄 index.ts
│   ├── 📁 payments/
│   │   ├── 📄 function.json
│   │   └── 📄 index.ts
│   ├── 📁 quote/
│   │   ├── 📄 function.json
│   │   └── 📄 index.ts
│   └── 📁 health-check/
│       ├── 📄 function.json
│       └── 📄 index.ts
├── 📁 src/
│   ├── 📁 application/
│   │   ├── 📄 lease-service.ts
│   │   ├── 📄 payment-service.ts
│   │   └── 📄 quote-service.ts
│   ├── 📁 domain/
│   │   ├── 📄 lease.ts
│   │   ├── 📄 payment.ts
│   │   └── 📄 quote.ts
│   ├── 📁 handlers/
│   │   ├── 📁 lease/
│   │   │   ├── 📄 create-lease.ts
│   │   │   └── 📄 get-lease.ts
│   │   ├── 📁 payment/
│   │   │   └── 📄 record-payment.ts
│   │   └── 📁 quote/
│   │       └── 📄 calculate-quote.ts
│   ├── 📁 lib/
│   │   ├── 📄 prisma.ts
│   │   ├── 📄 validation.ts
│   │   ├── 📄 api-key-middleware.ts
│   │   └── 📄 logger.ts
│   ├── 📁 persistence/
│   │   ├── 📄 lease-repository.ts
│   │   ├── 📄 payment-repository.ts
│   │   └── 📄 quote-repository.ts
│   └── 📄 index.ts
├── 📁 tests/
│   ├── 📁 unit/
│   │   ├── 📁 domain/
│   │   │   ├── 📄 lease.test.ts
│   │   │   └── 📄 payment.test.ts
│   │   └── 📁 application/
│   │       ├── 📄 lease-service.test.ts
│   │       └── 📄 payment-service.test.ts
│   └── 📁 integration/
│       ├── 📄 lease-api.test.ts
│       └── 📄 payment-api.test.ts
├── 📁 scripts/
│   ├── 📄 check-node-version.js
│   ├── 📄 setup-detector.js
│   ├── 📄 copy-function-configs.js
│   ├── 📄 setup-azurite.sh
│   └── 📄 deploy-azure-comprehensive.sh
├── 📄 package.json
├── 📄 tsconfig.json
├── 📄 host.json
└── 📄 README.md

Business Logic

Lease Calculation

  • Monthly Payment: Standard annuity formula
  • Payment Schedule: Generated for each lease term
  • Payment Allocation: Fees → Interest → Principal

Payment Processing

  • Allocation Order: Fees first, then interest, then principal
  • Multi-Installment: Payments can span multiple installments
  • Overpayment: Handled gracefully

Deployment

Local Development

./start-dev.sh  # Runs on http://localhost:7071

Production Deployment

# Deploy with comprehensive production setup
npm run azure:deploy:comprehensive

The deployment script provides:

Security Features:

  • All secrets stored in Azure Key Vault (API_KEY, JWT_SECRET, ENCRYPTION_KEY, DATABASE_URL)
  • Managed Identity for secure resource access
  • No hardcoded credentials in application code
  • Production-grade security configuration

Manual Deployment:

# Custom deployment with specific parameters
./scripts/deploy-azure-comprehensive.sh <function-app-name> <resource-group> <location>

# Example:
./scripts/deploy-azure-comprehensive.sh my-leasing-api my-rg westeurope

Testing Deployed API

# Get API Key from Azure Key Vault
API_KEY=$(az keyvault secret show --vault-name <key-vault-name> --name API-KEY --query value --output tsv)

# Health Check
curl -H "x-api-key: $API_KEY" https://<function-app-name>.azurewebsites.net/api/health

# Get Quote
curl -H "x-api-key: $API_KEY" "https://<function-app-name>.azurewebsites.net/api/quote?price=1000&termMonths=6&nominalRatePct=12&startDate=2023-01-01"

Monitoring

# View real-time logs
func azure functionapp logstream <function-app-name>

# View metrics in Azure Portal
# Navigate to: Azure Portal > Function App > Monitoring > Metrics

Environment Variables

The API uses environment variables for configuration:

Development Environment

# .env.local (your secrets - never commit to git)
API_KEY=your-secure-api-key-here
DATABASE_URL=file:./prisma/dev.db
JWT_SECRET=your-jwt-secret-here
ENCRYPTION_KEY=your-encryption-key-here

Production Environment

# Azure Key Vault or App Settings
API_KEY=managed-by-azure-key-vault
DATABASE_URL=postgresql://user:pass@host:5432/db
JWT_SECRET=managed-by-azure-key-vault

Setup Process

  1. Run ./script.init - Creates .env.local from template
  2. Edit .env.local - Add your secure values
  3. Start development - Application loads from .env.local

Security Workflow

Development Security

# 1. Setup creates template
./script.init

# 2. You edit with your secrets
nano .env.local
# Add: API_KEY=your-secure-key-here

# 3. Application reads from .env.local
./start-dev.sh

Verified Working Commands

The following commands have been tested and work perfectly:

# 1. One-command setup (does everything automatically)
./script.init

# 2. Start the API
./start-dev.sh

# 3. Test the API
curl -H "x-api-key: YOUR_API_KEY" http://localhost:7071/api/health
curl -H "x-api-key: YOUR_API_KEY" "http://localhost:7071/api/quote?price=1000&termMonths=6&nominalRatePct=12&startDate=2023-01-01"

# 4. Create a lease
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"companyId":"test-company","itemId":"test-item","price":1000,"termMonths":6,"nominalRatePct":12,"startDate":"2023-01-01","upfrontFee":0,"monthlyFee":50}' \
  http://localhost:7071/api/leases

# 5. Record a payment (use lease ID from above)
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"leaseId":"LEASE_ID_HERE","amount":200,"paidAt":"2023-01-15T10:00:00Z"}' \
  http://localhost:7071/api/payments

Future Recommendations

  • Upgrade to Node.js 16+ for the best experience with modern Azure Functions, Azurite and for better deployment.

About

Leasing (hire-purchase) backend API built with Azure Functions v3, TypeScript, and Prisma.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors