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 +95 -0
- package/bin/setup.js +146 -0
- package/package.json +84 -0
- package/templates/app/(payload)/admin/[[...admin]]/page.tsx +17 -0
- package/templates/app/(payload)/layout.tsx +22 -0
- package/templates/app/(payload)/styles.css +2 -0
- package/templates/app/api/[...payload]/route.ts +9 -0
- package/templates/collections/Media.ts +16 -0
- package/templates/collections/Users.ts +13 -0
- package/templates/payload.config.ts +34 -0
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,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,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
|
+
})
|