Go-style singleflight implementation for Node.js/TypeScript
npm install singleflight-js


Go-style singleflight implementation for Node.js / TypeScript.
Deduplicate concurrent function calls with the same key and share the result.
``bash`
yarn add singleflight-js
`typescript
import { Group } from 'singleflight-js';
const group = new Group();
// Expensive operation (API call, database query, etc.)
async function fetchUser(userId: string) {
console.log(Fetching user ${userId}...);User ${userId}
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate delay
return { id: userId, name: };
}
// Multiple concurrent calls with the same key
const promises = [
group.do('user:123', () => fetchUser('123')),
group.do('user:123', () => fetchUser('123')),
group.do('user:123', () => fetchUser('123'))
];
const results = await Promise.all(promises);
// Only one actual fetchUser() call is made!
// First call: { value: { id: '123', name: 'User 123' }, shared: false }
// Other calls: { value: { id: '123', name: 'User 123' }, shared: true }
`
`typescript
import express from 'express';
import { Group } from 'singleflight-js';
const app = express();
const group = new Group();
// Simulate database query
async function getUserFromDB(userId: string) {
console.log(🔍 Querying database for user ${userId}...);User ${userId}
await new Promise(resolve => setTimeout(resolve, 2000)); // 2s delay
return { id: userId, name: , email: user${userId}@example.com };
}
app.get('/users/:id', async (req, res) => {
const userId = req.params.id;
try {
// Multiple concurrent requests for the same user will be deduplicated
const result = await group.do(user:${userId}, () => getUserFromDB(userId));
res.json({
data: result.value,
shared: result.shared, // true if this request shared the result
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
console.log('Try: curl http://localhost:3000/users/123');
});
`
- Prevents duplicate work: Multiple concurrent requests for the same resource execute only once
- Automatic result sharing: All waiting requests get the same result
- Error propagation: If the operation fails, all waiting requests receive the same error
- Memory efficient: No caching overhead, results are shared only during execution
- TypeScript support: Full type safety with generic return types
#### do
Execute function fn for the given key. If there's already a call in progress for the same key, wait for it and share the result.
Returns:
- value: The result from the functionshared
- : false for the original call, true for subsequent calls that shared the result
#### forget(key: string): void
Remove the key from the group, allowing new calls for the same key to execute independently. Useful for cache invalidation.
`typescript`
// Update user data and invalidate ongoing requests
group.forget('user:123');
await updateUser('123', newData);
- API deduplication: Prevent multiple identical API calls
- Database query optimization: Share expensive query results
- Cache warming: Coordinate cache updates
- Rate limiting: Control concurrent operations to external services
- Microservices: Reduce load on downstream services
See the example/ directory for more detailed examples:
- basic-usage.ts - Basic deduplication patterns
- api-cache.ts - API caching with error handling
- advanced-usage.ts - Circuit breaker and advanced patterns
`bashInstall dependencies
yarn install
MIT
---
🔗 Links:
- GitHub Repository
- NPM Package
- Issues & Bug Reports
- Contributing Guide