Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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) return
Error: {snap.error.message}
;

return (


    {snap.todos.map(todo => (
  • {todo.title}

  • ))}

);
}
```

### 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