REST API
Routes
xxx
Usage
Now for the fun part - creating actual endpoints! Each route is a small but mighty function that handles HTTP requests.
import { z } from "@hono/zod-openapi";
import { buildRoute } from "@vitnode/core/api/lib/route";
import { CONFIG_PLUGIN } from "@/config";
export const getCategoriesRoute = buildRoute({
pluginId: CONFIG_PLUGIN.pluginId,
route: {
method: "get",
path: "/",
responses: {
200: {
content: {
"application/json": {
schema: z.object({
categories: z.array(
z.object({
id: z.string(),
name: z.string(),
description: z.string().optional()
})
)
})
}
},
description: "Successfully retrieved categories"
}
}
},
handler: (c) => {
// Your business logic goes here
return c.json({
categories: [
{ id: "1", name: "Technology", description: "All things tech" },
{ id: "2", name: "Lifestyle" }
]
});
}
});Connecting Routes to Modules
import { buildModule } from "@vitnode/core/api/lib/module";
import { CONFIG_PLUGIN } from "@/config";
import { getCategoriesRoute } from "./routes/get.route";
export const categoriesModule = buildModule({
pluginId: CONFIG_PLUGIN.id,
name: "categories",
routes: [getCategoriesRoute]
});Inputs
Path Parameters
Path parameters are perfect when you need to identify specific resources. They're like the ID card of your API endpoints!
import { z } from "@hono/zod-openapi";
import { buildRoute } from "@vitnode/core/api/lib/route";
import { HTTPException } from "hono/http-exception";
import { CONFIG_PLUGIN } from "@/config";
export const getCategoryByIdRoute = buildRoute({
pluginId: CONFIG_PLUGIN.pluginId,
route: {
method: "get",
path: "/{id}", // Dynamic path parameter
request: {
params: z.object({
id: z.string().openapi({
description: "Unique identifier for the category",
example: "tech-category-123"
})
})
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
id: z.string(),
name: z.string(),
description: z.string().optional()
})
}
},
description: "Category details retrieved successfully"
},
404: {
description: "Category not found"
}
}
},
handler: (c) => {
const { id } = c.req.valid("param"); // Extract the path parameter
// Simulate database lookup
if (id === "nonexistent") {
throw new HTTPException(404);
}
return c.json({
id,
name: `Category ${id}`,
description: "A fantastic category for amazing content"
});
}
});Query Parameters
Query parameters are your best friends for filtering, searching, and pagination. They make your API flexible and user-friendly!
import { z } from "@hono/zod-openapi";
import { buildRoute } from "@vitnode/core/api/lib/route";
import { CONFIG_PLUGIN } from "@/config";
export const searchCategoriesRoute = buildRoute({
pluginId: CONFIG_PLUGIN.pluginId,
route: {
method: "get",
path: "/search",
request: {
query: z.object({
search: z.string().optional().openapi({
description: "Search term to filter categories",
example: "technology"
})
})
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
categories: z.array(
z.object({
id: z.string(),
name: z.string(),
description: z.string().optional()
})
),
total: z.number()
})
}
},
description: "Search results with pagination info"
}
}
},
handler: (c) => {
const { search } = c.req.valid("query");
// Your search logic here
const mockResults = [
{ id: "1", name: "Technology", description: "Tech-related posts" },
{ id: "2", name: "Lifestyle" }
];
const filteredResults = search
? mockResults.filter((cat) => cat.name.toLowerCase().includes(search.toLowerCase()))
: mockResults;
const paginatedResults = filteredResults.slice(offset, offset + limit);
return c.json({
categories: paginatedResults,
total: filteredResults.length
});
}
});Request Body (JSON Payload)
When you need to send complex data (creating, updating), request bodies are your go-to solution. Perfect for forms and JSON payloads!
import { z } from "@hono/zod-openapi";
import { buildRoute } from "@vitnode/core/api/lib/route";
import { CONFIG_PLUGIN } from "@/config";
const createCategorySchema = z.object({
name: z.string().min(1).max(100).openapi({
description: "Name of the category",
example: "Web Development"
}),
description: z.string().optional().openapi({
description: "Optional description for the category",
example: "Everything about building websites and web applications"
}),
color: z
.string()
.regex(/^#[0-9A-F]{6}$/i)
.optional()
.openapi({
description: "Hex color code for the category",
example: "#3B82F6"
})
});
export const createCategoryRoute = buildRoute({
pluginId: CONFIG_PLUGIN.pluginId,
route: {
method: "post",
path: "/",
request: {
body: {
content: {
"application/json": {
schema: createCategorySchema
}
},
description: "Category data to create",
required: true
}
},
responses: {
201: {
content: {
"application/json": {
schema: z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
color: z.string().optional(),
createdAt: z.string()
})
}
},
description: "Category created successfully"
},
400: {
description: "Invalid input data"
}
}
},
handler: async (c) => {
const data = c.req.valid("json");
// Simulate category creation
const newCategory = {
id: `cat_${Date.now()}`,
...data,
createdAt: new Date().toISOString()
};
return c.json(newCategory, 201);
}
});