Replies: 6 comments
-
Hey @Aex12 a workaround that might be easier to swap out later would be to use the api endpoint: export async function getStaticPaths() {
const pageReq = await fetch(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/posts?limit=100`)
const { docs: posts } = await pageReq.json()
return {
paths: posts.map(({ slug }) => ({
params: { slug }, locale: 'es-ES',
})).concat(posts.map(slug => ({
params: { slug }, locale: 'en-US',
}))),
}
} Also I destructured |
Beta Was this translation helpful? Give feedback.
-
Hey @Aex12 — for a little more context here, you will see this error if you are attempting to run a local operation but Unfortunately, NextJS executes For this reason, I'd suggest using the REST or GraphQL API instead. But, we're actively evaluating ways to make this more simple so you will be able to make use of the Local API in the future! I'm going to move this to a Discussion for now if that's cool with you. |
Beta Was this translation helpful? Give feedback.
-
Thanks for that quick response @jmikrut, that was very helpful. I didn't know Next was using child processes to fork getStaticPaths execution. I've managed to get it working creating a file import payload from 'payload';
// if process is forked, then init payload on local mode.
if (process.send) {
payload.init({
secret: process.env.PAYLOAD_SECRET_KEY,
mongoURL: process.env.MONGO_URL,
local: true,
/*
email: {
transportOptions: {
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
},
fromName: process.env.EMAIL_FROM_NAME,
fromAddress: process.env.EMAIL_FROM_ADDRESS,
},
*/
});
}
export default payload and then importing it on a NextJS page and using it for both getStaticPaths and getStaticProps (it will only load the local api instance when running on a child process, thus preventing the unwanted crash). import payloadcms from '../../server/payloadcms';
export async function getStaticPaths() {
const postQuery = await payloadcms.find({
collection: 'news',
pagination: false,
})
const posts = postQuery.docs
return {
paths: posts.map(({ slug }) => ({
params: { slug }, locale: 'es-ES',
})).concat(posts.map(({ slug }) => ({
params: { slug }, locale: 'en-US',
}))),
fallback: true,
}
}
export async function getStaticProps({ params, locale }) {
const postQuery = await payloadcms.find({
collection: 'news',
locale,
fallbackLocale: 'es-ES',
where: {
slug: {
equals: params.slug,
},
},
})
const post = postQuery.docs[0]
if (!post) {
return { props: {} }
}
return {
props: {
post
}
}
} The local option seem to be undocumented - I found it when inspecting the code looking for a way to load only the local API without loading the full fledged HTTP server. Probably there are better options here, but for now, this suffices my requirements. Hope this helps someone else on the future. This assumes you are using the NextJS Custom Server implementation here where Payload is initialized alongside with NextJS on the same Express server. Cheers! |
Beta Was this translation helpful? Give feedback.
-
Hey everyone! Is there an update on this? I'm currently trying to get Payload + NextJS working using the custom server example. Unfortunately I run into the same issue here. Payload seems not to be initialized because NextJS is generating the page on a child process. However, even the proposed workaround seems not to do it. It would be amazing to be able to use the Local API with this setup. Thanks for any help and/or nudges into the right direction and keep up the awesome work! 🎉 |
Beta Was this translation helpful? Give feedback.
-
Hey everyone, what @Aex12 recommended above is along the same lines of what we do in our next-payload repository. I went ahead and updated the custom-server example to use the "shared payload" approach. Basically you just need to create a shareable version of payload and then access that. The getPayloadClient file// getPayload.ts
import dotenv from 'dotenv'
import path from 'path'
import type { Payload } from 'payload'
import payload from 'payload'
import type { InitOptions } from 'payload/config'
import { seed as seedData } from './seed'
dotenv.config({
path: path.resolve(__dirname, '../.env'),
})
let cached = (global as any).payload
if (!cached) {
cached = (global as any).payload = { client: null, promise: null }
}
interface Args {
initOptions?: Partial<InitOptions>
seed?: boolean
}
export const getPayloadClient = async ({ initOptions, seed }: Args = {}): Promise<Payload> => {
if (!process.env.MONGODB_URI) {
throw new Error('MONGODB_URI environment variable is missing')
}
if (!process.env.PAYLOAD_SECRET) {
throw new Error('PAYLOAD_SECRET environment variable is missing')
}
if (cached.client) {
return cached.client
}
if (!cached.promise) {
cached.promise = payload.init({
mongoURL: process.env.MONGODB_URI,
secret: process.env.PAYLOAD_SECRET,
local: initOptions?.express ? false : true,
...(initOptions || {}),
})
}
try {
process.env.PAYLOAD_DROP_DATABASE = seed ? 'true' : 'false'
cached.client = await cached.promise
if (seed) {
payload.logger.info('---- SEEDING DATABASE ----')
await seedData(payload)
}
} catch (e: unknown) {
cached.promise = null
throw e
}
return cached.client
} Using it to boot up the server// server.ts
import dotenv from 'dotenv'
import next from 'next'
import nextBuild from 'next/dist/build'
import path from 'path'
dotenv.config({
path: path.resolve(__dirname, '../.env'),
})
import express from 'express'
import { getPayloadClient } from './getPayload'
const app = express()
const PORT = process.env.PORT || 3000
const start = async (): Promise<void> => {
const payload = await getPayloadClient({
initOptions: {
express: app,
onInit: async newPayload => {
newPayload.logger.info(`Payload Admin URL: ${newPayload.getAdminURL()}`)
},
},
seed: process.env.PAYLOAD_SEED === 'true',
})
if (process.env.NEXT_BUILD) {
app.listen(PORT, async () => {
payload.logger.info(`Next.js is now building...`)
// @ts-expect-error
await nextBuild(path.join(__dirname, '../'))
process.exit()
})
return
}
const nextApp = next({
dev: process.env.NODE_ENV !== 'production',
})
const nextHandler = nextApp.getRequestHandler()
app.get('*', (req, res) => nextHandler(req, res))
nextApp.prepare().then(() => {
payload.logger.info('Next.js started')
app.listen(PORT, async () => {
payload.logger.info(`Next.js App URL: ${process.env.PAYLOAD_PUBLIC_SERVER_URL}`)
})
})
}
start() Using it on the server// app/home/page.tsx
//
// This example is within a react-server-component (RSC)
// but the same can be done within getStaticProps or getStaticPaths
import React, { Fragment } from 'react'
import { notFound } from 'next/navigation'
import { getPayloadClient } from '../getPayload'
export default async function Home() {
const payload = await getPayloadClient()
const { docs } = await payload.find({
collection: 'pages',
where: {
slug: {
equals: 'home',
},
},
})
const home = docs?.[0] as Page
if (!home) {
return notFound()
}
return (
<main className={classes.main}>
<div>home page</div>
</main>
)
} Wrapping upSo just to clarify, you need to |
Beta Was this translation helpful? Give feedback.
-
I actually built out a project using Am I correct in thinking that transitioning the project back towards the custom-server example and removing next-payload would be a good idea? Directly using Local API is a must, but I'd much rather just have the payload dependency to worry about when 2.0 comes around. |
Beta Was this translation helpful? Give feedback.
-
Bug Report
I'm trying to call payload.find from getStaticPaths on NextJS (To generate the static paths to my posts), but I'm getting the following error:
TypeError: Cannot read properties of undefined (reading 'collections')
the error occurs on
./src/collections/operations/local/find.ts (40:25)
Expected Behavior
Payload should return a list of posts to generate static paths
Current Behavior
The app crashes with the above error
Possible Solution
This doesn't seem to happen on getStaticProps, with the same query, so it has something to do with getStaticPaths.
I'm working it around using getServerSideProps, but ideally I would prefer to use getStaticProps alongside with getStaticPaths for better performance
Steps to Reproduce
Version used
Using latest payload from npm
Beta Was this translation helpful? Give feedback.
All reactions