@zubyjs/next
v1.0.84
Published
Zuby.js Next.js compatibility plugin
Readme
@zubyjs/next
The plugin for Zuby.js that adds compatibility for existing Next.js projects with the pages router. It automatically converts Next.js pages and API routes to Zuby.js handlers, providing a smooth migration path.
Features
- Automatic API Route Detection: Scans
./pages/apidirectory and converts Next.js API routes to Zuby handlers - Next.js API Compatibility: Provides
NextApiRequestandNextApiResponseobjects compatible with Next.js conventions - HTTP Method Support: Supports method-specific handlers (
get,post,put,delete,patch,head,options) - Default Exports: Works with both default exports (all methods) and method-specific exports
- Dynamic Routes: Automatically converts dynamic routes like
[id]to:idformat - Nested Routes: Supports nested API routes with proper path handling
- Error Handling: Built-in error handling and logging
Installation
First, install the @zubyjs/next package using your favorite package manager.
If you aren't sure, you can use npm:
npm install @zubyjs/nextThen add the @zubyjs/next plugin to your zuby.config.mjs file under the plugins option:
import { defineConfig } from 'zuby';
import preact from '@zubyjs/preact';
import next from '@zubyjs/next';
export default defineConfig({
outDir: '.zuby',
jsx: preact(),
plugins: [
next()
]
});Configuration
Options
The plugin accepts the following options:
interface NextPluginOptions {
/**
* Whether to automatically detect and convert Next.js pages/api routes
* @default true
*/
autoDetectApiRoutes?: boolean;
/**
* Custom pages directory relative to srcDir
* @default ./pages
*/
pagesDir?: string;
}Usage with Options
import next from '@zubyjs/next';
export default defineConfig({
plugins: [
next({
autoDetectApiRoutes: true,
pagesDir: './pages'
})
]
});API Routes
Next.js API routes in ./pages/api are automatically converted to Zuby handlers. The plugin supports:
1. Default Export Handler (All Methods)
// pages/api/hello.ts
export default function handler(req: NextApiRequest, res: NextApiResponse) {
return new Response('Hello from Zuby!', { status: 200 });
}2. HTTP Method-Specific Handlers
// pages/api/posts.ts
export async function get(req: NextApiRequest, res: NextApiResponse) {
const posts = await fetchPosts();
return new Response(JSON.stringify(posts), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}
export async function post(req: NextApiRequest, res: NextApiResponse) {
const newPost = await createPost(req.body);
return new Response(JSON.stringify(newPost), {
status: 201,
headers: { 'content-type': 'application/json' }
});
}
export async function put(req: NextApiRequest, res: NextApiResponse) {
const updatedPost = await updatePost(req.body);
return new Response(JSON.stringify(updatedPost), { status: 200 });
}
export async function deleteHandler(req: NextApiRequest, res: NextApiResponse) {
await deletePost(req.query.id);
return new Response(null, { status: 204 });
}3. Dynamic Routes
Dynamic route segments are automatically converted:
// pages/api/posts/[id].ts
export async function get(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query;
const post = await fetchPost(id);
if (!post) {
return new Response(JSON.stringify({ error: 'Not found' }), { status: 404 });
}
return new Response(JSON.stringify(post), { status: 200 });
}The route pages/api/posts/[id].ts becomes /posts/:id
4. Nested Dynamic Routes
// pages/api/[tenant]/posts/[id].ts
export async function get(req: NextApiRequest, res: NextApiResponse) {
const { tenant, id } = req.query;
const post = await fetchPost(tenant, id);
return new Response(JSON.stringify(post), { status: 200 });
}The route becomes /:tenant/posts/:id
NextApiRequest Object
The NextApiRequest object provides the following properties:
interface NextApiRequest {
// The request URL
url?: string;
// The HTTP method (GET, POST, PUT, DELETE, etc.)
method?: string;
// Query parameters from URL and path params
query: Record<string, string | string[]>;
// Request cookies
cookies: Record<string, string>;
// Request headers
headers: Record<string, string | string[] | undefined>;
// Request body (for POST/PUT requests)
body?: any;
// Base URL
baseUrl?: string;
// Path without query string
pathname?: string;
// HTTP version
httpVersion?: string;
// Socket information with client IP
socket?: { remoteAddress?: string };
}NextApiResponse Object
The NextApiResponse object provides the following methods and properties:
interface NextApiResponse {
// HTTP status code
statusCode: number;
// Response headers
headers: Record<string, string | string[] | number>;
// Set the status code (chainable)
status(code: number): this;
// Set a response header (chainable)
setHeader(name: string, value: string | string[] | number): this;
// Get a response header
getHeader(name: string): string | string[] | number | undefined;
// Check if headers have been sent
headersSent: boolean;
// Send JSON response
json(body: any): void;
// Send a response
send(body: any): void;
// End the response
end(body?: any): void;
// Write data to response
write(chunk: string | Buffer): void;
// Redirect to a URL
redirect(statusOrUrl: string | number, url?: string): void;
}Examples
Basic JSON API
// pages/api/users.ts
export async function get(req, res) {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
return new Response(JSON.stringify(users), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}
export async function post(req, res) {
const user = req.body;
const newUser = await saveUser(user);
return new Response(JSON.stringify(newUser), {
status: 201,
headers: { 'content-type': 'application/json' }
});
}Query Parameters
// pages/api/search.ts
export async function get(req, res) {
const { q, limit = '10' } = req.query;
if (!q) {
return new Response(JSON.stringify({ error: 'Missing query parameter' }), {
status: 400,
headers: { 'content-type': 'application/json' }
});
}
const results = await search(String(q), parseInt(String(limit)));
return new Response(JSON.stringify(results), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}Dynamic Routes with Params
// pages/api/posts/[id]/comments.ts
export async function get(req, res) {
const { id } = req.query;
const comments = await fetchComments(id);
return new Response(JSON.stringify(comments), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}Request Body
// pages/api/create-user.ts
export async function post(req, res) {
const { name, email } = req.body;
// Validate input
if (!name || !email) {
return new Response(JSON.stringify({ error: 'Missing fields' }), {
status: 400,
headers: { 'content-type': 'application/json' }
});
}
const user = await createUser({ name, email });
res.status(201);
return new Response(JSON.stringify(user), {
status: 201,
headers: { 'content-type': 'application/json' }
});
}Cookies
// pages/api/login.ts
export async function post(req, res) {
const { username, password } = req.body;
const authenticated = await authenticate(username, password);
if (authenticated) {
const sessionId = generateSessionId();
res.setHeader('set-cookie', `session=${sessionId}; Path=/; HttpOnly`);
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}
return new Response(JSON.stringify({ error: 'Invalid credentials' }), {
status: 401,
headers: { 'content-type': 'application/json' }
});
}Error Handling
// pages/api/data.ts
export async function get(req, res) {
try {
const data = await fetchData();
return new Response(JSON.stringify(data), { status: 200 });
} catch (error) {
console.error('Error fetching data:', error);
return new Response(
JSON.stringify({
error: 'Internal server error',
message: error instanceof Error ? error.message : 'Unknown error'
}),
{
status: 500,
headers: { 'content-type': 'application/json' }
}
);
}
}Custom Headers
// pages/api/download.ts
export async function get(req, res) {
const data = await generateReport();
return new Response(data, {
status: 200,
headers: {
'content-type': 'application/pdf',
'content-disposition': 'attachment; filename="report.pdf"'
}
});
}Migration from Next.js
Before (Next.js)
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json({ message: 'Hello World' });
}After (Zuby.js with @zubyjs/next)
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from '@zubyjs/next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
return new Response(JSON.stringify({ message: 'Hello World' }), {
status: 200,
headers: { 'content-type': 'application/json' }
});
}The plugin automatically converts these routes - no need to manually rewrite them!
Version Compatibility
Ensure all zuby packages are in sync in your package.json file:
{
"name": "my-zuby-app",
"version": "1.0.0",
"dependencies": {
"zuby": "latest",
"@zubyjs/preact": "latest",
"@zubyjs/next": "latest"
}
}Contributing
This package is part of Zuby.js workspace and maintained by the team behind the Zuby package. Please refer to it for more details how to contribute.
License
MIT
