zikit-js
v1.0.1
Published
Data that blends in. A self-healing Express middleware that uses AI (Claude) to automatically repair malformed data and caches transformations for zero-latency future requests.
Maintainers
Readme
ZIKIT 🦎
Data that blends in.
A self-healing Express middleware that automatically repairs malformed data using AI (Claude) and caches transformations for zero-latency future requests.
Features
- AI-Powered Repair - Uses Claude to intelligently fix data that doesn't match your Zod schema
- Zero-Latency Caching - Caches repair patterns so repeated requests are instant
- Strict Fields - Designate fields that must match exactly (no AI repair)
- Configurable - Global config file + per-route overrides
- Type-Safe - Full TypeScript support with Zod schemas
- Express Ready - Drop-in middleware for Express.js
Installation
npm install zikit-jsQuick Start
1. Set up your API key
Create a zikit.config.json file in your project root:
{
"apiKey": "your-anthropic-api-key-here",
"strictFields": ["id", "userId"]
}Or use environment variables:
export ANTHROPIC_API_KEY="your-key-here"2. Use the middleware
import express from 'express';
import { zikit } from 'zikit-js';
import { z } from 'zod';
const app = express();
app.use(express.json());
// Define your schema
const UserSchema = z.object({
id: z.string(), // Strict field - must match exactly
name: z.string(), // Can be repaired
age: z.number(), // Can be repaired
email: z.string().email(),
});
// Use zikit middleware
app.post('/users', zikit(UserSchema), (req, res) => {
// req.body is now guaranteed to match UserSchema!
// It was either already valid, or AI repaired it automatically
res.json({ success: true, user: req.body });
});How It Works
- Request arrives with potentially bad data:
{ name: "John", age: "30" } - Check cache → If we've seen this structure before, use cached repair (instant!)
- Validate strict fields → If any strict field fails, reject immediately
- Call AI → Claude repairs the data:
{ name: "John", age: 30 } - Cache the repair → Save for next time (same structure = instant response)
- Return clean data →
req.bodyis now validated/repaired
Configuration
Config File (zikit.config.json)
{
"apiKey": "your-anthropic-api-key",
"model": "claude-sonnet-4-20250514",
"maxTokens": 1024,
"strictFields": ["id", "userId", "email"]
}Per-Route Configuration
app.post('/users', zikit(UserSchema, {
strictFields: ['id'], // Override global strictFields
adapter: {
apiKey: 'custom-key', // Override API key
timeout: 60000, // 60 second timeout
retries: 2, // Retry twice on failure
retryDelay: 2000, // Wait 2s between retries
},
memory: {
maxSize: 1000, // Cache up to 1000 entries
ttl: 3600000, // 1 hour TTL
},
}), handler);Configuration Options
Global Config (zikit.config.json)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | process.env.ANTHROPIC_API_KEY | Anthropic API key |
| model | string | "claude-sonnet-4-20250514" | Claude model to use |
| maxTokens | number | 1024 | Maximum tokens for AI response |
| strictFields | string[] | [] | Fields that must match exactly |
Per-Route Config (zikit(schema, config))
| Option | Type | Description |
|--------|------|-------------|
| strictFields | string[] | Fields that must match exactly (overrides global) |
| adapter.apiKey | string | API key (overrides global) |
| adapter.model | string | Model name (overrides global) |
| adapter.maxTokens | number | Max tokens (overrides global) |
| adapter.timeout | number | Request timeout in ms (default: 30000) |
| adapter.retries | number | Number of retry attempts (default: 0) |
| adapter.retryDelay | number | Delay between retries in ms (default: 1000) |
| memory.maxSize | number | Max cache entries (default: 500) |
| memory.ttl | number | Cache TTL in ms (default: 3600000) |
| schemaId | string | Custom schema identifier for caching |
| maxInputSize | number | Max input size in bytes (default: 100000 = 100KB) |
Examples
Basic Usage
const UserSchema = z.object({
name: z.string(),
age: z.number(),
});
app.post('/users', zikit(UserSchema), (req, res) => {
// req.body is validated/repaired
res.json(req.body);
});With Strict Fields
const UserSchema = z.object({
id: z.string(), // Must match exactly
name: z.string(), // Can be repaired
age: z.number(), // Can be repaired
});
// Global strict fields from config file
app.post('/users', zikit(UserSchema), handler);
// Or override per-route
app.post('/admins', zikit(UserSchema, {
strictFields: ['id', 'email']
}), handler);Complex Nested Schemas
const OrderSchema = z.object({
user: z.object({
profile: z.object({
name: z.string(),
email: z.string().email(),
}),
}),
items: z.array(z.object({
productId: z.string(),
quantity: z.number(),
price: z.number(),
})),
});
app.post('/orders', zikit(OrderSchema), (req, res) => {
// All nested data is validated/repaired
res.json(req.body);
});Error Handling
app.post('/users', zikit(UserSchema), (req, res, next) => {
// If strict field validation fails, error is passed to Express error handler
res.json(req.body);
});
// Express error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err.message.includes('Strict field validation failed')) {
res.status(400).json({ error: err.message });
} else if (err.message.includes('AI repair failed')) {
res.status(500).json({ error: 'Data repair service unavailable' });
} else {
res.status(500).json({ error: 'Internal server error' });
}
});How Caching Works
ZIKIT uses two-tier caching for optimal performance:
Tier 1: Exact Match Cache (Structure + Values)
// Request 1: { name: "John", age: "30" }
// → AI repairs: { name: "John", age: 30 }
// → Cached with key: structure + values + schema
// Request 2: { name: "John", age: "30" } (exact same!)
// → Exact cache HIT! Instant response, no AI call
// → Returns: { name: "John", age: 30 }Tier 2: Pattern Cache (Structure Only)
// Request 3: { name: "Jane", age: "25" } (different values, same structure!)
// → Exact cache MISS
// → Pattern cache HIT! Applies transformation pattern
// → Transforms "25" → 25 (learned from Request 1)
// → Returns: { name: "Jane", age: 25 } - INSTANT, no AI call!How it works:
- Exact match: If structure + values match exactly → instant return
- Pattern match: If structure matches but values differ → apply learned transformation pattern
- Cache miss: If neither matches → call AI, then cache both exact + pattern
Benefits:
- ✅ Accuracy: Exact matches are always correct
- ✅ Performance: Pattern cache handles similar requests without AI calls
- ✅ Cost savings: Pattern cache dramatically reduces AI calls for high-volume APIs
Cost Optimization Tips
ZIKIT's two-tier caching automatically optimizes for high-volume scenarios:
Pattern cache reduces AI calls - Same structure problems share transformation patterns:
// First: { age: "30" } → AI call → caches pattern // Then: { age: "25" } → Pattern cache hit → NO AI call!Increase cache size - Store more patterns:
zikit(UserSchema, { memory: { maxSize: 10000 } // Store 10k entries (5k exact + 5k patterns) })Use strict fields - Reject invalid data early (no AI cost):
zikit(UserSchema, { strictFields: ['id', 'userId'] // Reject if these fail - no AI call })Monitor cache performance:
const stats = memory.getStats(); console.log(`Cache hit rate: ${stats.hitRate}%`); console.log(`Total entries: ${stats.size} (exact + patterns)`);Set appropriate TTL - Balance freshness vs. cache hits:
zikit(UserSchema, { memory: { ttl: 24 * 60 * 60 * 1000 } // 24 hours })
API Reference
zikit<T>(schema: ZodType<T>, config?: ZikitConfig): ZikitMiddleware
Creates an Express middleware function for the given Zod schema.
Parameters:
schema- Zod schema to validate/repair againstconfig- Optional configuration (see Configuration Options)
Returns: Express middleware function
Types
interface ZikitConfig {
adapter?: AdapterConfig;
memory?: MemoryConfig;
schemaId?: string;
strictFields?: string[];
maxInputSize?: number; // Maximum input size in bytes (default: 100000 = 100KB)
}
interface AdapterConfig {
apiKey?: string;
model?: string;
maxTokens?: number;
timeout?: number;
retries?: number;
retryDelay?: number;
}
interface MemoryConfig {
maxSize?: number;
ttl?: number;
}Advanced Usage
Custom Schema IDs
app.post('/users', zikit(UserSchema, {
schemaId: 'UserSchemaV2' // Custom cache key
}), handler);Accessing Cache Statistics
import { ZikitMemory } from 'zikit-js';
const memory = new ZikitMemory();
const stats = memory.getStats();
console.log({
hits: stats.hits, // Cache hits
misses: stats.misses, // Cache misses
hitRate: stats.hitRate, // Hit rate percentage
size: stats.size, // Current cache size
});Error Handling
ZIKIT handles errors gracefully:
- Strict field validation fails → 400 error passed to Express
- AI repair fails → Error passed to Express error handler
- Network timeout → Configurable timeout with retries
- Invalid JSON from AI → Error with details
Security
ZIKIT includes comprehensive prompt injection protection to prevent malicious users from manipulating the AI's behavior.
Prompt Injection Protection
ZIKIT implements multiple layers of security to prevent prompt injection attacks:
1. Input Sanitization
- Automatically filters dangerous patterns like "ignore previous instructions", "system:", etc.
- Removes code blocks that might contain malicious instructions
- Escapes special characters through JSON serialization
2. System/User Message Separation
- Uses Anthropic's
systemmessage for core instructions (harder to override) - Isolates user data in
usermessage with explicit delimiters - System instructions are protected from user input manipulation
3. Input Size Limits
- Default 100KB limit on request payloads (configurable)
- 10KB limit on data section in AI prompts
- Prevents prompt flooding attacks
app.post('/users', zikit(UserSchema, {
maxInputSize: 50000, // Limit to 50KB (default: 100KB)
}), handler);4. Explicit Security Instructions
- Multiple security warnings in system prompt
- Clear instructions to ignore any commands in user data
- Visual delimiters to separate data from instructions
5. Output Validation
- All AI responses are validated against Zod schema
- Even if AI is manipulated, output must match expected structure
- Invalid responses are rejected and errors are thrown
Security Best Practices
Use Strict Fields - Protect critical fields from AI repair:
zikit(UserSchema, { strictFields: ['id', 'userId', 'email'] // Reject if invalid, no AI call })Set Input Size Limits - Prevent large payload attacks:
zikit(UserSchema, { maxInputSize: 50000 // 50KB limit })Monitor for Suspicious Patterns - Log and monitor:
// In your Express error handler app.use((err, req, res, next) => { if (err.message.includes('Input size exceeds')) { // Log suspicious large payloads console.warn('Large payload detected:', req.body); } });Rate Limiting - Combine with rate limiting middleware:
import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs }); app.use('/api', limiter); app.post('/users', zikit(UserSchema), handler);
What ZIKIT Protects Against
✅ Prompt Injection - Malicious instructions in user data
✅ Instruction Override - Attempts to change AI behavior
✅ Prompt Flooding - Large payloads designed to overwhelm
✅ Code Injection - Malicious code blocks in data
✅ System Prompt Leakage - Attempts to extract system instructions
Security Considerations
- Always validate output - ZIKIT validates all AI responses, but you should also validate in your business logic
- Use strict fields - Critical fields should bypass AI repair entirely
- Monitor costs - Unusual spikes might indicate abuse
- Rate limiting - Combine with rate limiting for additional protection
- Keep dependencies updated - Regularly update ZIKIT and other dependencies
Performance
- Cache hit: ~0-10ms (instant, no AI call)
- Cache miss: ~500-2000ms (AI call time, depends on Claude API)
- Memory usage: Configurable (default: 500 entries)
- Cost: Only pays for AI calls on cache misses
Cost Considerations
- Exact cache hit: $0 (no API call, instant)
- Pattern cache hit: $0 (no API call, applies learned transformation)
- Cache miss: ~$0.003-0.015 per request (depends on Claude pricing)
Example with 10,000 requests/day:
- 3,000 exact matches → $0 (exact cache)
- 5,000 pattern matches → $0 (pattern cache)
- 2,000 AI calls → ~$6-30/day
Best case: With 80%+ cache hit rate (exact + pattern), you pay for only 20% of requests!
Tip: Pattern cache automatically learns transformation patterns, so similar structure problems share the same repair logic without additional AI calls.
Requirements
- Node.js 18+
- Express.js 4.x or 5.x
- Anthropic API key
License
MIT
Contributing
Contributions welcome! Please open an issue or submit a PR.
Support
Made with love ❤️ by 🦎 ZIKIT
