byte5digital

payload assist

by byte5digital
APIs & SDKs
v3
^3.0.0
MIT

Overview

Summary generated from the README

Wraps your Payload config to validate it against configurable rules at boot, such as requiring GraphQL to be disabled. It provides a Dto base class, a transformAndValidate helper built on class-transformer, a withResponse wrapper for custom endpoints, and a withDtoReadHook for collection afterRead hooks. It is used as a config wrapper rather than a plugin and requires Payload 3 and Next 15.

Features

  • Boot-time config validation with built-in rules and custom rule sets
  • Base Dto class with transformAndValidate based on class-transformer
  • withResponse wrapper that enforces DTO-only JSON responses in custom endpoints
  • withDtoReadHook selects a DTO per read using conditions such as user role
  • Built-in rules for disabling GraphQL and requiring withResponse and withDtoReadHook
  • AccessControl helper type covering all collection access methods

Use cases

  • Restrict API responses to explicitly exposed fields so internal data does not leak
  • Enforce project conventions across collections by failing startup when a rule is broken
  • Return different response shapes for admins and public users from the same collection

From the author

A TypeScript library providing DTO utilities and validation hooks for Payload CMS

Installation

npm i @byte5digital/payload-assist

0

Stars

0

Forks

0

Open Issues

5 months ago

Last Updated

9

Weekly Downloads

v1.2.8

npm Version

40.3 KB

Unpacked Size

2

Dependencies

GitHub stars

Trends appear after 7 days of data. Daily snapshots have not started yet.

npm weekly downloads

Trends appear after 7 days of data. Daily snapshots have not started yet.

Each point is npm's rolling 7-day download count.

Health Score

53/100Good

Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.

Badge for your README

