shisha-backend-commons
v1.0.1
Published
Shared utilities and types for Shisha Backend API
Readme
Common Package
This package contains common utilities, types, and middleware used across all Azure Functions in the Shisha Backend API. It provides a centralized foundation for database access, authentication, search functionality, and response formatting with serverless optimizations.
Published as: shisha-backend-common on npm registry
Table of Contents
- Architecture Overview
- Package Structure
- Core Libraries
- Middleware
- Authentication Strategy
- Serverless Optimizations
- Response Formatting
- Type Definitions
- Development
- Environment Configuration
- Dependencies
Architecture Overview
The common package implements a serverless-first architecture designed for Azure Functions with the following key principles:
- Connection Reuse: Singleton patterns for database and external service connections
- Cold Start Optimization: Minimal initialization overhead and lazy loading
- Memory Efficiency: Optimized connection pooling and resource management
- Future-Proof Authentication: Direct Azure CIAM integration supporting both NextAuth.js and direct frontend authentication
- PWA Compatibility: Response headers and caching strategies for Progressive Web App support
Package Structure
src/
├── lib/ # Core utilities and service clients
│ ├── prisma.ts # Database client with serverless optimizations
│ ├── azure-search.client.ts # Azure Search service client
│ ├── location-parser.ts # Location parsing and geocoding utilities
│ └── geocoding.ts # Geocoding service with known locations
├── middleware/ # Middleware functions for Azure Functions
│ ├── auth.ts # JWT authentication and authorization
│ ├── cors.ts # Cross-origin resource sharing
│ └── error-handler.ts # Centralized error handling
├── types/ # Shared TypeScript type definitions
│ ├── auth.types.ts # Authentication and user types
│ └── search.types.ts # Search and venue types
├── utils/ # Utility functions
│ └── response.ts # Response formatting with PWA support
├── config/ # Configuration files
│ └── azure-search.config.ts # Azure Search configuration
└── index.ts # Package exportsCore Libraries
Database Client (lib/prisma.ts)
Provides a singleton Prisma client optimized for serverless environments:
import { getPrismaClient, prisma } from 'shisha-backend-common';
// Use the singleton instance
const venues = await prisma.venue.findMany();
// Or get a fresh instance
const client = getPrismaClient();Serverless Optimizations:
- Single Connection Pool: Limited to 1 connection per function instance
- Fast Timeouts: 2-second connection timeout, 30-second idle timeout
- Connection Reuse: Global singleton pattern prevents multiple instances
- Error Recovery: Automatic pool reset on connection errors
- SSL Configuration: Optimized for Azure PostgreSQL
Environment Variables Required:
DB_USER- Database usernameDB_PASSWORD- Database passwordDB_HOST- Database hostDB_PORT- Database port (default: 5432)DB_NAME- Database name
Azure Search Client (lib/azure-search.client.ts)
Provides comprehensive search functionality with geospatial capabilities:
import { getSearchClient } from 'shisha-backend-common';
const client = getSearchClient();
const results = await client.search('shisha bar', {
filter: 'category eq "shisha_bar"',
top: 20
});Features:
- Singleton Pattern: Reuses client instances across function invocations
- Geospatial Search: Location-based filtering and distance calculations
- Advanced Filtering: Support for complex OData filters
- Error Handling: Graceful degradation when search service is unavailable
Location Services (lib/location-parser.ts, lib/geocoding.ts)
Provides location parsing and geocoding with known locations database:
import { parseLocation, geocodeLocation } from 'shisha-backend-common';
// Parse location from search query
const location = parseLocation('restaurants near Berlin');
// Geocode location to coordinates
const coordinates = await geocodeLocation('Berlin, Germany');Middleware
Authentication Middleware (middleware/auth.ts)
Implements JWT token validation with Azure CIAM integration:
import { validateAuth, requireAuth, optionalAuth } from 'shisha-backend-common';
// Validate authentication (throws on failure)
const authContext = await validateAuth(request);
// Middleware wrapper for protected functions
const protectedHandler = requireAuth(async (authContext, request) => {
// Handler receives validated auth context
return { userId: authContext.userId };
});
// Optional authentication
const optionalHandler = optionalAuth(async (authContext, request) => {
// authContext is null if no token provided
if (authContext) {
// User is authenticated
}
});Key Features:
- Direct Azure CIAM Integration: Validates tokens using Azure's public keys (JWKS)
- Future-Proof: Works with NextAuth.js tokens and direct Azure CIAM tokens
- Caching: JWKS keys cached for 10 minutes to reduce external calls
- Rate Limiting: Built-in rate limiting for JWKS requests
- Permission Checking: Basic resource-level authorization
Environment Variables Required:
AZURE_CIAM_TENANT_NAME- Azure CIAM tenant nameAZURE_CIAM_TENANT_ID- Azure CIAM tenant IDAZURE_CIAM_CLIENT_ID- Azure CIAM client/application ID
CORS Middleware (middleware/cors.ts)
Handles cross-origin requests with environment-based configuration:
import { corsMiddleware } from 'shisha-backend-common';
export async function handler(request: HttpRequest): Promise<HttpResponseInit> {
// Handle preflight requests
const corsResponse = corsMiddleware(request);
if (corsResponse) return corsResponse;
// Your function logic here
}Features:
- Environment-Based Origins: Configurable allowed origins
- Credentials Support: Handles authentication cookies and headers
- Preflight Handling: Automatic OPTIONS request handling
Error Handler (middleware/error-handler.ts)
Centralized error handling with consistent response formatting:
import { handleError } from 'shisha-backend-common';
try {
// Your function logic
} catch (error) {
return handleError(error, context);
}Error Mapping:
- Prisma Errors: Maps database errors to appropriate HTTP status codes
- Azure Search Errors: Handles search service unavailability
- Authentication Errors: Standardized 401/403 responses
- Validation Errors: Structured validation error responses
Authentication Strategy
The authentication system is designed to support both current NextAuth.js integration and future direct Azure CIAM authentication:
Current Architecture (with NextAuth.js)
graph LR
A[Next.js App] --> B[NextAuth.js]
B --> C[Azure CIAM]
C --> D[JWT Token]
A --> E[Azure Functions]
E --> F[Validate JWT]
F --> C- User authenticates with Next.js app via NextAuth.js
- NextAuth.js handles Azure CIAM OAuth flow
- JWT token contains user claims from Azure CIAM
- Azure Functions validate JWT directly against Azure CIAM public keys
Future Architecture (Direct Azure CIAM)
graph LR
A[Frontend App] --> B[Azure CIAM]
B --> C[JWT Token]
A --> D[Azure Functions]
D --> E[Validate JWT]
E --> B- Frontend authenticates directly with Azure CIAM
- Azure CIAM returns JWT token with user claims
- Azure Functions validate JWT using the same validation logic
Migration Benefits
- Zero Changes Required: Azure Functions continue working when NextAuth.js is removed
- No Shared Secrets: Uses public key cryptography (JWKS) for validation
- Direct Validation: Tokens validated against Azure CIAM, not through Next.js
- Security: Eliminates intermediate token handling
Token Validation Process
- Extract Token: Parse
Authorization: Bearer <token>header - Decode Header: Extract key ID (kid) from JWT header
- Fetch Public Key: Retrieve signing key from Azure CIAM JWKS endpoint
- Verify Signature: Validate token signature using public key
- Validate Claims: Check audience, issuer, and expiration
- Extract User Info: Parse user claims (sub, email, given_name, family_name)
Serverless Optimizations
Connection Reuse Patterns
Database Connections:
// Global singleton prevents multiple Prisma instances
let prismaClient: PrismaClient | null = null;
let pgPool: Pool | null = null;
export function getPrismaClient(): PrismaClient {
if (!prismaClient) {
// Initialize once, reuse across invocations
prismaClient = new PrismaClient({ adapter });
}
return prismaClient;
}External Service Clients:
// JWKS client with caching
let jwksClientInstance: jwksClient.JwksClient | null = null;
function getJwksClient(): jwksClient.JwksClient {
if (!jwksClientInstance) {
jwksClientInstance = jwksClient({
cache: true,
cacheMaxAge: 600000, // 10 minutes
});
}
return jwksClientInstance;
}Performance Optimizations
Cold Start Minimization:
- Lazy loading of heavy dependencies
- Minimal module imports at startup
- Singleton patterns for expensive initializations
Memory Management:
- Single database connection per function instance
- Connection pool limits (max: 1)
- Automatic cleanup on errors
Caching Strategies:
- JWKS key caching (10 minutes)
- Response caching headers for PWA support
- ETag generation for cache validation
Connection Pool Configuration
const pool = new Pool({
max: 1, // Single connection for serverless
idleTimeoutMillis: 30000, // Close idle connections quickly
connectionTimeoutMillis: 2000, // Fast connection timeout
keepAlive: true, // Enable TCP keep-alive
});Response Formatting
The response utilities ensure consistent API responses across all Azure Functions:
Success Responses
import { successResponse } from 'shisha-backend-common';
// Basic success response
return successResponse(data);
// With metadata and caching
return successResponse(
venues,
{ total: 100, page: 1 },
200,
{ maxAge: 300, staleWhileRevalidate: 150 }
);Response Format:
{
"success": true,
"data": { /* your data */ },
"metadata": { /* optional metadata */ }
}Error Responses
import {
errorResponse,
validationErrorResponse,
authErrorResponse,
notFoundErrorResponse
} from 'shisha-backend-common';
// Generic error
return errorResponse('Something went wrong', 'SERVER_ERROR', 500);
// Specific error types
return validationErrorResponse('Invalid email format');
return authErrorResponse('Token expired');
return notFoundErrorResponse('Venue not found');Error Format:
{
"success": false,
"error": {
"message": "User-friendly error message",
"code": "ERROR_CODE",
"details": { /* optional details */ }
}
}PWA Support
Response utilities include PWA-compatible caching headers:
// Automatic cache headers
headers: {
'Cache-Control': 'public, max-age=300, stale-while-revalidate=150',
'ETag': '"abc123"',
'Vary': 'Accept-Encoding, Authorization'
}Type Definitions
Authentication Types (types/auth.types.ts)
interface AuthContext {
userId: string;
email: string;
firstName?: string;
lastName?: string;
}
interface AzureCIAMTokenClaims {
sub: string; // User ID
email: string; // Email address
given_name?: string; // First name
family_name?: string; // Last name
aud: string; // Audience (client ID)
iss: string; // Issuer (Azure CIAM)
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp
}Search Types (types/search.types.ts)
Complete type definitions for venue search, including:
ShishaPlace- Venue data structureExploreRequest/ExploreResponse- Search API contractsSearchFilterOptions- Search filtering optionsLocationInfo- Geographic location data
Development
Building the Package
# Install dependencies
yarn install
# Build TypeScript to JavaScript
yarn build
# Watch for changes during development
yarn dev
# Clean build artifacts
yarn clean
# Run linting
yarn lint
# Run tests
yarn testTesting
The package includes property-based tests using fast-check:
# Run all tests
yarn test
# Run tests in watch mode
yarn test --watch
# Run specific test file
yarn test response.test.tsIntegration with Azure Functions
Each Azure Function package depends on this common package:
{
"dependencies": {
"shisha-backend-common": "^1.0.0"
}
}Import utilities in your Azure Functions:
import {
getPrismaClient,
validateAuth,
successResponse,
corsMiddleware
} from 'shisha-backend-common';Environment Configuration
Required Environment Variables
Database Configuration:
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=your_db_host
DB_PORT=5432
DB_NAME=your_db_nameAzure CIAM Authentication:
AZURE_CIAM_TENANT_NAME=your_tenant_name
AZURE_CIAM_TENANT_ID=your_tenant_id
AZURE_CIAM_CLIENT_ID=your_client_idAzure Search Service:
AZURE_SEARCH_ENDPOINT=https://your-search-service.search.windows.net
AZURE_SEARCH_API_KEY=your_search_api_key
AZURE_SEARCH_INDEX_NAME=your_index_nameCORS Configuration:
ALLOWED_ORIGINS=http://localhost:3000,https://your-domain.com
NODE_ENV=developmentLocal Development Setup
- Copy environment variables to each function's
local.settings.json - Ensure Azure PostgreSQL and Azure Search are accessible
- Start functions with
func startin each package directory - Test endpoints using Postman or curl
Security Considerations
- Never commit
local.settings.jsonfiles - Use Azure Key Vault for production secrets
- Rotate API keys regularly
- Monitor authentication logs for suspicious activity
Dependencies
Production Dependencies
@prisma/client(^7.2.0) - Database ORM client@prisma/adapter-pg(^7.1.0) - PostgreSQL adapter for Prisma@azure/search-documents(^12.2.0) - Azure Search SDKjsonwebtoken(^9.0.0) - JWT token handlingjwks-rsa(^3.0.0) - JWKS key retrieval for JWT validationpg(^8.16.3) - PostgreSQL clientaxios(^1.13.2) - HTTP client for external APIsdotenv(^17.2.3) - Environment variable loading
Development Dependencies
typescript(^5.0.0) - TypeScript compilerjest(^29.0.0) - Testing frameworkfast-check(^4.4.0) - Property-based testing library@types/*- TypeScript type definitionseslint(^9.0.0) - Code linting
Peer Dependencies
@azure/functions(^4.0.0) - Azure Functions runtime types
Troubleshooting
Common Issues
Database Connection Errors:
- Verify environment variables are set correctly
- Check PostgreSQL server accessibility
- Ensure SSL configuration matches Azure PostgreSQL requirements
Authentication Failures:
- Verify Azure CIAM configuration
- Check token format and claims
- Ensure JWKS endpoint is accessible
Search Service Errors:
- Verify Azure Search endpoint and API key
- Check index name and schema
- Monitor search service quotas and limits
Debugging
Enable detailed logging in development:
NODE_ENV=developmentThis enables:
- Prisma query logging
- Detailed error stack traces
- Authentication debug information
Performance Monitoring
Monitor key metrics:
- Function cold start times
- Database connection pool usage
- JWKS cache hit rates
- Response times for external services
