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

@timmons-group/snack-pack

v0.1.2

Published

High-level scaffolding components for rapid serverless API development. This package provides REST API utilities, authentication middleware, and endpoint builders to accelerate application development.

Downloads

957

Readme

Shared Node Architecture Component Kit (SNACK) Pack

High-level scaffolding components for rapid serverless API development. This package provides REST API utilities, authentication middleware, and endpoint builders to accelerate application development.

Installation

npm install @timmons-group/snack-pack

Components

REST API Utilities

The REST module provides a comprehensive set of utilities for building robust REST APIs with automatic database connectivity, user authentication, and error handling.

Core Providers

provideDatabase

Automatically injects a configured database connection into your handler function.

import { provideDatabase } from '@timmons-group/snack-pack/REST';

const myHandler = async (apiEvent, databaseDriver) => {
  const users = await databaseDriver.query('SELECT * FROM users');
  return {
    statusCode: 200,
    body: JSON.stringify(users)
  };
};

export const handler = provideDatabase(myHandler);
provideUser

Extracts and validates user information from the request, providing authenticated user context.

import { provideUser } from '@timmons-group/snack-pack/REST';

const myHandler = async (apiEvent, user) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: `Hello, ${user.name}!` })
  };
};

export const handler = provideUser(myHandler);
provideErrorHandling

Wraps your handler with comprehensive error handling, automatically returning appropriate HTTP error responses.

import { provideErrorHandling } from '@timmons-group/snack-pack/REST';

const myHandler = async (apiEvent) => {
  // If this throws an error, it will be caught and return a 500 response
  throw new Error('Something went wrong');
};

export const handler = provideErrorHandling(myHandler);
provideRESTDefaults

Combines database connectivity, user authentication, error handling, and permission checking in a single provider.

import { provideRESTDefaults } from '@timmons-group/snack-pack/REST';

const myHandler = async (apiEvent, { databaseDriver, user }) => {
  // Access to both database and authenticated user
  const userData = await databaseDriver.query(
    'SELECT * FROM user_data WHERE user_id = $1', 
    [user.id]
  );
  
  return {
    statusCode: 200,
    body: JSON.stringify(userData)
  };
};

// Requires 'read' and 'write' permissions
export const handler = provideRESTDefaults(myHandler, ['read', 'write']);

Permission System

The REST providers support a flexible permission system:

import { provideRESTDefaults } from '@timmons-group/snack-pack/REST';

// Single permission
export const readHandler = provideRESTDefaults(handler, ['read']);

// Multiple permissions (user must have ALL permissions)
export const adminHandler = provideRESTDefaults(handler, ['read', 'write', 'admin']);

// No permissions required
export const publicHandler = provideRESTDefaults(handler, []);

REST Endpoint Builder

A fluent API for building REST endpoints with explicit control over which providers to include.

Basic Usage

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

const internalHandler = async (apiEvent, { databaseDriver, user }) => {
  const result = await databaseDriver.query('SELECT * FROM data');
  return {
    statusCode: 200,
    body: JSON.stringify(result)
  };
};

// Full configuration
export const handler = new RESTEndpointBuilder(internalHandler)
  .withDatabase()
  .withUser()
  .withErrorHandling()
  .withPermissions(['read', 'write'])
  .build();

Simplified Configuration

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

// Equivalent to the above example
export const handler = new RESTEndpointBuilder(internalHandler)
  .withRESTDefaults(['read', 'write'])
  .build();

Selective Providers

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

// Only database and error handling, no authentication
const publicHandler = async (apiEvent, { databaseDriver }) => {
  const publicData = await databaseDriver.query('SELECT * FROM public_data');
  return { statusCode: 200, body: JSON.stringify(publicData) };
};

export const handler = new RESTEndpointBuilder(publicHandler)
  .withDatabase()
  .withErrorHandling()
  .build();

Available Methods

  • .withDatabase() - Add database connectivity
  • .withUser() - Add user authentication
  • .withErrorHandling() - Add error handling wrapper
  • .withPermissions(permissions) - Add permission checking
  • .withRESTDefaults(permissions) - Add all common providers
  • .build() - Create the final handler

Authentication

Complete OAuth authentication system with Cognito integration and JWT token handling.

OAuth Endpoints

Pre-built OAuth endpoints for handling authentication flow:

import { handler as oAuthHandler } from '@timmons-group/snack-pack/Auth/Endpoints';

// Provides complete OAuth flow endpoints
export const handler = oAuthHandler;

Available endpoints:

  • POST /oauth/callback - Exchange authorization code for tokens
  • POST /oauth/refresh - Refresh access tokens using refresh token
  • POST /oauth/logout - Logout and clear authentication cookies
  • GET /oauth/config - Get OAuth configuration for client applications

Example usage with API Gateway:

// For serverless.yml or AWS SAM
functions:
  oauth:
    handler: oauth.handler
    events:
      - http:
          path: oauth/{proxy+}
          method: ANY

Client-side integration:

The OAuth endpoints are designed to work with the useAuth React hook pattern:

// React client example
const { login, logout, user, isAuthenticated } = useAuth({
  oauthConfigUrl: 'https://api.example.com/oauth/config',
  callbackUrl: 'https://api.example.com/oauth/callback',
  refreshUrl: 'https://api.example.com/oauth/refresh',
  logoutUrl: 'https://api.example.com/oauth/logout'
});

Authentication Middleware

import { authenticateUser } from '@timmons-group/snack-pack/Auth';

export const handler = async (event) => {
  try {
    const user = await authenticateUser(event);
    // User is authenticated, proceed with business logic
    return { statusCode: 200, body: 'Authenticated' };
  } catch (error) {
    return { statusCode: 401, body: 'Unauthorized' };
  }
};

Authentication Utilities

The authentication system provides several utility functions for working with users and permissions:

User Management
import { 
  getUser, 
  getUserFromID, 
  getAnonymousUser 
} from '@timmons-group/snack-pack/Auth';

// Get user from Lambda event
const user = await getUser(event, databaseDriver);

// Get user by ID (for internal operations)
const user = await getUserFromID('user-123');

