Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/thalesfp/query-with-cache
Lightweight caching solution for async queries
https://github.com/thalesfp/query-with-cache
cache query valtio
Last synced: 3 days ago
JSON representation
Lightweight caching solution for async queries
- Host: GitHub
- URL: https://github.com/thalesfp/query-with-cache
- Owner: thalesfp
- Created: 2024-12-18T17:52:51.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2025-01-17T01:47:32.000Z (6 days ago)
- Last Synced: 2025-01-17T02:20:27.709Z (6 days ago)
- Topics: cache, query, valtio
- Language: TypeScript
- Homepage:
- Size: 93.8 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# query-with-cache
A lightweight, TypeScript-first caching solution for managing async query results. Inspired by React Query's patterns, it provides a simple yet powerful way to cache and invalidate data with support for stale-while-revalidate, automatic garbage collection, and hierarchical cache keys.
## Features
- ๐ Simple, intuitive API
- ๐พ In-memory caching with automatic garbage collection
- ๐ณ Hierarchical cache keys
- โก Stale-while-revalidate pattern
- ๐ TypeScript-first design
- ๐งน Zero dependencies## Installation
```bash
npm install query-with-cache
# or
yarn add query-with-cache
```## Quick Start
```typescript
import { queryWithCache, CacheStoreInMemory } from 'query-with-cache';// Create a cache instance
const cache = new CacheStoreInMemory();// Basic usage
await queryWithCache({
queryKey: ['todos'],
cache,
queryFn: () => fetch('/api/todos').then(r => r.json()),
onData: (todos) => {
console.log('Todos:', todos);
},
onIsFetching: (loading) => {
console.log('Loading:', loading);
},
onError: (error) => {
console.error('Error:', error);
},
});
```## Integration with Valtio
```typescript
import { proxy } from 'valtio';// Define your state
interface State {
todos: Todo[];
isLoading: boolean;
error: Error | null;
}const state = proxy({
todos: [],
isLoading: false,
error: null,
});// Create data fetching function
const fetchTodos = async () => {
await queryWithCache({
queryKey: ['todos'],
cache,
queryFn: () => fetch('/api/todos').then(r => r.json()),
onData: (todos) => {
state.todos = todos;
},
onIsFetching: (loading) => {
state.isLoading = loading;
},
onError: (error) => {
state.error = error as Error;
},
});
};// Use with React
import { useSnapshot } from 'valtio';function TodoList() {
const snap = useSnapshot(state);useEffect(() => {
fetchTodos();
}, []);if (snap.isLoading) return
Loading...;
if (snap.error) returnError: {snap.error.message};return (
- {todo.title}
{snap.todos.map(todo => (
))}
);
}
```
### Optimistic Updates with Valtio
```typescript
const addTodo = async (newTodo: Todo) => {
// Optimistic update
state.todos.push({ ...newTodo, id: 'temp-id' });
try {
await queryWithCache({
queryKey: ['todos', 'add'],
cache,
queryFn: () => fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
}).then(r => r.json()),
onData: (savedTodo) => {
// Replace optimistic todo with server response
const index = state.todos.findIndex(t => t.id === 'temp-id');
if (index !== -1) {
state.todos[index] = savedTodo;
}
},
onError: () => {
// Rollback on error
state.todos = state.todos.filter(t => t.id !== 'temp-id');
},
});
} catch (error) {
// Handle error
state.todos = state.todos.filter(t => t.id !== 'temp-id');
}
};
```
## Basic Cache Operations
```typescript
// Set cache entry
cache.set({
key: ['users', '123'],
data: userData,
staleTime: 5000, // Optional: custom stale time
cacheTime: 30000, // Optional: custom cache time
});
// Get cache entry
const { data, stale } = cache.get(['users', '123']);
// Invalidate cache entries
cache.invalidate(['users', '123']); // Single entry
cache.invalidate(['users']); // Collection
```
## TypeScript Support
```typescript
interface Todo {
id: string;
title: string;
completed: boolean;
}
// Typed cache operations
const { data, stale } = cache.get(['todos', '123']);
// Typed queries
await queryWithCache({
queryKey: ['todos'],
queryFn: () => fetchTodos(),
onData: (todos) => {
// todos is typed as Todo[]
console.log(todos[0].title);
}
});
```
## Configuration
```typescript
const cache = new CacheStoreInMemory({
// Default times
defaultStaleTime: 5000, // 5 seconds
defaultCacheTime: 30000, // 30 seconds
// Garbage collection interval
gcInterval: 60000, // 1 minute
// Debugging
debug: true,
logger: customLogger,
});
```
## Best Practices
1. **Cache Keys**
```typescript
// Good
['users', userId, 'posts']
['todos', { status: 'active' }]
// Avoid
['users', new Date()] // Non-serializable
['data'] // Too generic
```
2. **Cache Times**
- Set appropriate stale times based on data freshness needs
- Configure cache times based on memory constraints
- Use shorter times for frequently changing data
3. **Error Handling**
- Always provide error handlers
- Implement retry strategies if needed
- Handle cache misses appropriately