Skip to content

Latest commit

 

History

History
266 lines (216 loc) · 9.03 KB

File metadata and controls

266 lines (216 loc) · 9.03 KB

Payload CMS Architecture with Next.js

Overview

Payload CMS 3.0 represents a fundamental shift in how content management systems integrate with modern web frameworks. It's not a separate backend service but a fully integrated part of your Next.js application.

Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                     Next.js Application                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────┐  ┌──────────────┐  ┌───────────────┐ │
│  │   Frontend      │  │ Payload Admin│  │  Payload API  │ │
│  │   Pages/App     │  │    Panel     │  │  REST/GraphQL │ │
│  │   Directory     │  │   (React)    │  │   Endpoints   │ │
│  └────────┬────────┘  └──────┬───────┘  └───────┬───────┘ │
│           │                   │                   │         │
│  ┌────────┴───────────────────┴───────────────────┴───────┐│
│  │              Payload Core (Config-based)                ││
│  │  - Collections      - Globals        - Hooks            ││
│  │  - Fields          - Access Control  - Validation       ││
│  └─────────────────────────┬──────────────────────────────┘│
│                             │                               │
│  ┌─────────────────────────┴──────────────────────────────┐│
│  │                 Database Adapter                        ││
│  │          (PostgreSQL / MongoDB / SQLite)                ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

How It Works

1. Single Application Architecture

Unlike traditional headless CMS solutions where you have:

  • Separate CMS backend (different server/service)
  • Separate frontend application
  • API calls between them

Payload 3.0 works as:

  • One codebase: Your Next.js app contains everything
  • One deployment: Deploy once, get frontend + CMS + API
  • One server: Everything runs on the same Node.js process

2. File Structure Integration

your-nextjs-app/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── (frontend)/        # Your website pages
│   │   │   ├── page.tsx
│   │   │   └── about/page.tsx
│   │   ├── (payload)/         # Generated by Payload
│   │   │   ├── admin/         # Admin panel routes
│   │   │   └── api/           # API routes
│   │   └── layout.tsx
│   ├── payload/               # Payload configuration
│   │   ├── collections/       # Data models
│   │   ├── globals/          # Single-instance data
│   │   └── payload.config.ts  # Main config
│   └── components/           # Shared React components
├── public/                   # Static assets
└── package.json             # Single dependency list

3. Build Process

When you run pnpm build:

  1. Payload generates:

    • Admin panel React components
    • API route handlers
    • TypeScript types from your schema
    • Database migrations
  2. Next.js compiles:

    • Your frontend pages
    • Payload's generated admin UI
    • API routes (both yours and Payload's)
    • Optimizes everything for production
  3. Output:

    • Single .next build directory
    • Standalone deployment package
    • All assets optimized and bundled

4. Runtime Behavior

When deployed, your application handles:

  • / → Your Next.js frontend pages
  • /admin → Payload admin panel
  • /api → Combined API routes:
    • Your custom API routes
    • Payload's REST endpoints (/api/users, /api/posts, etc.)
    • GraphQL endpoint (/api/graphql)

5. Data Flow

User Request → Next.js Router → 
  ├─→ Frontend Route → React Server Component → 
  │     └─→ Payload Local API → Database
  ├─→ Admin Route → Payload Admin UI → 
  │     └─→ Payload API → Database
  └─→ API Route → Payload REST/GraphQL → Database

Key Integration Points

1. Payload Config (payload.config.ts)

import { buildConfig } from 'payload/config'
import { postgresAdapter } from '@payloadcms/db-postgres'

export default buildConfig({
  serverURL: process.env.NEXT_PUBLIC_SERVER_URL,
  collections: [
    // Your data models
  ],
  globals: [
    // Your singletons
  ],
  db: postgresAdapter({
    pool: {
      connectionString: process.env.DATABASE_URI,
    },
  }),
  // Integrates with Next.js
  admin: {
    bundler: webpackBundler(),
    meta: {
      titleSuffix: '- Payload',
    },
  },
})

2. Next.js Configuration (next.config.js)

import { withPayload } from '@payloadcms/next/withPayload'

export default withPayload({
  // Your Next.js config
})

3. Data Access in Components

// In a React Server Component
import { getPayloadHMR } from '@payloadcms/next/utilities'

export default async function Page() {
  const payload = await getPayloadHMR({ config })
  const posts = await payload.find({
    collection: 'posts',
    limit: 10,
  })
  
  return <div>{/* Render posts */}</div>
}

Benefits of This Architecture

1. Simplified Development

  • One repository to manage
  • Shared TypeScript types
  • Unified build process
  • Single test suite

2. Better Performance

  • No network latency between frontend and CMS
  • Shared Node.js process
  • Optimized data fetching with React Server Components
  • Single deployment reduces infrastructure overhead

3. Enhanced Developer Experience

  • Hot module replacement works across entire stack
  • Unified debugging
  • Consistent environment variables
  • Simplified local development

4. Deployment Simplicity

  • Deploy to any Node.js host
  • No need for separate services
  • Reduced infrastructure costs
  • Easier scaling (scale everything together)

Common Patterns

1. Static Generation with Dynamic CMS Data

export async function generateStaticParams() {
  const payload = await getPayloadHMR({ config })
  const posts = await payload.find({
    collection: 'posts',
    limit: 1000,
  })
  
  return posts.docs.map((post) => ({
    slug: post.slug,
  }))
}

2. Live Preview Integration

// Payload handles draft state automatically
const { docs } = await payload.find({
  collection: 'pages',
  draft: true, // Include drafts for preview
})

3. Authentication Sharing

// Same auth works for both admin and frontend
import { getPayloadHMR } from '@payloadcms/next/utilities'

const payload = await getPayloadHMR({ config })
const user = await payload.auth({ headers })

Deployment Considerations

1. Environment Variables

Both Next.js and Payload share the same environment:

  • NEXT_PUBLIC_* for client-side
  • Regular vars for server-side
  • No duplication needed

2. Build Optimization

  • Use standalone output for Docker
  • Payload admin UI is code-split automatically
  • API routes are optimized by Next.js

3. Scaling

  • Horizontal scaling works normally
  • Database connections pooled automatically
  • File uploads need shared storage (S3)

Migration from Payload 2.x

If migrating from Payload 2.x (separate backend):

  1. Move Payload config into Next.js app
  2. Update imports to use @payloadcms/next
  3. Migrate API calls to use Local API
  4. Remove separate Payload server
  5. Update deployment configuration

Summary

Payload 3.0's architecture represents a paradigm shift in CMS design:

  • Not a separate service: Fully integrated with Next.js
  • Config-driven: Define schema once, get everything
  • Full-stack by default: Frontend, backend, and admin in one
  • Modern patterns: Leverages React Server Components
  • Developer-friendly: Reduces complexity while increasing capability

This architecture makes Payload ideal for Next.js projects that need content management without the complexity of managing separate services.