// Get anonymous user object
const anonymousUser = await getAnonymousUser(databaseDriver);
Permission Checking
import { 
  hasPermission, 
  hasAnyPermissions, 
  hasAllPermissions 
} from '@timmons-group/snack-pack/Auth';

// Check single permission
if (hasPermission(user, 'read_users')) {
  // User has read_users permission
}

// Check if user has any of the specified permissions
if (hasAnyPermissions(user, ['read_users', 'write_users'])) {
  // User has at least one of these permissions
}

// Check if user has all specified permissions
if (hasAllPermissions(user, ['read_users', 'write_users'])) {
  // User has both permissions
}
Token Utilities
import { 
  decodeToken, 
  validateToken, 
  extractFromToken 
} from '@timmons-group/snack-pack/Auth';

// Decode JWT token
const { header, payload } = decodeToken(tokenString);

// Validate token with OAuth provider
const oAuthDriver = await getOAuthDriver();
const validation = await validateToken(decodedToken, tokenString, oAuthDriver);

// Extract user information from token
const { valid, user } = await extractFromToken(tokenString, oAuthDriver, databaseDriver);
OAuth Driver
import { getOAuthDriver } from '@timmons-group/snack-pack/Auth';

const oAuthDriver = await getOAuthDriver();
const config = await oAuthDriver.buildOAuthConfig();
const userGroups = await oAuthDriver.getGroups(token);

Cognito Integration

import { CognitoUserPool } from '@timmons-group/snack-pack/Auth';

// Configure Cognito user pool
const userPool = new CognitoUserPool({
  userPoolId: process.env.USER_POOL_ID,
  clientId: process.env.CLIENT_ID
});

Complete Examples

Simple CRUD API

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';
import { responses } from '@timmons-group/snack-utils';

// GET /users
const getUsers = async (apiEvent, { databaseDriver, user }) => {
  const users = await databaseDriver.query('SELECT * FROM users');
  return responses.jsonSuccess(users);
};

// POST /users  
const createUser = async (apiEvent, { databaseDriver, user }) => {
  const userData = JSON.parse(apiEvent.body);
  const result = await databaseDriver.query(
    'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
    [userData.name, userData.email]
  );
  return responses.jsonSuccess(result[0]);
};

// Export handlers
export const getUsersHandler = new RESTEndpointBuilder(getUsers)
  .withRESTDefaults(['read'])
  .build();

export const createUserHandler = new RESTEndpointBuilder(createUser)
  .withRESTDefaults(['write'])
  .build();

Public API with Database Only

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

const getPublicData = async (apiEvent, { databaseDriver }) => {
  const data = await databaseDriver.query('SELECT * FROM public_content');
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  };
};

export const handler = new RESTEndpointBuilder(getPublicData)
  .withDatabase()
  .withErrorHandling()
  .build();

Authentication-Only Endpoint

import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

const getUserProfile = async (apiEvent, { user }) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      id: user.id,
      name: user.name,
      email: user.email
    })
  };
};

export const handler = new RESTEndpointBuilder(getUserProfile)
  .withUser()
  .withErrorHandling()
  .build();

Configuration

Database Configuration

The REST providers automatically use the global database configuration from @timmons-group/snack-blocks. You can configure this manually or use a configuration file.

Manual Configuration

import { ConfigurationBuilder, setGlobalConfig } from '@timmons-group/snack-blocks';

const config = await new ConfigurationBuilder()
  .withSSM(process.env.SSM_PATH + "connectionString")
  .build();

setGlobalConfig(config);

Configuration File (Recommended for Production)

Create a snack-config.js file and set the SNACK_CONFIG environment variable:

# Environment variable
SNACK_CONFIG=./snack-config.js

# Or use project root alias (requires package.json configuration)
SNACK_CONFIG=@/snack-config.js

Project Root Alias Setup

To use the @/ prefix, configure your package.json with import aliases:

{
  "name": "your-project",
  "type": "module", 
  "imports": {
    "@/*": "./*"
  }
}

Complete snack-config.js example:

import { ConfigurationBuilder } from '@timmons-group/snack-blocks';
import { AuthConfigurationBuilder } from '@timmons-group/snack-pack/Auth/ConfigurationBuilder';

// Database Configuration
const databaseConfigurationBuilder = new ConfigurationBuilder();
export const databaseConfiguration = await (
    databaseConfigurationBuilder
        .withSSM(process.env.SSM_PATH + "connectionString")
        .build()
);

// Authentication Configuration
export const authConfiguration = await (
    new AuthConfigurationBuilder()
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        .withGroupPermissions("SystemAdministrators", ["system_admin"])
        .withGroupPermissions("UserManagers", ["read_users", "write_users"])
        .withDefaultPermissions(["sign_in", "read_profile"])
        .withAnonymousPermissions(["sign_in"])
        .build()
);

Authentication Configuration

The preferred method for configuring authentication is using the AuthConfigurationBuilder which provides a fluent API for setting up Cognito integration, permissions, and access control.

Using AuthConfigurationBuilder (Recommended)

import { AuthConfigurationBuilder } from '@timmons-group/snack-pack/Auth/ConfigurationBuilder';

const authConfigurationBuilder = new AuthConfigurationBuilder();
export const authConfiguration = await (
    authConfigurationBuilder
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        .withGroupPermissions("SystemAdministrators", [
            TAGS.SYSTEM_ADMIN
        ])
        .withDefaultPermissions([
            ACLS.SIGN_IN,
        ])
        .withAnonymousPermissions([
            ACLS.SIGN_IN,
        ])
        .build()
);

Advanced Authentication Configuration

The AuthConfigurationBuilder supports advanced features for complex authentication scenarios:

Database-Backed Permissions (PAM)

Store permissions in a PostgreSQL database for dynamic permission management:

const authConfiguration = await (
    new AuthConfigurationBuilder()
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        .withPAMPermissionSource("auth_schema")  // Uses auth_schema.groupacl table
        .build()
);

Required database table structure:

CREATE TABLE auth_schema.groupacl (
    groupkey VARCHAR(255) PRIMARY KEY,
    acl JSON  -- Array of permission strings
);

