Compare commits
2 Commits
fix/postgr
...
docs/draft
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6562f08f8a | ||
|
|
51a1ce36e1 |
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@@ -294,10 +294,14 @@ jobs:
|
||||
- fields__collections__Email
|
||||
- fields__collections__Indexed
|
||||
- fields__collections__JSON
|
||||
- fields__collections__Lexical__e2e__main
|
||||
- fields__collections__Lexical__e2e__blocks
|
||||
- fields__collections__Lexical__e2e__blocks#config.blockreferences.ts
|
||||
- fields__collections__Number
|
||||
- fields__collections__Point
|
||||
- fields__collections__Radio
|
||||
- fields__collections__Relationship
|
||||
- fields__collections__RichText
|
||||
- fields__collections__Row
|
||||
- fields__collections__Select
|
||||
- fields__collections__Tabs
|
||||
@@ -305,11 +309,6 @@ jobs:
|
||||
- fields__collections__Text
|
||||
- fields__collections__UI
|
||||
- fields__collections__Upload
|
||||
- hooks
|
||||
- lexical__collections__Lexical__e2e__main
|
||||
- lexical__collections__Lexical__e2e__blocks
|
||||
- lexical__collections__Lexical__e2e__blocks#config.blockreferences.ts
|
||||
- lexical__collections__RichText
|
||||
- query-presets
|
||||
- form-state
|
||||
- live-preview
|
||||
@@ -321,7 +320,6 @@ jobs:
|
||||
- plugin-import-export
|
||||
- plugin-nested-docs
|
||||
- plugin-seo
|
||||
- sort
|
||||
- versions
|
||||
- uploads
|
||||
env:
|
||||
|
||||
@@ -21,9 +21,10 @@ When a user starts editing a document, Payload locks it for that user. If anothe
|
||||
The lock will automatically expire after a set period of inactivity, configurable using the `duration` property in the `lockDocuments` configuration, after which others can resume editing.
|
||||
|
||||
<Banner type="info">
|
||||
{' '}
|
||||
**Note:** If your application does not require document locking, you can
|
||||
disable this feature for any collection or global by setting the
|
||||
`lockDocuments` property to `false`.
|
||||
`lockDocuments` property to `false`.{' '}
|
||||
</Banner>
|
||||
|
||||
### Config Options
|
||||
|
||||
@@ -158,7 +158,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.login({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'get-out',
|
||||
@@ -166,13 +166,6 @@ const result = await payload.login({
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Server Functions:** Payload offers a ready-to-use `login` server function
|
||||
that utilizes the Local API. For integration details and examples, check out
|
||||
the [Server Function
|
||||
docs](../local-api/server-functions#reusable-payload-server-functions).
|
||||
</Banner>
|
||||
|
||||
## Logout
|
||||
|
||||
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
|
||||
@@ -196,13 +189,6 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Server Functions:** Payload provides a ready-to-use `logout` server function
|
||||
that manages the user's cookie for a seamless logout. For integration details
|
||||
and examples, check out the [Server Function
|
||||
docs](../local-api/server-functions#reusable-payload-server-functions).
|
||||
</Banner>
|
||||
|
||||
## Refresh
|
||||
|
||||
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by executing this operation via the authenticated user.
|
||||
@@ -254,13 +240,6 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Server Functions:** Payload exports a ready-to-use `refresh` server function
|
||||
that automatically renews the user's token and updates the associated cookie.
|
||||
For integration details and examples, check out the [Server Function
|
||||
docs](../local-api/server-functions#reusable-payload-server-functions).
|
||||
</Banner>
|
||||
|
||||
## Verify by Email
|
||||
|
||||
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
|
||||
@@ -291,7 +270,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.verifyEmail({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
token: 'TOKEN_HERE',
|
||||
})
|
||||
```
|
||||
@@ -329,7 +308,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const result = await payload.unlock({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
})
|
||||
```
|
||||
|
||||
@@ -370,7 +349,7 @@ mutation {
|
||||
|
||||
```ts
|
||||
const token = await payload.forgotPassword({
|
||||
collection: 'collection-slug',
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ keywords: authentication, config, configuration, overview, documentation, Conten
|
||||
title="Simplified Authentication for Headless CMS: Unlocking Reusability in One Line"
|
||||
/>
|
||||
|
||||
Authentication is a critical part of any application. Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the [Admin Panel](../admin/overview), as well as your own external applications, completely eliminating the need for paid, third-party platforms and services.
|
||||
Authentication is a critical part of any application. Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the [Admin Panel](../admin/overview), all well as your own external applications, completely eliminating the need for paid, third-party platforms and services.
|
||||
|
||||
Here are some common use cases of Authentication in your own applications:
|
||||
|
||||
|
||||
@@ -60,31 +60,31 @@ export const Posts: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
|
||||
| `access` | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
|
||||
| `auth` | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
|
||||
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
||||
| `disableDuplicate` | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
|
||||
| `defaultSort` | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
|
||||
| `dbName` | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
|
||||
| `endpoints` | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
|
||||
| `graphQL` | Manage GraphQL-related properties for this collection. [More](#graphql) |
|
||||
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
|
||||
| `orderable` | If true, enables custom ordering for the collection, and documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
|
||||
| `labels` | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| `enableQueryPresets` | Enable query presets for this Collection. [More details](../query-presets/overview). |
|
||||
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
|
||||
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| `timestamps` | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
|
||||
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| `upload` | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
|
||||
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
||||
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
|
||||
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
|
||||
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
|
||||
| Option | Description |
|
||||
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
|
||||
| `access` | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
|
||||
| `auth` | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
|
||||
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
||||
| `disableDuplicate` | When true, do not show the "Duplicate" button while editing documents within this Collection and prevent `duplicate` from all APIs. |
|
||||
| `defaultSort` | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol ("-") to sort in descending order. Multiple fields can be specified by using a string array. |
|
||||
| `dbName` | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |
|
||||
| `endpoints` | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| `fields` \* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |
|
||||
| `graphQL` | Manage GraphQL-related properties for this collection. [More](#graphql) |
|
||||
| `hooks` | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |
|
||||
| `orderable` | If true, enables custom ordering for the collection, and documents can be reordered via drag and drop. Uses [fractional indexing](https://observablehq.com/@dgreensp/implementing-fractional-indexing) for efficient reordering. |
|
||||
| `labels` | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| `enableQueryPresets` | Enable query presets for this Collection. [More details](../query-presets/overview). |
|
||||
| `lockDocuments` | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |
|
||||
| `slug` \* | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| `timestamps` | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
|
||||
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| `upload` | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
|
||||
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
||||
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
|
||||
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
|
||||
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -120,25 +120,25 @@ export const MyCollection: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
|
||||
| `hooks` | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
|
||||
| `useAsTitle` | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title, unless it's linked to a relationship'. |
|
||||
| `description` | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |
|
||||
| `defaultColumns` | Array of field names that correspond to which columns to show by default in this Collection's List View. |
|
||||
| `disableCopyToLocale` | Disables the "Copy to Locale" button while editing documents within this Collection. Only applicable when localization is enabled. |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this Collection. |
|
||||
| `enableRichTextLink` | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `enableRichTextRelationship` | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `meta` | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](../admin/metadata). |
|
||||
| `preview` | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](../admin/preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| `components` | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
|
||||
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
|
||||
| `pagination` | Set pagination-specific options for this Collection. [More details](#pagination). |
|
||||
| `baseListFilter` | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
|
||||
| Option | Description |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
|
||||
| `hooks` | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
|
||||
| `useAsTitle` | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |
|
||||
| `description` | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |
|
||||
| `defaultColumns` | Array of field names that correspond to which columns to show by default in this Collection's List View. |
|
||||
| `disableCopyToLocale` | Disables the "Copy to Locale" button while editing documents within this Collection. Only applicable when localization is enabled. |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this Collection. |
|
||||
| `enableRichTextLink` | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `enableRichTextRelationship` | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `meta` | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](../admin/metadata). |
|
||||
| `preview` | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](../admin/preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| `components` | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
|
||||
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
|
||||
| `pagination` | Set pagination-specific options for this Collection. [More details](#pagination). |
|
||||
| `baseListFilter` | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
|
||||
|
||||
### Custom Components
|
||||
|
||||
@@ -193,13 +193,13 @@ export const MyCollection: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `SaveButton` | Replace the default Save Button within the Edit View. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#savebutton). |
|
||||
| `SaveDraftButton` | Replace the default Save Draft Button within the Edit View. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#savedraftbutton). |
|
||||
| `PublishButton` | Replace the default Publish Button within the Edit View. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publishbutton). |
|
||||
| `PreviewButton` | Replace the default Preview Button within the Edit View. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#previewbutton). |
|
||||
| `Upload` | Replace the default Upload component within the Edit View. [Upload](../upload/overview) must be enabled. [More details](../custom-components/edit-view#upload). |
|
||||
| Option | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `SaveButton` | Replace the default Save Button within the Edit View. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#save-button). |
|
||||
| `SaveDraftButton` | Replace the default Save Draft Button within the Edit View. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#save-draft-button). |
|
||||
| `PublishButton` | Replace the default Publish Button within the Edit View. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publish-button). |
|
||||
| `PreviewButton` | Replace the default Preview Button within the Edit View. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#preview-button). |
|
||||
| `Upload` | Replace the default Upload component within the Edit View. [Upload](../upload/overview) must be enabled. [More details](../custom-components/edit-view#upload). |
|
||||
|
||||
<Banner type="success">
|
||||
**Note:** For details on how to build Custom Components, see [Building Custom
|
||||
|
||||
@@ -179,12 +179,12 @@ export const MyGlobal: SanitizedGlobalConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `SaveButton` | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#savebutton). |
|
||||
| `SaveDraftButton` | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#savedraftbutton). |
|
||||
| `PublishButton` | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publishbutton). |
|
||||
| `PreviewButton` | Replace the default Preview Button with a Custom Component. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#previewbutton). |
|
||||
| Option | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `SaveButton` | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. [More details](../custom-components/edit-view#save-button). |
|
||||
| `SaveDraftButton` | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. [More details](../custom-components/edit-view#save-draft-button). |
|
||||
| `PublishButton` | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. [More details](../custom-components/edit-view#publish-button). |
|
||||
| `PreviewButton` | Replace the default Preview Button with a Custom Component. [Preview](../admin/preview) must be enabled. [More details](../custom-components/edit-view#preview-button). |
|
||||
|
||||
<Banner type="success">
|
||||
**Note:** For details on how to build Custom Components, see [Building Custom
|
||||
|
||||
@@ -239,9 +239,9 @@ export default buildConfig({
|
||||
// ...
|
||||
// highlight-start
|
||||
cors: {
|
||||
origins: ['http://localhost:3000'],
|
||||
headers: ['x-custom-header'],
|
||||
},
|
||||
origins: ['http://localhost:3000']
|
||||
headers: ['x-custom-header']
|
||||
}
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ desc:
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
The Edit View is where users interact with individual [Collection](../configuration/collections) and [Global](../configuration/globals) Documents within the [Admin Panel](../admin/overview). The Edit View contains the actual form in which submits the data to the server. This is where they can view, edit, and save their content. It contains controls for saving, publishing, and previewing the document, all of which can be customized to a high degree.
|
||||
The Edit View is where users interact with individual [Collection](../collections/overview) and [Global](../globals/overview) Documents within the [Admin Panel](../admin/overview). The Edit View contains the actual form in which submits the data to the server. This is where they can view, edit, and save their content. It contains controls for saving, publishing, and previewing the document, all of which can be customized to a high degree.
|
||||
|
||||
The Edit View can be swapped out in its entirety for a Custom View, or it can be injected with a number of Custom Components to add additional functionality or presentational elements without replacing the entire view.
|
||||
|
||||
@@ -101,15 +101,14 @@ export const MyCollection: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
|
||||
| `beforeDocumentControls` | Inject custom components before the Save / Publish buttons. [More details](#beforedocumentcontrols). |
|
||||
| `SaveButton` | A button that saves the current document. [More details](#savebutton). |
|
||||
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#savedraftbutton). |
|
||||
| `PublishButton` | A button that publishes the current document. [More details](#publishbutton). |
|
||||
| `PreviewButton` | A button that previews the current document. [More details](#previewbutton). |
|
||||
| `Description` | A description of the Collection. [More details](#description). |
|
||||
| `Upload` | A file upload component. [More details](#upload). |
|
||||
| Path | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------- |
|
||||
| `SaveButton` | A button that saves the current document. [More details](#SaveButton). |
|
||||
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#SaveDraftButton). |
|
||||
| `PublishButton` | A button that publishes the current document. [More details](#PublishButton). |
|
||||
| `PreviewButton` | A button that previews the current document. [More details](#PreviewButton). |
|
||||
| `Description` | A description of the Collection. [More details](#Description). |
|
||||
| `Upload` | A file upload component. [More details](#Upload). |
|
||||
|
||||
#### Globals
|
||||
|
||||
@@ -134,14 +133,13 @@ export const MyGlobal: GlobalConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
|
||||
| `beforeDocumentControls` | Inject custom components before the Save / Publish buttons. [More details](#beforedocumentcontrols). |
|
||||
| `SaveButton` | A button that saves the current document. [More details](#savebutton). |
|
||||
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#savedraftbutton). |
|
||||
| `PublishButton` | A button that publishes the current document. [More details](#publishbutton). |
|
||||
| `PreviewButton` | A button that previews the current document. [More details](#previewbutton). |
|
||||
| `Description` | A description of the Global. [More details](#description). |
|
||||
| Path | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------- |
|
||||
| `SaveButton` | A button that saves the current document. [More details](#SaveButton). |
|
||||
| `SaveDraftButton` | A button that saves the current document as a draft. [More details](#SaveDraftButton). |
|
||||
| `PublishButton` | A button that publishes the current document. [More details](#PublishButton). |
|
||||
| `PreviewButton` | A button that previews the current document. [More details](#PreviewButton). |
|
||||
| `Description` | A description of the Global. [More details](#Description). |
|
||||
|
||||
### SaveButton
|
||||
|
||||
@@ -193,73 +191,6 @@ export function MySaveButton(props: SaveButtonClientProps) {
|
||||
}
|
||||
```
|
||||
|
||||
### beforeDocumentControls
|
||||
|
||||
The `beforeDocumentControls` property allows you to render custom components just before the default document action buttons (like Save, Publish, or Preview). This is useful for injecting custom buttons, status indicators, or any other UI elements before the built-in controls.
|
||||
|
||||
To add `beforeDocumentControls` components, use the `components.edit.beforeDocumentControls` property in you [Collection Config](../configuration/collections) or `components.elements.beforeDocumentControls` in your [Global Config](../configuration/globals):
|
||||
|
||||
#### Collections
|
||||
|
||||
```
|
||||
export const MyCollection: CollectionConfig = {
|
||||
admin: {
|
||||
components: {
|
||||
edit: {
|
||||
// highlight-start
|
||||
beforeDocumentControls: ['/path/to/CustomComponent'],
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Globals
|
||||
|
||||
```
|
||||
export const MyGlobal: GlobalConfig = {
|
||||
admin: {
|
||||
components: {
|
||||
elements: {
|
||||
// highlight-start
|
||||
beforeDocumentControls: ['/path/to/CustomComponent'],
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Here's an example of a custom `beforeDocumentControls` component:
|
||||
|
||||
#### Server Component
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import type { BeforeDocumentControlsServerProps } from 'payload'
|
||||
|
||||
export function MyCustomDocumentControlButton(
|
||||
props: BeforeDocumentControlsServerProps,
|
||||
) {
|
||||
return <div>This is a custom beforeDocumentControl button (Server)</div>
|
||||
}
|
||||
```
|
||||
|
||||
#### Client Component
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import type { BeforeDocumentControlsClientProps } from 'payload'
|
||||
|
||||
export function MyCustomDocumentControlButton(
|
||||
props: BeforeDocumentControlsClientProps,
|
||||
) {
|
||||
return <div>This is a custom beforeDocumentControl button (Client)</div>
|
||||
}
|
||||
```
|
||||
|
||||
### SaveDraftButton
|
||||
|
||||
The `SaveDraftButton` property allows you to render a custom Save Draft Button in the Edit View.
|
||||
|
||||
@@ -298,15 +298,3 @@ Passing your migrations as shown above will tell Payload, in production only, to
|
||||
may slow down serverless cold starts on platforms such as Vercel. Generally,
|
||||
this option should only be used for long-running servers / containers.
|
||||
</Banner>
|
||||
|
||||
## Environment-Specific Configurations and Migrations
|
||||
|
||||
Your configuration may include environment-specific settings (e.g., enabling a plugin only in production). If you generate migrations without considering the environment, it can lead to discrepancies and issues. When running migrations locally, Payload uses the development environment, which might miss production-specific configurations. Similarly, running migrations in production could miss development-specific entities.
|
||||
|
||||
This is an easy oversight, so be mindful of any environment-specific logic in your config when handling migrations.
|
||||
|
||||
**Ways to address this:**
|
||||
|
||||
- Manually update your migration file after it is generated to include any environment-specific configurations.
|
||||
- Temporarily enable any required production environment variables in your local setup when generating the migration to capture the necessary updates.
|
||||
- Use separate migration files for each environment to ensure the correct migration is executed in the corresponding environment.
|
||||
|
||||
@@ -39,28 +39,28 @@ export const MyArrayField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as the heading in the [Admin Panel](../admin/overview) or an object with keys for each language. Auto-generated from name if not defined. |
|
||||
| **`fields`** \* | Array of field types to correspond to each row of the Array. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the [Admin Panel](../admin/overview) and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide an array of row data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Array will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`labels`** | Customize the row labels appearing in the Admin dashboard. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`dbName`** | Custom table name for the field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as the heading in the [Admin Panel](../admin/overview) or an object with keys for each language. Auto-generated from name if not defined. |
|
||||
| **`fields`** \* | Array of field types to correspond to each row of the Array. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the [Admin Panel](../admin/overview) and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide an array of row data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Array will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`labels`** | Customize the row labels appearing in the Admin dashboard. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`dbName`** | Custom table name for the field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -39,26 +39,26 @@ export const MyBlocksField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as the heading in the Admin Panel or an object with keys for each language. Auto-generated from name if not defined. |
|
||||
| **`blocks`** \* | Array of [block configs](/docs/fields/blocks#block-configs) to be made available to this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API response or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide an array of block data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this field will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`labels`** | Customize the block row labels appearing in the Admin dashboard. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as the heading in the Admin Panel or an object with keys for each language. Auto-generated from name if not defined. |
|
||||
| **`blocks`** \* | Array of [block configs](/docs/fields/blocks#block-configs) to be made available to this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API response or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide an array of block data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this field will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`labels`** | Customize the block row labels appearing in the Admin dashboard. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -352,20 +352,18 @@ const config = buildConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
{
|
||||
slug: 'collection2',
|
||||
fields: [
|
||||
{
|
||||
name: 'editor',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: [
|
||||
BlocksFeature({
|
||||
// Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
|
||||
blocks: ['TextBlock'],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
BlocksFeature({
|
||||
// Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
|
||||
blocks: ['TextBlock'],
|
||||
})
|
||||
})
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -28,23 +28,23 @@ export const MyCheckboxField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -29,26 +29,26 @@ export const MyBlocksField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -28,24 +28,24 @@ export const MyDateField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`timezone`** \* | Set to `true` to enable timezone selection on this field. [More details](#timezones). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`timezone`** \* | Set to `true` to enable timezone selection on this field. [More details](#timezones). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -28,24 +28,24 @@ export const MyEmailField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -271,6 +271,21 @@ const result = await payload.find({
|
||||
and blocks.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
Currently, querying by the Join Field itself is not supported, meaning:
|
||||
```ts
|
||||
payload.find({
|
||||
collection: 'categories',
|
||||
where: {
|
||||
'relatedPosts.title': { // relatedPosts is a join field
|
||||
equals: "post"
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
does not work yet.
|
||||
</Banner>
|
||||
|
||||
### Rest API
|
||||
|
||||
The REST API supports the same query options as the Local API. You can use the `joins` query parameter to customize the
|
||||
|
||||
@@ -29,25 +29,25 @@ export const MyJSONField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -28,29 +28,29 @@ export const MyNumberField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
|
||||
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
|
||||
| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |
|
||||
| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
|
||||
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
|
||||
| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |
|
||||
| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -541,7 +541,6 @@ The `ctx` object:
|
||||
| Property | Description |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`blockData`** | The nearest parent block's data. If the field is not inside a block, this will be `undefined`. |
|
||||
| **`operation`** | A string relating to which operation the field type is currently executing within. |
|
||||
| **`path`** | The full path to the field in the schema, represented as an array of string segments, including array indexes. I.e `['group', 'myArray', '1', 'textField']`. |
|
||||
| **`user`** | The currently authenticated user object. |
|
||||
|
||||
|
||||
@@ -32,24 +32,24 @@ export const MyPointField: Field = {
|
||||
|
||||
## Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Used as a field label in the Admin Panel and to name the generated GraphQL type. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. To support location queries, point index defaults to `2dsphere`, to disable the index set to `false`. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Used as a field label in the Admin Panel and to name the generated GraphQL type. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. To support location queries, point index defaults to `2dsphere`, to disable the index set to `false`. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -33,26 +33,26 @@ export const MyRadioField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing an `label` string and a `value` string. |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. The default value must exist within provided values in `options`. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing an `label` string and a `value` string. |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. The default value must exist within provided values in `options`. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -37,31 +37,31 @@ export const MyRelationshipField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
|
||||
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
|
||||
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
|
||||
| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/queries/depth#max-depth) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
|
||||
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
|
||||
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
|
||||
| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/queries/depth#max-depth) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -94,8 +94,6 @@ The Relationship Field inherits all of the default options from the base [Field
|
||||
| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |
|
||||
| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |
|
||||
| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More](#sort-options) |
|
||||
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
|
||||
| **`appearance`** | Set to `drawer` or `select` to change the behavior of the field. Defaults to `select`. |
|
||||
|
||||
### Sort Options
|
||||
|
||||
@@ -150,7 +148,7 @@ The `filterOptions` property can either be a `Where` query, or a function return
|
||||
| `id` | The `id` of the current document being edited. Will be `undefined` during the `create` operation or when called on a `Filter` component within the list view. |
|
||||
| `relationTo` | The collection `slug` to filter against, limited to this field's `relationTo` property. |
|
||||
| `req` | The Payload Request, which contains references to `payload`, `user`, `locale`, and more. |
|
||||
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. Will be an empty object when called on a `Filter` component within the list view. |
|
||||
| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. Will be an emprt object when called on a `Filter` component within the list view. |
|
||||
| `user` | An object containing the currently authenticated user. |
|
||||
|
||||
## Example
|
||||
@@ -377,45 +375,6 @@ non-polymorphic relationship.
|
||||
|
||||
</Banner>
|
||||
|
||||
### Linking virtual fields with relationships
|
||||
|
||||
You can link virtual fields to fields in other collections through a relationship (or upload) field, for example:
|
||||
|
||||
```ts
|
||||
{
|
||||
collections: [
|
||||
{
|
||||
slug: 'categories',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'category',
|
||||
relationTo: 'categories',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'categoryTitle',
|
||||
virtual: 'category.title',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Here, `categoryTitle` will _always_ be populated with the corresponding value, even if the current `depth` is `0`, You can also query and sort by this field.
|
||||
The relationship must not be `hasMany: true` or polymorphic.
|
||||
The path can be deeply nested into 2 or more relationship fields, for example `post.category.title` as long as all the relationship fields meet the above requirement.
|
||||
|
||||
## Custom Components
|
||||
|
||||
### Field
|
||||
|
||||
@@ -21,23 +21,23 @@ Instead, you can invest your time and effort into learning the underlying open-s
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](./overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](./overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](./overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`editor`** | Customize or override the rich text editor. [More details](../rich-text/overview). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](./overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](./overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](./overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`editor`** | Customize or override the rich text editor. [More details](../rich-text/overview). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
\*_ An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -33,29 +33,29 @@ export const MySelectField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |
|
||||
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many selections instead of only one. |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-options) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`options`** \* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |
|
||||
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many selections instead of only one. |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-options) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -89,7 +89,6 @@ The Select Field inherits all of the default options from the base [Field Admin
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`isClearable`** | Set to `true` if you'd like this field to be clearable within the Admin UI. |
|
||||
| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`) |
|
||||
| **`placeholder`** | Define a custom text or function to replace the generic default placeholder |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -43,14 +43,14 @@ export const MyTabsField: Field = {
|
||||
|
||||
Each tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab.
|
||||
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** | Groups field data into an object when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | The label to render on the tab itself. Required when name is undefined, defaults to name converted to words. |
|
||||
| **`fields`** \* | The fields to render within this tab. |
|
||||
| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). (`name` must be present) |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`name`** | Groups field data into an object when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | The label to render on the tab itself. Required when name is undefined, defaults to name converted to words. |
|
||||
| **`fields`** \* | The fields to render within this tab. |
|
||||
| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). (`name` must be present) |
|
||||
| **`virtual`** | Provide `true` to disable field in the database (`name` must be present). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -28,29 +28,29 @@ export const MyTextField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`hasMany`** | Makes this field an ordered array of text instead of just a single text. |
|
||||
| **`minRows`** | Minimum number of texts in the array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of texts in the array, if `hasMany` is set to true. |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`hasMany`** | Makes this field an ordered array of text instead of just a single text. |
|
||||
| **`minRows`** | Minimum number of texts in the array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of texts in the array, if `hasMany` is set to true. |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -28,26 +28,26 @@ export const MyTextareaField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |
|
||||
| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -44,32 +44,32 @@ export const MyUploadField: Field = {
|
||||
|
||||
## Config Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`relationTo`** \* | Provide a single collection `slug` to allow this field to accept a relation to. **Note: the related collection must be configured to support Uploads.** |
|
||||
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-upload-options). |
|
||||
| **`hasMany`** | Boolean which, if set to true, allows this field to have many relations instead of only one. |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with hasMany. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with hasMany. |
|
||||
| **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](../queries/depth) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](/docs/upload/overview#collection-upload-options). |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [Admin Options](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database, or provide a string path to [link the field with a relationship](/docs/fields/relationship#linking-virtual-fields-with-relationships). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`relationTo`** \* | Provide a single collection `slug` to allow this field to accept a relation to. **Note: the related collection must be configured to support Uploads.** |
|
||||
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-upload-options). |
|
||||
| **`hasMany`** | Boolean which, if set to true, allows this field to have many relations instead of only one. |
|
||||
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with hasMany. |
|
||||
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with hasMany. |
|
||||
| **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](../queries/depth) |
|
||||
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](/docs/upload/overview#collection-upload-options). |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [Admin Options](./overview#admin-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -63,16 +63,10 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
```
|
||||
|
||||
- To install the [Postgres Adapter](../database/postgres), run:
|
||||
|
||||
```bash
|
||||
pnpm i @payloadcms/db-postgres
|
||||
```
|
||||
|
||||
- To install the [SQLite Adapter](../database/sqlite), run:
|
||||
```bash
|
||||
pnpm i @payloadcms/db-sqlite
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Note:** New [Database Adapters](/docs/database/overview) are becoming
|
||||
available every day. Check the docs for the most up-to-date list of what's
|
||||
@@ -81,7 +75,7 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
|
||||
#### 2. Copy Payload files into your Next.js app folder
|
||||
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](<https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)>) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
|
||||
```plaintext
|
||||
app/
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
title: Respecting Access Control with Local API Operations
|
||||
label: Access Control
|
||||
order: 40
|
||||
desc: Learn how to implement and enforce access control in Payload's Local API operations, ensuring that the right permissions are respected during data manipulation.
|
||||
keywords: server functions, local API, Payload, CMS, access control, permissions, user context, server-side logic, custom workflows, data management, headless CMS, TypeScript, Node.js, backend
|
||||
---
|
||||
|
||||
In Payload, local API operations **override access control by default**. This means that operations will run without checking if the current user has permission to perform the action. This is useful in certain scenarios where access control is not necessary, but it is important to be aware of when to enforce it for security reasons.
|
||||
|
||||
### Default Behavior: Access Control Skipped
|
||||
|
||||
By default, **local API operations skip access control**. This allows operations to execute without the system checking if the current user has appropriate permissions. This might be helpful in admin or server-side scripts where the user context is not required to perform the operation.
|
||||
|
||||
#### For example:
|
||||
|
||||
```ts
|
||||
// Access control is this operation would be skipped by default
|
||||
const test = await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'test',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Respecting Access Control
|
||||
|
||||
If you want to respect access control and ensure that the operation is performed only if the user has appropriate permissions, you need to explicitly pass the `user` object and set the `overrideAccess` option to `false`.
|
||||
|
||||
- `overrideAccess: false`: This ensures that access control is **not skipped** and the operation respects the current user's permissions.
|
||||
- `user`: Pass the authenticated user context to the operation. This ensures the system checks whether the user has the right permissions to perform the action.
|
||||
|
||||
```ts
|
||||
const authedCreate = await payload.create({
|
||||
collection: 'users',
|
||||
overrideAccess: false, // This ensures access control will be applied
|
||||
user, // Pass the authenticated user to check permissions
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'test',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This example will only allow the document to be created if the `user` we passed has the appropriate access control permissions.
|
||||
@@ -1,524 +0,0 @@
|
||||
---
|
||||
title: Using Local API Operations with Server Functions
|
||||
label: Server Functions
|
||||
order: 30
|
||||
desc: Learn to use Local API operations with Server Functions in Payload to manage server-side logic, data interactions, and custom workflows directly within your CMS.
|
||||
keywords: server functions, local API, Payload, CMS, server-side logic, custom workflows, data management, headless CMS, TypeScript, Node.js, backend
|
||||
---
|
||||
|
||||
In Next.js, **server functions** (previously called **server actions**) are special functions that run exclusively on the server, enabling secure backend logic execution while being callable from the frontend. These functions bridge the gap between client and server, allowing frontend components to perform backend operations without exposing sensitive logic.
|
||||
|
||||
### Why Use Server Functions?
|
||||
|
||||
- **Executing Backend Logic from the Frontend**: The Local API is designed for server environments and cannot be directly accessed from client-side code. Server functions enable frontend components to trigger backend operations securely.
|
||||
- **Security Benefits**: Instead of exposing a full REST or GraphQL API, server functions restrict access to only the necessary operations, reducing potential security risks.
|
||||
- **Performance Optimizations**: Next.js handles server functions efficiently, offering benefits like caching, optimized database queries, and reduced network overhead compared to traditional API calls.
|
||||
- **Simplified Development Workflow**: Rather than setting up full API routes with authentication and authorization checks, server functions allow for lightweight, direct execution of necessary operations.
|
||||
|
||||
### When to Use Server Functions
|
||||
|
||||
Use server functions whenever you need to call Local API operations from the frontend. Since the Local API is only accessible from the backend, server functions act as a secure bridge, eliminating the need to expose additional API endpoints.
|
||||
|
||||
## Examples
|
||||
|
||||
All Local API operations can be used within server functions, allowing you to interact with Payload's backend securely.
|
||||
|
||||
For a full list of available operations, see the [Local API](https://payloadcms.com/docs/local-api/overview) overview.
|
||||
|
||||
In the following examples, we'll cover some common use cases, including:
|
||||
|
||||
- Creating a document
|
||||
- Updating a document
|
||||
- Handling file uploads when creating or updating a document
|
||||
- Authenticating a user
|
||||
|
||||
### Creating a Document
|
||||
|
||||
First, let's create our server function. Here are some key points for this process:
|
||||
|
||||
- Begin by adding `'use server'` at the top of the file.
|
||||
- You can still use utilities such as `getPayload()`.
|
||||
- Once the function structure is in place, call the Local API operation `payload.create()` and pass in the relevant data.
|
||||
- It's good practice to wrap this in a `try...catch` block for error handling.
|
||||
- Finally, make sure to return the created document (don't just run the operation).
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function createPost(data) {
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
try {
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
data,
|
||||
})
|
||||
return post
|
||||
} catch (error) {
|
||||
throw new Error(`Error creating post: ${error.message}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's look at how to call the `createPost` function we just created from the frontend in a React component when a user clicks a button:
|
||||
|
||||
```ts
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { createPost } from '../server/actions'; // import the server function
|
||||
|
||||
export const PostForm: React.FC = () => {
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{result}</p>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
// Call the server function
|
||||
const newPost = await createPost({ title: 'Sample Post' });
|
||||
setResult('Post created: ' + newPost.title);
|
||||
}}
|
||||
>
|
||||
Create Post
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Updating a Document
|
||||
|
||||
The key points from the previous example also apply here.
|
||||
|
||||
To update a document instead of creating one, you would use `payload.update()` with the relevant data and **passing the document ID.**
|
||||
|
||||
Here's how the server function would look:
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function updatePost(id, data) {
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
try {
|
||||
const post = await payload.update({
|
||||
collection: 'posts',
|
||||
id, // the document id is required
|
||||
data,
|
||||
})
|
||||
return post
|
||||
} catch (error) {
|
||||
throw new Error(`Error updating post: ${error.message}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is how you would call the `updatePost` function from a frontend React component:
|
||||
|
||||
```ts
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { updatePost } from '../server/actions'; // import the server function
|
||||
|
||||
export const UpdatePostForm: React.FC = () => {
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>{result}</p>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
// Call the server function to update the post
|
||||
const updatedPost = await updatePost('your-post-id-123', { title: 'Updated Post' });
|
||||
setResult('Post updated: ' + updatedPost.title);
|
||||
}}
|
||||
>
|
||||
Update Post
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Authenticating a User
|
||||
|
||||
In this example, we will check if a user is authenticated using Payload's authentication system. Here's how it works:
|
||||
|
||||
- First, we use the headers function from `next/headers` to retrieve the request headers.
|
||||
- Next, we pass these headers to `payload.auth()` to fetch the user's authentication details.
|
||||
- If the user is authenticated, their information is returned. If not, handle the unauthenticated case accordingly.
|
||||
|
||||
Here's the server function to authenticate a user:
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { headers as getHeaders } from 'next/headers'
|
||||
import config from '@payload-config'
|
||||
import { getPayload } from 'payload'
|
||||
|
||||
export const authenticateUser = async () => {
|
||||
const payload = await getPayload({ config })
|
||||
const headers = await getHeaders()
|
||||
const { user } = await payload.auth({ headers })
|
||||
|
||||
if (user) {
|
||||
return { hello: user.email }
|
||||
}
|
||||
|
||||
return { hello: 'Not authenticated' }
|
||||
}
|
||||
```
|
||||
|
||||
Here's a basic example of how to call the authentication server function from the frontend to test it:
|
||||
|
||||
```ts
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { authenticateUser } from '../server/actions'; // Import the server function
|
||||
|
||||
export const AuthComponent: React.FC = () => {
|
||||
const [userInfo, setUserInfo] = useState<string>('');
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p>{userInfo}</p>
|
||||
|
||||
<button
|
||||
onClick={async () => {
|
||||
// Call the server function to authenticate the user
|
||||
const result = await authenticateUser();
|
||||
setUserInfo(result.hello);
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Check Authentication
|
||||
</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Creating a Document with File Upload
|
||||
|
||||
This example demonstrates how to write a server function that creates a document with a file upload. Here are the key steps:
|
||||
|
||||
- Pass two arguments: **data** for the document content and **upload** for the file
|
||||
- Merge the upload file into the document data as the media field
|
||||
- Use `payload.create()` to create a new post document with both the document data and file
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { getPayload } from 'payload'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function createPostWithUpload(data, upload) {
|
||||
const payload = await getPayload({ config })
|
||||
|
||||
try {
|
||||
// Prepare the data with the file
|
||||
const postData = {
|
||||
...data,
|
||||
media: upload,
|
||||
}
|
||||
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
data: postData,
|
||||
})
|
||||
|
||||
return post
|
||||
} catch (error) {
|
||||
throw new Error(`Error creating post: ${error.message}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is how you would use the server function we just created in a frontend component to allow users to submit a post along with a file upload:
|
||||
|
||||
- The user enters the post title and selects a file to upload.
|
||||
- When the form is submitted, the `handleSubmit` function checks if a file has been chosen.
|
||||
- If a file is selected, it passes both the title and the file to the `createPostWithFile` server function.
|
||||
- And you are done!
|
||||
|
||||
```ts
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { createPostWithUpload } from '../server/actions';
|
||||
|
||||
export const PostForm: React.FC = () => {
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files) {
|
||||
setFile(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!file) {
|
||||
setResult('Please upload a file.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Call the server function to create the post with the file
|
||||
const newPost = await createPostWithUpload({ title }, file);
|
||||
setResult('Post created with file: ' + newPost.title);
|
||||
} catch (error) {
|
||||
setResult('Error: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Post Title"
|
||||
/>
|
||||
<input type="file" onChange={handleFileChange} />
|
||||
<button type="submit">Create Post</button>
|
||||
<p>{result}</p>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Reusable Payload Server Functions
|
||||
|
||||
Managing authentication with the Local API can be tricky as you have to handle cookies and tokens yourself, and there aren't built-in logout or refresh functions since these only modify cookies. To make this easier, we provide `login`, `logout`, and `refresh` as ready-to-use server functions. They take care of the underlying complexity so you don't have to.
|
||||
|
||||
### Login
|
||||
|
||||
Logs in a user by verifying credentials and setting the authentication cookie. This function allows login via username or email, depending on the collection auth configuration.
|
||||
|
||||
#### Importing the `login` function
|
||||
|
||||
```ts
|
||||
import { login } from '@payloadcms/next/auth'
|
||||
```
|
||||
|
||||
The login function needs your Payload config, which cannot be imported in a client component. To work around this, create a simple server function like the one below, and call it from your client.
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { login } from '@payloadcms/next/auth'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function loginAction({
|
||||
email,
|
||||
password,
|
||||
}: {
|
||||
email: string
|
||||
password: string
|
||||
}) {
|
||||
try {
|
||||
const result = await login({
|
||||
collection: 'users',
|
||||
config,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
return result
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Login failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Login from the React Client Component
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { loginAction } from '../loginAction'
|
||||
|
||||
export default function LoginForm() {
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [password, setPassword] = useState<string>('')
|
||||
|
||||
return (
|
||||
<form onSubmit={() => loginAction({ email, password })}>
|
||||
<label htmlFor="email">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setEmail(e.target.value)
|
||||
}
|
||||
type="email"
|
||||
value={email}
|
||||
/>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setPassword(e.target.value)
|
||||
}
|
||||
type="password"
|
||||
value={password}
|
||||
/>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Logout
|
||||
|
||||
Logs out the current user by clearing the authentication cookie.
|
||||
|
||||
#### Importing the `logout` function
|
||||
|
||||
```ts
|
||||
import { logout } from '@payloadcms/next/auth'
|
||||
```
|
||||
|
||||
Similar to the login function, you now need to pass your Payload config to this function and this cannot be done in a client component. Use a helper server function as shown below.
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { logout } from '@payloadcms/next/auth'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function logoutAction() {
|
||||
try {
|
||||
return await logout({ config })
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Logout failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Logout from the React Client Component
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { logoutAction } from '../logoutAction'
|
||||
|
||||
export default function LogoutButton() {
|
||||
return <button onClick={() => logoutFunction()}>Logout</button>
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh
|
||||
|
||||
Refreshes the authentication token for the logged-in user.
|
||||
|
||||
#### Importing the `refresh` function
|
||||
|
||||
```ts
|
||||
import { refresh } from '@payloadcms/next/auth'
|
||||
```
|
||||
|
||||
As with login and logout, you need to pass your Payload config to this function. Create a helper server function like the one below. Passing the config directly to the client is not possible and will throw errors.
|
||||
|
||||
```ts
|
||||
'use server'
|
||||
|
||||
import { refresh } from '@payloadcms/next/auth'
|
||||
import config from '@payload-config'
|
||||
|
||||
export async function refreshAction() {
|
||||
try {
|
||||
return await refresh({
|
||||
collection: 'users', // pass your collection slug
|
||||
config,
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Refresh failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Using Refresh from the React Client Component
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { refreshAction } from '../actions/refreshAction'
|
||||
|
||||
export default function RefreshTokenButton() {
|
||||
return <button onClick={() => refreshFunction()}>Refresh</button>
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling in Server Functions
|
||||
|
||||
When using server functions, proper error handling is essential to prevent unhandled exceptions and provide meaningful feedback to the frontend.
|
||||
|
||||
### Best Practices#error-handling-best-practices
|
||||
|
||||
- Wrap Local API calls in **try/catch blocks** to catch potential errors.
|
||||
- **Log errors** on the server for debugging purposes.
|
||||
- Return structured **error responses** instead of exposing raw errors to the frontend.
|
||||
|
||||
Example of good error handling:
|
||||
|
||||
```ts
|
||||
export async function createPost(data) {
|
||||
try {
|
||||
const payload = await getPayload({ config })
|
||||
return await payload.create({ collection: 'posts', data })
|
||||
} catch (error) {
|
||||
console.error('Error creating post:', error)
|
||||
return { error: 'Failed to create post' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Using server functions helps prevent direct exposure of Local API operations to the frontend, but additional security best practices should be followed:
|
||||
|
||||
### Best Practices#security-best-practices
|
||||
|
||||
- **Restrict access**: Ensure that sensitive actions (like user management) are only callable by authorized users.
|
||||
- **Avoid passing sensitive data**: Do not return sensitive information such as user data, passwords, etc.
|
||||
- **Use authentication & authorization**: Check user roles before performing actions.
|
||||
|
||||
Example of restricting access based on user role:
|
||||
|
||||
```ts
|
||||
export async function deletePost(postId, user) {
|
||||
if (!user || user.role !== 'admin') {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
|
||||
const payload = await getPayload({ config })
|
||||
return await payload.delete({ collection: 'posts', id: postId })
|
||||
}
|
||||
```
|
||||
@@ -55,9 +55,18 @@ Because _**you**_ are in complete control of who can do what with your data, you
|
||||
wield that power responsibly before deploying to Production.
|
||||
|
||||
<Banner type="error">
|
||||
**By default, all Access Control functions require that a user is successfully logged in to Payload to create, read, update, or delete data.**
|
||||
**
|
||||
By default, all Access Control functions require that a user is successfully logged in to
|
||||
Payload to create, read, update, or delete data.
|
||||
**
|
||||
But, if you allow public user registration, for example, you will want to make sure that your
|
||||
access control functions are more strict - permitting
|
||||
|
||||
But, if you allow public user registration, for example, you will want to make sure that your access control functions are more strict - permitting **only appropriate users** to perform appropriate actions.
|
||||
**
|
||||
only appropriate users
|
||||
**
|
||||
|
||||
to perform appropriate actions.
|
||||
|
||||
</Banner>
|
||||
|
||||
|
||||
@@ -55,11 +55,10 @@ All collection `find` queries are paginated automatically. Responses are returne
|
||||
|
||||
All Payload APIs support the pagination controls below. With them, you can create paginated lists of documents within your application:
|
||||
|
||||
| Control | Default | Description |
|
||||
| ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `limit` | `10` | Limits the number of documents returned per page - set to `0` to show all documents, we automatically disabled pagination for you when `limit` is `0` for optimisation |
|
||||
| `pagination` | `true` | Set to `false` to disable pagination and return all documents |
|
||||
| `page` | `1` | Get a specific page number |
|
||||
| Control | Description |
|
||||
| ------- | --------------------------------------- |
|
||||
| `limit` | Limits the number of documents returned |
|
||||
| `page` | Get a specific page number |
|
||||
|
||||
### Disabling pagination within Local API
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: query, documents, pagination, documentation, Content Management System
|
||||
|
||||
Documents in Payload can be easily sorted by a specific [Field](../fields/overview). When querying Documents, you can pass the name of any top-level field, and the response will sort the Documents by that field in _ascending_ order. If prefixed with a minus symbol ("-"), they will be sorted in _descending_ order. In Local API multiple fields can be specified by using an array of strings. In REST API multiple fields can be specified by separating fields with comma. The minus symbol can be in front of individual fields.
|
||||
|
||||
Because sorting is handled by the database, the field cannot be a [Virtual Field](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) unless it's [linked with a relationship field](/docs/fields/relationship#linking-virtual-fields-with-relationships). It must be stored in the database to be searchable.
|
||||
Because sorting is handled by the database, the field cannot be a [Virtual Field](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges). It must be stored in the database to be searchable.
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:** For performance reasons, it is recommended to enable `index: true`
|
||||
|
||||
@@ -58,8 +58,8 @@ Query Presets are subject to the same [Access Control](../access-control/overvie
|
||||
|
||||
Access Control for Query Presets can be customized in two ways:
|
||||
|
||||
1. [Collection Access Control](#collection-access-control): Applies to all presets. These rules are not controllable by the user and are statically defined in the config.
|
||||
2. [Document Access Control](#document-access-control): Applies to each individual preset. These rules are controllable by the user and are saved to the document.
|
||||
1. [Collection Access Control](#static-access-control): Applies to all presets. These rules are not controllable by the user and are statically defined in the config.
|
||||
2. [Document Access Control](#dynamic-access-control): Applies to each individual preset. These rules are controllable by the user and are saved to the document.
|
||||
|
||||
### Collection Access Control
|
||||
|
||||
@@ -117,7 +117,7 @@ Adding custom access control rules requires:
|
||||
2. A set of fields to conditionally render when that option is selected
|
||||
3. A function that returns the access control rules for that option
|
||||
|
||||
To do this, use the `queryPresets.constraints` property in your [Payload Config](../configuration/overview).
|
||||
To do this, use the `queryPresets.constraints` property in your [Payload Config](../configuration/payload-config).
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
@@ -128,28 +128,26 @@ const config = buildConfig({
|
||||
// ...
|
||||
// highlight-start
|
||||
constraints: {
|
||||
read: [
|
||||
{
|
||||
label: 'Specific Roles',
|
||||
value: 'specificRoles',
|
||||
fields: [
|
||||
{
|
||||
name: 'roles',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: [
|
||||
{ label: 'Admin', value: 'admin' },
|
||||
{ label: 'User', value: 'user' },
|
||||
],
|
||||
},
|
||||
],
|
||||
access: ({ req: { user } }) => ({
|
||||
'access.read.roles': {
|
||||
in: [user?.roles],
|
||||
},
|
||||
}),
|
||||
},
|
||||
],
|
||||
read: {
|
||||
label: 'Specific Roles',
|
||||
value: 'specificRoles',
|
||||
fields: [
|
||||
{
|
||||
name: 'roles',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: [
|
||||
{ label: 'Admin', value: 'admin' },
|
||||
{ label: 'User', value: 'user' },
|
||||
],
|
||||
},
|
||||
],
|
||||
access: ({ req: { user } }) => ({
|
||||
'access.read.roles': {
|
||||
in: [user?.roles],
|
||||
},
|
||||
}),
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
// Your richtext data here
|
||||
const data: SerializedEditorState = {}
|
||||
|
||||
const markdown = convertLexicalToMarkdown({
|
||||
const html = convertLexicalToMarkdown({
|
||||
data,
|
||||
editorConfig: await editorConfigFactory.default({
|
||||
config, // <= make sure you have access to your Payload Config
|
||||
@@ -101,7 +101,7 @@ import {
|
||||
editorConfigFactory,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
|
||||
const lexicalJSON = convertMarkdownToLexical({
|
||||
const html = convertMarkdownToLexical({
|
||||
editorConfig: await editorConfigFactory.default({
|
||||
config, // <= make sure you have access to your Payload Config
|
||||
}),
|
||||
|
||||
@@ -223,7 +223,7 @@ This allows you to add i18n translations scoped to your feature. This specific e
|
||||
|
||||
### Markdown Transformers#server-feature-markdown-transformers
|
||||
|
||||
The Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](/docs/rich-text/converting-markdown).
|
||||
The Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](/docs/rich-text/converters#markdown-lexical).
|
||||
|
||||
```ts
|
||||
import { createServerFeature } from '@payloadcms/richtext-lexical'
|
||||
|
||||
@@ -334,28 +334,12 @@ To upload a file, use your collection's [`create`](/docs/rest-api/overview#colle
|
||||
|
||||
Send your request as a `multipart/form-data` request, using [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) if possible.
|
||||
|
||||
<Banner type="info">
|
||||
**Note:** To include any additional fields (like `title`, `alt`, etc.), append
|
||||
a `_payload` field containing a JSON-stringified object of the required
|
||||
values. These values must match the schema of your upload-enabled collection.
|
||||
</Banner>
|
||||
|
||||
```ts
|
||||
const fileInput = document.querySelector('#your-file-input')
|
||||
const formData = new FormData()
|
||||
|
||||
formData.append('file', fileInput.files[0])
|
||||
|
||||
// Replace with the fields defined in your upload-enabled collection.
|
||||
// The example below includes an optional field like 'title'.
|
||||
formData.append(
|
||||
'_payload',
|
||||
JSON.stringify({
|
||||
title: 'Example Title',
|
||||
description: 'An optional description for the file',
|
||||
}),
|
||||
)
|
||||
|
||||
fetch('api/:upload-slug', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
|
||||
@@ -84,7 +84,6 @@ pnpm add @payloadcms/storage-s3
|
||||
- The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
- When deploying to Vercel, server uploads are limited with 4.5MB. Set `clientUploads` to `true` to do uploads directly on the client. You must allow CORS PUT method for the bucket to your website.
|
||||
- Configure `signedDownloads` (either globally of per-collection in `collections`) to use [presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) for files downloading. This can improve performance for large files (like videos) while still respecting your access control.
|
||||
|
||||
```ts
|
||||
import { s3Storage } from '@payloadcms/storage-s3'
|
||||
|
||||
@@ -22,7 +22,6 @@ Collections and Globals both support the same options for configuring autosave.
|
||||
| Drafts Autosave Options | Description |
|
||||
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `interval` | Define an `interval` in milliseconds to automatically save progress while documents are edited. Document updates are "debounced" at this interval. Defaults to `800`. |
|
||||
| `showSaveDraftButton` | Set this to `true` to show the "Save as draft" button even while autosave is enabled. Defaults to `false`. |
|
||||
|
||||
**Example config with versions, drafts, and autosave enabled:**
|
||||
|
||||
@@ -51,13 +50,9 @@ export const Pages: CollectionConfig = {
|
||||
drafts: {
|
||||
autosave: true,
|
||||
|
||||
// Alternatively, you can specify an object to customize autosave:
|
||||
// Alternatively, you can specify an `interval`:
|
||||
// autosave: {
|
||||
// Define how often the document should be autosaved (in milliseconds)
|
||||
// interval: 1500,
|
||||
//
|
||||
// Show the "Save as draft" button even while autosave is enabled
|
||||
// showSaveDraftButton: true,
|
||||
// },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -49,17 +49,21 @@ Within the Admin UI, if drafts are enabled, a document can be shown with one of
|
||||
specify if you are interacting with drafts or with live documents.
|
||||
</Banner>
|
||||
|
||||
#### Updating or creating drafts
|
||||
#### Updating drafts
|
||||
|
||||
If you enable drafts on a collection or global, the `create` and `update` operations for REST, GraphQL, and Local APIs expose a new option called `draft` which allows you to specify if you are creating or updating a **draft**, or if you're just sending your changes straight to the published document. For example, if you pass the query parameter `?draft=true` to a REST `create` or `update` operation, your action will be treated as if you are creating a `draft` and not a published document. By default, the `draft` argument is set to `false`.
|
||||
If you enable drafts on a collection or global, the `update` operation for REST, GraphQL, and Local APIs exposes a new option called `draft` which allows you to specify if you are updating a **draft**, or if you're just sending your changes straight to the published document. For example, if you pass the query parameter `?draft=true` to a REST `update` operation, your action will be treated as if you are updating a `draft` and not a published document. By default, the `draft` argument is set to `false`.
|
||||
|
||||
**Required fields**
|
||||
|
||||
If `draft` is enabled while creating or updating a document, all fields are considered as not required, so that you can save drafts that are incomplete.
|
||||
If `draft` is enabled while updating a document, all fields are considered as not required, so that you can save drafts that are incomplete.
|
||||
|
||||
#### Creating drafts
|
||||
|
||||
By default, draft-enabled collections will create draft documents when you create a new document. In order to create a published document, you need to pass `_status: 'published'` to the document data.
|
||||
|
||||
#### Reading drafts vs. published documents
|
||||
|
||||
In addition to the `draft` argument within `create` and `update` operations, a `draft` argument is also exposed for `find` and `findByID` operations.
|
||||
In addition to the `draft` argument within `update` operations, a `draft` argument is also exposed for `find` and `findByID` operations.
|
||||
|
||||
If `draft` is set to `true` while reading a document, **Payload will automatically replace returned document(s) with their newest drafts** if any newer drafts are available.
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
|
||||
}
|
||||
```
|
||||
|
||||
For more details on how to extend this functionality, see the [Live Preview](https://payloadcms.com/docs/live-preview/overview) docs.
|
||||
For more details on how to extend this functionality, see the [Live Preview](https://payloadcms.com/docs/live-preview) docs.
|
||||
|
||||
## Front-end
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const home: Partial<Page> = {
|
||||
type: 'link',
|
||||
children: [{ text: 'Live Preview' }],
|
||||
newTab: true,
|
||||
url: 'https://payloadcms.com/docs/live-preview/overview',
|
||||
url: 'https://payloadcms.com/docs/live-preview',
|
||||
},
|
||||
{
|
||||
text: ' you can edit this page in the admin panel and see the changes reflected here in real time.',
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -120,7 +120,7 @@
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@libsql/client": "0.14.0",
|
||||
"@next/bundle-analyzer": "15.3.0",
|
||||
"@next/bundle-analyzer": "15.2.3",
|
||||
"@payloadcms/db-postgres": "workspace:*",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/eslint-plugin": "workspace:*",
|
||||
@@ -135,8 +135,8 @@
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/minimist": "1.2.5",
|
||||
"@types/node": "22.5.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.2",
|
||||
"@types/react": "19.0.12",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@types/shelljs": "0.8.15",
|
||||
"chalk": "^4.1.2",
|
||||
"comment-json": "^4.2.3",
|
||||
@@ -156,14 +156,14 @@
|
||||
"lint-staged": "15.2.7",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "^10",
|
||||
"next": "15.3.0",
|
||||
"next": "15.2.3",
|
||||
"open": "^10.1.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"playwright": "1.50.0",
|
||||
"playwright-core": "1.50.0",
|
||||
"prettier": "3.5.3",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"rimraf": "6.0.1",
|
||||
"sharp": "0.32.6",
|
||||
"shelljs": "0.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/admin-bar",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "An admin bar for React apps using Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.2",
|
||||
"@types/react": "19.0.12",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -10,7 +10,6 @@ import type { CliArgs, DbType, ProjectExample, ProjectTemplate } from '../types.
|
||||
import { createProject } from './create-project.js'
|
||||
import { dbReplacements } from './replacements.js'
|
||||
import { getValidTemplates } from './templates.js'
|
||||
import { manageEnvFiles } from './manage-env-files.js'
|
||||
|
||||
describe('createProject', () => {
|
||||
let projectDir: string
|
||||
@@ -155,75 +154,5 @@ describe('createProject', () => {
|
||||
expect(content).toContain(dbReplacement.configReplacement().join('\n'))
|
||||
})
|
||||
})
|
||||
describe('managing env files', () => {
|
||||
it('updates .env files without overwriting existing data', async () => {
|
||||
const envFilePath = path.join(projectDir, '.env')
|
||||
const envExampleFilePath = path.join(projectDir, '.env.example')
|
||||
|
||||
fse.ensureDirSync(projectDir)
|
||||
fse.ensureFileSync(envFilePath)
|
||||
fse.ensureFileSync(envExampleFilePath)
|
||||
|
||||
const initialEnvContent = `CUSTOM_VAR=custom-value\nDATABASE_URI=old-connection\n`
|
||||
const initialEnvExampleContent = `CUSTOM_VAR=custom-value\nDATABASE_URI=old-connection\nPAYLOAD_SECRET=YOUR_SECRET_HERE\n`
|
||||
|
||||
fse.writeFileSync(envFilePath, initialEnvContent)
|
||||
fse.writeFileSync(envExampleFilePath, initialEnvExampleContent)
|
||||
|
||||
await manageEnvFiles({
|
||||
cliArgs: {
|
||||
'--debug': true,
|
||||
} as CliArgs,
|
||||
databaseType: 'mongodb',
|
||||
databaseUri: 'mongodb://localhost:27017/test',
|
||||
payloadSecret: 'test-secret',
|
||||
projectDir,
|
||||
template: undefined,
|
||||
})
|
||||
|
||||
const updatedEnvContent = fse.readFileSync(envFilePath, 'utf-8')
|
||||
|
||||
expect(updatedEnvContent).toContain('CUSTOM_VAR=custom-value')
|
||||
expect(updatedEnvContent).toContain('DATABASE_URI=mongodb://localhost:27017/test')
|
||||
expect(updatedEnvContent).toContain('PAYLOAD_SECRET=test-secret')
|
||||
|
||||
const updatedEnvExampleContent = fse.readFileSync(envExampleFilePath, 'utf-8')
|
||||
|
||||
expect(updatedEnvExampleContent).toContain('CUSTOM_VAR=custom-value')
|
||||
expect(updatedEnvContent).toContain('DATABASE_URI=mongodb://localhost:27017/test')
|
||||
expect(updatedEnvContent).toContain('PAYLOAD_SECRET=test-secret')
|
||||
})
|
||||
|
||||
it('creates .env and .env.example if they do not exist', async () => {
|
||||
const envFilePath = path.join(projectDir, '.env')
|
||||
const envExampleFilePath = path.join(projectDir, '.env.example')
|
||||
|
||||
fse.ensureDirSync(projectDir)
|
||||
|
||||
if (fse.existsSync(envFilePath)) fse.removeSync(envFilePath)
|
||||
if (fse.existsSync(envExampleFilePath)) fse.removeSync(envExampleFilePath)
|
||||
|
||||
await manageEnvFiles({
|
||||
cliArgs: {
|
||||
'--debug': true,
|
||||
} as CliArgs,
|
||||
databaseUri: '',
|
||||
payloadSecret: '',
|
||||
projectDir,
|
||||
template: undefined,
|
||||
})
|
||||
|
||||
expect(fse.existsSync(envFilePath)).toBe(true)
|
||||
expect(fse.existsSync(envExampleFilePath)).toBe(true)
|
||||
|
||||
const updatedEnvContent = fse.readFileSync(envFilePath, 'utf-8')
|
||||
expect(updatedEnvContent).toContain('DATABASE_URI=your-connection-string-here')
|
||||
expect(updatedEnvContent).toContain('PAYLOAD_SECRET=YOUR_SECRET_HERE')
|
||||
|
||||
const updatedEnvExampleContent = fse.readFileSync(envExampleFilePath, 'utf-8')
|
||||
expect(updatedEnvExampleContent).toContain('DATABASE_URI=your-connection-string-here')
|
||||
expect(updatedEnvExampleContent).toContain('PAYLOAD_SECRET=YOUR_SECRET_HERE')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,55 +6,66 @@ import type { CliArgs, DbType, ProjectTemplate } from '../types.js'
|
||||
import { debug, error } from '../utils/log.js'
|
||||
import { dbChoiceRecord } from './select-db.js'
|
||||
|
||||
const updateEnvExampleVariables = (
|
||||
contents: string,
|
||||
databaseType: DbType | undefined,
|
||||
payloadSecret?: string,
|
||||
databaseUri?: string,
|
||||
): string => {
|
||||
const seenKeys = new Set<string>()
|
||||
const updatedEnv = contents
|
||||
const updateEnvExampleVariables = (contents: string, databaseType: DbType | undefined): string => {
|
||||
return contents
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
if (line.startsWith('#') || !line.includes('=')) {
|
||||
return line
|
||||
return line // Preserve comments and unrelated lines
|
||||
}
|
||||
|
||||
const [key] = line.split('=')
|
||||
|
||||
if (!key) {return}
|
||||
|
||||
if (key === 'DATABASE_URI' || key === 'POSTGRES_URL' || key === 'MONGODB_URI') {
|
||||
const dbChoice = databaseType ? dbChoiceRecord[databaseType] : null
|
||||
|
||||
if (dbChoice) {
|
||||
const placeholderUri = databaseUri
|
||||
? databaseUri
|
||||
: `${dbChoice.dbConnectionPrefix}your-database-name${dbChoice.dbConnectionSuffix || ''}`
|
||||
line =
|
||||
databaseType === 'vercel-postgres'
|
||||
? `POSTGRES_URL=${placeholderUri}`
|
||||
: `DATABASE_URI=${placeholderUri}`
|
||||
const placeholderUri = `${dbChoice.dbConnectionPrefix}your-database-name${
|
||||
dbChoice.dbConnectionSuffix || ''
|
||||
}`
|
||||
return databaseType === 'vercel-postgres'
|
||||
? `POSTGRES_URL=${placeholderUri}`
|
||||
: `DATABASE_URI=${placeholderUri}`
|
||||
}
|
||||
|
||||
return `DATABASE_URI=your-database-connection-here` // Fallback
|
||||
}
|
||||
|
||||
if (key === 'PAYLOAD_SECRET' || key === 'PAYLOAD_SECRET_KEY') {
|
||||
line = `PAYLOAD_SECRET=${payloadSecret || 'YOUR_SECRET_HERE'}`
|
||||
return `PAYLOAD_SECRET=YOUR_SECRET_HERE`
|
||||
}
|
||||
|
||||
// handles dupes
|
||||
if (seenKeys.has(key)) {
|
||||
return null
|
||||
}
|
||||
|
||||
seenKeys.add(key)
|
||||
|
||||
return line
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reverse()
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
return updatedEnv
|
||||
const generateEnvContent = (
|
||||
existingEnv: string,
|
||||
databaseType: DbType | undefined,
|
||||
databaseUri: string,
|
||||
payloadSecret: string,
|
||||
): string => {
|
||||
const dbKey = databaseType === 'vercel-postgres' ? 'POSTGRES_URL' : 'DATABASE_URI'
|
||||
|
||||
const envVars: Record<string, string> = {}
|
||||
existingEnv
|
||||
.split('\n')
|
||||
.filter((line) => line.includes('=') && !line.startsWith('#'))
|
||||
.forEach((line) => {
|
||||
const [key, value] = line.split('=')
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
envVars[key] = value
|
||||
})
|
||||
|
||||
// Override specific keys
|
||||
envVars[dbKey] = databaseUri
|
||||
envVars['PAYLOAD_SECRET'] = payloadSecret
|
||||
|
||||
// Rebuild content
|
||||
return Object.entries(envVars)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
/** Parse and swap .env.example values and write .env */
|
||||
@@ -77,71 +88,42 @@ export async function manageEnvFiles(args: {
|
||||
|
||||
const envExamplePath = path.join(projectDir, '.env.example')
|
||||
const envPath = path.join(projectDir, '.env')
|
||||
const emptyEnvContent = `# Added by Payload\nDATABASE_URI=your-connection-string-here\nPAYLOAD_SECRET=YOUR_SECRET_HERE\n`
|
||||
|
||||
try {
|
||||
let updatedExampleContents: string
|
||||
|
||||
if (template?.type === 'plugin') {
|
||||
if (debugFlag) {
|
||||
debug(`plugin template detected - no .env added .env.example added`)
|
||||
// Update .env.example
|
||||
if (template?.type === 'starter') {
|
||||
if (!fs.existsSync(envExamplePath)) {
|
||||
error(`.env.example file not found at ${envExamplePath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!fs.existsSync(envExamplePath)) {
|
||||
updatedExampleContents = updateEnvExampleVariables(
|
||||
emptyEnvContent,
|
||||
databaseType,
|
||||
payloadSecret,
|
||||
databaseUri,
|
||||
)
|
||||
|
||||
await fs.writeFile(envExamplePath, updatedExampleContents)
|
||||
if (debugFlag) {
|
||||
debug(`.env.example file successfully created`)
|
||||
}
|
||||
} else {
|
||||
const envExampleContents = await fs.readFile(envExamplePath, 'utf8')
|
||||
const mergedEnvs = envExampleContents + '\n' + emptyEnvContent
|
||||
updatedExampleContents = updateEnvExampleVariables(
|
||||
mergedEnvs,
|
||||
databaseType,
|
||||
payloadSecret,
|
||||
databaseUri,
|
||||
)
|
||||
updatedExampleContents = updateEnvExampleVariables(envExampleContents, databaseType)
|
||||
|
||||
await fs.writeFile(envExamplePath, updatedExampleContents.trimEnd() + '\n')
|
||||
|
||||
await fs.writeFile(envExamplePath, updatedExampleContents)
|
||||
if (debugFlag) {
|
||||
debug(`.env.example file successfully updated`)
|
||||
}
|
||||
} else {
|
||||
updatedExampleContents = `# Added by Payload\nDATABASE_URI=your-connection-string-here\nPAYLOAD_SECRET=YOUR_SECRET_HERE\n`
|
||||
await fs.writeFile(envExamplePath, updatedExampleContents.trimEnd() + '\n')
|
||||
}
|
||||
|
||||
if (!fs.existsSync(envPath)) {
|
||||
const envContent = updateEnvExampleVariables(
|
||||
emptyEnvContent,
|
||||
databaseType,
|
||||
payloadSecret,
|
||||
databaseUri,
|
||||
)
|
||||
await fs.writeFile(envPath, envContent)
|
||||
// Merge existing variables and create or update .env
|
||||
const envExampleContents = await fs.readFile(envExamplePath, 'utf8')
|
||||
const envContent = generateEnvContent(
|
||||
envExampleContents,
|
||||
databaseType,
|
||||
databaseUri,
|
||||
payloadSecret,
|
||||
)
|
||||
await fs.writeFile(envPath, `# Added by Payload\n${envContent.trimEnd()}\n`)
|
||||
|
||||
if (debugFlag) {
|
||||
debug(`.env file successfully created`)
|
||||
}
|
||||
} else {
|
||||
const envContents = await fs.readFile(envPath, 'utf8')
|
||||
const mergedEnvs = envContents + '\n' + emptyEnvContent
|
||||
const updatedEnvContents = updateEnvExampleVariables(
|
||||
mergedEnvs,
|
||||
databaseType,
|
||||
payloadSecret,
|
||||
databaseUri,
|
||||
)
|
||||
|
||||
await fs.writeFile(envPath, updatedEnvContents)
|
||||
if (debugFlag) {
|
||||
debug(`.env file successfully updated`)
|
||||
}
|
||||
if (debugFlag) {
|
||||
debug(`.env file successfully created or updated`)
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
error('Unable to manage environment files')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { FilterQuery } from 'mongoose'
|
||||
import type { FlattenedField, Operator, PathToQuery, Payload } from 'payload'
|
||||
|
||||
import { Types } from 'mongoose'
|
||||
import { APIError, getFieldByPath, getLocalizedPaths } from 'payload'
|
||||
import { APIError, getLocalizedPaths } from 'payload'
|
||||
import { validOperatorSet } from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
@@ -138,7 +138,7 @@ export async function buildSearchParam({
|
||||
throw new APIError(`Collection with the slug ${collectionSlug} was not found.`)
|
||||
}
|
||||
|
||||
const { collectionConfig, Model: SubModel } = getCollection({
|
||||
const { Model: SubModel } = getCollection({
|
||||
adapter: payload.db as MongooseAdapter,
|
||||
collectionSlug,
|
||||
})
|
||||
@@ -154,72 +154,22 @@ export async function buildSearchParam({
|
||||
},
|
||||
})
|
||||
|
||||
const field = paths[0].field
|
||||
|
||||
const select: Record<string, boolean> = {
|
||||
_id: true,
|
||||
}
|
||||
|
||||
let joinPath: null | string = null
|
||||
|
||||
if (field.type === 'join') {
|
||||
const relationshipField = getFieldByPath({
|
||||
fields: collectionConfig.flattenedFields,
|
||||
path: field.on,
|
||||
})
|
||||
if (!relationshipField) {
|
||||
throw new APIError('Relationship field was not found')
|
||||
}
|
||||
|
||||
let path = relationshipField.localizedPath
|
||||
if (relationshipField.pathHasLocalized && payload.config.localization) {
|
||||
path = path.replace('<locale>', locale || payload.config.localization.defaultLocale)
|
||||
}
|
||||
select[path] = true
|
||||
|
||||
joinPath = path
|
||||
}
|
||||
|
||||
if (joinPath) {
|
||||
select[joinPath] = true
|
||||
}
|
||||
|
||||
const result = await SubModel.find(subQuery).lean().limit(50).select(select)
|
||||
const result = await SubModel.find(subQuery, subQueryOptions)
|
||||
|
||||
const $in: unknown[] = []
|
||||
|
||||
result.forEach((doc: any) => {
|
||||
if (joinPath) {
|
||||
let ref = doc
|
||||
result.forEach((doc) => {
|
||||
const stringID = doc._id.toString()
|
||||
$in.push(stringID)
|
||||
|
||||
for (const segment of joinPath.split('.')) {
|
||||
if (typeof ref === 'object' && ref) {
|
||||
ref = ref[segment]
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(ref)) {
|
||||
for (const item of ref) {
|
||||
if (item instanceof Types.ObjectId) {
|
||||
$in.push(item)
|
||||
}
|
||||
}
|
||||
} else if (ref instanceof Types.ObjectId) {
|
||||
$in.push(ref)
|
||||
}
|
||||
} else {
|
||||
const stringID = doc._id.toString()
|
||||
$in.push(stringID)
|
||||
|
||||
if (Types.ObjectId.isValid(stringID)) {
|
||||
$in.push(doc._id)
|
||||
}
|
||||
if (Types.ObjectId.isValid(stringID)) {
|
||||
$in.push(doc._id)
|
||||
}
|
||||
})
|
||||
|
||||
if (pathsToQuery.length === 1) {
|
||||
return {
|
||||
path: joinPath ? '_id' : path,
|
||||
path,
|
||||
value: { $in },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,19 +81,7 @@ export async function parseParams({
|
||||
[searchParam.path]: searchParam.value,
|
||||
})
|
||||
} else {
|
||||
if (result[searchParam.path]) {
|
||||
if (!result.$and) {
|
||||
result.$and = []
|
||||
}
|
||||
|
||||
result.$and.push({ [searchParam.path]: result[searchParam.path] })
|
||||
result.$and.push({
|
||||
[searchParam.path]: searchParam.value,
|
||||
})
|
||||
delete result[searchParam.path]
|
||||
} else {
|
||||
result[searchParam.path] = searchParam.value
|
||||
}
|
||||
result[searchParam.path] = searchParam.value
|
||||
}
|
||||
} else if (typeof searchParam?.value === 'object') {
|
||||
result = deepMergeWithCombinedArrays(result, searchParam.value ?? {}, {
|
||||
|
||||
@@ -166,7 +166,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
|
||||
for (let i = 0; i < result.docs.length; i++) {
|
||||
const id = result.docs[i].parent
|
||||
result.docs[i] = result.docs[i].version ?? {}
|
||||
result.docs[i] = result.docs[i].version
|
||||
result.docs[i].id = id
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Connect, Migration, Payload } from 'payload'
|
||||
|
||||
import { pushDevSchema } from '@payloadcms/drizzle'
|
||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||
import pg from 'pg'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
@@ -60,7 +61,7 @@ export const connect: Connect = async function connect(
|
||||
|
||||
try {
|
||||
if (!this.pool) {
|
||||
this.pool = new this.pg.Pool(this.poolOptions)
|
||||
this.pool = new pg.Pool(this.poolOptions)
|
||||
await connectWithReconnect({ adapter: this, payload: this.payload })
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ import {
|
||||
} from '@payloadcms/drizzle/postgres'
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { createDatabaseAdapter, defaultBeginTransaction } from 'payload'
|
||||
import pgDependency from 'pg'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { Args, PostgresAdapter } from './types.js'
|
||||
@@ -131,7 +130,6 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators: operatorMap,
|
||||
pg: args.pg || pgDependency,
|
||||
pgSchema: adapterSchema,
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
pool: undefined,
|
||||
|
||||
@@ -12,8 +12,6 @@ import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
|
||||
import type { PgSchema, PgTableFn, PgTransactionConfig } from 'drizzle-orm/pg-core'
|
||||
import type { Pool, PoolConfig } from 'pg'
|
||||
|
||||
type PgDependency = typeof import('pg')
|
||||
|
||||
export type Args = {
|
||||
/**
|
||||
* Transform the schema after it's built.
|
||||
@@ -47,7 +45,6 @@ export type Args = {
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
pg?: PgDependency
|
||||
pool: PoolConfig
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
@@ -77,7 +74,6 @@ type ResolveSchemaType<T> = 'schema' extends keyof T
|
||||
type Drizzle = NodePgDatabase<ResolveSchemaType<GeneratedDatabaseSchema>>
|
||||
export type PostgresAdapter = {
|
||||
drizzle: Drizzle
|
||||
pg: PgDependency
|
||||
pool: Pool
|
||||
poolOptions: PoolConfig
|
||||
} & BasePostgresAdapter
|
||||
@@ -102,8 +98,6 @@ declare module 'payload' {
|
||||
initializing: Promise<void>
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
/** Optionally inject your own node-postgres. This is required if you wish to instrument the driver with @payloadcms/plugin-sentry. */
|
||||
pg?: PgDependency
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -16,7 +16,7 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where)
|
||||
return Number(countResult?.[0]?.count ?? 0)
|
||||
return Number(countResult[0]?.count)
|
||||
}
|
||||
|
||||
let query: SQLiteSelect = db
|
||||
@@ -39,5 +39,5 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
// Instead, COUNT (GROUP BY id) can be used which is still slower than COUNT(*) but acceptable.
|
||||
const countResult = await query
|
||||
|
||||
return Number(countResult?.[0]?.count ?? 0)
|
||||
return Number(countResult[0]?.count)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-vercel-postgres",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "Vercel Postgres adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -78,9 +78,7 @@ export const createTableName = ({
|
||||
|
||||
if (result.length > 63) {
|
||||
throw new APIError(
|
||||
`Exceeded max identifier length for table or enum name of 63 characters. Invalid name: ${result}.
|
||||
Tip: You can use the dbName property to reduce the table name length.
|
||||
`,
|
||||
`Exceeded max identifier length for table or enum name of 63 characters. Invalid name: ${result}`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -59,10 +59,6 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
docToDelete = await db.query[tableName].findFirst(findManyArgs)
|
||||
}
|
||||
|
||||
if (!docToDelete) {
|
||||
return null
|
||||
}
|
||||
|
||||
const result =
|
||||
returning === false
|
||||
? null
|
||||
@@ -72,7 +68,6 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
data: docToDelete,
|
||||
fields: collection.flattenedFields,
|
||||
joinQuery: false,
|
||||
tableName,
|
||||
})
|
||||
|
||||
await this.deleteWhere({
|
||||
|
||||
@@ -46,7 +46,6 @@ export const findMany = async function find({
|
||||
const offset = skip || (page - 1) * limit
|
||||
|
||||
if (limit === 0) {
|
||||
pagination = false
|
||||
limit = undefined
|
||||
}
|
||||
|
||||
@@ -159,7 +158,6 @@ export const findMany = async function find({
|
||||
data,
|
||||
fields,
|
||||
joinQuery,
|
||||
tableName,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -196,8 +196,7 @@ export const traverseFields = ({
|
||||
}
|
||||
}
|
||||
|
||||
const relationName = field.dbName ? `_${arrayTableName}` : `${path}${field.name}`
|
||||
currentArgs.with[relationName] = withArray
|
||||
currentArgs.with[`${path}${field.name}`] = withArray
|
||||
|
||||
traverseFields({
|
||||
_locales: withArray.with._locales,
|
||||
|
||||
@@ -23,10 +23,8 @@ export { migrateFresh } from './migrateFresh.js'
|
||||
export { migrateRefresh } from './migrateRefresh.js'
|
||||
export { migrateReset } from './migrateReset.js'
|
||||
export { migrateStatus } from './migrateStatus.js'
|
||||
export { default as buildQuery } from './queries/buildQuery.js'
|
||||
export { operatorMap } from './queries/operatorMap.js'
|
||||
export type { Operators } from './queries/operatorMap.js'
|
||||
export { parseParams } from './queries/parseParams.js'
|
||||
export { queryDrafts } from './queryDrafts.js'
|
||||
export { buildDrizzleRelations } from './schema/buildDrizzleRelations.js'
|
||||
export { buildRawSchema } from './schema/buildRawSchema.js'
|
||||
|
||||
@@ -42,36 +42,33 @@ export const migrate: DrizzleAdapter['migrate'] = async function migrate(
|
||||
limit: 0,
|
||||
sort: '-name',
|
||||
}))
|
||||
|
||||
if (migrationsInDB.find((m) => m.batch === -1)) {
|
||||
const { confirm: runMigrations } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message:
|
||||
"It looks like you've run Payload in dev mode, meaning you've dynamically pushed changes to your database.\n\n" +
|
||||
"If you'd like to run migrations, data loss will occur. Would you like to proceed?",
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (!runMigrations) {
|
||||
process.exit(0)
|
||||
}
|
||||
// ignore the dev migration so that the latest batch number increments correctly
|
||||
migrationsInDB = migrationsInDB.filter((m) => m.batch !== -1)
|
||||
}
|
||||
|
||||
if (Number(migrationsInDB?.[0]?.batch) > 0) {
|
||||
latestBatch = Number(migrationsInDB[0]?.batch)
|
||||
}
|
||||
}
|
||||
|
||||
if (migrationsInDB.find((m) => m.batch === -1)) {
|
||||
const { confirm: runMigrations } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message:
|
||||
"It looks like you've run Payload in dev mode, meaning you've dynamically pushed changes to your database.\n\n" +
|
||||
"If you'd like to run migrations, data loss will occur. Would you like to proceed?",
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (!runMigrations) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
const newBatch = latestBatch + 1
|
||||
|
||||
// Execute 'up' function for each migration sequentially
|
||||
|
||||
@@ -50,8 +50,7 @@ export async function migrateDown(this: DrizzleAdapter): Promise<void> {
|
||||
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this, db)
|
||||
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
id: migration.id,
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function migrateRefresh(this: DrizzleAdapter) {
|
||||
msg: `Migrated down: ${migration.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this, db)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
collection: 'payload-migrations',
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function migrateReset(this: DrizzleAdapter): Promise<void> {
|
||||
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this, db)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
id: migration.id,
|
||||
|
||||
@@ -16,8 +16,7 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where)
|
||||
|
||||
return Number(countResult?.[0]?.count ?? 0)
|
||||
return Number(countResult[0].count)
|
||||
}
|
||||
|
||||
let query = db
|
||||
@@ -40,5 +39,5 @@ export const countDistinct: CountDistinct = async function countDistinct(
|
||||
// Instead, COUNT (GROUP BY id) can be used which is still slower than COUNT(*) but acceptable.
|
||||
const countResult = await query
|
||||
|
||||
return Number(countResult?.[0]?.count ?? 0)
|
||||
return Number(countResult[0].count)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type Args = {
|
||||
*/
|
||||
export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
|
||||
const adapter = payload.db as unknown as BasePostgresAdapter
|
||||
const db = await getTransaction(adapter, req)
|
||||
const dir = payload.db.migrationDir
|
||||
|
||||
// get the drizzle migrateUpSQL from drizzle using the last schema
|
||||
@@ -88,8 +89,6 @@ export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
|
||||
payload.logger.info(addColumnsStatement)
|
||||
}
|
||||
|
||||
const db = await getTransaction(adapter, req)
|
||||
|
||||
await db.execute(sql.raw(addColumnsStatement))
|
||||
|
||||
for (const collection of payload.config.collections) {
|
||||
|
||||
@@ -3,14 +3,12 @@ import type { FlattenedField, Where } from 'payload'
|
||||
|
||||
import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||
import type { QueryContext } from './parseParams.js'
|
||||
|
||||
import { parseParams } from './parseParams.js'
|
||||
|
||||
export function buildAndOrConditions({
|
||||
adapter,
|
||||
aliasTable,
|
||||
context,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
@@ -23,7 +21,6 @@ export function buildAndOrConditions({
|
||||
adapter: DrizzleAdapter
|
||||
aliasTable?: Table
|
||||
collectionSlug?: string
|
||||
context: QueryContext
|
||||
fields: FlattenedField[]
|
||||
globalSlug?: string
|
||||
joins: BuildQueryJoinAliases
|
||||
@@ -44,7 +41,6 @@ export function buildAndOrConditions({
|
||||
const result = parseParams({
|
||||
adapter,
|
||||
aliasTable,
|
||||
context,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
import type { FlattenedField, Sort, Where } from 'payload'
|
||||
|
||||
import type { DrizzleAdapter, GenericColumn, GenericTable } from '../types.js'
|
||||
import type { QueryContext } from './parseParams.js'
|
||||
|
||||
import { buildOrderBy } from './buildOrderBy.js'
|
||||
import { parseParams } from './parseParams.js'
|
||||
@@ -53,14 +52,24 @@ const buildQuery = function buildQuery({
|
||||
id: adapter.tables[tableName].id,
|
||||
}
|
||||
|
||||
const orderBy = buildOrderBy({
|
||||
adapter,
|
||||
aliasTable,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
parentIsLocalized,
|
||||
selectFields,
|
||||
sort,
|
||||
tableName,
|
||||
})
|
||||
|
||||
let where: SQL
|
||||
|
||||
const context: QueryContext = { sort }
|
||||
if (incomingWhere && Object.keys(incomingWhere).length > 0) {
|
||||
where = parseParams({
|
||||
adapter,
|
||||
aliasTable,
|
||||
context,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
@@ -72,18 +81,6 @@ const buildQuery = function buildQuery({
|
||||
})
|
||||
}
|
||||
|
||||
const orderBy = buildOrderBy({
|
||||
adapter,
|
||||
aliasTable,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
parentIsLocalized,
|
||||
selectFields,
|
||||
sort: context.sort,
|
||||
tableName,
|
||||
})
|
||||
|
||||
return {
|
||||
joins,
|
||||
orderBy,
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core'
|
||||
import type {
|
||||
FlattenedBlock,
|
||||
FlattenedField,
|
||||
NumberField,
|
||||
RelationshipField,
|
||||
TextField,
|
||||
} from 'payload'
|
||||
import type { FlattenedBlock, FlattenedField, NumberField, TextField } from 'payload'
|
||||
|
||||
import { and, eq, like, sql } from 'drizzle-orm'
|
||||
import { type PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
import { APIError, getFieldByPath } from 'payload'
|
||||
import { APIError } from 'payload'
|
||||
import { fieldShouldBeLocalized, tabHasName } from 'payload/shared'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
import { validate as uuidValidate } from 'uuid'
|
||||
@@ -344,112 +338,6 @@ export const getTableColumnFromPath = ({
|
||||
})
|
||||
}
|
||||
|
||||
case 'join': {
|
||||
if (Array.isArray(field.collection)) {
|
||||
throw new APIError('Not supported')
|
||||
}
|
||||
|
||||
const newCollectionPath = pathSegments.slice(1).join('.')
|
||||
|
||||
if (field.hasMany) {
|
||||
const relationTableName = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`
|
||||
const { newAliasTable: aliasRelationshipTable } = getTableAlias({
|
||||
adapter,
|
||||
tableName: relationTableName,
|
||||
})
|
||||
|
||||
const relationshipField = getFieldByPath({
|
||||
fields: adapter.payload.collections[field.collection].config.flattenedFields,
|
||||
path: field.on,
|
||||
})
|
||||
if (!relationshipField) {
|
||||
throw new APIError('Relationship was not found')
|
||||
}
|
||||
|
||||
addJoinTable({
|
||||
condition: and(
|
||||
eq(
|
||||
adapter.tables[rootTableName].id,
|
||||
aliasRelationshipTable[
|
||||
`${(relationshipField.field as RelationshipField).relationTo as string}ID`
|
||||
],
|
||||
),
|
||||
like(aliasRelationshipTable.path, field.on),
|
||||
),
|
||||
joins,
|
||||
queryPath: field.on,
|
||||
table: aliasRelationshipTable,
|
||||
})
|
||||
|
||||
const relationshipConfig = adapter.payload.collections[field.collection].config
|
||||
const relationshipTableName = adapter.tableNameMap.get(
|
||||
toSnakeCase(relationshipConfig.slug),
|
||||
)
|
||||
|
||||
// parent to relationship join table
|
||||
const relationshipFields = relationshipConfig.flattenedFields
|
||||
|
||||
const { newAliasTable: relationshipTable } = getTableAlias({
|
||||
adapter,
|
||||
tableName: relationshipTableName,
|
||||
})
|
||||
|
||||
joins.push({
|
||||
condition: eq(aliasRelationshipTable.parent, relationshipTable.id),
|
||||
table: relationshipTable,
|
||||
})
|
||||
|
||||
return getTableColumnFromPath({
|
||||
adapter,
|
||||
aliasTable: relationshipTable,
|
||||
collectionPath: newCollectionPath,
|
||||
constraints,
|
||||
// relationshipFields are fields from a different collection => no parentIsLocalized
|
||||
fields: relationshipFields,
|
||||
joins,
|
||||
locale,
|
||||
parentIsLocalized: false,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rootTableName: relationshipTableName,
|
||||
selectFields,
|
||||
selectLocale,
|
||||
tableName: relationshipTableName,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
const newTableName = adapter.tableNameMap.get(
|
||||
toSnakeCase(adapter.payload.collections[field.collection].config.slug),
|
||||
)
|
||||
const { newAliasTable } = getTableAlias({ adapter, tableName: newTableName })
|
||||
|
||||
joins.push({
|
||||
condition: eq(
|
||||
newAliasTable[field.on.replaceAll('.', '_')],
|
||||
aliasTable ? aliasTable.id : adapter.tables[tableName].id,
|
||||
),
|
||||
table: newAliasTable,
|
||||
})
|
||||
|
||||
return getTableColumnFromPath({
|
||||
adapter,
|
||||
aliasTable: newAliasTable,
|
||||
collectionPath: newCollectionPath,
|
||||
constraintPath: '',
|
||||
constraints,
|
||||
fields: adapter.payload.collections[field.collection].config.flattenedFields,
|
||||
joins,
|
||||
locale,
|
||||
parentIsLocalized: parentIsLocalized || field.localized,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
value,
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'number':
|
||||
case 'text': {
|
||||
if (field.hasMany) {
|
||||
@@ -493,6 +381,7 @@ export const getTableColumnFromPath = ({
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'relationship':
|
||||
case 'upload': {
|
||||
const newCollectionPath = pathSegments.slice(1).join('.')
|
||||
@@ -756,7 +645,6 @@ export const getTableColumnFromPath = ({
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SQL, Table } from 'drizzle-orm'
|
||||
import type { FlattenedField, Operator, Sort, Where } from 'payload'
|
||||
import type { FlattenedField, Operator, Where } from 'payload'
|
||||
|
||||
import { and, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm'
|
||||
import { PgUUID } from 'drizzle-orm/pg-core'
|
||||
@@ -14,15 +14,12 @@ import { buildAndOrConditions } from './buildAndOrConditions.js'
|
||||
import { getTableColumnFromPath } from './getTableColumnFromPath.js'
|
||||
import { sanitizeQueryValue } from './sanitizeQueryValue.js'
|
||||
|
||||
export type QueryContext = { sort: Sort }
|
||||
|
||||
type Args = {
|
||||
adapter: DrizzleAdapter
|
||||
aliasTable?: Table
|
||||
context: QueryContext
|
||||
fields: FlattenedField[]
|
||||
joins: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
locale: string
|
||||
parentIsLocalized: boolean
|
||||
selectFields: Record<string, GenericColumn>
|
||||
selectLocale?: boolean
|
||||
@@ -33,7 +30,6 @@ type Args = {
|
||||
export function parseParams({
|
||||
adapter,
|
||||
aliasTable,
|
||||
context,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
@@ -61,7 +57,6 @@ export function parseParams({
|
||||
const builtConditions = buildAndOrConditions({
|
||||
adapter,
|
||||
aliasTable,
|
||||
context,
|
||||
fields,
|
||||
joins,
|
||||
locale,
|
||||
@@ -347,7 +342,6 @@ export function parseParams({
|
||||
)
|
||||
}
|
||||
if (geoConstraints.length) {
|
||||
context.sort = relationOrPath
|
||||
constraints.push(and(...geoConstraints))
|
||||
}
|
||||
break
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { CompoundIndex, FlattenedField } from 'payload'
|
||||
|
||||
import { InvalidConfiguration } from 'payload'
|
||||
import {
|
||||
array,
|
||||
fieldAffectsData,
|
||||
fieldIsVirtual,
|
||||
fieldShouldBeLocalized,
|
||||
@@ -288,9 +287,7 @@ export const traverseFields = ({
|
||||
}
|
||||
}
|
||||
|
||||
const relationName = field.dbName ? `_${arrayTableName}` : fieldName
|
||||
|
||||
relationsToBuild.set(relationName, {
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'many',
|
||||
// arrays have their own localized table, independent of the base table.
|
||||
localized: false,
|
||||
@@ -307,7 +304,7 @@ export const traverseFields = ({
|
||||
},
|
||||
],
|
||||
references: ['id'],
|
||||
relationName,
|
||||
relationName: fieldName,
|
||||
to: parentTableName,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ type TransformArgs = {
|
||||
joinQuery?: JoinQuery
|
||||
locale?: string
|
||||
parentIsLocalized?: boolean
|
||||
tableName: string
|
||||
}
|
||||
|
||||
// This is the entry point to transform Drizzle output data
|
||||
@@ -27,7 +26,6 @@ export const transform = <T extends Record<string, unknown> | TypeWithID>({
|
||||
fields,
|
||||
joinQuery,
|
||||
parentIsLocalized,
|
||||
tableName,
|
||||
}: TransformArgs): T => {
|
||||
let relationships: Record<string, Record<string, unknown>[]> = {}
|
||||
let texts: Record<string, Record<string, unknown>[]> = {}
|
||||
@@ -55,7 +53,6 @@ export const transform = <T extends Record<string, unknown> | TypeWithID>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName: tableName,
|
||||
dataRef: {
|
||||
id: data.id,
|
||||
},
|
||||
@@ -68,9 +65,7 @@ export const transform = <T extends Record<string, unknown> | TypeWithID>({
|
||||
path: '',
|
||||
relationships,
|
||||
table: data,
|
||||
tablePath: '',
|
||||
texts,
|
||||
topLevelTableName: tableName,
|
||||
})
|
||||
|
||||
deletions.forEach((deletion) => deletion())
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { FlattenedBlock, FlattenedField, JoinQuery, SanitizedConfig } from 'payload'
|
||||
|
||||
import { fieldIsVirtual, fieldShouldBeLocalized } from 'payload/shared'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { DrizzleAdapter } from '../../types.js'
|
||||
import type { BlocksMap } from '../../utilities/createBlocksMap.js'
|
||||
@@ -23,7 +22,6 @@ type TraverseFieldsArgs = {
|
||||
* The full Payload config
|
||||
*/
|
||||
config: SanitizedConfig
|
||||
currentTableName: string
|
||||
/**
|
||||
* The data reference to be mutated within this recursive function
|
||||
*/
|
||||
@@ -61,12 +59,10 @@ type TraverseFieldsArgs = {
|
||||
* Data structure representing the nearest table from db
|
||||
*/
|
||||
table: Record<string, unknown>
|
||||
tablePath: string
|
||||
/**
|
||||
* All hasMany text fields, as returned by Drizzle, keyed on an object by field path
|
||||
*/
|
||||
texts: Record<string, Record<string, unknown>[]>
|
||||
topLevelTableName: string
|
||||
/**
|
||||
* Set to a locale if this group of fields is within a localized array or block.
|
||||
*/
|
||||
@@ -79,7 +75,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName,
|
||||
dataRef,
|
||||
deletions,
|
||||
fieldPrefix,
|
||||
@@ -90,9 +85,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path,
|
||||
relationships,
|
||||
table,
|
||||
tablePath,
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale,
|
||||
}: TraverseFieldsArgs): T => {
|
||||
const sanitizedPath = path ? `${path}.` : path
|
||||
@@ -117,14 +110,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
const isLocalized = fieldShouldBeLocalized({ field, parentIsLocalized })
|
||||
|
||||
if (field.type === 'array') {
|
||||
const arrayTableName = adapter.tableNameMap.get(
|
||||
`${currentTableName}_${tablePath}${toSnakeCase(field.name)}`,
|
||||
)
|
||||
|
||||
if (field.dbName) {
|
||||
fieldData = table[`_${arrayTableName}`]
|
||||
}
|
||||
|
||||
if (Array.isArray(fieldData)) {
|
||||
if (isLocalized) {
|
||||
result[field.name] = fieldData.reduce((arrayResult, row) => {
|
||||
@@ -144,7 +129,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName: arrayTableName,
|
||||
dataRef: data,
|
||||
deletions,
|
||||
fieldPrefix: '',
|
||||
@@ -154,9 +138,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path: `${sanitizedPath}${field.name}.${row._order - 1}`,
|
||||
relationships,
|
||||
table: row,
|
||||
tablePath: '',
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale: locale,
|
||||
})
|
||||
|
||||
@@ -193,7 +175,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName: arrayTableName,
|
||||
dataRef: row,
|
||||
deletions,
|
||||
fieldPrefix: '',
|
||||
@@ -203,9 +184,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path: `${sanitizedPath}${field.name}.${i}`,
|
||||
relationships,
|
||||
table: row,
|
||||
tablePath: '',
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale,
|
||||
}),
|
||||
)
|
||||
@@ -249,16 +228,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
(block) => typeof block !== 'string' && block.slug === row.blockType,
|
||||
) as FlattenedBlock | undefined)
|
||||
|
||||
const tableName = adapter.tableNameMap.get(
|
||||
`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`,
|
||||
)
|
||||
|
||||
if (block) {
|
||||
const blockResult = traverseFields<T>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName: tableName,
|
||||
dataRef: row,
|
||||
deletions,
|
||||
fieldPrefix: '',
|
||||
@@ -268,9 +242,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path: `${blockFieldPath}.${row._order - 1}`,
|
||||
relationships,
|
||||
table: row,
|
||||
tablePath: '',
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale: locale,
|
||||
})
|
||||
|
||||
@@ -328,16 +300,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
delete row._index
|
||||
}
|
||||
|
||||
const tableName = adapter.tableNameMap.get(
|
||||
`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`,
|
||||
)
|
||||
|
||||
acc.push(
|
||||
traverseFields<T>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName: tableName,
|
||||
dataRef: row,
|
||||
deletions,
|
||||
fieldPrefix: '',
|
||||
@@ -347,9 +314,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path: `${blockFieldPath}.${i}`,
|
||||
relationships,
|
||||
table: row,
|
||||
tablePath: '',
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale,
|
||||
}),
|
||||
)
|
||||
@@ -649,7 +614,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
adapter,
|
||||
blocks,
|
||||
config,
|
||||
currentTableName,
|
||||
dataRef: groupData as Record<string, unknown>,
|
||||
deletions,
|
||||
fieldPrefix: groupFieldPrefix,
|
||||
@@ -660,16 +624,13 @@ export const traverseFields = <T extends Record<string, unknown>>({
|
||||
path: `${sanitizedPath}${field.name}`,
|
||||
relationships,
|
||||
table,
|
||||
tablePath: `${tablePath}${toSnakeCase(field.name)}_`,
|
||||
texts,
|
||||
topLevelTableName,
|
||||
withinArrayOrBlockLocale: locale || withinArrayOrBlockLocale,
|
||||
})
|
||||
|
||||
// TODO: we need to only clean this up for arrays, blocks, and hasMany fields
|
||||
// if ('_order' in ref) {
|
||||
// delete ref._order
|
||||
// }
|
||||
if ('_order' in ref) {
|
||||
delete ref._order
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -496,10 +496,6 @@ export const traverseFields = ({
|
||||
formattedValue = sql`ST_GeomFromGeoJSON(${JSON.stringify(value)})`
|
||||
}
|
||||
|
||||
if (field.type === 'text' && value && typeof value !== 'string') {
|
||||
formattedValue = JSON.stringify(value)
|
||||
}
|
||||
|
||||
if (field.type === 'date') {
|
||||
if (typeof value === 'number' && !Number.isNaN(value)) {
|
||||
formattedValue = new Date(value).toISOString()
|
||||
|
||||
@@ -423,7 +423,6 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
path: fieldName,
|
||||
},
|
||||
],
|
||||
req,
|
||||
},
|
||||
req?.t,
|
||||
)
|
||||
@@ -467,7 +466,6 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
data: doc,
|
||||
fields,
|
||||
joinQuery: false,
|
||||
tableName,
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
@@ -131,7 +131,7 @@ export const createSchemaGenerator = ({
|
||||
let foreignKeyDeclaration = `${sanitizeObjectKey(key)}: foreignKey({
|
||||
columns: [${foreignKey.columns.map((col) => `columns['${col}']`).join(', ')}],
|
||||
foreignColumns: [${foreignKey.foreignColumns.map((col) => `${accessProperty(col.table, col.name)}`).join(', ')}],
|
||||
name: '${foreignKey.name}'
|
||||
name: '${foreignKey.name}'
|
||||
})`
|
||||
|
||||
if (foreignKey.onDelete) {
|
||||
@@ -167,11 +167,11 @@ ${Object.entries(table.columns)
|
||||
}${
|
||||
extrasDeclarations.length
|
||||
? `, (columns) => ({
|
||||
${extrasDeclarations.join('\n ')}
|
||||
${extrasDeclarations.join('\n ')}
|
||||
})`
|
||||
: ''
|
||||
}
|
||||
)
|
||||
)
|
||||
`
|
||||
|
||||
tableDeclarations.push(tableCode)
|
||||
@@ -250,7 +250,7 @@ type DatabaseSchema = {
|
||||
`
|
||||
|
||||
const finalDeclaration = `
|
||||
declare module '${this.packageName}' {
|
||||
declare module '${this.packageName}/types' {
|
||||
export interface GeneratedDatabaseSchema {
|
||||
schema: DatabaseSchema
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type { DrizzleAdapter } from '../types.js'
|
||||
|
||||
import type { DrizzleAdapter, PostgresDB } from '../types.js'
|
||||
|
||||
export const migrationTableExists = async (
|
||||
adapter: DrizzleAdapter,
|
||||
db?: LibSQLDatabase | PostgresDB,
|
||||
): Promise<boolean> => {
|
||||
export const migrationTableExists = async (adapter: DrizzleAdapter): Promise<boolean> => {
|
||||
let statement
|
||||
|
||||
if (adapter.name === 'postgres') {
|
||||
@@ -25,7 +20,7 @@ export const migrationTableExists = async (
|
||||
}
|
||||
|
||||
const result = await adapter.execute({
|
||||
drizzle: db ?? adapter.drizzle,
|
||||
drizzle: adapter.drizzle,
|
||||
raw: statement,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -11,7 +11,6 @@ export type ObjectTypeConfig = {
|
||||
|
||||
type Args = {
|
||||
baseFields?: ObjectTypeConfig
|
||||
collectionSlug?: string
|
||||
config: SanitizedConfig
|
||||
fields: Field[]
|
||||
forceNullable?: boolean
|
||||
@@ -24,7 +23,6 @@ type Args = {
|
||||
export function buildObjectType({
|
||||
name,
|
||||
baseFields = {},
|
||||
collectionSlug,
|
||||
config,
|
||||
fields,
|
||||
forceNullable,
|
||||
@@ -45,7 +43,6 @@ export function buildObjectType({
|
||||
return {
|
||||
...objectTypeConfig,
|
||||
...fieldSchema({
|
||||
collectionSlug,
|
||||
config,
|
||||
field,
|
||||
forceNullable,
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { GraphQLBoolean, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType } from 'graphql'
|
||||
import { GraphQLBoolean, GraphQLInt, GraphQLList, GraphQLObjectType } from 'graphql'
|
||||
|
||||
export const buildPaginatedListType = (name, docType) =>
|
||||
new GraphQLObjectType({
|
||||
name,
|
||||
fields: {
|
||||
docs: {
|
||||
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(docType))),
|
||||
type: new GraphQLList(docType),
|
||||
},
|
||||
hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
||||
hasPrevPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
||||
limit: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
hasNextPage: { type: GraphQLBoolean },
|
||||
hasPrevPage: { type: GraphQLBoolean },
|
||||
limit: { type: GraphQLInt },
|
||||
nextPage: { type: GraphQLInt },
|
||||
offset: { type: GraphQLInt },
|
||||
page: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
pagingCounter: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
page: { type: GraphQLInt },
|
||||
pagingCounter: { type: GraphQLInt },
|
||||
prevPage: { type: GraphQLInt },
|
||||
totalDocs: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
totalPages: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
totalDocs: { type: GraphQLInt },
|
||||
totalPages: { type: GraphQLInt },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@ import type {
|
||||
DateField,
|
||||
EmailField,
|
||||
Field,
|
||||
FlattenedJoinField,
|
||||
GraphQLInfo,
|
||||
GroupField,
|
||||
JoinField,
|
||||
@@ -69,7 +68,6 @@ function formattedNameResolver({
|
||||
}
|
||||
|
||||
type SharedArgs = {
|
||||
collectionSlug?: string
|
||||
config: SanitizedConfig
|
||||
forceNullable?: boolean
|
||||
graphqlResult: GraphQLInfo
|
||||
@@ -342,7 +340,7 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
|
||||
},
|
||||
}
|
||||
},
|
||||
join: ({ collectionSlug, field, graphqlResult, objectTypeConfig, parentName }) => {
|
||||
join: ({ field, graphqlResult, objectTypeConfig, parentName }) => {
|
||||
const joinName = combineParentName(parentName, toWords(field.name, true))
|
||||
|
||||
const joinType = {
|
||||
@@ -350,15 +348,11 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
|
||||
name: joinName,
|
||||
fields: {
|
||||
docs: {
|
||||
type: new GraphQLNonNull(
|
||||
Array.isArray(field.collection)
|
||||
? GraphQLJSON
|
||||
: new GraphQLList(
|
||||
new GraphQLNonNull(graphqlResult.collections[field.collection].graphQL.type),
|
||||
),
|
||||
),
|
||||
type: Array.isArray(field.collection)
|
||||
? GraphQLJSON
|
||||
: new GraphQLList(graphqlResult.collections[field.collection].graphQL.type),
|
||||
},
|
||||
hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
||||
hasNextPage: { type: GraphQLBoolean },
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
@@ -387,54 +381,27 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
|
||||
|
||||
const draft = Boolean(args.draft ?? context.req.query?.draft)
|
||||
|
||||
const targetField = (field as FlattenedJoinField).targetField
|
||||
|
||||
const fullWhere = combineQueries(
|
||||
where,
|
||||
Array.isArray(targetField.relationTo)
|
||||
? {
|
||||
[field.on]: {
|
||||
equals: {
|
||||
relationTo: collectionSlug,
|
||||
value: parent._id ?? parent.id,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
[field.on]: { equals: parent._id ?? parent.id },
|
||||
},
|
||||
)
|
||||
const fullWhere = combineQueries(where, {
|
||||
[field.on]: { equals: parent._id ?? parent.id },
|
||||
})
|
||||
|
||||
if (Array.isArray(collection)) {
|
||||
throw new Error('GraphQL with array of join.field.collection is not implemented')
|
||||
}
|
||||
|
||||
const { docs } = await req.payload.find({
|
||||
return await req.payload.find({
|
||||
collection,
|
||||
depth: 0,
|
||||
draft,
|
||||
fallbackLocale: req.fallbackLocale,
|
||||
// Fetch one extra document to determine if there are more documents beyond the requested limit (used for hasNextPage calculation).
|
||||
limit: typeof limit === 'number' && limit > 0 ? limit + 1 : 0,
|
||||
limit,
|
||||
locale: req.locale,
|
||||
overrideAccess: false,
|
||||
page,
|
||||
pagination: false,
|
||||
req,
|
||||
sort,
|
||||
where: fullWhere,
|
||||
})
|
||||
|
||||
let shouldSlice = false
|
||||
|
||||
if (typeof limit === 'number' && limit !== 0 && limit < docs.length) {
|
||||
shouldSlice = true
|
||||
}
|
||||
|
||||
return {
|
||||
docs: shouldSlice ? docs.slice(0, -1) : docs,
|
||||
hasNextPage: limit === 0 ? false : limit < docs.length,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -461,7 +428,7 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
|
||||
...objectTypeConfig,
|
||||
[formatName(field.name)]: formattedNameResolver({
|
||||
type: withNullableType({
|
||||
type: field?.hasMany === true ? new GraphQLList(new GraphQLNonNull(type)) : type,
|
||||
type: field?.hasMany === true ? new GraphQLList(type) : type,
|
||||
field,
|
||||
forceNullable,
|
||||
parentIsLocalized,
|
||||
@@ -889,10 +856,7 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
|
||||
...objectTypeConfig,
|
||||
[formatName(field.name)]: formattedNameResolver({
|
||||
type: withNullableType({
|
||||
type:
|
||||
field.hasMany === true
|
||||
? new GraphQLList(new GraphQLNonNull(GraphQLString))
|
||||
: GraphQLString,
|
||||
type: field.hasMany === true ? new GraphQLList(GraphQLString) : GraphQLString,
|
||||
field,
|
||||
forceNullable,
|
||||
parentIsLocalized,
|
||||
|
||||
@@ -29,7 +29,6 @@ import { recursivelyBuildNestedPaths } from './recursivelyBuildNestedPaths.js'
|
||||
import { withOperators } from './withOperators.js'
|
||||
|
||||
type Args = {
|
||||
collectionSlug?: string
|
||||
nestedFieldName?: string
|
||||
parentName: string
|
||||
}
|
||||
|
||||
@@ -111,7 +111,6 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
collection.graphQL.type = buildObjectType({
|
||||
name: singularName,
|
||||
baseFields,
|
||||
collectionSlug: collectionConfig.slug,
|
||||
config,
|
||||
fields,
|
||||
forceNullable: forceNullableObjectType,
|
||||
@@ -340,7 +339,6 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
|
||||
collection.graphQL.versionType = buildObjectType({
|
||||
name: `${singularName}Version`,
|
||||
collectionSlug: collectionConfig.slug,
|
||||
config,
|
||||
fields: versionCollectionFields,
|
||||
forceNullable: forceNullableObjectType,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -45,8 +45,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.2",
|
||||
"@types/react": "19.0.12",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
// To prevent the flicker of stale data while the post message is being sent,
|
||||
// you can conditionally render loading UI based on the `isLoading` state
|
||||
|
||||
export const useLivePreview = <T extends Record<string, unknown>>(props: {
|
||||
export const useLivePreview = <T extends any>(props: {
|
||||
apiRoute?: string
|
||||
depth?: number
|
||||
initialData: T
|
||||
@@ -21,7 +21,7 @@ export const useLivePreview = <T extends Record<string, unknown>>(props: {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true)
|
||||
const hasSentReadyMessage = useRef<boolean>(false)
|
||||
|
||||
const onChange = useCallback((mergedData: T) => {
|
||||
const onChange = useCallback((mergedData) => {
|
||||
setData(mergedData)
|
||||
setIsLoading(false)
|
||||
}, [])
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
/* TODO: remove the following lines */
|
||||
"strict": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
},
|
||||
"references": [{ "path": "../payload" }]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { onMounted, onUnmounted, ref } from 'vue'
|
||||
*
|
||||
* {@link https://payloadcms.com/docs/live-preview/frontend View the documentation}
|
||||
*/
|
||||
export const useLivePreview = <T extends Record<string, unknown>>(props: {
|
||||
export const useLivePreview = <T>(props: {
|
||||
apiRoute?: string
|
||||
depth?: number
|
||||
initialData: T
|
||||
@@ -27,7 +27,7 @@ export const useLivePreview = <T extends Record<string, unknown>>(props: {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
let subscription: (event: MessageEvent) => Promise<void> | void
|
||||
let subscription: (event: MessageEvent) => void
|
||||
|
||||
onMounted(() => {
|
||||
subscription = subscribe({
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
/* TODO: remove the following lines */
|
||||
"strict": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
},
|
||||
"references": [{ "path": "../payload" }] // db-mongodb depends on payload
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.37.0",
|
||||
"version": "3.32.0",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FieldSchemaJSON } from 'payload'
|
||||
|
||||
import type { CollectionPopulationRequestHandler, LivePreviewMessageEvent } from './types.js'
|
||||
import type { LivePreviewMessageEvent } from './types.js'
|
||||
|
||||
import { isLivePreviewEvent } from './isLivePreviewEvent.js'
|
||||
import { mergeData } from './mergeData.js'
|
||||
@@ -29,10 +29,9 @@ export const handleMessage = async <T extends Record<string, any>>(args: {
|
||||
depth?: number
|
||||
event: LivePreviewMessageEvent<T>
|
||||
initialData: T
|
||||
requestHandler?: CollectionPopulationRequestHandler
|
||||
serverURL: string
|
||||
}): Promise<T> => {
|
||||
const { apiRoute, depth, event, initialData, requestHandler, serverURL } = args
|
||||
const { apiRoute, depth, event, initialData, serverURL } = args
|
||||
|
||||
if (isLivePreviewEvent(event, serverURL)) {
|
||||
const { data, externallyUpdatedRelationship, fieldSchemaJSON, locale } = event.data
|
||||
@@ -58,7 +57,6 @@ export const handleMessage = async <T extends Record<string, any>>(args: {
|
||||
incomingData: data,
|
||||
initialData: _payloadLivePreview?.previousData || initialData,
|
||||
locale,
|
||||
requestHandler,
|
||||
serverURL,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DocumentEvent, FieldSchemaJSON, PaginatedDocs } from 'payload'
|
||||
|
||||
import type { CollectionPopulationRequestHandler, PopulationsByCollection } from './types.js'
|
||||
import type { PopulationsByCollection } from './types.js'
|
||||
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
@@ -29,17 +29,21 @@ let prevLocale: string | undefined
|
||||
|
||||
export const mergeData = async <T extends Record<string, any>>(args: {
|
||||
apiRoute?: string
|
||||
/**
|
||||
* @deprecated Use `requestHandler` instead
|
||||
*/
|
||||
collectionPopulationRequestHandler?: CollectionPopulationRequestHandler
|
||||
collectionPopulationRequestHandler?: ({
|
||||
apiPath,
|
||||
endpoint,
|
||||
serverURL,
|
||||
}: {
|
||||
apiPath: string
|
||||
endpoint: string
|
||||
serverURL: string
|
||||
}) => Promise<Response>
|
||||
depth?: number
|
||||
externallyUpdatedRelationship?: DocumentEvent
|
||||
fieldSchema: FieldSchemaJSON
|
||||
incomingData: Partial<T>
|
||||
initialData: T
|
||||
locale?: string
|
||||
requestHandler?: CollectionPopulationRequestHandler
|
||||
returnNumberOfRequests?: boolean
|
||||
serverURL: string
|
||||
}): Promise<
|
||||
@@ -77,8 +81,7 @@ export const mergeData = async <T extends Record<string, any>>(args: {
|
||||
let res: PaginatedDocs
|
||||
|
||||
const ids = new Set(populations.map(({ id }) => id))
|
||||
const requestHandler =
|
||||
args.collectionPopulationRequestHandler || args.requestHandler || defaultRequestHandler
|
||||
const requestHandler = args.collectionPopulationRequestHandler || defaultRequestHandler
|
||||
|
||||
try {
|
||||
res = await requestHandler({
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { CollectionPopulationRequestHandler } from './types.js'
|
||||
|
||||
import { handleMessage } from './handleMessage.js'
|
||||
|
||||
export const subscribe = <T extends Record<string, any>>(args: {
|
||||
@@ -7,10 +5,9 @@ export const subscribe = <T extends Record<string, any>>(args: {
|
||||
callback: (data: T) => void
|
||||
depth?: number
|
||||
initialData: T
|
||||
requestHandler?: CollectionPopulationRequestHandler
|
||||
serverURL: string
|
||||
}): ((event: MessageEvent) => Promise<void> | void) => {
|
||||
const { apiRoute, callback, depth, initialData, requestHandler, serverURL } = args
|
||||
const { apiRoute, callback, depth, initialData, serverURL } = args
|
||||
|
||||
const onMessage = async (event: MessageEvent) => {
|
||||
const mergedData = await handleMessage<T>({
|
||||
@@ -18,7 +15,6 @@ export const subscribe = <T extends Record<string, any>>(args: {
|
||||
depth,
|
||||
event,
|
||||
initialData,
|
||||
requestHandler,
|
||||
serverURL,
|
||||
})
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user