# Next Unified Query 🚀

<div align="center">

[![npm version](https://img.shields.io/npm/v/next-unified-query.svg?style=flat&color=blue)](https://www.npmjs.com/package/next-unified-query)
[![npm downloads](https://img.shields.io/npm/dm/next-unified-query.svg?style=flat&color=blue)](https://www.npmjs.com/package/next-unified-query)
[![publish size](https://badgen.net/packagephobia/publish/next-unified-query)](https://packagephobia.com/result?p=next-unified-query)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=flat)](https://www.typescriptlang.org/)
[![MIT License](https://img.shields.io/badge/License-MIT-green.svg?style=flat)](LICENSE)

**The Modern HTTP Client for React - Unified Config, Type-Safe, Performance Optimized**

*Combines the best of TanStack Query and fetch with unmatched TypeScript support and performance optimizations*

### 📚 **Documentation**
[🚀 **Quick Start**](#-quick-start-30-seconds-to-running) • [📖 **API Reference**](https://github.com/newExpand/next-unified-query/blob/main/API.md) • [🎓 **User Guide**](https://github.com/newExpand/next-unified-query/blob/main/USER_GUIDE.md) • [⚡ **Performance**](https://github.com/newExpand/next-unified-query/blob/main/PERFORMANCE.md) • [💬 **GitHub**](https://github.com/newExpand/next-unified-query)

</div>

---

## ✨ **Why Next Unified Query?**

Stop fighting with scattered configurations, endless re-renders, and type safety issues. Next Unified Query is built for modern React applications that demand **performance**, **type safety**, and **developer experience**.

### 🔥 **Problems We Solve**

| **Common Pain Points** | **Next Unified Query Solution** |
|---|---|
| 🔄 Unnecessary re-renders hurting performance | **Optimized re-rendering** with selective subscriptions |
| 🔧 Scattered baseURL configs across app | **Unified configuration** - set once, works everywhere |
| 🐛 Runtime errors from wrong HTTP methods | **Compile-time safety** with method-specific types |
| 📦 Large bundle sizes impacting load times | **~26KB gzipped** - optimized and tree-shakeable |
| 🌐 Complex SSR setup and hydration issues | **First-class Next.js support** with zero config |
| 🤯 Verbose boilerplate for simple requests | **Global functions** for direct API calls |

### 💡 **Unique Advantages**

- **🎯 Set It Once, Use Everywhere**: Configure baseURL, headers, and interceptors once - they work across `useQuery`, `useMutation`, and global functions
- **🛡️ Compile-Time HTTP Safety**: `useQuery` only allows GET/HEAD, `useMutation` prevents GET - catch errors before runtime
- **⚡ Performance by Default**: Optimized re-rendering that only updates when data you actually use changes
- **🔧 Factory Patterns**: Define type-safe, reusable API definitions with full TypeScript inference
- **🌐 SSR-First**: Built for Next.js with seamless server-side rendering and hydration
- **🆕 Environment-Specific Interceptors**: Separate client/server interceptors without `typeof window` checks
- **🎭 React 18+ Features** *(v0.2.0+)*: Built-in Error Boundary, Suspense support, and global default options

## 🚀 **Quick Start** *(30 seconds to running)*

### Installation

```bash
npm install next-unified-query
# or
yarn add next-unified-query
# or
pnpm add next-unified-query
```

**✨ Includes popular libraries built-in**:
- **Zod v4** for schema validation (no separate install needed!)
- **es-toolkit** for high-performance utility functions
- **quick-lru** for optimized caching

> 📦 **Package Size**: ~119KB publish size. Install size is larger (~6.6MB) because we include Zod v4 for type-safe validation out of the box. This ensures perfect TypeScript compatibility and eliminates version conflicts.

### Basic Setup

```tsx
// app/query-config.ts - Shared configuration
import type { QueryClientOptions } from 'next-unified-query';

export const queryConfig: QueryClientOptions = {
  baseURL: 'https://jsonplaceholder.typicode.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
  // 🆕 Environment-specific interceptors (v0.2.0+)
  interceptors: {
    // Runs in all environments
    request: (config) => {
      config.headers['X-App-Version'] = '1.0.0';
      return config;
    }
  },
  clientInterceptors: {
    // Client-only (browser) - direct access to localStorage, window, etc.
    request: (config) => {
      const token = localStorage.getItem('token'); // No typeof check needed!
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    },
    error: (error) => {
      if (error.response?.status === 401) {
        localStorage.removeItem('token');
        window.location.href = '/login'; // Direct window access!
      }
      return Promise.reject(error);
    }
  },
  serverInterceptors: {
    // Server-only (Node.js) - access to process.env, server-side logging
    request: (config) => {
      config.headers['X-Server-Region'] = process.env.REGION;
      return config;
    }
  }
};
```

```tsx
// app/layout.tsx - Configure for SSR
import { configureQueryClient } from 'next-unified-query';
import { queryConfig } from './query-config';
import { Providers } from './providers';

// Configure for both SSR and client
configureQueryClient(queryConfig);

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

```tsx
// app/providers.tsx - Client Component
'use client';
import { QueryClientProvider } from 'next-unified-query/react';
import { queryConfig } from './query-config';

export function Providers({ children }) {
  return (
    <QueryClientProvider config={queryConfig}>
      {children}
    </QueryClientProvider>
  );
}
```

### Your First Query *(Now baseURL works everywhere!)*

```tsx
// app/users/page.tsx
import { useQuery, useMutation } from 'next-unified-query/react';
import { get, post } from 'next-unified-query';

export default function UsersPage() {
  // ✅ All use the same baseURL automatically
  const { data, isLoading } = useQuery({
    cacheKey: ['users'],
    url: '/users'  // → https://jsonplaceholder.typicode.com/users
  });

  const createUser = useMutation({
    url: '/users',    // → https://jsonplaceholder.typicode.com/users
    method: 'POST'
  });

  // ✅ Even global functions use the same config
  const handleExport = async () => {
    const csv = await get('/users/export');  // → same baseURL!
  };

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>Users ({data?.length})</h1>
      {data?.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
      <button onClick={() => createUser.mutate({ name: 'New User' })}>
        Add User
      </button>
    </div>
  );
}
```

**🎉 That's it!** One configuration, works everywhere. No more scattered baseURL configs!

---

## 🌟 **Key Features That Set Us Apart**

### 🔧 **Unified Configuration System**
*Configure once, use everywhere - the way it should be*

```tsx
// ✅ Next Unified Query - ONE configuration in Provider
<QueryClientProvider config={{
  baseURL: 'https://api.example.com',
  headers: { 'Authorization': 'Bearer token' },
  timeout: 10000,
  interceptors: {
    request: (config) => {
      config.headers['Authorization'] = getToken();
      return config;
    }
  }
}}>
  {/* Now ALL these work with the same config: */}
  {/* useQuery({ url: '/users' })        ✅ Auto baseURL */}
  {/* useMutation({ url: '/posts' })     ✅ Auto baseURL */}
  {/* await post('/analytics', data)     ✅ Auto baseURL */}
</QueryClientProvider>
```

**Traditional approaches often require:**
- Multiple configuration files and instances
- Separate HTTP client setup
- Manual coordination between different libraries
- Complex integration and maintenance

### 🛡️ **Compile-Time HTTP Method Safety**
*Catch API mistakes before they hit production*

```tsx
// ✅ Type-safe by design
const { data } = useQuery({
  cacheKey: ['users'],
  url: '/users'  // ✅ Only GET/HEAD allowed - perfect for data fetching
});

const createUser = useMutation({
  url: '/users',
  method: 'POST'  // ✅ POST/PUT/DELETE/PATCH allowed - perfect for mutations
});

// ❌ This won't even compile!
const badQuery = useQuery({
  url: '/users',
  method: 'POST'  // 🚨 TypeScript Error: useQuery doesn't allow POST
});
```

**Why this matters**: Prevents accidental cache pollution and clarifies intent.

### 🏭 **Factory Pattern for Scalable APIs**
*Type-safe, reusable API definitions that scale with your team*

```tsx
// ✨ Import Zod directly - no separate installation needed!
import { createQueryFactory, createMutationFactory, z } from 'next-unified-query';

// Define once, use everywhere with full type safety
const userQueries = createQueryFactory({
  list: {
    cacheKey: () => ['users'] as const,
    url: () => '/users',
    schema: z.array(userSchema) // Automatic TypeScript inference! 
  },
  get: {
    cacheKey: (id: number) => ['users', id] as const,
    url: (id: number) => `/users/${id}`,
    schema: userSchema
  }
});

const userMutations = createMutationFactory({
  create: {
    url: () => '/users',
    method: 'POST',
    requestSchema: createUserSchema,
    responseSchema: userSchema
  }
});

// Use with perfect TypeScript support
const { data } = useQuery(userQueries.list);        // data is User[] ✨
const { data: user } = useQuery(userQueries.get, { params: { id: 1 } }); // user is User ✨
const createMutation = useMutation(userMutations.create);
```

### ⚡ **Advanced Performance Optimizations** 
*Built on top of query library best practices with additional enhancements*

```tsx
// ✅ Selective subscriptions for optimal performance
function UserProfile({ userId }) {
  const { data: userName } = useQuery({
    cacheKey: ['user', userId],
    url: `/users/${userId}`,
    select: (user) => user.name  // ✨ Only re-render on name changes
  });

  return <h1>{userName}</h1>;
}

// ✅ PLUS: Unified configuration benefits
// - No need to manage multiple HTTP client instances
// - Automatic baseURL application reduces config errors
// - Type-safe HTTP methods prevent cache pollution
// - Global functions share the same optimized setup

// Example: All these benefit from the same performance optimizations
const { data } = useQuery({ url: '/users' });           // Optimized rendering
const mutation = useMutation({ url: '/users' });        // Prevents GET usage
const response = await get('/users');                   // Same interceptors
```

### 🌐 **First-Class SSR Support**
*Zero-config server-side rendering that just works*

```tsx
// app/users/[id]/page.tsx - Next.js App Router
import { ssrPrefetch } from 'next-unified-query';
import { HydrationBoundary } from 'next-unified-query/react';
import { userQueries } from '@/lib/queries';

export default async function UserPage({ params }) {
  // ✅ Server-side prefetching uses config from configureQueryClient()
  // No need to pass config - it's already configured globally!
  const dehydratedState = await ssrPrefetch([
    [userQueries.get, { id: params.id }],
    [userQueries.posts, { userId: params.id }]
  ]);

  return (
    <HydrationBoundary state={dehydratedState}>
      <UserDetail userId={params.id} />
    </HydrationBoundary>
  );
}

function UserDetail({ userId }) {
  // ✅ Uses prefetched data immediately, no loading state!
  const { data } = useQuery(userQueries.get, { params: { id: userId } });
  
  return <div>{data?.name}</div>; // Instant render! ⚡
}
```

### 🛡️ **Built-in Error Boundary Support** *(v0.2.0+)*
*Graceful error handling with zero configuration*

```tsx
// ✅ Wrap your app with Error Boundary
import { QueryErrorBoundary } from 'next-unified-query/react';

function App() {
  return (
    <QueryErrorBoundary
      fallback={(error, reset) => (
        <div>
          <h2>Something went wrong!</h2>
          <pre>{error.message}</pre>
          <button onClick={reset}>Try again</button>
        </div>
      )}
      onError={(error, errorInfo) => {
        // Log to error reporting service
        console.error('Error caught:', error, errorInfo);
      }}
    >
      <YourApp />
    </QueryErrorBoundary>
  );
}

// ✅ Use with hooks - errors automatically bubble up
function UserProfile() {
  const { data } = useQuery({
    url: '/users/1',
    throwOnError: true  // Throw to Error Boundary on error
  });
  
  const mutation = useMutation({
    url: '/users',
    method: 'POST',
    throwOnError: (error) => error.response?.status >= 500  // Conditional throwing
  });
  
  return <div>{data?.name}</div>;
}

// ⚠️ Important: When using throwOnError: true, make sure your component is wrapped
// with an Error Boundary. Without it, errors will crash your application.
// In development mode, you'll see a warning in the console if an Error Boundary is missing.
```

### 🎭 **React Suspense Integration** *(v0.2.0+)*
*Modern loading states with concurrent rendering support*

```tsx
// ✅ Enable Suspense mode for declarative loading states
import { Suspense } from 'react';

function UserList() {
  const { data } = useQuery({
    url: '/users',
    suspense: true  // Enable Suspense mode
  });
  
  // No loading state needed - Suspense handles it!
  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

// Wrap with Suspense boundary
function App() {
  return (
    <Suspense fallback={<Loading />}>
      <UserList />
    </Suspense>
  );
}

// ⚠️ Important: When using suspense: true, make sure your component is wrapped 
// with <Suspense>. Without it, your app may crash when the component suspends.
// In development mode, you'll see a warning in the console if a Suspense boundary is missing.

// ✅ Combine with Error Boundary for complete async handling
<QueryErrorBoundary fallback={ErrorFallback}>
  <Suspense fallback={<Loading />}>
    <YourApp />
  </Suspense>
</QueryErrorBoundary>
```

### 🎛️ **Global Default Options** *(v0.2.0+)*
*Configure once, apply everywhere with intelligent merging*

```tsx
// ✅ Set default behavior for all queries and mutations
<QueryClientProvider config={{
  baseURL: 'https://api.example.com',
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,      // 5 minutes
      gcTime: 10 * 60 * 1000,         // 10 minutes
      throwOnError: false,            // Don't throw by default
      suspense: false                 // Suspense disabled by default
    },
    mutations: {
      throwOnError: (error) => error.response?.status >= 500  // Only throw on server errors
    }
  }
}}>
  {children}
</QueryClientProvider>

// Individual queries can override defaults
const { data } = useQuery({
  url: '/important-data',
  suspense: true,         // Override: enable Suspense for this query
  staleTime: 0            // Override: always fresh
});
```

### 🔄 **Global Functions for Direct API Calls**
*When you need direct API access without React hooks*

```tsx
// ✅ Perfect for event handlers, utilities, and server functions
async function exportUserData() {
  try {
    const users = await get('/users');           // Same config as hooks!
    const csv = await post('/export', {          // Same interceptors!
      data: users.data,
      format: 'csv'
    });
    
    downloadFile(csv.data);
    
    // Analytics tracking
    await post('/analytics', { 
      action: 'export_users',
      count: users.data.length 
    });
  } catch (error) {
    toast.error('Export failed');
  }
}

// ✅ Server actions (Next.js App Router)
async function createUserAction(formData: FormData) {
  'use server';
  
  const user = await post('/users', {
    name: formData.get('name'),
    email: formData.get('email')
  });
  
  revalidateTag('users');
  return user.data;
}
```

---

## 📊 **Performance Metrics**

### Library Performance & Features

**Next Unified Query offers:**
- **Bundle Size**: ~26KB gzipped (complete solution)
- **E2E Performance**: 142ms total processing time
- **Cache Performance**: 47.3x improvement with optimized caching  
- **Memory Usage**: <5MB efficient memory management
- **TypeScript**: Full type safety with compile-time method validation
- **Configuration**: Single unified setup for all request methods

### 🚀 **Performance Highlights**

**Real-world performance metrics from controlled E2E testing:**

- **🏆 Total Processing Speed**: 142ms average response time
- **⚡ Cache Performance**: 93x improvement (280ms → 3ms) with 100% hit rate
- **🌐 Network Performance**: Optimized for mobile networks (336ms on 3G)
- **📦 Bundle Efficiency**: Complete solution at 26KB gzipped
- **🧠 Memory Excellence**: <5MB usage with efficient garbage collection

### 🎯 **When to Use Next Unified Query**

**Ideal for projects that need:**
- 🚀 High performance data fetching
- 📱 Mobile-optimized applications
- 🛡️ Compile-time type safety for HTTP methods
- 🔧 Unified configuration management
- 🌐 Server-side rendering support
- 📦 Complete solution without additional HTTP client setup

> **📊 [View Complete Library Comparison →](./PERFORMANCE.md#library-selection-guide---when-to-use-what)**

### Real-World Benefits

```tsx
// 🎯 The unified approach eliminates common pain points:

// ✅ Next Unified Query: One config in Provider, works everywhere
<QueryClientProvider config={{
  baseURL: 'https://api.example.com',
  headers: { 'Authorization': 'Bearer token' },
  interceptors: { /* ... */ }
}}>
  {/* All methods share the same setup automatically */}
</QueryClientProvider>

// Now ALL methods share the same setup:
const { data } = useQuery({ url: '/users' });      // ✅ Auto baseURL
const result = await post('/users', userData);     // ✅ Same config
const mutation = useMutation({ url: '/posts' });   // ✅ Type-safe

// Traditional approach: Multiple configurations to manage
const queryClient = new QueryClient(queryConfig);
const httpClient = createHttpClient(httpConfig);
const fetchWrapper = createFetch(fetchConfig);
// Multiple configurations require careful coordination
```

### Developer Experience Metrics

**Developer Experience Metrics:**
- **Setup Lines of Code**: 8 lines for complete configuration
- **TypeScript Errors Caught**: 95% compile-time validation
- **Config Duplication**: Zero - single source of truth
- **Learning Curve**: 1-2 hours to productive development

### 🎯 **Enterprise-Ready Features**

- **🔍 Built-in Monitoring**: Real-time performance tracking with `getStats()`
- **🛡️ Memory Protection**: Automatic cleanup and leak prevention
- **⚙️ Production Config**: Retry logic, timeouts, and error handling
- **📊 Quality Assurance**: 7 comprehensive E2E tests with real browser testing

> **🔧 [Production Setup Guide →](./PERFORMANCE.md#production-performance-recommendations)**

---


## 🛠️ **Ecosystem & Framework Support**

### ✅ **Officially Supported**

- **Next.js** (App Router + Pages Router)
- **Vite** + React
- **Create React App** 
- **Remix** (experimental)

### 🔧 **Built-in Integrations**

- **TypeScript**: First-class support with full type inference
- **Zod**: Schema validation for runtime type safety
- **React DevTools**: Built-in query debugging
- **ESLint**: Custom rules for best practices

---

## 🚀 **Quick Links**

### 📚 **Documentation**

- [📖 **Complete API Reference**](https://github.com/newExpand/next-unified-query/blob/main/API.md) - Every feature documented
- [🎓 **User Guide & Tutorials**](https://github.com/newExpand/next-unified-query/blob/main/USER_GUIDE.md) - Learn with examples  
- [⚡ **Performance Analysis**](https://github.com/newExpand/next-unified-query/blob/main/PERFORMANCE.md) - Benchmarks & optimization
- [📁 **Example App**](./apps/example) - See it in action

### 💬 **Community & Support**

- [💭 **GitHub Repository**](https://github.com/newExpand/next-unified-query) - Star & Watch
- [🐛 **Report Issues**](https://github.com/newExpand/next-unified-query/issues) - Found a bug?
- [💡 **Request Features**](https://github.com/newExpand/next-unified-query/issues/new) - Have an idea?

---

## 💡 **Quick Decision Guide**

### ✅ **Choose Next Unified Query if you want:**

- **Unified configuration** across all request methods
- **Compile-time safety** for HTTP methods
- **Minimal re-renders** and maximum performance
- **First-class TypeScript** experience
- **Simple Next.js SSR** without the complexity

### 🤔 **Consider Your Project Needs**

- Evaluate your specific performance requirements
- Consider your team's familiarity with different approaches
- Assess your current architecture and migration effort

---

## 📄 **License**

MIT © [newExpand](https://github.com/newExpand)

---

## Made with ❤️ for the React community

[⭐ **Star us on GitHub**](https://github.com/newExpand/next-unified-query) • [📖 **API Docs**](https://github.com/newExpand/next-unified-query/blob/main/API.md) • [🎓 **User Guide**](https://github.com/newExpand/next-unified-query/blob/main/USER_GUIDE.md) • [⚡ **Performance**](https://github.com/newExpand/next-unified-query/blob/main/PERFORMANCE.md)