-- Example data
INSERT INTO auth_schema.groupacl VALUES 
('SystemAdministrators', '["system_admin", "user_management"]'),
('UserManagers', '["read_users", "write_users"]');
Custom Providers

Extend authentication with custom logic for claims, groups, and permissions:

const authConfiguration = await (
    new AuthConfigurationBuilder()
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        // Add custom claims to user object
        .withCustomClaimProvider(async (user) => {
            const profile = await getUserProfile(user.id);
            return { 
                department: profile.department,
                level: profile.level,
                manager: profile.managerId 
            };
        })
        // Add groups from external sources
        .withCustomGroupProvider(async (user) => {
            const ldapGroups = await getLDAPGroups(user.id);
            return ldapGroups;
        })
        // Add permissions from external systems
        .withCustomPermissionsProvider(async (user) => {
            const externalPerms = await getExternalPermissions(user.id);
            return externalPerms;
        })
        .build()
);
Multiple Permission Sources

Combine multiple permission sources for complex authorization:

const authConfiguration = await (
    new AuthConfigurationBuilder()
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        // Static permissions (code-defined)
        .withGroupPermissions("Administrators", ["admin_access"])
        // Database permissions
        .withPAMPermissionSource("auth_schema")
        // Custom permission source
        .withPermissionSource({
            name: "external_api",
            type: "custom",
            provider: async (group) => {
                return await fetchPermissionsFromAPI(group);
            }
        })
        .build()
);
Complete Advanced Example
const authConfiguration = await (
    new AuthConfigurationBuilder()
        .withCognito(process.env.POOL_ID, process.env.CLIENT_ID)
        
        // Basic permissions
        .withGroupPermissions("SystemAdministrators", ["system_admin"])
        .withGroupPermissions("UserManagers", ["read_users", "write_users"])
        .withDefaultPermissions(["sign_in", "read_profile"])
        .withAnonymousPermissions(["sign_in"])
        
        // Database-backed permissions
        .withPAMPermissionSource("auth_schema")
        
        // Custom providers
        .withCustomClaimProvider(async (user) => {
            // Enrich user with organization data
            const orgData = await getOrganizationData(user.id);
            return {
                organization: orgData.name,
                role: orgData.role,
                permissions: orgData.customPermissions
            };
        })
        
        .withCustomGroupProvider(async (user) => {
            // Add project-based groups
            const projects = await getUserProjects(user.id);
            return projects.map(p => `project_${p.id}`);
        })
        
        .build()
);

Environment Variables

Set the following environment variables for authentication:

# Configuration
SNACK_CONFIG=./snack-config.js  # Path to configuration file (recommended)

# Authentication
POOL_ID=your-cognito-user-pool-id
CLIENT_ID=your-cognito-client-id
JWT_SECRET=your-jwt-secret

# Database (if not using config file)
SSM_PATH=your-ssm-base-path

Permission Constants

Define your permission constants for consistent access control:

// Example permission constants
export const ACLS = {
    SIGN_IN: 'sign_in',
    READ_USERS: 'read_users',
    WRITE_USERS: 'write_users',
    DELETE_USERS: 'delete_users'
};

export const TAGS = {
    SYSTEM_ADMIN: 'system_admin',
    USER_MANAGER: 'user_manager',
    READ_ONLY: 'read_only'
};

Error Handling

All providers include comprehensive error handling:

  • Database Errors: Automatically logged and return 500 responses
  • Authentication Errors: Return 401 responses for invalid tokens
  • Permission Errors: Return 403 responses for insufficient permissions
  • Validation Errors: Return 400 responses for malformed requests

Best Practices

  1. Use RESTDefaults: For most endpoints, withRESTDefaults() provides everything you need
  2. Granular Permissions: Define specific permissions for different operations
  3. Error Handling: Always include error handling in your endpoints
  4. Database Configuration: Set up global database configuration once at startup
  5. Logging: Use @timmons-group/snack-utils loggers for consistent logging

Dependencies

  • @timmons-group/snack-utils - Logging and response utilities
  • @aws-sdk/client-cognito-identity-provider - AWS Cognito integration
  • jsonwebtoken - JWT token handling
  • jwk-to-pem - JWT verification utilities

Exports

// REST providers
import { 
  provideDatabase, 
  provideUser, 
  provideErrorHandling, 
  provideRESTDefaults 
} from '@timmons-group/snack-pack/REST';

// REST endpoint builder
import { RESTEndpointBuilder } from '@timmons-group/snack-pack/REST/EndpointBuilder';

// Authentication configuration
import { AuthConfigurationBuilder } from '@timmons-group/snack-pack/Auth/ConfigurationBuilder';

// Authentication middleware and utilities
import { 
  getUser,
  getUserFromID,
  getAnonymousUser,
  hasPermission,
  hasAnyPermissions,
  hasAllPermissions,
  decodeToken,
  validateToken,
  extractFromToken,
  getOAuthDriver
} from '@timmons-group/snack-pack/Auth';

// OAuth endpoints
import { handler as oAuthHandler } from '@timmons-group/snack-pack/Auth/Endpoints';

Feature Flags

The FeatureFlags module provides a flexible, configurable feature flag system that supports multiple storage backends with caching and environment-based configuration.

Quick Start

