Next.js Integration
Learn how to integrate Qryn with Next.js for full-stack applications.
Installation
Install Qryn and configure it with Next.js for seamless integration.
Install Dependencies
npm install qryn
npm install -D @types/nodeBasic Setup
// lib/qryn.ts
import { Qryn } from 'qryn';
const qryn = new Qryn({
database: {
type: 'sqlite',
connection: './database.sqlite'
}
});
export default qryn;API Routes
Create API routes with Qryn in Next.js.
API Route Setup
Create API routes with Qryn
Dynamic API Route
// pages/api/qryn/[...path].ts
import { NextApiRequest, NextApiResponse } from 'next';
import qryn from '../../../lib/qryn';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { path } = req.query;
const pathString = Array.isArray(path) ? path.join('/') : path;
const result = await qryn.handleRequest({
method: req.method,
url: `/api/qryn/${pathString}`,
headers: req.headers,
body: req.body
});
res.status(result.status).json(result.data);
} catch (error) {
console.error('API Error:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
}App Router API Route
// app/api/qryn/[...path]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import qryn from '../../../../lib/qryn';
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const path = searchParams.get('path') || '';
const result = await qryn.handleRequest({
method: 'GET',
url: `/api/qryn/${path}`,
headers: Object.fromEntries(request.headers.entries()),
body: null
});
return NextResponse.json(result.data, { status: result.status });
} catch (error) {
console.error('API Error:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
export async function POST(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const path = searchParams.get('path') || '';
const body = await request.json();
const result = await qryn.handleRequest({
method: 'POST',
url: `/api/qryn/${path}`,
headers: Object.fromEntries(request.headers.entries()),
body
});
return NextResponse.json(result.data, { status: result.status });
} catch (error) {
console.error('API Error:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}Server Components
Use Qryn in Next.js Server Components for server-side data fetching.
Server Component Example
// app/users/page.tsx
import qryn from '../../lib/qryn';
export default async function UsersPage() {
const users = await qryn.query({
model: 'User',
where: { status: 'active' },
limit: 10
});
return (
<div>
<h1>Users</h1>
<ul>
{users.data.map((user) => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
</div>
);
}Server Actions
// app/actions/user.ts
'use server';
import qryn from '../../lib/qryn';
import { revalidatePath } from 'next/cache';
export async function createUser(formData: FormData) {
try {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const user = await qryn.create({
model: 'User',
data: { name, email }
});
revalidatePath('/users');
return { success: true, user };
} catch (error) {
return { success: false, error: 'Failed to create user' };
}
}
export async function updateUser(id: string, formData: FormData) {
try {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const user = await qryn.update({
model: 'User',
id,
data: { name, email }
});
revalidatePath('/users');
return { success: true, user };
} catch (error) {
return { success: false, error: 'Failed to update user' };
}
}Client Components
Use Qryn in client components with proper data fetching.
Client Component with SWR
'use client';
import useSWR from 'swr';
import { useState } from 'react';
const fetcher = (url: string) => fetch(url).then((res) => res.json());
export default function UsersList() {
const { data, error, mutate } = useSWR('/api/qryn/User', fetcher);
const [isCreating, setIsCreating] = useState(false);
const createUser = async (userData: { name: string; email: string }) => {
setIsCreating(true);
try {
const response = await fetch('/api/qryn/User', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (response.ok) {
mutate(); // Revalidate data
}
} catch (error) {
console.error('Failed to create user:', error);
} finally {
setIsCreating(false);
}
};
if (error) return <div>Failed to load users</div>;
if (!data) return <div>Loading...</div>;
return (
<div>
<h2>Users</h2>
<ul>
{data.data.map((user: any) => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
<button
onClick={() => createUser({ name: 'New User', email: 'new@example.com' })}
disabled={isCreating}
>
{isCreating ? 'Creating...' : 'Add User'}
</button>
</div>
);
}Client Component with React Query
'use client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
export default function UsersList() {
const queryClient = useQueryClient();
const [isCreating, setIsCreating] = useState(false);
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/qryn/User').then((res) => res.json())
});
const createUserMutation = useMutation({
mutationFn: (userData: { name: string; email: string }) =>
fetch('/api/qryn/User', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
}
});
const createUser = async (userData: { name: string; email: string }) => {
setIsCreating(true);
try {
await createUserMutation.mutateAsync(userData);
} catch (error) {
console.error('Failed to create user:', error);
} finally {
setIsCreating(false);
}
};
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Failed to load users</div>;
return (
<div>
<h2>Users</h2>
<ul>
{users?.data.map((user: any) => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
<button
onClick={() => createUser({ name: 'New User', email: 'new@example.com' })}
disabled={isCreating}
>
{isCreating ? 'Creating...' : 'Add User'}
</button>
</div>
);
}Middleware
Use Next.js middleware with Qryn for authentication and authorization.
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Check if the request is for Qryn API
if (request.nextUrl.pathname.startsWith('/api/qryn')) {
// Check for authentication token
const token = request.headers.get('authorization');
if (!token) {
return NextResponse.json(
{ error: 'Authentication required' },
{ status: 401 }
);
}
// Validate token (implement your validation logic)
if (!isValidToken(token)) {
return NextResponse.json(
{ error: 'Invalid token' },
{ status: 403 }
);
}
// Add user info to headers
const user = getUserFromToken(token);
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-user-id', user.id);
requestHeaders.set('x-user-role', user.role);
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}
return NextResponse.next();
}
export const config = {
matcher: '/api/qryn/:path*',
};
function isValidToken(token: string): boolean {
// Implement your token validation logic
return true;
}
function getUserFromToken(token: string): { id: string; role: string } {
// Implement your token parsing logic
return { id: 'user-123', role: 'user' };
}Configuration
Configure Qryn for Next.js with proper environment variables and settings.
Environment Variables
# .env.local
DATABASE_URL=postgresql://user:pass@localhost:5432/qryn
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-key
NODE_ENV=developmentNext.js Configuration
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ['qryn']
},
env: {
DATABASE_URL: process.env.DATABASE_URL,
REDIS_URL: process.env.REDIS_URL,
JWT_SECRET: process.env.JWT_SECRET
}
};
module.exports = nextConfig;Qryn Configuration
// lib/qryn.ts
import { Qryn } from 'qryn';
const qryn = new Qryn({
database: {
type: 'postgresql',
connection: process.env.DATABASE_URL
},
cache: {
type: 'redis',
connection: process.env.REDIS_URL
},
security: {
auth: {
enabled: true,
secret: process.env.JWT_SECRET
}
}
});
export default qryn;Best Practices
Do
- • Use Server Components for data fetching
- • Implement proper error handling
- • Use environment variables
- • Implement authentication middleware
- • Use proper caching strategies
- • Optimize bundle size
- • Use TypeScript for type safety
Don't
- • Skip error handling
- • Hardcode configuration
- • Ignore authentication
- • Use inefficient queries
- • Skip caching
- • Ignore bundle optimization
- • Skip TypeScript