Skip to content

bnowak008/cacheq

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DB Query - Server-Side Data Fetching Library

A server-side data-fetching library inspired by TanStack Query, designed for caching and managing database queries on the server with support for multiple scoping strategies and pluggable cache backends.

Features

  • 🚀 Simple, declarative API for server-side data fetching
  • 💾 Pluggable caching system with in-memory and Redis adapters
  • 🔄 Automatic retries with exponential backoff
  • Request deduplication to prevent duplicate database calls
  • 🎯 Multiple scoping strategies: request, session, and global
  • 🔄 Query invalidation with exact and partial matching
  • 🏗️ Database agnostic - works with any ORM or database driver
  • 📊 Built-in mutation support with lifecycle callbacks
  • 🧪 TypeScript first with full type safety
  • 🛠️ Monorepo structure with dedicated adapters

Quick Start

Installation

# Core library
bun install cacheq

# Optional: Redis cache adapter
bun install @cacheq/redis-adapter

# Optional: Drizzle ORM adapter
bun install @cacheq/drizzle-adapter

Basic Usage (Request Scoped)

import { QueryClient } from 'cacheq';

// Create a new client for each request
const queryClient = new QueryClient();

// Define your data fetcher
const fetchUser = async ({ queryKey }) => {
  const [, userId] = queryKey;
  return await db.select().from(users).where(eq(users.id, userId));
};

// Create and execute query
const userQuery = queryClient.createQuery(['user', 1], fetchUser);

// Access query state
console.log(userQuery.status); // 'loading' | 'success' | 'error'
console.log(userQuery.data);   // User data when loaded
console.log(userQuery.error);  // Error if failed

Session-Scoped Caching

import { QueryClientManager } from 'cacheq';
import { createRedisCache } from '@cacheq/redis-adapter';

// Set up distributed cache
const redisCache = createRedisCache({
  redis: { url: 'redis://localhost:6379' },
  keyPrefix: 'myapp:',
  defaultTTL: 600 // 10 minutes
});

// Create manager for session-scoped clients
const manager = new QueryClientManager({
  cache: redisCache,
  defaultCacheTime: 10 * 60 * 1000
});

// Get client for specific session (persists across requests)
const sessionClient = manager.getClient({ sessionId: 'user-123' });

const userPrefs = sessionClient.createQuery(['preferences', 'user-123'], fetchPreferences);

Global Caching

// For data shared across all users
const globalClient = manager.getGlobalClient();

const appConfig = globalClient.createQuery(['app-config'], fetchAppConfig);

Mutations

const createUserMutation = queryClient.createMutation({
  mutationFn: async (userData) => {
    return await db.insert(users).values(userData);
  },
  onSuccess: (data, variables) => {
    // Invalidate users list to refetch
    queryClient.invalidateQueries({ queryKey: ['users'] });
  },
  onError: (error, variables) => {
    console.error('Failed to create user:', error);
  }
});

// Execute mutation
await createUserMutation({ name: 'John', email: '[email protected]' });

API Reference

QueryClient

The main class for managing queries and mutations.

const queryClient = new QueryClient({
  cache?: Cache;              // Custom cache implementation
  defaultCacheTime?: number;  // Default TTL in milliseconds
});

Methods

  • createQuery<T>(queryKey, fetcher, options?) - Create a query
  • createMutation<T>(options) - Create a mutation
  • invalidateQueries(options) - Invalidate and refetch queries
  • setQueryData(queryKey, data) - Manually set query data
  • getQueryData(queryKey) - Get cached query data
  • prefetchQuery(queryKey, fetcher) - Prefetch data
  • clear() - Clear all cached data

QueryClientManager

Manages multiple QueryClient instances for session and global scoping.

const manager = new QueryClientManager({
  cache: Cache;               // Distributed cache implementation
  defaultCacheTime?: number;  // Default TTL in milliseconds
});

Methods

  • getClient({ sessionId }) - Get session-scoped client
  • getGlobalClient() - Get global singleton client
  • removeSession(sessionId) - Clean up session
  • clearAllSessions() - Clear all session data
  • getSessionCount() - Get active session count

Cache Interface

Implement this interface to create custom cache adapters:

interface Cache {
  get<T>(key: string): Promise<T | undefined>;
  set(key: string, value: any, ttl?: number): Promise<void>;
  delete(key: string): Promise<void>;
  clear(): Promise<void>;
}

Query Options

interface QueryOptions {
  retry?: number;                           // Number of retries (default: 3)
  retryDelay?: (attemptIndex: number) => number;  // Retry delay function
  cacheTime?: number;                       // Cache TTL in milliseconds
}

Framework Integration

Express.js

import express from 'express';
import { QueryClient } from 'cacheq';

const app = express();

// Middleware to create QueryClient per request
app.use((req, res, next) => {
  req.queryClient = new QueryClient();
  next();
});

app.get('/users/:id', async (req, res) => {
  const userQuery = req.queryClient.createQuery(
    ['user', req.params.id],
    fetchUser
  );
  
  // Wait for query to complete and respond
  res.json({ user: userQuery.data });
});

Fastify

import Fastify from 'fastify';
import { QueryClient } from 'cacheq';

const fastify = Fastify();

// Plugin to add QueryClient to request
fastify.addHook('onRequest', async (request) => {
  request.queryClient = new QueryClient();
});

fastify.get('/users/:id', async (request) => {
  const userQuery = request.queryClient.createQuery(
    ['user', request.params.id],
    fetchUser
  );
  
  return { user: userQuery.data };
});

Packages

This is a monorepo containing several packages:

cacheq

The main library with QueryClient, QueryClientManager, and core functionality.

@cacheq/redis-adapter

Redis cache adapter for distributed caching across multiple server instances.

@cacheq/drizzle-adapter

Integration helpers for Drizzle ORM to simplify query creation.

Development

# Install dependencies
bun install

# Run tests
bun test

# Build all packages
bun run build

# Test specific package
cd packages/core && bun test

Roadmap

  • ✅ Core query and mutation functionality
  • ✅ Pluggable cache system with Redis adapter
  • ✅ Session and global scoping via QueryClientManager
  • ✅ Automatic retries and request deduplication
  • ✅ Query invalidation with partial matching
  • 🚧 Stale-while-revalidate functionality
  • 🚧 Optimistic updates for mutations
  • 🚧 Enhanced Drizzle ORM integration
  • 🚧 DevTools for debugging and inspection
  • 🚧 Additional cache adapters (Memcached, DynamoDB)

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to the main branch.

License

MIT License - see LICENSE file for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published