Vite
Run hypequery as a standalone API next to a Vite/React frontend.
Vite
Expose analytics through a dedicated hypequery server and call it from your Vite frontend via a proxy.
Runable Example
You can find a runnable vite example in our examples repo
Use case
You want separate frontend/backend processes so you can:
- Keep the analytics API in its own Node process (or container)
- Proxy
/api/*requests during development to avoid CORS - Deploy the API independently (Render, Railway, Fly.io, etc.)
Prerequisites
- Node.js 18+
- Vite + React project (
npm create vite@latest my-app -- --template react) - Packages:
@hypequery/serve,@hypequery/clickhouse,zod,@tanstack/react-query - Dev packages:
@hypequery/cli,concurrently
npm install @hypequery/serve @hypequery/clickhouse zod @tanstack/react-query
npm install --save-dev @hypequery/cli concurrentlyInstall dependencies
npm install @hypequery/serve @hypequery/clickhouse zod @tanstack/react-query
npm install --save-dev @hypequery/cli concurrentlyDefine the API (standalone Node entry)
Create api/queries.ts:
import 'dotenv/config';
import { initServe } from '@hypequery/serve';
import { createQueryBuilder } from '@hypequery/clickhouse';
import { z } from 'zod';
const { define, queries, query } = initServe({
context: () => ({}),
basePath: '/api'
});
const apiDefinition = define({
queries: queries({
hello: query
.describe('Simple greeting')
.output(z.object({ message: z.string(), at: z.string() }))
.query(async () => ({
message: 'Hello from hypequery!',
at: new Date().toISOString(),
})),
}),
});
export type ApiDefinition = InferApiType<typeof apiDefinition>;
export const api = apiDefinition;
api.route('/hello', api.queries.hello, { method: 'GET' });Run the API locally:
npm run api
# or manually: npx hypequery dev api/queries.ts --port 4000
# Docs → http://localhost:4000/docsProxy /api/* requests in Vite
Update vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});During development fetch('/api/hello') hits the hypequery server without CORS hassles.
Generate React Query hooks
Create src/lib/hypequery.ts:
import { createHooks } from '@hypequery/react';
import type { ApiDefinition } from '../../api/queries';
export const { useQuery, useMutation } = createHooks<ApiDefinition>({
baseUrl: '/api',
});Use metrics in React components
Create src/App.tsx:
import './App.css';
import { useMutation } from './lib/hypequery';
export default function App() {
const helloQuery = useMutation('hello');
return (
<main className="app">
<h1>hypequery + Vite</h1>
<div className="actions">
<button
disabled={helloQuery.isPending}
onClick={() => helloQuery.mutate({})}
>
{helloQuery.isPending ? 'Loading…' : 'Greet'}
</button>
</div>
{helloQuery.error && (
<p className="error">{helloQuery.error.message}</p>
)}
{helloQuery.data && (
<section>
<h2>Hello metric</h2>
<pre>{JSON.stringify(helloQuery.data, null, 2)}</pre>
</section>
)}
</main>
);
}Add styles in src/App.css:
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
.app {
display: flex;
flex-direction: column;
gap: 2rem;
text-align: center;
}
.actions {
display: flex;
justify-content: center;
gap: 1rem;
}
.error {
color: #dc2626;
}Wrap Vite with QueryClientProvider
Update src/main.tsx:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import App from './App';
import './index.css';
const queryClient = new QueryClient();
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Root element not found');
createRoot(rootElement).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</StrictMode>
);Run both servers together
Update scripts in package.json:
{
"scripts": {
"dev": "vite",
"api": "hypequery dev api/queries.ts --port 4000",
"dev:all": "concurrently \"npm run dev\" \"npm run api\""
},
"devDependencies": {
"@hypequery/cli": "latest",
"concurrently": "latest"
}
}Run npm run dev:all to start Vite (port 5173) and hypequery (port 4000) simultaneously.
Project Structure
After following this guide, your structure should look like:
Deployment tips
- Ship the hypequery API as its own service (
npx hypequery serve api/queries.ts --port 4000or Docker). - Point
baseUrlto the hosted API (e.g.,https://analytics.myapp.com). - In production builds, point
baseUrlto your hosted API (e.g.,https://analytics.myapp.com).
Next steps
- Protect the API with
auth: async () => ({ userId, roles, tenantId })andtenant: { extract } - Generate more hooks (e.g.,
useMutation) viacreateHooks<ApiDefinition> - Add caching to the ClickHouse builder (
cache: { mode: 'stale-while-revalidate', ttlMs: ... })
Core Concepts
hypequery has three core ideas, a typed schema that mirrors your ClickHouse tables, query definitions that wrap your analytics logic in reusable units, and execution modes that let you run those definitions anywhere. This page covers each briefly.
Next.js
Complete guide to integrating hypequery into a Next.js App Router project