Node, React and MongoDB Headless CMS and Application Framework
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive configuration system for defining collections, globals, fields, authentication, and admin settings. The configuration serves as the foundation for your entire Payload CMS setup.
Core function for building and validating Payload configuration with plugin support.
/**
* Builds and validates Payload configuration with plugin processing
* @param config - The Payload configuration object
* @returns Sanitized and validated configuration
*/
function buildConfig(config: Config): SanitizedConfig;
/**
* Plugin function type for extending Payload functionality
*/
type Plugin = (config: Config) => Config;Usage Examples:
import { buildConfig } from "payload/config";
const config = buildConfig({
collections: [
// your collections
],
globals: [
// your globals
],
plugins: [
// plugin functions that modify config
myCustomPlugin(),
anotherPlugin(options),
],
// ... other config options
});
// Plugin example
function myCustomPlugin(): Plugin {
return (config: Config) => {
// Modify and return config
return {
...config,
// Add custom collections, modify existing config, etc.
};
};
}The primary configuration object that defines your entire Payload setup.
/**
* Main Payload configuration interface
*/
interface Config {
/** Base URL for your server (required for emails and admin) */
serverURL?: string;
/** Array of collection configurations */
collections?: CollectionConfig[];
/** Array of global configurations */
globals?: GlobalConfig[];
/** Admin panel configuration */
admin?: AdminConfig;
/** Authentication configuration */
auth?: AuthConfig;
/** Email service configuration */
email?: EmailConfig;
/** File upload configuration */
upload?: UploadConfig;
/** Internationalization configuration */
localization?: LocalizationConfig;
/** TypeScript configuration */
typescript?: TypeScriptConfig;
/** GraphQL configuration */
graphQL?: GraphQLConfig;
/** CORS configuration */
cors?: CORSConfig;
/** CSRF protection configuration */
csrf?: CSRFConfig;
/** Express configuration */
express?: ExpressConfig;
/** API route configuration */
routes?: {
/** Admin panel route (default: "/admin") */
admin?: string;
/** API base route (default: "/api") */
api?: string;
/** GraphQL endpoint (default: "/api/graphql") */
graphQL?: string;
/** GraphQL playground route (default: "/api/graphql-playground") */
graphQLPlayground?: string;
};
/** Rate limiting configuration */
rateLimit?: RateLimitConfig;
/** Global hooks */
hooks?: {
/** Hook called after errors occur */
afterError?: AfterErrorHook;
};
/** Enable/disable telemetry */
telemetry?: boolean;
/** Enable debug mode */
debug?: boolean;
/** Logger configuration */
loggerOptions?: LoggerOptions;
}Basic Configuration Example:
import { Config } from "payload/config";
const config: Config = {
serverURL: "http://localhost:3000",
collections: [
{
slug: "posts",
fields: [
{
name: "title",
type: "text",
required: true,
},
{
name: "content",
type: "richText",
},
],
},
],
globals: [
{
slug: "header",
fields: [
{
name: "title",
type: "text",
},
],
},
],
};
export default config;Configuration for custom REST API endpoints that extend Payload's built-in API.
/**
* Custom endpoint configuration
*/
interface Endpoint {
/** URL path for the endpoint */
path: string;
/** HTTP method */
method: 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete' | 'connect' | 'options' | string;
/** Handler function(s) for the endpoint */
handler: PayloadHandler | PayloadHandler[];
}
/**
* Payload request handler function
*/
interface PayloadHandler {
(req: PayloadRequest, res: Response, next: NextFunction): void;
}Usage Examples:
const config: Config = {
// ... other config
endpoints: [
{
path: '/custom-api',
method: 'get',
handler: (req, res, next) => {
res.json({ message: 'Custom endpoint' });
},
},
{
path: '/webhook',
method: 'post',
handler: [
// Multiple handlers can be used
authMiddleware,
webhookHandler,
],
},
],
};Configuration for custom admin panel routes and components.
/**
* Custom admin view component type
*/
type AdminView = React.ComponentType<{
user: User;
canAccessAdmin: boolean;
}>;
/**
* Custom admin route configuration
*/
interface AdminRoute {
/** React component to render */
Component: AdminView;
/** URL path for the route */
path: string;
/** Exact path matching (default: false) */
exact?: boolean;
/** Strict path matching (default: false) */
strict?: boolean;
}Usage Examples:
import CustomDashboard from './CustomDashboard';
import AnalyticsView from './AnalyticsView';
const config: Config = {
// ... other config
admin: {
// ... other admin config
routes: [
{
Component: CustomDashboard,
path: '/custom-dashboard',
exact: true,
},
{
Component: AnalyticsView,
path: '/analytics',
},
],
},
};Configuration for content collections (e.g., posts, products, users).
interface CollectionConfig {
/** Unique identifier for the collection */
slug: string;
/** Array of field configurations */
fields: Field[];
/** Display labels for the collection */
labels?: {
singular?: string;
plural?: string;
};
/** Admin panel configuration */
admin?: CollectionAdminConfig;
/** Authentication configuration (makes collection auth-enabled) */
auth?: AuthConfig | boolean;
/** Access control rules */
access?: CollectionAccessConfig;
/** Collection-level hooks */
hooks?: CollectionHooks;
/** File upload configuration */
upload?: UploadConfig | boolean;
/** Document versioning configuration */
versions?: VersionsConfig | boolean;
/** Automatic timestamp fields */
timestamps?: boolean;
/** Database collection name override */
dbName?: string;
/** Default sort field */
defaultSort?: string;
/** Disable duplicate checking */
disableDuplicate?: boolean;
/** GraphQL configuration */
graphQL?: {
singularName?: string;
pluralName?: string;
};
/** TypeScript configuration */
typescript?: {
interface?: string;
};
}
interface CollectionAdminConfig {
/** Default columns to show in list view */
defaultColumns?: string[];
/** Description shown in admin */
description?: string;
/** Fields to use for document preview */
useAsTitle?: string;
/** Custom pagination limit */
pagination?: {
defaultLimit?: number;
limits?: number[];
};
/** List view configuration */
listSearchableFields?: string[];
/** Group collections in admin sidebar */
group?: string;
/** Hide collection from admin */
hidden?: boolean;
/** Disable create operation in admin */
disableCreate?: boolean;
/** Disable edit operation in admin */
disableEdit?: boolean;
/** Disable delete operation in admin */
disableDelete?: boolean;
/** Custom admin components */
components?: {
Edit?: React.ComponentType;
List?: React.ComponentType;
};
}Collection Configuration Example:
const PostsCollection: CollectionConfig = {
slug: "posts",
labels: {
singular: "Post",
plural: "Posts",
},
admin: {
defaultColumns: ["title", "status", "createdAt"],
useAsTitle: "title",
description: "Blog posts and articles",
group: "Content",
},
access: {
read: () => true,
create: ({ req: { user } }) => !!user,
update: ({ req: { user } }) => !!user,
delete: ({ req: { user } }) => !!user,
},
fields: [
{
name: "title",
type: "text",
required: true,
},
{
name: "content",
type: "richText",
required: true,
},
{
name: "status",
type: "select",
options: [
{ label: "Draft", value: "draft" },
{ label: "Published", value: "published" },
],
defaultValue: "draft",
},
],
hooks: {
beforeChange: [
({ data, operation }) => {
if (operation === "create") {
data.createdBy = req.user.id;
}
return data;
},
],
},
versions: {
drafts: true,
maxPerDoc: 10,
},
timestamps: true,
};Configuration for global documents (singleton content like site settings).
interface GlobalConfig {
/** Unique identifier for the global */
slug: string;
/** Array of field configurations */
fields: Field[];
/** Display label for the global */
label?: string;
/** Admin panel configuration */
admin?: GlobalAdminConfig;
/** Access control rules */
access?: GlobalAccessConfig;
/** Global-level hooks */
hooks?: GlobalHooks;
/** Document versioning configuration */
versions?: VersionsConfig | boolean;
/** Automatic timestamp fields */
timestamps?: boolean;
/** Database collection name override */
dbName?: string;
/** GraphQL configuration */
graphQL?: {
name?: string;
};
/** TypeScript configuration */
typescript?: {
interface?: string;
};
}
interface GlobalAdminConfig {
/** Description shown in admin */
description?: string;
/** Group globals in admin sidebar */
group?: string;
/** Hide global from admin */
hidden?: boolean;
/** Custom admin components */
components?: {
Edit?: React.ComponentType;
};
}Global Configuration Example:
const HeaderGlobal: GlobalConfig = {
slug: "header",
label: "Site Header",
admin: {
description: "Configure the site header content",
group: "Layout",
},
access: {
read: () => true,
update: ({ req: { user } }) => user?.role === "admin",
},
fields: [
{
name: "logo",
type: "upload",
relationTo: "media",
},
{
name: "navigation",
type: "array",
fields: [
{
name: "label",
type: "text",
required: true,
},
{
name: "url",
type: "text",
required: true,
},
],
},
],
versions: true,
};Fine-grained access control for collections and globals.
interface CollectionAccessConfig {
/** Control who can read documents */
read?: AccessFunction;
/** Control who can create documents */
create?: AccessFunction;
/** Control who can update documents */
update?: AccessFunction;
/** Control who can delete documents */
delete?: AccessFunction;
/** Control admin panel access */
admin?: AccessFunction;
}
interface GlobalAccessConfig {
/** Control who can read the global */
read?: AccessFunction;
/** Control who can update the global */
update?: AccessFunction;
/** Control admin panel access */
admin?: AccessFunction;
}
type AccessFunction = (args: {
req: PayloadRequest;
data?: any;
id?: string;
}) => boolean | Promise<boolean> | Where;Access Control Examples:
// Collection access control
const access: CollectionAccessConfig = {
// Public read access
read: () => true,
// Only authenticated users can create
create: ({ req: { user } }) => !!user,
// Users can only update their own posts
update: ({ req: { user } }) => {
if (user?.role === "admin") return true;
return {
author: {
equals: user?.id,
},
};
},
// Only admins can delete
delete: ({ req: { user } }) => user?.role === "admin",
// Admin access based on role
admin: ({ req: { user } }) => user?.role === "admin",
};
// Field-level access control
const titleField: Field = {
name: "title",
type: "text",
access: {
read: () => true,
update: ({ req: { user } }) => user?.role === "admin",
},
};Collection and global hooks for extending functionality.
interface CollectionHooks {
/** Before operation hooks */
beforeOperation?: BeforeOperationHook[];
/** Before validation hooks */
beforeValidate?: BeforeValidateHook[];
/** Before change hooks */
beforeChange?: BeforeChangeHook[];
/** After change hooks */
afterChange?: AfterChangeHook[];
/** Before read hooks */
beforeRead?: BeforeReadHook[];
/** After read hooks */
afterReadHook?: AfterReadHook[];
/** Before delete hooks */
beforeDelete?: BeforeDeleteHook[];
/** After delete hooks */
afterDelete?: AfterDeleteHook[];
/** Auth-specific hooks */
beforeLogin?: BeforeLoginHook[];
afterLogin?: AfterLoginHook[];
afterForgotPassword?: AfterForgotPasswordHook[];
}
type BeforeChangeHook = (args: {
data: any;
req: PayloadRequest;
operation: "create" | "update";
originalDoc?: any;
}) => any | Promise<any>;
type AfterChangeHook = (args: {
doc: any;
req: PayloadRequest;
previousDoc?: any;
operation: "create" | "update";
}) => any | Promise<any>;Admin panel configuration and customization.
interface AdminConfig {
/** Custom bundled admin build */
bundler?: Bundler;
/** Custom CSS file path */
css?: string;
/** Custom SCSS file path */
scss?: string;
/** Date display format */
dateFormat?: string;
/** Disable admin completely */
disable?: boolean;
/** Custom login logo */
logoBg?: string;
/** Custom login view */
components?: {
Nav?: React.ComponentType;
Dashboard?: React.ComponentType;
logout?: React.ComponentType;
};
/** Custom admin meta tags */
meta?: {
titleSuffix?: string;
ogImage?: string;
favicon?: string;
};
/** User collection slug for admin authentication */
user?: string;
/** Avatar configuration */
avatar?: "default" | "gravatar" | React.ComponentType;
/** Pagination settings */
pagination?: {
defaultLimit?: number;
limits?: number[];
};
}Comprehensive hook system for extending collection behavior at different lifecycle stages.
/**
* Collection hooks configuration
*/
interface CollectionHooks {
/** Before any operation starts */
beforeOperation?: BeforeOperationHook[];
/** Before data validation */
beforeValidate?: BeforeValidateHook[];
/** Before data change (create/update) */
beforeChange?: BeforeChangeHook[];
/** After data change (create/update) */
afterChange?: AfterChangeHook[];
/** Before document read */
beforeRead?: BeforeReadHook[];
/** After document read */
afterRead?: AfterReadHook[];
/** Before document deletion */
beforeDelete?: BeforeDeleteHook[];
/** After document deletion */
afterDelete?: AfterDeleteHook[];
/** After operation error */
afterError?: AfterErrorHook;
/** Before user login (auth collections only) */
beforeLogin?: BeforeLoginHook[];
/** After user login (auth collections only) */
afterLogin?: AfterLoginHook[];
/** After user logout (auth collections only) */
afterLogout?: AfterLogoutHook[];
/** After "me" operation (auth collections only) */
afterMe?: AfterMeHook[];
/** After token refresh (auth collections only) */
afterRefresh?: AfterRefreshHook[];
/** After forgot password (auth collections only) */
afterForgotPassword?: AfterForgotPasswordHook[];
}
/**
* Hook called before any operation
*/
type BeforeOperationHook = (args: {
/** Operation arguments */
args?: any;
/** Operation type being performed */
operation: 'create' | 'read' | 'update' | 'delete' | 'login' | 'refresh' | 'logout' | 'me' | 'forgotPassword' | 'resetPassword';
}) => any;
/**
* Hook called before data validation
*/
type BeforeValidateHook<T = any> = (args: {
/** Document data to be validated */
data?: Partial<T>;
/** Express request object */
req?: PayloadRequest;
/** Operation type */
operation: 'create' | 'update';
/** Original document (for updates) */
originalDoc?: T;
}) => any;
/**
* Hook called before data changes are saved
*/
type BeforeChangeHook<T = any> = (args: {
/** Document data to be saved */
data: Partial<T>;
/** Express request object */
req: PayloadRequest;
/** Operation type */
operation: 'create' | 'update';
/** Original document (for updates) */
originalDoc?: T;
}) => any;
/**
* Hook called after data changes are saved
*/
type AfterChangeHook<T = any> = (args: {
/** Saved document */
doc: T;
/** Express request object */
req: PayloadRequest;
/** Operation type */
operation: 'create' | 'update';
/** Original document (for updates) */
previousDoc?: T;
}) => void;
/**
* Hook called before document read
*/
type BeforeReadHook = (args: {
/** Express request object */
req: PayloadRequest;
/** Query arguments */
query?: any;
}) => any;
/**
* Hook called after document read
*/
type AfterReadHook<T = any> = (args: {
/** Read document(s) */
doc: T | T[];
/** Express request object */
req: PayloadRequest;
/** Query arguments */
query?: any;
}) => T | T[];
/**
* Hook called before document deletion
*/
type BeforeDeleteHook<T = any> = (args: {
/** Express request object */
req: PayloadRequest;
/** Document ID being deleted */
id: string | number;
/** Document being deleted */
doc?: T;
}) => void;
/**
* Hook called after document deletion
*/
type AfterDeleteHook<T = any> = (args: {
/** Express request object */
req: PayloadRequest;
/** Document ID that was deleted */
id: string | number;
/** Document that was deleted */
doc: T;
}) => void;
/**
* Authentication-specific hooks
*/
type BeforeLoginHook<T = any> = (args: {
/** Express request object */
req: PayloadRequest;
/** User attempting to login */
user: T;
}) => void;
type AfterLoginHook<T = any> = (args: {
/** Express request object */
req: PayloadRequest;
/** Logged in user */
user: T;
/** JWT token */
token: string;
}) => void;
type AfterLogoutHook<T = any> = (args: {
/** Express request object */
req: PayloadRequest;
/** User that logged out */
user?: T;
}) => void;Hook Usage Examples:
// Collection with comprehensive hooks
const PostsCollection: CollectionConfig = {
slug: "posts",
hooks: {
// Log all operations
beforeOperation: [
({ args, operation }) => {
console.log(`Starting ${operation} operation`, args);
return args;
},
],
// Auto-populate author field on create
beforeChange: [
({ data, req, operation }) => {
if (operation === 'create' && req.user) {
data.author = req.user.id;
data.createdBy = req.user.id;
}
if (operation === 'update' && req.user) {
data.updatedBy = req.user.id;
}
return data;
},
],
// Generate slug from title
beforeValidate: [
({ data, operation }) => {
if ((operation === 'create' || operation === 'update') && data.title && !data.slug) {
data.slug = data.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '');
}
return data;
},
],
// Clear cache after changes
afterChange: [
({ doc, operation }) => {
console.log(`${operation} completed for post: ${doc.title}`);
// Clear cache, send notifications, etc.
clearPostCache(doc.id);
if (operation === 'create') {
sendNewPostNotification(doc);
}
},
],
// Filter sensitive data from reads
afterRead: [
({ doc, req }) => {
// Remove internal fields for non-admin users
if (!req.user || req.user.role !== 'admin') {
if (Array.isArray(doc)) {
return doc.map(item => ({
...item,
internalNotes: undefined,
adminOnlyField: undefined,
}));
} else {
return {
...doc,
internalNotes: undefined,
adminOnlyField: undefined,
};
}
}
return doc;
},
],
// Cleanup related data on delete
beforeDelete: [
async ({ req, id, doc }) => {
// Delete related comments
await req.payload.delete({
collection: 'comments',
where: {
post: {
equals: id,
},
},
});
// Log deletion
console.log(`Deleting post: ${doc?.title} (${id})`);
},
],
// Post-deletion cleanup
afterDelete: [
({ req, id, doc }) => {
// Clear caches, update search indexes, etc.
clearPostCache(id);
updateSearchIndex();
console.log(`Post deleted: ${doc.title}`);
},
],
// Handle errors
afterError: (err, args) => {
console.error('Collection operation failed:', err);
// Send error to monitoring service
sendErrorToMonitoring(err, args);
return null;
},
},
fields: [
{
name: "title",
type: "text",
required: true,
},
{
name: "slug",
type: "text",
unique: true,
},
{
name: "author",
type: "relationship",
relationTo: "users",
},
],
};
// Auth collection with login hooks
const UsersCollection: CollectionConfig = {
slug: "users",
auth: true,
hooks: {
// Track login attempts
beforeLogin: [
({ user, req }) => {
console.log(`Login attempt for user: ${user.email}`);
// Log to analytics, check for suspicious activity, etc.
},
],
// Post-login processing
afterLogin: [
({ user, req, token }) => {
console.log(`User logged in: ${user.email}`);
// Update last login timestamp, log activity, send welcome email, etc.
updateUserActivity(user.id, 'login');
},
],
// Cleanup on logout
afterLogout: [
({ user, req }) => {
if (user) {
console.log(`User logged out: ${user.email}`);
updateUserActivity(user.id, 'logout');
}
},
],
},
fields: [
{
name: "firstName",
type: "text",
required: true,
},
{
name: "lastName",
type: "text",
required: true,
},
],
};Environment variables commonly used with Payload configuration.
// Common environment variables
interface EnvironmentVariables {
/** Database connection string */
DATABASE_URI?: string;
/** Payload secret key */
PAYLOAD_SECRET?: string;
/** Server URL */
PAYLOAD_PUBLIC_SERVER_URL?: string;
/** Email service configuration */
SMTP_HOST?: string;
SMTP_PORT?: string;
SMTP_USER?: string;
SMTP_PASS?: string;
/** File upload configuration */
CLOUDINARY_CLOUD_NAME?: string;
CLOUDINARY_API_KEY?: string;
CLOUDINARY_API_SECRET?: string;
/** Node environment */
NODE_ENV?: "development" | "production" | "test";
}Environment Configuration Example:
const config: Config = {
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL || "http://localhost:3000",
mongoURL: process.env.DATABASE_URI,
secret: process.env.PAYLOAD_SECRET,
email: {
transport: {
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT),
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
},
fromName: "My Site",
fromAddress: "noreply@mysite.com",
},
};Install with Tessl CLI
npx tessl i tessl/npm-payload