obsidian/wiki/concepts/payload-cms-node26-esm-workaround.md
2026-05-10 21:26:56 +01:00

1.8 KiB

title source updated tags
Payload CMS + Node 26 — Seed Script ESM Workaround daily/2026-05-10.md 2026-05-10
payload-cms
nextjs
nodejs
esm
seed

Payload CMS + Node 26 — Seed Script ESM Workaround

Problem

Running a Payload CMS seed script directly via npx tsx seed.ts on Node 26 fails with:

ERR_REQUIRE_ESM: require() of ES Module ... not supported

or module resolution errors because Node 26's stricter ESM/CJS interop breaks the way Payload loads its config and collections outside a Next.js process.

Fix: Seed via API Route

Instead of a standalone script, expose seed logic as a Next.js API route and call it from a running dev server:

// app/api/seed/route.ts
import { getPayload } from 'payload'
import config from '@payload-config'
import { NextResponse } from 'next/server'

export async function GET() {
  const payload = await getPayload({ config })

  // Check if already seeded
  const existing = await payload.find({ collection: 'users', limit: 1 })
  if (existing.totalDocs > 0) {
    return NextResponse.json({ message: 'Already seeded' })
  }

  await payload.create({
    collection: 'users',
    data: {
      email: 'admin@example.com',
      password: 'changeme',
      role: 'admin',
    },
  })

  return NextResponse.json({ message: 'Seeded successfully' })
}

Then seed by hitting http://localhost:3000/api/seed while the dev server is running.

Why This Works

The API route runs inside the already-running Next.js process which has already resolved all ESM/CJS module boundaries correctly. getPayload({ config }) reuses the singleton Payload instance without a standalone boot sequence.

Future

Remove this workaround once Payload officially supports Node 26 module resolution. The standalone seed approach (payload/dist/bin/seed) is cleaner but requires ESM-stable Node.