SkillAgentSearch skills...

endpoints

Custom REST API endpoints with authentication and helpers

Install / Use

npx skills add sanjayguwaju/himalayascontinental

Installs into whichever agent you are using.

About this skill
📐

Cursor Rules

Cursor IDE rules (v2)

Quality Score

59/100

Supported Platforms

Cursor

title: Custom Endpoints description: Custom REST API endpoints with authentication and helpers tags: [payload, endpoints, api, routes, webhooks]

Payload Custom Endpoints

Basic Endpoint Pattern

Custom endpoints are not authenticated by default. Always check req.user.

import { APIError } from "payload";
import type { Endpoint } from "payload";

export const protectedEndpoint: Endpoint = {
  path: "/protected",
  method: "get",
  handler: async (req) => {
    if (!req.user) {
      throw new APIError("Unauthorized", 401);
    }

    // Use req.payload for database operations
    const data = await req.payload.find({
      collection: "posts",
      where: { author: { equals: req.user.id } },
    });

    return Response.json(data);
  },
};

Route Parameters

export const trackingEndpoint: Endpoint = {
  path: "/:id/tracking",
  method: "get",
  handler: async (req) => {
    const { id } = req.routeParams;

    const tracking = await getTrackingInfo(id);

    if (!tracking) {
      return Response.json({ error: "not found" }, { status: 404 });
    }

    return Response.json(tracking);
  },
};

Request Body Handling

// Manual JSON parsing
export const createEndpoint: Endpoint = {
  path: "/create",
  method: "post",
  handler: async (req) => {
    const data = await req.json();

    const result = await req.payload.create({
      collection: "posts",
      data,
    });

    return Response.json(result);
  },
};

// Using helper (handles JSON + files)
import { addDataAndFileToRequest } from "payload";

export const uploadEndpoint: Endpoint = {
  path: "/upload",
  method: "post",
  handler: async (req) => {
    await addDataAndFileToRequest(req);

    // req.data contains parsed body
    // req.file contains uploaded file (if multipart)

    const result = await req.payload.create({
      collection: "media",
      data: req.data,
      file: req.file,
    });

    return Response.json(result);
  },
};

Query Parameters

export const searchEndpoint: Endpoint = {
  path: "/search",
  method: "get",
  handler: async (req) => {
    const url = new URL(req.url);
    const query = url.searchParams.get("q");
    const limit = parseInt(url.searchParams.get("limit") || "10");

    const results = await req.payload.find({
      collection: "posts",
      where: {
        title: {
          contains: query,
        },
      },
      limit,
    });

    return Response.json(results);
  },
};

CORS Headers

import { headersWithCors } from "payload";

export const corsEndpoint: Endpoint = {
  path: "/public-data",
  method: "get",
  handler: async (req) => {
    const data = await fetchPublicData();

    return Response.json(data, {
      headers: headersWithCors({
        headers: new Headers(),
        req,
      }),
    });
  },
};

Error Handling

import { APIError } from "payload";

export const validateEndpoint: Endpoint = {
  path: "/validate",
  method: "post",
  handler: async (req) => {
    const data = await req.json();

    if (!data.email) {
      throw new APIError("Email is required", 400);
    }

    return Response.json({ valid: true });
  },
};

Endpoint Placement

Collection Endpoints

Mounted at /api/{collection-slug}/{path}.

export const Orders: CollectionConfig = {
  slug: "orders",
  endpoints: [
    {
      path: "/:id/tracking",
      method: "get",
      handler: async (req) => {
        // Available at: /api/orders/:id/tracking
        const orderId = req.routeParams.id;
        return Response.json({ orderId });
      },
    },
  ],
};

Global Endpoints

Mounted at /api/globals/{global-slug}/{path}.

export const Settings: GlobalConfig = {
  slug: "settings",
  endpoints: [
    {
      path: "/clear-cache",
      method: "post",
      handler: async (req) => {
        // Available at: /api/globals/settings/clear-cache
        await clearCache();
        return Response.json({ message: "Cache cleared" });
      },
    },
  ],
};

Root Endpoints

Mounted at /api/{path}.

export default buildConfig({
  endpoints: [
    {
      path: "/hello",
      method: "get",
      handler: () => {
        // Available at: /api/hello
        return Response.json({ message: "Hello!" });
      },
    },
  ],
});

Best Practices

  1. Always check authentication - Custom endpoints are not authenticated by default
  2. Use req.payload for operations - Ensures access control and hooks execute
  3. Use helpers for common tasks - addDataAndFileToRequest, headersWithCors
  4. Throw APIError for errors - Provides consistent error responses
  5. Return Web API Response - Use Response.json() for consistent responses
  6. Validate input - Check required fields, validate types
  7. Log errors - Use req.payload.logger for debugging

Related Skills

View on GitHub
GitHub Stars0
CategoryDevelopment
UpdatedNaNy ago
Forks0

Security Score

68/100

Audited on Invalid Date

2 medium1 low