@savvagent/node-server
v1.0.1
Published
Savvagent Node.js Server SDK for feature flags with AI-powered error detection
Maintainers
Readme
@savvagent/node-server
Official Node.js Server SDK for Savvagent - AI-powered feature flags with automatic error detection.
Features
- 🚀 Fast Evaluation: In-memory caching with configurable TTL
- 🔄 Real-time Updates: Automatic cache invalidation via Server-Sent Events
- 📊 Telemetry: Automatic tracking of flag evaluations and errors
- 🤖 AI Error Detection: Correlate errors with flag changes
- 📦 TypeScript: Full type safety with TypeScript definitions
- 🌐 Server-Optimized: Built specifically for Node.js server environments
Installation
npm install @savvagent/node-server
# or
yarn add @savvagent/node-server
# or
pnpm add @savvagent/node-serverQuick Start
import { FlagClient } from '@savvagent/node-server';
// Initialize the client
const client = new FlagClient({
apiKey: 'sdk_your_api_key_here',
applicationId: 'your-app-id', // optional
});
// Evaluate a flag
const result = await client.evaluate('new-feature', {
user_id: 'user-123',
environment: 'production',
});
console.log(`Feature enabled: ${result.value}`);
// Or use the convenience method
const isEnabled = await client.isEnabled('new-feature', {
user_id: 'user-123',
});Configuration
const client = new FlagClient({
apiKey: 'sdk_your_api_key_here',
applicationId: 'your-app-id',
baseUrl: 'https://api.savvagent.com', // optional
enableRealtime: true, // default: true
cacheTtl: 60000, // default: 60 seconds
enableTelemetry: true, // default: true
timeout: 5000, // request timeout in ms, default: 5000
defaults: {
'feature-a': false,
'feature-b': true,
},
onError: (error) => {
console.error('Savvagent error:', error);
},
});Usage Examples
Basic Flag Evaluation
// Simple boolean check
const enabled = await client.isEnabled('premium-features', {
user_id: req.user.id,
});
if (enabled) {
// Premium features code
}With Request Context
import express from 'express';
app.get('/api/data', async (req, res) => {
const useNewAlgorithm = await client.isEnabled('new-algorithm', {
user_id: req.user?.id,
session_id: req.sessionID,
ip_address: req.ip,
user_agent: req.headers['user-agent'],
environment: process.env.NODE_ENV,
attributes: {
plan: req.user?.plan,
country: req.user?.country,
},
});
if (useNewAlgorithm) {
res.json(await newAlgorithm());
} else {
res.json(await oldAlgorithm());
}
});Error Tracking
const featureEnabled = await client.isEnabled('new-payment-flow');
try {
if (featureEnabled) {
await processPaymentV2(order);
} else {
await processPaymentV1(order);
}
} catch (error) {
// Track error with flag context for AI correlation
client.trackError('new-payment-flow', error, {
user_id: order.userId,
attributes: { orderId: order.id },
});
throw error;
}Real-time Updates
// Subscribe to flag changes
const unsubscribe = client.subscribe('feature-toggle', () => {
console.log('Feature toggle changed!');
// Invalidation happens automatically
});
// Unsubscribe when done
process.on('SIGTERM', () => {
unsubscribe();
client.close();
});Express Middleware
import express from 'express';
// Create a middleware for flag evaluation
const flagMiddleware = (flagKey: string) => {
return async (req, res, next) => {
const enabled = await client.isEnabled(flagKey, {
user_id: req.user?.id,
session_id: req.sessionID,
});
req.flagEnabled = enabled;
next();
};
};
app.get('/premium-feature', flagMiddleware('premium-access'), (req, res) => {
if (!req.flagEnabled) {
return res.status(403).json({ error: 'Premium access required' });
}
res.json({ data: premiumData });
});API Reference
FlagClient
Constructor
new FlagClient(config: FlagClientConfig)Methods
evaluate(flagKey, context?)
Evaluate a feature flag and return detailed results.
const result = await client.evaluate('my-flag', { user_id: '123' });
// Returns: { key, value, reason, metadata? }isEnabled(flagKey, context?)
Check if a flag is enabled (returns boolean).
const enabled = await client.isEnabled('my-flag', { user_id: '123' });trackError(flagKey, error, context?)
Track an error that occurred in flagged code.
client.trackError('my-flag', new Error('Something failed'), {
user_id: '123',
});subscribe(flagKey, callback)
Subscribe to real-time flag updates.
const unsubscribe = client.subscribe('my-flag', () => {
console.log('Flag updated!');
});invalidateCache(flagKey?)
Manually invalidate cache for a flag or all flags.
client.invalidateCache('my-flag'); // Specific flag
client.invalidateCache(); // All flagsclose()
Clean up resources (flush telemetry, close connections).
client.close();Best Practices
- Reuse Client Instance: Create one client instance and reuse it across your application
- Error Handling: Always wrap flagged code with error tracking
- Context: Provide rich context for better targeting
- Cleanup: Call
client.close()on shutdown - Defaults: Set sensible defaults for all flags
Examples
NestJS Integration
import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { FlagClient } from '@savvagent/node-server';
@Injectable()
export class FlagsService implements OnModuleDestroy {
private client: FlagClient;
constructor() {
this.client = new FlagClient({
apiKey: process.env.SAVVAGENT_API_KEY,
applicationId: process.env.APP_ID,
});
}
async isEnabled(flagKey: string, userId?: string) {
return this.client.isEnabled(flagKey, { user_id: userId });
}
onModuleDestroy() {
this.client.close();
}
}License
MIT
Support
- Documentation: https://docs.savvagent.com
- Issues: https://github.com/savvagent/savvagent-sdks/issues
- Email: [email protected]