Payload Directory health badge
Markdown
[![Payload Directory](https://payloaddirectory.dev/badge/1049574899-root.svg)](https://payloaddirectory.dev/plugins/1049574899-root)
HTML
<a href="https://payloaddirectory.dev/plugins/1049574899-root"><img src="https://payloaddirectory.dev/badge/1049574899-root.svg" alt="Payload Directory"></a>
Updated Apr 27, 2026Created Sep 3, 2025Published Apr 27, 2026

README

Assist for Payload
Assist for Payload Tests passing License MIT

Assist for Payload

Utilities to add guardrails, DTO tooling, and ergonomic rules to Payload CMS projects.

  • Rules: Validate your Payload config at boot (e.g., kebab-case slugs).
  • DTOs: First-class helpers to define, transform, validate, and enforce DTO-only responses.
  • Ergonomics: Thin wrappers to keep endpoints and collection reads consistent and secure.

Installation

yarn add @byte5digital/payload-assist
# or
npm install @byte5digital/payload-assist

Peer deps: Payload v3+, Next v15+. Dependencies class-transformer and reflect-metadata are included in the package.

API overview

  • payloadAssist(payloadConfig, options?): Main function that initializes Assist for Payload, validates your payload config against defined rules, and returns the built config.
  • defaultConfig: Default config with built-in rules and transformAndValidate function (can be spread/overridden).
  • Dto: Base abstract class for response DTOs.
  • transformAndValidate(Dto, data): Validate Payload data and transform to DTO. Uses the configured transformer/validator.
  • withResponse(handler): Enforce DTO-only JSON responses in your custom payload endpoints.
  • withDtoReadHook([{ dto, condition }, { dto }]): Attach to collections to return DTOs from read operations with conditional DTO selection.

Helper Types

AccessControl

A comprehensive type for Payload collection access control that includes all available access control methods.

import { AccessControl } from "@byte5digital/payload-assist";

export const MyCollection: CollectionConfig = {
  slug: "my-collection",
  access: {
    admin: ({ req }) => req.user?.role === "admin",
    create: ({ req }) => req.user?.role === "admin",
    read: ({ req }) => true, // public read
    update: ({ req }) => req.user?.role === "admin",
    delete: ({ req }) => req.user?.role === "admin",
  } satisfies AccessControl,
  // ...fields
};

Usage

Initialize Assist for Payload

The main payloadAssist function initializes the library, validates your payload config against defined rules, and returns the built config. You can customize the ruleSet and transformAndValidate function through options. payloadAssist is implemenented as a wrapper function and not as a payload plugin, to ensure it validates the raw config that is set by the user, instead of the config that was previously processed by other plugins and enriched by payload.

  • ruleSet: An object map of named rules; merge defaults with your own, if required. Deactivate a default rule by setting the rule to false.
  • rules: (config: payloadConfig) => boolean | void; throw to fail with an actionable message and return true if the rule is satisfied.
  • transformAndValidate: (dto: Dto, data: unknown) => Dto Turn raw Payload data into typed DTOs.

Built-in rules:

  • disableQraphQL: Ensures GraphQL is disabled in your config
  • collectionsEndpointsUseWithResponse: Ensures all collection endpoints use withResponse
  • collectionsUseWithDtoReadHook: Ensures all collections use withDtoReadHook in their afterRead hooks
import { buildConfig } from "payload";
import payloadAssist, {
  defaultConfig,
  PayloadAssistError,
} from "@byte5digital/payload-assist";

export default payloadAssist(
  {
    // your Payload config
  },
  {
    ruleSet: {
      ...defaultConfig.ruleSet,

      // add/override rules here
      secretIsSet: (config) => {
        if (config.secret?.length > 0) return true;
        throw new PayloadAssistError("A secret needs to be set");
      },
    },
  }
);

DTOs: Purpose and usage

Define exactly what leaves your API by modeling responses as DTOs. Only explicitly exposed fields and nested DTOs are serialized. It is important that all DTOs extend the Dto class. The example below shows the usage with the default transformAndValidate.

import { Dto, Expose, Type } from "@byte5digital/payload-assist";

export class MediaResponse extends Dto {
  @Expose() url: string;
  @Expose() mimeType: string;
}

export class UserResponse extends Dto {
  @Expose() id: number;
  @Expose() name: string;
}

export class MyCollectionDto extends Dto {
  @Expose() name: string;
  @Expose() @Type(() => MediaResponse) image: MediaResponse;
  @Expose() @Type(() => UserResponse) owner: UserResponse;
}

Transform any raw Payload doc into a DTO. By default transformAndValidate uses class-transformer, but it can be configured through the payloadAssist options.

import { transformAndValidate } from "@byte5digital/payload-assist";

const payloadDoc = await getPayloadDoc();
const dto = transformAndValidate(MyCollectionDto, payloadDoc);

Enforcing DTO-only responses in endpoints

Use withResponse to guarantee your endpoints return DTOs (and nothing else). It centralizes transform/serialize and standardizes error responses. The transformation strategy is configurable.

import payload from "payload";
import {
  withResponse,
  transformAndValidate,
} from "@byte5digital/payload-assist";
import { MyDataDto } from "path/to/dtos";

export const MyCollection: CollectionConfig = {
  slug: "my-collection",
  endpoints: [
    {
      path: "/my-custom-endpoint",
      method: "get",
      handler: withResponse(async (req: PayloadRequest) => {
        const myData = await req.payload.find({
          collection: "my-collection",
          // ...additional filter logic
        });

        const myDataDto = transformAndValidate(MyDataDto, myData);
        return {
          response: myDataDto,
          status: 200,
        };
      }),
    },
  ],

  // ...fields
};

Enforcing DTO-only responses from collections

Attach withDtoReadHook to your collections afterReadHooks to transform read results automatically to the given DTOs. Multiple objects with DTOs can be passed, when they include a condition, only the last item can be without a condition. The first DTO where the condition is met will be used or the DTO without a condition, if given. If none applies, null will be returned. So the order of the given DTOs should be: More specific first, default last.

// src/collections/MyCollection.ts
import { CollectionConfig } from "payload/types";
import { withDtoReadHook } from "@byte5digital/payload-assist";
import { MyCollectionDto, MyCollectionAdminDto } from "path/to/dtos";

export const MyCollection: CollectionConfig = {
  slug: "my-collection",
  hooks: {
    afterRead: [
      withDtoReadHook(
        {
          dto: MyCollectionAdminDto,
          condition: ({ req: { user } }) => user?.role === "admin",
        },
        {
          dto: MyCollectionDto,
        }
      ),
    ],
  },
  // ...fields
};

License

MIT

About byte5

We're a development company based in Frankfurt, Germany — remote-friendly, open-minded, and tech-driven. Our team brings deep expertise in Node.js, MedusaJS, Laravel, Umbraco, and decentralized tech like IOTA. We collaborate with clients who care about clean code, scalable solutions, and long-term maintainability.

We contribute to open source, run Laravel DACH Meetups, and support developer communities across the DACH region. Our expertise in e-commerce platforms makes us the perfect partner for building robust, scalable solutions.

If you love building smart solutions with real impact — we should talk.

Connect with us:

Support


Built with 🩵 by byte5

Similar plugins