payload-next-starter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Payload Next.js Starter
2
+
3
+ Quickly add PayloadCMS to any existing Next.js project. This package scaffolds PayloadCMS with PostgreSQL support into your Next.js application.
4
+
5
+ ## Installation
6
+
7
+ Run the setup command in your Next.js project:
8
+
9
+ ```bash
10
+ npx payload-next-starter
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g payload-next-starter
17
+ payload-next-starter
18
+ ```
19
+
20
+ ## What it does
21
+
22
+ This CLI tool will:
23
+ 1. Copy Payload admin routes to `src/app/(payload)/`
24
+ 2. Copy API routes to `src/app/api/[...payload]/`
25
+ 3. Copy collections (Users, Media) to `src/collections/`
26
+ 4. Install required dependencies
27
+ 5. Update `next.config.ts` with Payload plugin
28
+ 6. Create `.env.example` with required variables
29
+
30
+ ## After installation
31
+
32
+ 1. Copy `.env.example` to `.env` and fill in your values:
33
+ ```bash
34
+ DATABASE_URI=postgresql://user:password@host:5432/database
35
+ PAYLOAD_SECRET=your-secret-key-min-16-chars
36
+ NEXT_PUBLIC_SERVER_URL=http://localhost:3000
37
+ ```
38
+
39
+ 2. Generate Payload import maps:
40
+ ```bash
41
+ npx payload generate:importmap
42
+ ```
43
+
44
+ 3. Generate TypeScript types:
45
+ ```bash
46
+ npx payload generate:types
47
+ ```
48
+
49
+ 4. Start your dev server:
50
+ ```bash
51
+ npm run dev
52
+ ```
53
+
54
+ 5. Visit `http://localhost:3000/admin` to create your first admin user
55
+
56
+ ## Requirements
57
+
58
+ - Next.js 13+ with App Router
59
+ - Node.js 18.20+ or 20.9+
60
+ - PostgreSQL database
61
+
62
+ ## Collections included
63
+
64
+ - **Users** - Authentication-enabled collection with admin access
65
+ - **Media** - Uploads collection with image handling
66
+
67
+ ## Deploying
68
+
69
+ Deploy to Vercel, Railway, or any platform supporting Next.js:
70
+
71
+ ```bash
72
+ npm run build
73
+ ```
74
+
75
+ Ensure these environment variables are set in production:
76
+ - `DATABASE_URI`
77
+ - `PAYLOAD_SECRET`
78
+ - `NEXT_PUBLIC_SERVER_URL`
79
+
80
+ ## Customization
81
+
82
+ Edit `src/payload.config.ts` to add:
83
+ - More collections
84
+ - Globals
85
+ - Hooks
86
+ - Access control
87
+ - Custom components
88
+
89
+ ## License
90
+
91
+ MIT
92
+
93
+ ## Support
94
+
95
+ [GitHub Issues](https://github.com/sentayhu19/test-payload/issues)
package/bin/setup.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process'
4
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, cpSync } from 'fs'
5
+ import { dirname, join } from 'path'
6
+ import { fileURLToPath } from 'url'
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+ const rootDir = join(__dirname, '..')
11
+ const targetDir = process.cwd()
12
+
13
+ console.log('🚀 Setting up PayloadCMS...\n')
14
+
15
+ // Check if target is a Next.js project
16
+ if (!existsSync(join(targetDir, 'package.json'))) {
17
+ console.error('❌ No package.json found. Run this in a Next.js project.')
18
+ process.exit(1)
19
+ }
20
+
21
+ const pkg = JSON.parse(readFileSync(join(targetDir, 'package.json'), 'utf8'))
22
+ if (!pkg.dependencies?.next) {
23
+ console.error('❌ This doesn\'t appear to be a Next.js project.')
24
+ process.exit(1)
25
+ }
26
+
27
+ console.log('✓ Found Next.js project\n')
28
+
29
+ // Copy template files
30
+ console.log('📁 Copying Payload files...')
31
+
32
+ const templatesDir = join(rootDir, 'templates')
33
+
34
+ // Create directories
35
+ try {
36
+ mkdirSync(join(targetDir, 'src/app/(payload)'), { recursive: true })
37
+ mkdirSync(join(targetDir, 'src/app/api/[...payload]'), { recursive: true })
38
+ mkdirSync(join(targetDir, 'src/collections'), { recursive: true })
39
+ } catch (e) {
40
+ // Directories might already exist
41
+ }
42
+
43
+ // Copy payload.config.ts
44
+ if (existsSync(join(templatesDir, 'payload.config.ts'))) {
45
+ copyFileSync(
46
+ join(templatesDir, 'payload.config.ts'),
47
+ join(targetDir, 'src/payload.config.ts')
48
+ )
49
+ console.log(' ✓ payload.config.ts')
50
+ }
51
+
52
+ // Copy collections
53
+ if (existsSync(join(templatesDir, 'collections'))) {
54
+ cpSync(join(templatesDir, 'collections'), join(targetDir, 'src/collections'), { recursive: true, force: true })
55
+ console.log(' ✓ collections/')
56
+ }
57
+
58
+ // Copy app routes
59
+ if (existsSync(join(templatesDir, 'app/(payload)'))) {
60
+ cpSync(join(templatesDir, 'app/(payload)'), join(targetDir, 'src/app/(payload)'), { recursive: true, force: true })
61
+ console.log(' ✓ app/(payload)/')
62
+ }
63
+
64
+ if (existsSync(join(templatesDir, 'app/api/[...payload]'))) {
65
+ cpSync(join(templatesDir, 'app/api/[...payload]'), join(targetDir, 'src/app/api/[...payload]'), { recursive: true, force: true })
66
+ console.log(' ✓ app/api/[...payload]/')
67
+ }
68
+
69
+ console.log('\n📦 Installing dependencies...')
70
+
71
+ const deps = [
72
+ 'payload',
73
+ '@payloadcms/next',
74
+ '@payloadcms/db-postgres',
75
+ '@payloadcms/richtext-lexical',
76
+ '@payloadcms/ui',
77
+ 'dotenv',
78
+ 'cross-env'
79
+ ]
80
+
81
+ try {
82
+ const pkgManager = existsSync(join(targetDir, 'pnpm-lock.yaml')) ? 'pnpm' :
83
+ existsSync(join(targetDir, 'yarn.lock')) ? 'yarn' : 'npm'
84
+
85
+ execSync(`${pkgManager} add ${deps.join(' ')}`, {
86
+ cwd: targetDir,
87
+ stdio: 'inherit'
88
+ })
89
+ console.log('✓ Dependencies installed\n')
90
+ } catch (e) {
91
+ console.error('❌ Failed to install dependencies:', e.message)
92
+ process.exit(1)
93
+ }
94
+
95
+ // Update next.config
96
+ console.log('⚙️ Updating next.config...')
97
+ const nextConfigPath = existsSync(join(targetDir, 'next.config.ts'))
98
+ ? join(targetDir, 'next.config.ts')
99
+ : existsSync(join(targetDir, 'next.config.js'))
100
+ ? join(targetDir, 'next.config.js')
101
+ : null
102
+
103
+ if (nextConfigPath) {
104
+ const content = readFileSync(nextConfigPath, 'utf8')
105
+ if (!content.includes('withPayload')) {
106
+ const newConfig = `import { withPayload } from '@payloadcms/next/withPayload'
107
+
108
+ ${content.replace('export default ', 'const nextConfig = ')}
109
+
110
+ export default withPayload(nextConfig)
111
+ `
112
+ writeFileSync(nextConfigPath, newConfig)
113
+ console.log(' ✓ next.config updated\n')
114
+ } else {
115
+ console.log(' ℹ next.config already has Payload\n')
116
+ }
117
+ } else {
118
+ console.log(' ⚠ No next.config found, creating one...')
119
+ writeFileSync(join(targetDir, 'next.config.ts'), `import { withPayload } from '@payloadcms/next/withPayload'
120
+
121
+ const nextConfig = {
122
+ // your config
123
+ }
124
+
125
+ export default withPayload(nextConfig)
126
+ `)
127
+ console.log(' ✓ next.config.ts created\n')
128
+ }
129
+
130
+ // Create .env.example
131
+ if (!existsSync(join(targetDir, '.env'))) {
132
+ writeFileSync(join(targetDir, '.env.example'), `# PayloadCMS
133
+ DATABASE_URI=postgresql://user:password@host:5432/database
134
+ PAYLOAD_SECRET=your-secret-key-here-at-least-16-chars
135
+ NEXT_PUBLIC_SERVER_URL=http://localhost:3000
136
+ `)
137
+ console.log(' ✓ .env.example created')
138
+ }
139
+
140
+ console.log('\n🎉 PayloadCMS setup complete!')
141
+ console.log('\nNext steps:')
142
+ console.log(' 1. Copy .env.example to .env and fill in your values')
143
+ console.log(' 2. Run: npx payload generate:importmap')
144
+ console.log(' 3. Run: npx payload generate:types')
145
+ console.log(' 4. Start your dev server: npm run dev')
146
+ console.log('\nAdmin panel will be at: http://localhost:3000/admin')
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "payload-next-starter",
3
+ "version": "1.0.0",
4
+ "description": "PayloadCMS starter for Next.js - install into any Next.js app",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": "./bin/setup.js",
8
+ "files": [
9
+ "bin/",
10
+ "templates/",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "payload",
15
+ "payloadcms",
16
+ "nextjs",
17
+ "cms",
18
+ "headless-cms",
19
+ "starter"
20
+ ],
21
+ "author": "Sentayhu",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/sentayhu19/test-payload.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/sentayhu19/test-payload/issues"
28
+ },
29
+ "homepage": "https://github.com/sentayhu19/test-payload#readme",
30
+ "scripts": {
31
+ "build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build",
32
+ "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
33
+ "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
34
+ "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap",
35
+ "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
36
+ "lint": "cross-env NODE_OPTIONS=--no-deprecation eslint .",
37
+ "payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
38
+ "start": "cross-env NODE_OPTIONS=--no-deprecation next start",
39
+ "test": "pnpm run test:int && pnpm run test:e2e",
40
+ "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" playwright test --config=playwright.config.ts",
41
+ "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
42
+ },
43
+ "dependencies": {
44
+ "@payloadcms/next": "3.82.1",
45
+ "@payloadcms/richtext-lexical": "3.82.1",
46
+ "@payloadcms/ui": "3.82.1",
47
+ "cross-env": "^7.0.3",
48
+ "dotenv": "16.4.7",
49
+ "graphql": "^16.8.1",
50
+ "next": "16.2.3",
51
+ "payload": "3.82.1",
52
+ "react": "19.2.4",
53
+ "react-dom": "19.2.4",
54
+ "sharp": "0.34.2",
55
+ "@payloadcms/db-postgres": "3.82.1"
56
+ },
57
+ "devDependencies": {
58
+ "@playwright/test": "1.58.2",
59
+ "@testing-library/react": "16.3.0",
60
+ "@types/node": "22.19.9",
61
+ "@types/react": "19.2.14",
62
+ "@types/react-dom": "19.2.3",
63
+ "@vitejs/plugin-react": "4.5.2",
64
+ "eslint": "^9.16.0",
65
+ "eslint-config-next": "16.2.3",
66
+ "jsdom": "28.0.0",
67
+ "prettier": "^3.4.2",
68
+ "tsx": "4.21.0",
69
+ "typescript": "5.7.3",
70
+ "vite-tsconfig-paths": "6.0.5",
71
+ "vitest": "4.0.18"
72
+ },
73
+ "engines": {
74
+ "node": "^18.20.2 || >=20.9.0",
75
+ "pnpm": "^9 || ^10"
76
+ },
77
+ "pnpm": {
78
+ "onlyBuiltDependencies": [
79
+ "sharp",
80
+ "esbuild",
81
+ "unrs-resolver"
82
+ ]
83
+ }
84
+ }
@@ -0,0 +1,17 @@
1
+ import type { ServerFunctionClient } from 'payload'
2
+
3
+ import config from '@/payload.config'
4
+ import { AdminPage } from '@payloadcms/next/pages'
5
+
6
+ const serverFunction: ServerFunctionClient = async (args) => {
7
+ 'use server'
8
+ const { createServerFunctionClient } = await import('@payloadcms/next')
9
+ const { serverFunction: payloadServerFunction } = createServerFunctionClient({ config, importMap: {} })
10
+ return payloadServerFunction(args)
11
+ }
12
+
13
+ const Page = ({ params, searchParams }: { params: Promise<{ admin?: string[] }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) => (
14
+ <AdminPage config={config} importMap={{}} params={params} searchParams={searchParams} serverFunction={serverFunction} />
15
+ )
16
+
17
+ export default Page
@@ -0,0 +1,22 @@
1
+ import type { ServerFunctionClient } from 'payload'
2
+
3
+ import config from '@/payload.config'
4
+ import { RootLayout } from '@payloadcms/next/layouts'
5
+ import React from 'react'
6
+
7
+ import './styles.css'
8
+
9
+ type Args = {
10
+ children: React.ReactNode
11
+ }
12
+
13
+ const serverFunction: ServerFunctionClient = async (args) => {
14
+ 'use server'
15
+ const { createServerFunctionClient } = await import('@payloadcms/next')
16
+ const { serverFunction: payloadServerFunction } = createServerFunctionClient({ config, importMap: {} })
17
+ return payloadServerFunction(args)
18
+ }
19
+
20
+ const Layout = ({ children }: Args) => <RootLayout config={config} importMap={{}} serverFunction={serverFunction}>{children}</RootLayout>
21
+
22
+ export default Layout
@@ -0,0 +1,2 @@
1
+ @import 'tailwindcss';
2
+ @import '@payloadcms/next/css';
@@ -0,0 +1,9 @@
1
+ import config from '@/payload.config'
2
+ import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes'
3
+
4
+ export const GET = REST_GET(config)
5
+ export const POST = REST_POST(config)
6
+ export const DELETE = REST_DELETE(config)
7
+ export const PATCH = REST_PATCH(config)
8
+ export const PUT = REST_PUT(config)
9
+ export const OPTIONS = REST_OPTIONS(config)
@@ -0,0 +1,16 @@
1
+ import type { CollectionConfig } from 'payload'
2
+
3
+ export const Media: CollectionConfig = {
4
+ slug: 'media',
5
+ access: {
6
+ read: () => true,
7
+ },
8
+ fields: [
9
+ {
10
+ name: 'alt',
11
+ type: 'text',
12
+ required: true,
13
+ },
14
+ ],
15
+ upload: true,
16
+ }
@@ -0,0 +1,13 @@
1
+ import type { CollectionConfig } from 'payload'
2
+
3
+ export const Users: CollectionConfig = {
4
+ slug: 'users',
5
+ admin: {
6
+ useAsTitle: 'email',
7
+ },
8
+ auth: true,
9
+ fields: [
10
+ // Email added by default
11
+ // Add more fields as needed
12
+ ],
13
+ }
@@ -0,0 +1,34 @@
1
+ import { postgresAdapter } from '@payloadcms/db-postgres'
2
+ import { lexicalEditor } from '@payloadcms/richtext-lexical'
3
+ import path from 'path'
4
+ import { buildConfig } from 'payload'
5
+ import { fileURLToPath } from 'url'
6
+ import sharp from 'sharp'
7
+
8
+ import { Users } from './collections/Users'
9
+ import { Media } from './collections/Media'
10
+
11
+ const filename = fileURLToPath(import.meta.url)
12
+ const dirname = path.dirname(filename)
13
+
14
+ export default buildConfig({
15
+ admin: {
16
+ user: Users.slug,
17
+ importMap: {
18
+ baseDir: path.resolve(dirname),
19
+ },
20
+ },
21
+ collections: [Users, Media],
22
+ editor: lexicalEditor(),
23
+ secret: process.env.PAYLOAD_SECRET || '',
24
+ typescript: {
25
+ outputFile: path.resolve(dirname, 'payload-types.ts'),
26
+ },
27
+ db: postgresAdapter({
28
+ pool: {
29
+ connectionString: process.env.DATABASE_URL || '',
30
+ },
31
+ }),
32
+ sharp,
33
+ plugins: [],
34
+ })