Skip to Content
Server-Side Code ExamplesNode.js

Node.js

Node.js integration for server-side applications and APIs using the JavaScript SDK.

Requirements

  • Node.js 16+
  • NPM, Yarn, or PNPM

Installation

npm install @darkfeature/sdk-javascript # or yarn add @darkfeature/sdk-javascript # or pnpm add @darkfeature/sdk-javascript

Basic Usage

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: 'your-api-key', // Store in environment variable context: { userId: '123', email: '[email protected]', version: '1.0.0', }, }) // Get a single feature flag const isEnabled = await client.getFeature('new-navigation', { fallback: false }) console.log(`Feature is ${isEnabled ? 'enabled' : 'disabled'}`)

Express.js Middleware

Integrate feature flags into your Express.js application:

import express from 'express' import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const app = express() const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) // Middleware to add feature flags to requests app.use(async (req, res, next) => { const context = { userId: req.user?.id, email: req.user?.email, userAgent: req.get('User-Agent'), } req.features = await client.getFeatures({ features: { 'new-navigation': false, 'advanced-analytics': false, 'beta-ui': false, }, context, }) next() }) app.get('/api/data', async (req, res) => { if (req.features['new-navigation']) { // New implementation res.json({ data: 'enhanced-data', version: 'v2' }) } else { // Legacy implementation res.json({ data: 'basic-data', version: 'v1' }) } }) app.listen(3000, () => { console.log('Server running on port 3000') })

TypeScript with Fastify

Use with Fastify and TypeScript for type-safe feature flag integration:

import Fastify, { FastifyRequest, FastifyReply } from 'fastify' import { DarkFeatureClient } from '@darkfeature/sdk-javascript' interface UserContext { userId?: string email?: string plan: 'free' | 'pro' | 'enterprise' } const fastify = Fastify({ logger: true }) const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) // Declare feature flags interface declare module 'fastify' { interface FastifyRequest { features: { 'new-navigation': boolean 'premium-support': boolean 'advanced-metrics': boolean } } } // Plugin to add feature flags fastify.addHook( 'preHandler', async (request: FastifyRequest, reply: FastifyReply) => { const context: UserContext = { userId: request.user?.id, email: request.user?.email, plan: request.user?.plan || 'free', } request.features = await client.getFeatures({ features: { 'new-navigation': false, 'premium-support': false, 'advanced-metrics': false, }, context, }) }, ) fastify.get('/api/dashboard', async (request, reply) => { const data: any = { dashboard: 'basic' } if (request.features['new-navigation']) { data.enhanced = true data.charts = ['revenue', 'users', 'engagement'] } if (request.features['advanced-metrics']) { data.metrics = { realtime: true, historical: true } } return data }) fastify.listen({ port: 3000 }, err => { if (err) throw err console.log('Server running on port 3000') })

Advanced Configuration

Environment Variables

// .env DARKFEATURE_API_KEY=your-server-side-api-key DARKFEATURE_BASE_URL=https://api.darkfeature.com DARKFEATURE_TIMEOUT=5000 // app.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, baseUrl: process.env.DARKFEATURE_BASE_URL, timeout: parseInt(process.env.DARKFEATURE_TIMEOUT) || 5000, cache: { enabled: true, ttl: 300000, // 5 minutes maxSize: 1000, }, debug: process.env.NODE_ENV === 'development', })

Error Handling

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) async function getFeatureWithFallback(featureName, fallback = false) { try { return await client.getFeature(featureName, { fallback }) } catch (error) { console.error(`Error getting feature ${featureName}:`, error) return fallback } } // Usage const isEnabled = await getFeatureWithFallback('new-feature', false)

Server-Side Rendering (Next.js)

// pages/api/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) export default async function handler(req, res) { const { userId, email } = req.query const features = await client.getFeatures({ features: { 'new-navigation': false, 'premium-features': false, }, context: { userId, email, }, }) res.json(features) } // pages/index.js export async function getServerSideProps(context) { const user = getUserFromContext(context) // Pre-fetch feature flags on server const features = await fetch( '/api/features?' + new URLSearchParams({ userId: user.id, email: user.email, }), ).then(res => res.json()) return { props: { user, features, }, } }

Testing

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' // Mock client for testing class MockDarkFeatureClient extends DarkFeatureClient { constructor(mockFlags = {}) { super({ apiKey: 'test-key' }) this.mockFlags = mockFlags } async getFeature(featureName, options = {}) { return this.mockFlags[featureName] ?? options.fallback } async getFeatures(features, options = {}) { const result = {} for (const [key, fallback] of Object.entries(features)) { result[key] = this.mockFlags[key] ?? fallback } return result } } // Test usage const mockClient = new MockDarkFeatureClient({ 'new-navigation': true, 'premium-features': false, }) const isEnabled = await mockClient.getFeature('new-navigation', { fallback: false, }) console.log(isEnabled) // true

Performance Optimization

Caching

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, cache: { enabled: true, ttl: 300000, // 5 minutes maxSize: 1000, }, }) // Features will be cached automatically const isEnabled = await client.getFeature('new-navigation', { fallback: false })

Batch Requests

// Get multiple features in one request const features = await client.getFeatures({ features: { 'feature-a': false, 'feature-b': false, 'feature-c': false, }, context: { userId: '123' }, }) // Use features if (features['feature-a']) { // Feature A logic } if (features['feature-b']) { // Feature B logic }

Best Practices

1. Environment Configuration

// config/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const createFeatureClient = () => { return new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, baseUrl: process.env.DARKFEATURE_BASE_URL, timeout: parseInt(process.env.DARKFEATURE_TIMEOUT) || 5000, cache: { enabled: process.env.NODE_ENV === 'production', ttl: 300000, maxSize: 1000, }, debug: process.env.NODE_ENV === 'development', }) } export default createFeatureClient

2. Middleware Pattern

// middleware/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) export const featureFlagsMiddleware = featureKeys => { return async (req, res, next) => { try { const context = { userId: req.user?.id, email: req.user?.email, userAgent: req.get('User-Agent'), ip: req.ip, } req.features = await client.getFeatures({ features: featureKeys, context, }) next() } catch (error) { console.error('Feature flags error:', error) // Use fallback values req.features = Object.fromEntries( Object.keys(featureKeys).map(key => [key, featureKeys[key]]), ) next() } } } // Usage app.use( featureFlagsMiddleware({ 'new-navigation': false, 'premium-features': false, }), )

3. Error Handling

// utils/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, }) export async function getFeatureSafely(featureName, fallback = false) { try { return await client.getFeature(featureName, { fallback }) } catch (error) { console.error(`Feature flag error for ${featureName}:`, error) return fallback } } export async function getFeaturesSafely(features, context = {}) { try { return await client.getFeatures({ features, context }) } catch (error) { console.error('Feature flags error:', error) return features // Return fallback values } }

Security Considerations

API Key Management

// Never expose server-side API keys to clients const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_API_KEY, // ✅ Secure // apiKey: 'hardcoded-key', // ❌ Never do this })

User Context Validation

// Validate and sanitize user context const sanitizeContext = (user) => { return { userId: user?.id?.toString(), email: user?.email?.toLowerCase(), plan: ['free', 'pro', 'enterprise'].includes(user?.plan) ? user.plan : 'free', } } const context = sanitizeContext(req.user) const features = await client.getFeatures({ features: {...}, context })

Next Steps

Last updated on