@rocket-kit/next
v2.0.4
Published
A powerful Next.js API toolkit that provides type-safe route handlers and server actions with Zod validation, eliminating the need for manual request parsing and validation.
Maintainers
Readme
@rocket-kit/next Documentation
A powerful Next.js API toolkit that provides type-safe route handlers and server actions with Zod validation, eliminating the need for manual request parsing and validation.
🚀 Getting Started
Installation
npm install @rocket-kit/next @rocket-kit/next-resolver-zod zodBasic Setup
import { createRocket } from '@rocket-kit/next';
import { zodResolver } from '@rocket-kit/next-resolver-zod';
const rocket = createRocket({
resolver: zodResolver,
});⚙️ createRocket
The createRocket function is the main entry point of the library. It creates a Rocket instance with the specified resolver for validation.
Parameters
config.resolver: The validation resolver to use (e.g.,zodResolver)config.openApiFormat(optional): Format for OpenAPI documentation ('swagger' | 'openapi')
Example
import { createRocket } from '@rocket-kit/next';
import { zodResolver } from '@rocket-kit/next-resolver-zod';
const rocket = createRocket({
resolver: zodResolver,
openApiFormat: 'openapi',
});🛣️ Route Function
The Route function creates type-safe API route handlers with automatic validation and parsing.
Basic Usage
import { rocket } from './rocket-config';
import { z } from 'zod';
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
});
export const POST = rocket.Route({
schema: {
body: userSchema,
},
handler: async (req, reply) => {
const body = req.getBody(); // Type-safe body
const params = req.getParams(); // Type-safe params
const query = req.getQueryParams(); // Type-safe query params
// Your logic here
return reply.json({ success: true, data: body });
},
});Schema Configuration
The schema object accepts three optional properties:
body: Zod schema for request body validationparams: Zod schema for route parameters validationqueryParams: Zod schema for query parameters validation
Request Methods
The extended request object provides three main methods:
getBody()
Returns the validated and parsed request body.
const body = req.getBody(); // Returns TBodygetParams()
Returns the validated route parameters.
const params = req.getParams(); // Returns TParamsgetQueryParams()
Returns the validated query parameters.
const query = req.getQueryParams(); // Returns TQueryComplete Route Example
import { rocket } from './rocket-config';
import { z } from 'zod';
const createUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().min(18),
});
const userParamsSchema = z.object({
id: z.string().uuid(),
});
const userQuerySchema = z.object({
include: z.enum(['profile', 'posts']).optional(),
});
export const POST = rocket.Route({
schema: {
body: createUserSchema,
params: userParamsSchema,
queryParams: userQuerySchema,
},
handler: async (req, reply) => {
const body = req.getBody(); // { name: string, email: string, age: number }
const params = req.getParams(); // { id: string }
const query = req.getQueryParams(); // { include?: 'profile' | 'posts' }
// All data is type-safe and validated
const user = await createUser(body);
return reply.json({ success: true, user });
},
});⚡ Action Function
The Action function creates type-safe server actions with automatic validation.
Basic Usage
import { rocket } from './rocket-config';
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(1),
content: z.string().min(10),
});
export const createPost = rocket.Action({
schema: createPostSchema,
handler: async (input) => {
// input is type-safe and validated
const post = await db.posts.create(input);
return post;
},
});Action without Schema
export const simpleAction = rocket.Action({
handler: async (input) => {
// input is any type when no schema is provided
return { message: 'Success' };
},
});🔧 Zod Integration
@rocket-kit/next is built with Zod v4 and provides seamless integration for type-safe validation.
What are Resolvers?
Resolvers are functions that handle the validation and parsing of incoming data. They take raw input and a schema, then return validated and typed data.
zodResolver
The zodResolver is the primary resolver that uses Zod schemas for validation.
import { zodResolver } from '@rocket-kit/next-resolver-zod';
const rocket = createRocket({
resolver: zodResolver,
});Custom Resolvers
You can create custom resolvers for different validation libraries:
import { ResolverFunction } from '@rocket-kit/next';
const customResolver: ResolverFunction = {
route: async (req, schemas) => {
// Custom validation logic
return extendedReq;
},
action: async (input, schema) => {
// Custom validation logic
return validatedInput;
},
};
const rocket = createRocket({
resolver: customResolver,
});Zod Schema Examples
import { z } from 'zod';
// Basic validation
const userSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().min(18).max(120),
});
// Complex validation
const orderSchema = z.object({
items: z.array(
z.object({
id: z.string().uuid(),
quantity: z.number().positive(),
price: z.number().positive(),
}),
),
shipping: z.object({
address: z.string(),
city: z.string(),
country: z.string(),
}),
payment: z.object({
method: z.enum(['credit_card', 'paypal']),
cardNumber: z
.string()
.regex(/^\d{16}$/)
.optional(),
}),
});
// Conditional validation
const conditionalSchema = z
.object({
type: z.enum(['individual', 'company']),
name: z.string(),
email: z.string().email(),
companyName: z.string().optional(),
taxId: z.string().optional(),
})
.refine(
(data) => {
if (data.type === 'company') {
return data.companyName && data.taxId;
}
return true;
},
{
message: 'Company name and tax ID are required for company accounts',
},
);📝 Examples
Complete API Route Example
// app/api/users/[id]/posts/route.ts
import { rocket } from '@/lib/rocket-config';
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(1).max(100),
content: z.string().min(10),
tags: z.array(z.string()).optional(),
});
const postParamsSchema = z.object({
id: z.string().uuid(),
});
const postQuerySchema = z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(50).default(10),
sort: z.enum(['created_at', 'title']).default('created_at'),
});
export const POST = rocket.Route({
schema: {
body: createPostSchema,
params: postParamsSchema,
queryParams: postQuerySchema,
},
handler: async (req, reply) => {
const body = req.getBody();
const params = req.getParams();
const query = req.getQueryParams();
const post = await createPost({
...body,
userId: params.id,
});
return reply.json({ success: true, post });
},
});
export const GET = rocket.Route({
schema: {
params: postParamsSchema,
queryParams: postQuerySchema,
},
handler: async (req, reply) => {
const params = req.getParams();
const query = req.getQueryParams();
const posts = await getPosts({
userId: params.id,
page: query.page,
limit: query.limit,
sort: query.sort,
});
return reply.json({ success: true, posts });
},
});Server Action Example
// app/actions/user-actions.ts
import { rocket } from '@/lib/rocket-config';
import { z } from 'zod';
const updateProfileSchema = z.object({
name: z.string().min(2).optional(),
email: z.string().email().optional(),
bio: z.string().max(500).optional(),
avatar: z.string().url().optional(),
});
export const updateProfile = rocket.Action({
schema: updateProfileSchema,
handler: async (input) => {
const userId = await getCurrentUserId();
const updatedUser = await updateUser(userId, input);
return updatedUser;
},
});
const deleteAccountSchema = z.object({
password: z.string().min(8),
confirm: z.literal('DELETE'),
});
export const deleteAccount = rocket.Action({
schema: deleteAccountSchema,
handler: async (input) => {
if (input.confirm !== 'DELETE') {
throw new Error('Confirmation required');
}
const userId = await getCurrentUserId();
await deleteUser(userId, input.password);
return { success: true };
},
});Error Handling
export const POST = rocket.Route({
schema: {
body: userSchema,
},
handler: async (req, reply) => {
try {
const body = req.getBody();
const user = await createUser(body);
return reply.json({ success: true, user });
} catch (error) {
if (error instanceof ValidationError) {
return reply.json(
{ error: 'Validation failed', details: error.details },
{ status: 400 },
);
}
return reply.json({ error: 'Internal server error' }, { status: 500 });
}
},
});TypeScript Benefits
// Full type inference
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number(),
});
type User = z.infer<typeof userSchema>; // Automatically inferred
export const POST = rocket.Route({
schema: {
body: userSchema,
},
handler: async (req, reply) => {
const body = req.getBody(); // Type: User
// TypeScript will provide full IntelliSense
console.log(body.name); // ✅ Type-safe
console.log(body.email); // ✅ Type-safe
console.log(body.age); // ✅ Type-safe
// console.log(body.invalid); // ❌ TypeScript error
},
});🎯 Key Benefits
- 🔒 Type Safety: Full TypeScript support with automatic type inference
- ✅ Validation: Automatic request validation using Zod v4
- 🚀 Performance: Zero runtime overhead for type checking
- 📚 Documentation: Built-in OpenAPI documentation generation
- 🛠️ Developer Experience: Excellent IntelliSense and error messages
- 🔧 Flexibility: Support for custom resolvers and validation libraries
📦 Package Information
- Version: 2.0.2
- License: MIT
- Repository: GitHub
- NPM: @rocket-kit/next