import { FeatureFlagConfigurationBuilder, FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Configure feature flags
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironmentDefaults() // Auto-configure based on NODE_ENV
  .withDefaultFlags([
    { name: 'new-ui', value: false },
    { name: 'enhanced-logging', value: true }
  ])
  .build();

// Check if a feature is enabled
const isEnabled = await FeatureFlags.isEnabled('new-ui', false);

Configuration Builder

The FeatureFlagConfigurationBuilder provides a fluent API for configuring your feature flag system:

import { FeatureFlagConfigurationBuilder } from '@timmons-group/snack-pack/FeatureFlags';

const config = new FeatureFlagConfigurationBuilder()
  .withEnvironment('production')
  .withDynamoDBSource({
    tableName: 'feature-flags',
    region: 'us-east-1'
  })
  .withCaching({
    enabled: true,
    ttl: 300000 // 5 minutes
  })
  .withDefaultFlags([
    { name: 'feature-a', value: true },
    { name: 'feature-b', value: false }
  ])
  .build();
Builder Methods
withEnvironment(environment)

Sets the environment for the feature flag configuration.

const config = new FeatureFlagConfigurationBuilder()
  .withEnvironment('staging')
  .build();
withEnvironmentDefaults()

Auto-configures based on NODE_ENV environment variable:

  • production: DynamoDB source with caching enabled
  • development/test: In-memory source
  • Default: In-memory source
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironmentDefaults()
  .build();
withInMemorySource(flags)

Configures an in-memory feature flag source (ideal for development/testing).

const config = new FeatureFlagConfigurationBuilder()
  .withInMemorySource([
    { name: 'debug-mode', value: true },
    { name: 'beta-features', value: false }
  ])
  .build();
withDynamoDBSource(options)

Configures a DynamoDB feature flag source (ideal for production).

const config = new FeatureFlagConfigurationBuilder()
  .withDynamoDBSource({
    tableName: 'my-feature-flags',
    region: 'us-west-2',
    endpoint: 'http://localhost:8000' // Optional for local DynamoDB
  })
  .build();
withCaching(options)

Enables caching for feature flags to improve performance.

const config = new FeatureFlagConfigurationBuilder()
  .withCaching({
    enabled: true,
    ttl: 600000 // 10 minutes
  })
  .build();
withDefaultFlags(flags)

Sets default feature flags that will be available if not found in the primary source.

const config = new FeatureFlagConfigurationBuilder()
  .withDefaultFlags([
    { name: 'fallback-feature', value: true }
  ])
  .build();

Feature Flag Utilities

The FeatureFlags utility class provides convenient methods for working with feature flags:

isEnabled(flagName, defaultValue)

Check if a feature flag is enabled:

import { FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

const isNewUIEnabled = await FeatureFlags.isEnabled('new-ui', false);
if (isNewUIEnabled) {
  // Show new UI
}
getAll()

Get all feature flags:

const allFlags = await FeatureFlags.getAll();
console.log('Current flags:', allFlags);
getMultiple(flagNames, defaultValue)

Check multiple flags at once:

const flags = await FeatureFlags.getMultiple([
  'feature-a',
  'feature-b',
  'feature-c'
], false);

console.log(flags); // { 'feature-a': true, 'feature-b': false, 'feature-c': true }
when(flagName, enabledCallback, disabledCallback, defaultValue)

Conditional execution based on feature flag:

await FeatureFlags.when(
  'new-algorithm',
  async () => {
    console.log('Using new algorithm');
    return await newAlgorithm();
  },
  async () => {
    console.log('Using old algorithm');
    return await oldAlgorithm();
  },
  false
);
middleware(flagNames)

Express/Lambda middleware to inject feature flags into request context:

import express from 'express';
import { FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

const app = express();

// Add all feature flags to req.featureFlags
app.use(FeatureFlags.middleware());

// Or add specific flags only
app.use(FeatureFlags.middleware(['ui-redesign', 'api-v2']));

app.get('/api/data', (req, res) => {
  if (req.featureFlags['api-v2']) {
    return res.json(await getDataV2());
  }
  return res.json(await getDataV1());
});

Storage Backends

The feature flag system supports multiple storage backends, each optimized for different use cases:

In-Memory Storage

Perfect for development and testing:

import { setInMemoryFeatureFlag, getInMemoryFeatureFlag } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withInMemorySource([
    { name: 'debug-mode', value: true },
    { name: 'beta-features', value: false }
  ])
  .build();

// Direct usage
setInMemoryFeatureFlag('debug-mode', true);
const isDebugEnabled = getInMemoryFeatureFlag('debug-mode', false);
DynamoDB Storage

Recommended for production AWS environments:

import { setDynamoFeatureFlag, getDynamoFeatureFlag } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withDynamoDBSource({
    tableName: 'feature-flags-prod',
    region: 'us-east-1'
  })
  .withCaching({ enabled: true, ttl: 300000 })
  .build();

// Direct usage
await setDynamoFeatureFlag('feature-rollout', true);
const isRolloutEnabled = await getDynamoFeatureFlag('feature-rollout', false);

DynamoDB table structure:

{
  "name": "feature-rollout",
  "value": true,
  "description": "Enable the new feature rollout",
  "lastModified": "2024-01-15T10:30:00.000Z"
}
Redis Storage

High-performance distributed caching (requires ioredis):

import { RedisFeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withRedisSource({
    host: 'localhost',
    port: 6379,
    password: 'your-password',
    keyPrefix: 'app:flags:'
  })
  .withCaching({ enabled: true, ttl: 60000 })
  .build();

// Direct usage
await RedisFeatureFlags.setFeatureFlag('new-algorithm', true);
const isEnabled = await RedisFeatureFlags.getFeatureFlag('new-algorithm', false);
Environment Variables Storage

Simple and secure for containerized deployments:

import { setEnvironmentFeatureFlag, getEnvironmentFeatureFlag } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironmentSource('MYAPP_FEATURE_') // Custom prefix
  .build();

// Set environment variables:
// MYAPP_FEATURE_NEW_UI=true
// MYAPP_FEATURE_BETA_MODE=false

// Direct usage
const isNewUIEnabled = getEnvironmentFeatureFlag('new-ui', false);
File-based Storage

Good for deployment-time configuration:

import { setFileFeatureFlag, getFileFeatureFlag } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withFileSource('./config/feature-flags.json', true) // Watch for changes
  .build();

// feature-flags.json structure:
// [
//   { "name": "new-feature", "value": true, "description": "Enable new feature" },
//   { "name": "maintenance-mode", "value": false }
// ]

// Direct usage
const isMaintenanceMode = getFileFeatureFlag('maintenance-mode', false);
await setFileFeatureFlag('new-feature', true);
AWS Parameter Store Storage

Secure, managed configuration (requires @aws-sdk/client-ssm):

import { ParameterStoreFeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withParameterStoreSource({
    region: 'us-east-1',
    prefix: '/myapp/feature-flags/',
    clientConfig: { maxRetries: 3 }
  })
  .withCaching({ enabled: true, ttl: 300000 })
  .build();

// Direct usage
await ParameterStoreFeatureFlags.setFeatureFlag('rollout-phase-1', true);
const isEnabled = await ParameterStoreFeatureFlags.getFeatureFlag('rollout-phase-1', false);
Database Storage

For applications already using a database:

import { DatabaseFeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration (assuming you have a database connection)
const config = new FeatureFlagConfigurationBuilder()
  .withDatabaseSource(databaseConnection, 'app_feature_flags')
  .withCaching({ enabled: true, ttl: 120000 })
  .build();

// Initialize table (run once)
await DatabaseFeatureFlags.initializeTable();

// Direct usage
await DatabaseFeatureFlags.setFeatureFlag('premium-features', true, 'Enable premium tier');
const isPremiumEnabled = await DatabaseFeatureFlags.getFeatureFlag('premium-features', false);

// Extra database features
await DatabaseFeatureFlags.enableFeatureFlag('premium-features', false); // Disable without deleting
const flagDetails = await DatabaseFeatureFlags.getFeatureFlagDetails('premium-features');

Database table structure:

CREATE TABLE feature_flags (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) UNIQUE NOT NULL,
    value BOOLEAN NOT NULL,
    description TEXT,
    enabled BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
HTTP Remote Service Storage

For centralized feature flag management:

import { HttpFeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Configuration
const config = new FeatureFlagConfigurationBuilder()
  .withHttpSource('https://feature-flags.mycompany.com/api/v1', {
    apiKey: process.env.FEATURE_FLAGS_API_KEY,
    timeout: 5000,
    headers: {
      'X-App-Name': 'my-application'
    }
  })
  .withCaching({ enabled: true, ttl: 180000 })
  .build();

// Direct usage
await HttpFeatureFlags.setFeatureFlag('global-rollout', true);
const isEnabled = await HttpFeatureFlags.getFeatureFlag('global-rollout', false);

// Bulk operations
await HttpFeatureFlags.setMultipleFlags([
  { name: 'feature-a', value: true },
  { name: 'feature-b', value: false }
]);
Storage Backend Comparison

| Backend | Best For | Performance | Setup Complexity | Dependencies | |---------|----------|-------------|------------------|--------------| | In-Memory | Development, Testing | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | None | | Environment Variables | Containers, Serverless | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | None | | File-based | Deployment-time config | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | None | | DynamoDB | AWS Production | ⭐⭐⭐⭐ | ⭐⭐⭐ | AWS SDK | | Redis | High-traffic, Distributed | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ioredis | | Parameter Store | Enterprise AWS | ⭐⭐⭐ | ⭐⭐ | AWS SDK | | Database | Existing DB apps | ⭐⭐⭐ | ⭐⭐ | Database | | HTTP Service | Centralized management | ⭐⭐⭐ | ⭐ | HTTP client |

Advanced Configuration Examples

Multi-Environment Factory Pattern
import { FeatureFlagConfigurationBuilder } from '@timmons-group/snack-pack/FeatureFlags';

export const createConfigForEnvironment = (environment, options = {}) => {
  const builder = new FeatureFlagConfigurationBuilder()
    .withEnvironment(environment);
  
  switch (environment) {
    case 'development':
    case 'test':
      return builder
        .withInMemorySource(options.developmentFlags || [
          { name: 'debug-ui', value: true },
          { name: 'mock-data', value: true },
          { name: 'performance-logging', value: true }
        ])
        .build();
        
    case 'staging':
      if (options.useFile) {
        return builder
          .withFileSource('./feature-flags-staging.json')
          .withCaching({ enabled: true, ttl: 60000 })
          .build();
      } else {
        return builder
          .withDynamoDBSource({
            tableName: options.tableName || 'feature-flags-staging',
            region: options.region
          })
          .withCaching({ enabled: true, ttl: 60000 })
          .build();
      }
      
    case 'production':
      if (options.useParameterStore) {
        return builder
          .withParameterStoreSource({
            prefix: '/app/feature-flags/',
            region: options.region
          })
          .withCaching({ enabled: true, ttl: 300000 })
          .build();
      } else if (options.useRedis) {
        return builder
          .withRedisSource(options.redis || {})
          .withCaching({ enabled: true, ttl: 180000 })
          .build();
      } else {
        return builder
          .withDynamoDBSource({
            tableName: 'feature-flags-production',
            region: options.region
          })
          .withCaching({ enabled: true, ttl: 300000 })
          .build();
      }
      
    default:
      throw new Error(`Unknown environment: ${environment}`);
  }
};
Smart Initialization with Fallbacks
import { setGlobalFeatureFlagConfig } from '@timmons-group/snack-pack/FeatureFlags';

export const initializeFeatureFlags = async () => {
  try {
    const environment = process.env.NODE_ENV || 'development';
    
    // Try environment-specific configuration
    const config = createConfigForEnvironment(environment, {
      region: process.env.AWS_REGION,
      tableName: process.env.FEATURE_FLAGS_TABLE,
      useParameterStore: process.env.USE_PARAMETER_STORE === 'true',
      useRedis: process.env.USE_REDIS === 'true',
      redis: {
        host: process.env.REDIS_HOST,
        port: process.env.REDIS_PORT,
        password: process.env.REDIS_PASSWORD
      }
    });
    
    setGlobalFeatureFlagConfig(config);
    console.log(`Feature flags initialized for ${environment} environment`);
    
    return config;
    
  } catch (error) {
    console.error('Failed to initialize feature flags:', error);
    
    // Fallback to safe in-memory configuration
    const fallbackConfig = new FeatureFlagConfigurationBuilder()
      .withInMemorySource([
        { name: 'fallback-mode', value: true }
      ])
      .build();
      
    setGlobalFeatureFlagConfig(fallbackConfig);
    console.warn('Using fallback in-memory feature flags configuration');
    
    return fallbackConfig;
  }
};

// Usage in your application
await initializeFeatureFlags();
Container/Docker Configuration
// Perfect for containerized applications
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironmentSource('APP_FEATURE_')
  .withDefaultFlags([
    { name: 'health-checks', value: true },
    { name: 'metrics-enabled', value: true }
  ])
  .build();

// Set via environment variables:
// APP_FEATURE_NEW_API=true
// APP_FEATURE_BETA_UI=false
Microservices Configuration
// Service-specific feature flags via HTTP
const config = new FeatureFlagConfigurationBuilder()
  .withHttpSource('https://flags.company.com/api/v1', {
    apiKey: process.env.FLAGS_API_KEY,
    headers: {
      'X-Service-Name': process.env.SERVICE_NAME,
      'X-Service-Version': process.env.SERVICE_VERSION
    }
  })
  .withCaching({ enabled: true, ttl: 120000 })
  .build();
High-Performance Configuration
// Redis with aggressive caching for high-traffic apps
const config = new FeatureFlagConfigurationBuilder()
  .withRedisSource({
    host: process.env.REDIS_HOST,
    port: 6379,
    keyPrefix: 'flags:',
    retryDelayOnFailover: 100
  })
  .withCaching({ enabled: true, ttl: 30000 }) // 30 second local cache
  .withDefaultFlags([
    { name: 'circuit-breaker', value: false }
  ])
  .build();

Environment-Based Configuration

Set up different configurations for different environments:

// config/feature-flags.js
import { FeatureFlagConfigurationBuilder } from '@timmons-group/snack-pack/FeatureFlags';

const getFeatureFlagConfig = () => {
  const builder = new FeatureFlagConfigurationBuilder();
  
  switch (process.env.NODE_ENV) {
    case 'production':
      return builder
        .withEnvironment('production')
        .withDynamoDBSource({
          tableName: process.env.FEATURE_FLAGS_TABLE || 'feature-flags-prod',
          region: process.env.AWS_REGION || 'us-east-1'
        })
        .withCaching({ enabled: true, ttl: 300000 })
        .build();
        
    case 'staging':
      return builder
        .withEnvironment('staging')
        .withDynamoDBSource({
          tableName: 'feature-flags-staging',
          region: 'us-east-1'
        })
        .withCaching({ enabled: true, ttl: 60000 })
        .withDefaultFlags([
          { name: 'staging-features', value: true }
        ])
        .build();
        
    default: // development/test
      return builder
        .withEnvironment('development')
        .withInMemorySource([
          { name: 'debug-mode', value: true },
          { name: 'mock-services', value: true },
          { name: 'detailed-logging', value: true }
        ])
        .build();
  }
};

export default getFeatureFlagConfig();

Integration Examples

AWS Lambda Function with Parameter Store
import { FeatureFlagConfigurationBuilder, FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Initialize feature flags with Parameter Store for production
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironment(process.env.NODE_ENV || 'development')
  .withEnvironmentDefaults({
    productionTableName: 'lambda-feature-flags',
    region: process.env.AWS_REGION
  })
  .withDefaultFlags([
    { name: 'error-tracking', value: true }
  ])
  .build();

export const handler = async (event) => {
  try {
    // Check feature flags
    const useNewProcessor = await FeatureFlags.isEnabled('new-processor', false);
    const enableDetailedLogging = await FeatureFlags.isEnabled('detailed-logging', false);
    
    if (enableDetailedLogging) {
      console.log('Processing event:', JSON.stringify(event, null, 2));
    }
    
    let result;
    if (useNewProcessor) {
      result = await processWithNewAlgorithm(event);
    } else {
      result = await processWithLegacyAlgorithm(event);
    }
    
    return {
      statusCode: 200,
      body: JSON.stringify(result)
    };
  } catch (error) {
    const enableErrorReporting = await FeatureFlags.isEnabled('error-tracking', true);
    if (enableErrorReporting) {
      // Report error to monitoring service
      await reportError(error);
    }
    throw error;
  }
};
Express.js Application with Redis
import express from 'express';
import { FeatureFlagConfigurationBuilder, FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

const app = express();

// Initialize feature flags with Redis for high performance
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironment('production')
  .withRedisSource({
    host: process.env.REDIS_HOST || 'localhost',
    port: process.env.REDIS_PORT || 6379,
    password: process.env.REDIS_PASSWORD
  })
  .withCaching({ enabled: true, ttl: 30000 }) // 30 second local cache
  .withDefaultFlags([
    { name: 'maintenance-mode', value: false },
    { name: 'rate-limiting', value: true }
  ])
  .build();

// Maintenance mode middleware
app.use(async (req, res, next) => {
  const isMaintenanceMode = await FeatureFlags.isEnabled('maintenance-mode', false);
  if (isMaintenanceMode && !req.path.startsWith('/health')) {
    return res.status(503).json({ 
      message: 'Service temporarily unavailable for maintenance' 
    });
  }
  next();
});

// Feature flag middleware
app.use(FeatureFlags.middleware(['enhanced-user-data', 'new-api-version', 'beta-features']));

// Rate limiting based on feature flag
app.use(async (req, res, next) => {
  const enableRateLimit = await FeatureFlags.isEnabled('rate-limiting', true);
  if (enableRateLimit) {
    // Apply rate limiting logic
    return rateLimitMiddleware(req, res, next);
  }
  next();
});

app.get('/api/users', async (req, res) => {
  const users = req.featureFlags['enhanced-user-data'] 
    ? await getUsersWithExtendedData()
    : await getBasicUsers();
    
  res.json(users);
});

app.get('/api/v2/data', async (req, res) => {
  const useNewAPI = req.featureFlags['new-api-version'];
  if (!useNewAPI) {
    return res.status(404).json({ message: 'API version not available' });
  }
  
  const data = await getV2Data();
  res.json(data);
});
Microservice with Environment Variables
import { FeatureFlagConfigurationBuilder, FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

// Perfect for Docker/Kubernetes deployments
const config = new FeatureFlagConfigurationBuilder()
  .withEnvironmentSource('SERVICE_FEATURE_')
  .withDefaultFlags([
    { name: 'health-check', value: true },
    { name: 'metrics-collection', value: true }
  ])
  .build();

// Environment variables:
// SERVICE_FEATURE_CIRCUIT_BREAKER=true
// SERVICE_FEATURE_ASYNC_PROCESSING=false

class OrderService {
  async processOrder(order) {
    const useCircuitBreaker = await FeatureFlags.isEnabled('circuit-breaker', false);
    const useAsyncProcessing = await FeatureFlags.isEnabled('async-processing', false);
    
    if (useCircuitBreaker) {
      return await this.processWithCircuitBreaker(order);
    }
    
    if (useAsyncProcessing) {
      return await this.processOrderAsync(order);
    }
    
    return await this.processOrderSync(order);
  }
  
  async processWithCircuitBreaker(order) {
    // Circuit breaker implementation
  }
  
  async processOrderAsync(order) {
    // Queue for async processing
  }
  
  async processOrderSync(order) {
    // Direct synchronous processing
  }
}
Next.js Application with File-based Config
// config/feature-flags.js
import { FeatureFlagConfigurationBuilder } from '@timmons-group/snack-pack/FeatureFlags';

const isDevelopment = process.env.NODE_ENV === 'development';

const config = new FeatureFlagConfigurationBuilder()
  .withEnvironment(process.env.NODE_ENV)
  .withFileSource(
    isDevelopment 
      ? './config/feature-flags-dev.json'
      : './config/feature-flags-prod.json',
    isDevelopment // Watch for changes in development
  )
  .withCaching({ enabled: !isDevelopment, ttl: 60000 })
  .build();

export default config;

// pages/api/users.js
import { FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

export default async function handler(req, res) {
  const enableNewUserFlow = await FeatureFlags.isEnabled('new-user-flow', false);
  const enableUserAnalytics = await FeatureFlags.isEnabled('user-analytics', true);
  
  if (enableNewUserFlow) {
    // New user registration flow
    const result = await handleNewUserFlow(req.body);
    
    if (enableUserAnalytics) {
      await trackUserRegistration(result.userId);
    }
    
    return res.json(result);
  }
  
  // Legacy user flow
  return res.json(await handleLegacyUserFlow(req.body));
}

// components/FeatureFlag.jsx
import { useEffect, useState } from 'react';
import { FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

export function FeatureFlag({ flag, fallback = null, children }) {
  const [isEnabled, setIsEnabled] = useState(false);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    FeatureFlags.isEnabled(flag, false)
      .then(setIsEnabled)
      .finally(() => setLoading(false));
  }, [flag]);
  
  if (loading) return fallback;
  return isEnabled ? children : fallback;
}

// Usage in component
<FeatureFlag flag="new-dashboard" fallback={<LegacyDashboard />}>
  <NewDashboard />
</FeatureFlag>
Database Integration Example
import { DatabaseFeatureFlags, FeatureFlagConfigurationBuilder } from '@timmons-group/snack-pack/FeatureFlags';
import { getDatabaseConnection } from './database.js';

// Initialize database-backed feature flags
const db = await getDatabaseConnection();
await DatabaseFeatureFlags.initializeTable(); // Run once

const config = new FeatureFlagConfigurationBuilder()
  .withDatabaseSource(db, 'app_feature_flags')
  .withCaching({ enabled: true, ttl: 120000 })
  .build();

// Admin API for managing feature flags
app.post('/admin/feature-flags', async (req, res) => {
  const { name, value, description } = req.body;
  
  try {
    await DatabaseFeatureFlags.setFeatureFlag(name, value, description);
    res.json({ success: true, message: `Feature flag '${name}' updated` });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.get('/admin/feature-flags', async (req, res) => {
  try {
    const flags = await DatabaseFeatureFlags.allFeatureFlags();
    res.json(flags);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.patch('/admin/feature-flags/:name/toggle', async (req, res) => {
  const { name } = req.params;
  const { enabled } = req.body;
  
  try {
    await DatabaseFeatureFlags.enableFeatureFlag(name, enabled);
    res.json({ success: true, message: `Feature flag '${name}' ${enabled ? 'enabled' : 'disabled'}` });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
React Hook for Feature Flags
// hooks/useFeatureFlag.js
import { useState, useEffect } from 'react';
import { FeatureFlags } from '@timmons-group/snack-pack/FeatureFlags';

export function useFeatureFlag(flagName, defaultValue = false) {
  const [isEnabled, setIsEnabled] = useState(defaultValue);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let mounted = true;
    
    FeatureFlags.isEnabled(flagName, defaultValue)
      .then(enabled => {
        if (mounted) {
          setIsEnabled(enabled);
          setError(null);
        }
      })
      .catch(err => {
        if (mounted) {
          setError(err);
          setIsEnabled(defaultValue); // Fallback to default
        }
      })
      .finally(() => {
        if (mounted) {
          setLoading(false);
        }
      });
    
    return () => { mounted = false; };
  }, [flagName, defaultValue]);
  
  return { isEnabled, loading, error };
}

// Usage in component
function UserDashboard() {
  const { isEnabled: showNewFeatures, loading } = useFeatureFlag('new-user-features');
  const { isEnabled: enableAnalytics } = useFeatureFlag('user-analytics', true);
  
  useEffect(() => {
    if (enableAnalytics) {
      trackPageView('user-dashboard');
    }
  }, [enableAnalytics]);
  
  if (loading) return <div>Loading...</div>;
  
  return (
    <div>
      <h1>Dashboard</h1>
      {showNewFeatures && <NewFeaturePanel />}
      <StandardDashboard />
    </div>
  );
}

GraphQL Integration

SNACK provides comprehensive GraphQL utilities for Apollo Server integration, including directives for access control, feature flags, and environment-specific functionality.

Quick Start

import { ApolloServer } from '@apollo/server';
import { 
  createSNACKApolloConfig,
  createSNACKApolloContext,
  usePermissionFilter 
} from '@timmons-group/snack-pack/GraphQL';
import { gql } from 'graphql-tag';

const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    
    # Admin-only field
    sensitiveData: String @acl(permission: "admin")
    
    # Development environment only
    debugInfo: String @development
    
    # Feature flag controlled
    betaFeature: String @featureFlag(name: "user-beta-features")
  }

  type Query {
    me: User
    adminUsers: [User!]! @acl(permission: "admin")
  }
`;

const resolvers = {
  Query: {
    me: (parent, args, context) => context.user,
    
    adminUsers: usePermissionFilter(
      'admin',
      async () => await getAllUsers()
    )
  }
};

// Create server with SNACK integration
const server = new ApolloServer(
  createSNACKApolloConfig({
    typeDefs,
    resolvers,
    snackOptions: {
      includeUser: true,
      includeFeatureFlags: true
    }
  })
);

Available Directives

@acl - Access Control

Restrict fields and operations based on user permissions:

type Query {
  # Requires specific permission
  sensitiveData: String @acl(permission: "admin")
  
  # Requires any of multiple permissions
  moderationTools: [String!]! @acl(permissions: ["moderator", "admin"])
  
  # Requires all permissions
  superAdminData: String @acl(permissions: ["admin", "super"], requireAll: true)
  
  # Group-based access
  internalAPI: String @acl(groups: ["internal-team"])
}

@featureFlag - Feature Flags

Control field availability based on feature flags:

type User {
  # Basic feature flag check
  experimentalField: String @featureFlag(name: "experimental-features")
  
  # Require specific flag value
  premiumFeature: String @featureFlag(name: "premium-tier", value: true)
  
  # Multiple flag dependencies
  advancedFeature: String @featureFlag(
    name: "advanced-features"
    dependencies: ["base-features", "user-tier-premium"]
  )
}

@development - Environment Control

Hide fields in production environments:

type Query {
  # Only available in development
  debugEndpoint: String @development
  
  # Development and staging only
  testingTools: [String!]! @development(environments: ["development", "staging"])
}

type User {
  # Debug information for development
  internalState: UserDebugInfo @development
}

type UserDebugInfo @development {
  createdAt: String!
  lastQuery: String
  cacheStats: CacheStats
}

Context Integration

The SNACK context automatically provides user information and feature flags:

import { createSNACKApolloContext } from '@timmons-group/snack-pack/GraphQL';

const context = createSNACKApolloContext({
  includeUser: true,
  includeFeatureFlags: true,
  
  // Custom context merger
  customContext: async ({ req }) => ({
    requestId: req.headers['x-request-id'],
    clientVersion: req.headers['x-client-version']
  })
});

// In resolvers, access SNACK context:
const resolvers = {
  Query: {
    profile: async (parent, args, context) => {
      // User from JWT token
      console.log(context.user.id, context.user.permissions);
      
      // Feature flags for user
      const flags = context.featureFlags || [];
      const newUI = flags.find(f => f.name === 'new-ui')?.value;
      
      // Custom context
      console.log(context.requestId);
      
      return await getProfile(context.user.id, { newUI });
    }
  }
};

Permission Helpers

Use permission helper functions for complex authorization logic:

import { 
  usePermissionFilter,
  useRoleFilter,
  useOwnerFilter,
  useCompositeFilter 
} from '@timmons-group/snack-pack/GraphQL';

const resolvers = {
  Query: {
    // Simple permission check
    adminData: usePermissionFilter(
      'admin',
      async () => await getAdminData()
    ),
    
    // Multiple roles
    moderationTools: useRoleFilter(
      ['moderator', 'admin', 'super-admin'],
      async () => await getModerationTools()
    ),
    
    // Owner-based access
    userDocuments: useOwnerFilter(
      'userId', // field containing owner ID
      async (parent, args) => await getUserDocs(args.userId)
    ),
    
    // Complex permission logic
    sensitiveOperation: useCompositeFilter({
      permissions: { all: ['read:sensitive', 'access:internal'] },
      roles: { any: ['admin', 'super-admin'] },
      groups: { any: ['internal-team'] },
      custom: async (user, args) => {
        return user.department === 'security' && user.clearanceLevel >= 3;
      }
    }, async () => await getSensitiveData())
  }
};

Express Integration

For Express-based Apollo servers:

import express from 'express';
import { expressMiddleware } from '@apollo/server/express4';
import { createSNACKApolloContext } from '@timmons-group/snack-pack/GraphQL';

const app = express();
const server = new ApolloServer({
  typeDefs,
  resolvers,
  schemaTransforms: createSNACKSchemaTransforms()
});

await server.start();

app.use(
  '/graphql',
  cors(),
  express.json(),
  expressMiddleware(server, {
    context: createSNACKApolloContext({
      includeUser: true,
      includeFeatureFlags: true
    })
  })
);

Lambda/Serverless Integration

For serverless deployments:

import { ApolloServer } from '@apollo/server';
import { handlers } from '@apollo/server-lambda';
import { createSNACKApolloConfig } from '@timmons-group/snack-pack/GraphQL';

const server = new ApolloServer(
  createSNACKApolloConfig({
    typeDefs,
    resolvers
  })
);

export const graphqlHandler = handlers.createAPIGatewayProxyEventRequestHandler({
  server
});

Advanced Schema Composition

For modular GraphQL applications:

import { createSNACKSchemaTransforms } from '@timmons-group/snack-pack/GraphQL';

// Apply SNACK transforms to existing schema
const schema = buildSchema({
  typeDefs: [directiveDefs, ...yourTypeDefs],
  resolvers: yourResolvers
});

const transformedSchema = createSNACKSchemaTransforms({
  enableDevelopment: process.env.NODE_ENV === 'development',
  enableACL: true,
  enableFeatureFlags: true
})(schema);

See GraphQL examples for comprehensive integration patterns and use cases.

Roadmap

  • [x] REST API providers
  • [x] Endpoint builder pattern
  • [x] OAuth authentication flow
  • [x] Permission system
  • [x] Feature flags system
  • [x] GraphQL support
  • [x] Error handling
  • [ ] WebSocket support
  • [ ] Rate limiting
  • [ ] API versioning
  • [ ] Request validation middleware
  • [ ] Response caching
  • [ ] Metrics and monitoring