npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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 exports

Core 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 username
  • DB_PASSWORD - Database password
  • DB_HOST - Database host
  • DB_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 name
  • AZURE_CIAM_TENANT_ID - Azure CIAM tenant ID
  • AZURE_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
  1. User authenticates with Next.js app via NextAuth.js
  2. NextAuth.js handles Azure CIAM OAuth flow
  3. JWT token contains user claims from Azure CIAM
  4. 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
  1. Frontend authenticates directly with Azure CIAM
  2. Azure CIAM returns JWT token with user claims
  3. 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

  1. Extract Token: Parse Authorization: Bearer <token> header
  2. Decode Header: Extract key ID (kid) from JWT header
  3. Fetch Public Key: Retrieve signing key from Azure CIAM JWKS endpoint
  4. Verify Signature: Validate token signature using public key
  5. Validate Claims: Check audience, issuer, and expiration
  6. 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 structure
  • ExploreRequest/ExploreResponse - Search API contracts
  • SearchFilterOptions - Search filtering options
  • LocationInfo - 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 test

Testing

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.ts

Integration 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_name

Azure CIAM Authentication:

AZURE_CIAM_TENANT_NAME=your_tenant_name
AZURE_CIAM_TENANT_ID=your_tenant_id
AZURE_CIAM_CLIENT_ID=your_client_id

Azure 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_name

CORS Configuration:

ALLOWED_ORIGINS=http://localhost:3000,https://your-domain.com
NODE_ENV=development

Local Development Setup

  1. Copy environment variables to each function's local.settings.json
  2. Ensure Azure PostgreSQL and Azure Search are accessible
  3. Start functions with func start in each package directory
  4. Test endpoints using Postman or curl

Security Considerations

  • Never commit local.settings.json files
  • 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 SDK
  • jsonwebtoken (^9.0.0) - JWT token handling
  • jwks-rsa (^3.0.0) - JWKS key retrieval for JWT validation
  • pg (^8.16.3) - PostgreSQL client
  • axios (^1.13.2) - HTTP client for external APIs
  • dotenv (^17.2.3) - Environment variable loading

Development Dependencies

  • typescript (^5.0.0) - TypeScript compiler
  • jest (^29.0.0) - Testing framework
  • fast-check (^4.4.0) - Property-based testing library
  • @types/* - TypeScript type definitions
  • eslint (^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=development

This 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