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.

plugins/blog/src/api/modules/categories/routes/get.route.ts
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

plugins/blog/src/api/modules/categories/categories.module.ts
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!

plugins/blog/src/api/modules/categories/routes/get_by_id.route.ts
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!

plugins/blog/src/api/modules/categories/routes/search.route.ts
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!

plugins/blog/src/api/modules/categories/routes/create.route.ts
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);
  }
});
Routes - VitNode