Files
payload/examples/live-preview/payload
Dan Ribbens edfa85bcd5 feat(db-postgres)!: relationship column (#6339)
BREAKING CHANGE:

Moves `upload` field and `relationship` fields with `hasMany: false` &
`relationTo: string` from the many-to-many `_rels` join table to simple
columns. This only affects Postgres database users.

## TL;DR

We have dramatically simplified the storage of simple relationships in
relational databases to boost performance and align with more expected
relational paradigms. If you are using the beta Postgres adapter, and
you need to keep simple relationship data, you'll need to run a
migration script that we provide you.

### Background

For example, prior to this update, a collection of "posts" with a simple
`hasMany: false` and `relationTo: 'categories'` field would have a
`posts_rels` table where the category relations would be stored.

This was somewhat unnecessary as simple relations like this can be
expressed with a `category_id` column which is configured as a foreign
key. This also introduced added complexity for dealing directly with the
database if all you have are simple relations.

### Who needs to migrate

You need to migrate if you are using the beta Postgres database adapter
and any of the following applies to you.

- If you have versions enabled on any collection / global
- If you use the `upload` field 
- If you have relationship fields that are `hasMany: false` (default)
and `relationTo` to a single category ([has
one](https://payloadcms.com/docs/fields/relationship#has-one)) relations

### We have a migration for you

Even though the Postgres adapter is in beta, we've prepared a predefined
migration that will work out of the box for you to migrate from an
earlier version of the adapter to the most recent version easily.

It makes the schema changes in step with actually moving the data from
the old locations to the new before adding any null constraints and
dropping the old columns and tables.

### How to migrate

The steps to preserve your data while making this update are as follows.
These steps are the same whether you are moving from Payload v2 to v3 or
a previous version of v3 beta to the most recent v3 beta.

**Important: during these steps, don't start the dev server unless you
have `push: false` set on your Postgres adapter.**

#### Step 1 - backup

Always back up your database before performing big changes, especially
in production cases.

#### Step 2 - create a pre-update migration 
Before updating to new Payload and Postgres adapter versions, run
`payload migrate:create` without any other config changes to have a
prior snapshot of the schema from the previous adapter version

#### Step 3 - if you're migrating a dev DB, delete the dev `push` row
from your `payload_migrations` table

If you're migrating a dev database where you have the default setting to
push database changes directly to your DB, and you need to preserve data
in your development database, then you need to delete a `dev` migration
record from your database.

Connect directly to your database in any tool you'd like and delete the
dev push record from the `payload_migrations` table using the following
SQL statement:

```sql
DELETE FROM payload_migrations where batch = -1`
```

#### Step 4 - update Payload and Postgres versions to most recent

Update packages, making sure you have matching versions across all
`@payloadcms/*` and `payload` packages (including
`@payloadcms/db-postgres`)

#### Step 5 - create the predefined migration

Run the following command to create the predefined migration we've
provided:

```
payload migrate:create --file @payloadcms/db-postgres/relationships-v2-v3
```

#### Step 6 - migrate!

Run migrations with the following command: 

```
payload migrate
```

Assuming the migration worked, you can proceed to commit this change and
distribute it to be run on all other environments.

Note that if two servers connect to the same database, only one should
be running migrations to avoid transaction conflicts.

Related discussion:
https://github.com/payloadcms/payload/discussions/4163

---------

Co-authored-by: James <james@trbl.design>
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
2024-05-30 14:09:11 -04:00
..

Payload Live Preview Example

The Payload Live Preview Example demonstrates how to implement Live Preview in Payload. With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes.

IMPORTANT—This example includes a fully integrated Next.js App Router front-end that runs on the same server as Payload. If you are working on an application running on an entirely separate server, there are various fully working, separately running front-ends made explicitly for this example, including:

Those applications run directly alongside this one. Follow the instructions in each respective README to get started. If you are setting up authentication for another front-end, please consider contributing to this repo with your own example!

To learn more about this, check out how Payload can be used in its various headless capacities.

Quick Start

  1. Clone this repo

  2. cd into this directory and run pnpm i --ignore-workspace*, yarn, or npm install

    *If you are running using pnpm within the Payload Monorepo, the --ignore-workspace flag is needed so that pnpm generates a lockfile in this example's directory despite the fact that one exists in root.

  3. cp .env.example .env to copy the example environment variables

    Adjust PAYLOAD_PUBLIC_SITE_URL in the .env if your front-end is running on a separate domain or port.

  4. pnpm dev, yarn dev or npm run dev to start the server

    • Press y when prompted to seed the database
  5. open http://localhost:3000 to access the home page

  6. open http://localhost:3000/admin to access the admin panel

    • Login with email demo@payloadcms.com and password demo

That's it! Changes made in ./src will be reflected in your app. See the Development section for more details.

How it works

Live Preview works by rendering an iframe on the page that loads your front-end application. The Admin panel communicates with your app through window.postMessage events. These events are emitted every time a change is made to the document. Your app then listens for these events and re-renders itself with the data it receives.

Collections

See the Collections docs for details on how to extend any of this functionality.

  • Users

    The users collection is auth-enabled which provides access to the admin panel. This is where your front-end application will be rendered with live page data. See Pages for more details.

    // ./src/collections/Users.ts
    {
      // ...
      auth: true
    }
    

    For additional help with authentication, see the Authentication docs or the official Auth Example.

  • Pages

    The pages collection has Live Preview enabled through the admin.livePreview property of the pages collection config:

    // ./src/collections/Pages.ts
    {
      // ...
      admin: {
        livePreview: {
          url: ({ data }) => `${process.env.PAYLOAD_URL}/${data.slug}`
        }
      }
    }
    

    For more details on how to extend this functionality, see the Live Preview docs.

Front-end

While using Live Preview, the Admin panel emits a new window.postMessage event every time a change is made to the document. Your front-end application can listen for these events and re-render accordingly.

There are two ways to use Live Preview in your own application depending on whether your front-end framework supports server components:

We suggest using server-side Live Preview if your framework supports it, it is both simpler to setup and more performant to run than the client-side alternative.

Server

Server-side Live Preview is only for front-end frameworks that support the concept of Server Components, i.e. React Server Components. If your front-end application is built with a client-side framework like the Next.js Pages Router, React Router, Vue 3, etc., see client-side Live Preview.

Server-side Live Preview works by making a roundtrip to the server every time your document is saved, i.e. draft save, autosave, or publish. While using Live Preview, the Admin panel emits a new window.postMessage event which your front-end application can use to invoke this process. In Next.js, this means simply calling router.refresh() which will hydrate the HTML using new data straight from the Local API.

If your server-side front-end application is built with React, you can use the RefreshRouteOnChange function that Payload provides. In the future, all other major frameworks like Vue and Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See building your own router refresh component for more information.

React

If your front-end application is built with server-side React, i.e. Next.js App Router, you can use the RefreshRouteOnSave component that Payload provides and thread it your framework's refresh function.

First, install the @payloadcms/live-preview-react package:

npm install @payloadcms/live-preview-react

Then, render RefreshRouteOnSave anywhere in your page.tsx. Here's an example:

page.tsx:

import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next'
import config from '../payload.config'

export default async function Page() {
  const payload = await getPayloadHMR({ config })

  const page = await payload.find({
    collection: 'pages',
    draft: true,
  })

  return (
    <Fragment>
      <RefreshRouteOnSave />
      <h1>{page.title}</h1>
    </Fragment>
  )
}

RefreshRouteOnSave.tsx:

'use client'
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react'
import { useRouter } from 'next/navigation.js'
import React from 'react'

export const RefreshRouteOnSave: React.FC = () => {
  const router = useRouter()
  return <PayloadLivePreview refresh={router.refresh} serverURL={process.env.PAYLOAD_SERVER_URL} />
}

For more details on how to setup server-side Live Preview, see the server-side Live Preview docs.

Client

If your front-end application is supports Server Components like the Next.js App Router, etc., we suggest setting up server-side Live Preview.

React

If your front-end application is built with client-side React such as Next.js Pages Router, React Router, etc., use the useLivePreview React hook that Payload provides.

First, install the @payloadcms/live-preview-react package:

npm install @payloadcms/live-preview-react

Then, use the useLivePreview hook in your React component:

'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import { Page as PageType } from '@/payload-types'

// Fetch the page in a server component, pass it to the client component, then thread it through the hook
// The hook will take over from there and keep the preview in sync with the changes you make
// The `data` property will contain the live data of the document
export const PageClient: React.FC<{
  page: {
    title: string
  }
}> = ({ page: initialPage }) => {
  const { data } = useLivePreview<PageType>({
    initialData: initialPage,
    serverURL: PAYLOAD_SERVER_URL,
    depth: 2, // Ensure that the depth matches the request for `initialPage`
  })

  return <h1>{data.title}</h1>
}

JavaScript

In the future, all other major frameworks like Vue, Svelte, etc will be officially supported. If you are using any of these framework today, you can still integrate with Live Preview yourself using the tooling that Payload provides.

First, install the @payloadcms/live-preview package:

npm install @payloadcms/live-preview

Then, build your own hook:

import { subscribe, unsubscribe } from '@payloadcms/live-preview'

// Build your own hook to subscribe to the live preview events
// This function will handle everything for you like
// 1. subscribing to `window.postMessage` events
// 2. merging initial page data with incoming form state
// 3. populating relationships and uploads

See building your own Live Preview hook for more details.

For more details on how to setup client-side Live Preview, see the client-side Live Preview docs.

Development

To spin up this example locally, follow the Quick Start.

Seed

On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the PAYLOAD_DROP_DATABASE and PAYLOAD_PUBLIC_SEED environment variables which are included in the .env.example by default. You can remove these from your .env to prevent this behavior. You can also freshly seed your project at any time by running yarn seed. This seed creates a user with email demo@payloadcms.com and password demo along with a home page.

NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.

Production

To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps:

  1. First invoke the payload build script by running yarn build or npm run build in your project root. This creates a ./build directory with a production-ready admin bundle.
  2. Then run yarn serve or npm run serve to run Node in production and serve Payload from the ./build directory.

Deployment

The easiest way to deploy your project is to use Payload Cloud, a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also choose to self-host your app, check out the Deployment docs for more details.

Questions

If you have any issues or questions, reach out to us on Discord or start a GitHub discussion.