310 lines
13 KiB
Plaintext
310 lines
13 KiB
Plaintext
---
|
|
title: Payload Concepts
|
|
label: Concepts
|
|
order: 20
|
|
desc: Payload is based around a small and intuitive set of concepts. Key concepts include collections, globals, fields and more.
|
|
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, nextjs
|
|
---
|
|
|
|
Payload is based around a small and intuitive set of concepts. Before starting to work with Payload, it's a good idea to familiarize yourself with the following:
|
|
|
|
## Config
|
|
|
|
<Banner type="info">The Payload config is where you configure everything that Payload does.</Banner>
|
|
|
|
By default, the Payload config lives in the root folder of your code and is named `payload.config.ts`, but you can customize its name and where you store it. You can write full functions and even full React components right into your config.
|
|
|
|
## Collections
|
|
|
|
<Banner type="info">
|
|
A Collection represents a type of content that Payload will store and can contain many documents.
|
|
</Banner>
|
|
|
|
Collections define the shape of your data as well as all functionalities attached to that data. They will contain one or many "documents", all corresponding with the same fields and functionalities that you define.
|
|
|
|
They can represent anything you can store in a database - for example - pages, posts, users, people, orders, categories, events, customers, transactions, and anything else your app needs.
|
|
|
|
## Globals
|
|
|
|
<Banner type="info">
|
|
A Global is a "one-off" piece of content that is perfect for storing navigational structures,
|
|
themes, top-level meta data, and more.
|
|
</Banner>
|
|
|
|
Globals are in many ways similar to Collections, but there is only ever **one** instance of a Global, whereas Collections can contain many documents.
|
|
|
|
## Fields
|
|
|
|
<Banner type="info">
|
|
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the
|
|
shape of the data that they store.
|
|
</Banner>
|
|
|
|
Payload comes with [many different field types](../fields/overview) that give you a ton of flexibility while designing your API. Each Field type has its own potential properties that allow you to customize how they work.
|
|
|
|
## Hooks
|
|
|
|
<Banner type="info">
|
|
Hooks are where you can "tie in" to existing Payload actions to perform your own additional logic
|
|
or modify how Payload operates altogether.
|
|
</Banner>
|
|
|
|
Hooks are an extremely powerful concept and are central to extending and customizing your app. Payload provides a wide variety of hooks which you can utilize. For example, imagine if you'd like to send an email every time a document is created in your Orders collection. To do so, you can add an `afterChange` hook function to your Orders collection that receives the Order data and allows you to send an email accordingly.
|
|
|
|
There are many more potential reasons to use Hooks. For more, visit the [Hooks documentation](/docs/hooks/overview).
|
|
|
|
## Access Control
|
|
|
|
<Banner type="info">
|
|
Access Control refers to Payload's system of defining who can do what to your API.
|
|
</Banner>
|
|
|
|
Access Control is extremely powerful but easy and intuitive to manage. You can easily define your own full-blown RBAC (role-based access control) or any other access control pattern that your scenario requires. No conventions or structure is forced on you whatsoever.
|
|
|
|
For more, visit the [Access Control documentation](/docs/access-control/overview).
|
|
|
|
## Retrieving Payload data
|
|
|
|
Everything Payload does (create, read, update, delete, login, logout, etc) is exposed to you via three APIs:
|
|
|
|
**1 - Payload's Local API**
|
|
|
|
By far one of the most powerful aspects of Payload is the fact that it gives you direct-to-database access to your data. It's _extremely_ fast and does not incur any typical REST API / GraphQL / HTTP overhead - you query your database directly in Node.js / TypeScript.
|
|
|
|
Everything is strongly typed and it's extremely nice to use. It works anywhere on the server, including custom Next.js route handlers, Payload hooks, Payload access control, and React Server Components.
|
|
|
|
Here's a quick example of a React Server Component fetching page data with Payload's Local API:
|
|
|
|
```tsx
|
|
import React from 'react'
|
|
import config from '@payload-config'
|
|
import { getPayloadHMR } from '@payloadcms/next'
|
|
|
|
const MyServerComponent: React.FC = () => {
|
|
// If you're working in Next.js, and you want HMR,
|
|
// you should get Payload via the `getPayloadHMR` function.
|
|
const payload = await getPayloadHMR({ config })
|
|
|
|
// If you are writing a standalone script and do not need HMR,
|
|
// you can get Payload via import { getPayload } from 'payload' instead.
|
|
|
|
// The `findResult` here will be fully typed as `PaginatedDocs<Page>`,
|
|
// where you will have the `docs` that are returned as well as
|
|
// information about how many items are returned / are available in total / etc
|
|
const findResult = await payload.find({ collection: 'pages' })
|
|
|
|
return (
|
|
<ul>
|
|
{findResult.docs.map((page) => {
|
|
// Render whatever here!
|
|
// The `page` is fully typed as your Pages collection!
|
|
})}
|
|
</ul>
|
|
)
|
|
}
|
|
```
|
|
|
|
For more docs about the Payload Local API, [click here](/docs/beta/local-api/overview).
|
|
|
|
**2 - The REST API**
|
|
|
|
By default, the Payload REST API will be mounted automatically for you at the `/api` path of your app.
|
|
|
|
For example, if you have a collection with the `slug` as `pages`, you'd fetch pages via `http://localhost:3000/api/pages`.
|
|
|
|
Here's an example:
|
|
|
|
```ts
|
|
const pages = await fetch('http://localhost:3000/api/pages', {
|
|
// Include cookies, like the Payload auth token
|
|
credentials: 'include',
|
|
}).then((res) => res.json())
|
|
```
|
|
|
|
For a full list of REST API endpoints, including examples, [click here](/docs/beta/rest-api/overview).
|
|
|
|
**3 - GraphQL**
|
|
|
|
Payload automatically exposes GraphQL queries and mutations for everything it does.
|
|
|
|
By default, you'll find the GraphQL route handler in your `/app/(payload)/api/graphql` folder, which makes the GraphQL endpoint available by default at `http://localhost:3000/api/graphql`.
|
|
|
|
You'll also find a full GraphQL Playground which can be accessible via `http://localhost:3000/api/graphql-playground`.
|
|
|
|
You can use any GraphQL client with Payload's GraphQL endpoint. Here are a few packages:
|
|
|
|
- [`graphql-request`](https://www.npmjs.com/package/graphql-request) - a very lightweight GraphQL client
|
|
- [`@apollo/client`](https://www.apollographql.com/docs/react/api/core/ApolloClient/) - an industry-standard GraphQL client with lots of nice features
|
|
|
|
If you don't use GraphQL, you can delete those files! But if you do, you'll find that GraphQL is a first-class API in Payload. Either way, the overhead of GraphQL is completely constrained to these endpoints, and will not slow down / affect Payload outside of those endpoints themselves.
|
|
|
|
For more docs on GraphQL, [click here](/docs/beta/graphql/overview).
|
|
|
|
## Depth
|
|
|
|
<Banner type="info">
|
|
"Depth" gives you control over how many levels down related documents should be automatically
|
|
populated when retrieved.
|
|
</Banner>
|
|
|
|
You can specify population `depth` via query parameter in the REST API and by an option in the local API. _Depth has no effect in the GraphQL API, because there, depth is based on the shape of your queries._
|
|
It is also possible to limit the depth for specific `relation` and `upload` fields using the `maxDepth` property in your configuration.
|
|
**For example, let's look at the following Collections:** `departments`, `users`, `posts`
|
|
|
|
```
|
|
// type: 'relationship' fields are equal to 1 depth level
|
|
|
|
{
|
|
slug: 'posts',
|
|
fields: [
|
|
{
|
|
name: 'title',
|
|
type: 'text',
|
|
},
|
|
{
|
|
name: 'author',
|
|
label: 'Post Author',
|
|
type: 'relationship',
|
|
relationTo: 'users',
|
|
}
|
|
]
|
|
}
|
|
|
|
{
|
|
slug: 'users',
|
|
fields: [
|
|
{
|
|
name: 'email',
|
|
type: 'email',
|
|
},
|
|
{
|
|
name: 'department'
|
|
type: 'relationship',
|
|
relationTo: 'departments'
|
|
}
|
|
]
|
|
}
|
|
|
|
{
|
|
slug: 'departments',
|
|
fields: [
|
|
{
|
|
name: 'name'
|
|
type: 'text',
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
If you were to query the Posts endpoint at, say, `http://localhost:3000/api/posts?depth=1`, you will retrieve Posts with populations one level deep. This depth parameter can be thought of as N, where N is the number of levels you want to populate. To populate one level further, you would simply specify N+1 as the depth. A returned result may look like the following:
|
|
|
|
```
|
|
// ?depth=1
|
|
|
|
{
|
|
id: '5ae8f9bde69e394e717c8832',
|
|
title: 'This post sucks',
|
|
author: {
|
|
id: '5f7dd05cd50d4005f8bcab17',
|
|
email: 'spiderman@superheroes.gov',
|
|
department: '5e3ca05cd50d4005f8bdab15'
|
|
}
|
|
}
|
|
```
|
|
|
|
Notice how the `user.author` is fully populated, but `user.author.department` is left as a document ID? That's because the User collection counted as the first level of `depth` and got populated—but then prevented any further populations from taking place.
|
|
|
|
To populate `user.author.department` in it's entirety you could specify `?depth=2` or _higher_.
|
|
|
|
```
|
|
// ?depth=2
|
|
|
|
{
|
|
id: '5ae8f9bde69e394e717c8832',
|
|
title: 'This post sucks',
|
|
author: {
|
|
id: '5f7dd05cd50d4005f8bcab17',
|
|
email: 'spiderman@superheroes.gov',
|
|
department: {
|
|
id: '5e3ca05cd50d4005f8bdab15',
|
|
name: 'Marvel'
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Field-level max depth
|
|
|
|
Fields like relationships or uploads can have a `maxDepth` property that limits the depth of the population for that field. Here are some examples:
|
|
|
|
Depth: 10
|
|
Current depth when field is accessed: 1
|
|
`maxDepth`: undefined
|
|
|
|
In this case, the field would be populated to 9 levels of population.
|
|
|
|
Depth: 10
|
|
Current depth when field is accessed: 0
|
|
`maxDepth`: 2
|
|
|
|
In this case, the field would be populated to 2 levels of population, despite there being a remaining depth of 8.
|
|
|
|
Depth: 10
|
|
Current depth when field is accessed: 2
|
|
`maxDepth`: 1
|
|
|
|
In this case, the field would not be populated, as the current depth (2) has exceeded the `maxDepth` for this field (1).
|
|
|
|
<Banner type="warning">
|
|
<strong>Note:</strong>
|
|
<br />
|
|
When access control on collections prevents relationship fields from populating, the API response
|
|
will contain the relationship id instead of the full document.
|
|
</Banner>
|
|
|
|
## Package organization
|
|
|
|
Payload is abstracted into a set of dedicated packages, and it's a good idea to familiarize yourself with what each one is responsible for.
|
|
|
|
<Banner type="success">
|
|
<strong>Note:</strong>
|
|
<br/>
|
|
Version numbers of all official Payload packages are kept in sync - and you should always make sure that you use matching versions for all official Payload packages.
|
|
</Banner>
|
|
|
|
`payload`
|
|
|
|
The `payload` package is where core business logic for Payload lives. You can think of Payload as an ORM with superpowers - it contains the logic for all Payload "operations" like `find`, `create`, `update`, and `delete`. It executes access control, hooks, validation, and more.
|
|
|
|
Payload itself is extremely compact, and can be used in any Node environment. As long as you have `payload` installed and you have access to your Payload config, you can query and mutate your database directly without going through an unnecessary HTTP layer.
|
|
|
|
Payload also contains all TypeScript definitions, which can be imported from `payload` directly.
|
|
|
|
Here's how to import some common Payload types:
|
|
|
|
```ts
|
|
import { CollectionConfig, Field, GlobalConfig, Config } from 'payload'
|
|
```
|
|
|
|
`@payloadcms/next`
|
|
|
|
Whereas Payload itself is responsible for direct database access, and control over Payload business logic, the `@payloadcms/next` package is responsible for the admin panel and the entire HTTP layer (REST, GraphQL) that Payload exposes.
|
|
|
|
`@payloadcms/graphql`
|
|
|
|
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within in the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
|
|
|
|
`@payloadcms/ui`
|
|
|
|
This is the UI library that Payload's admin panel uses. All components are exported from this package and can be re-used as you build extensions to the Payload admin UI, or want to use Payload components in your own React apps. Some exports are server components and some are client components.
|
|
|
|
`@payloadcms/db-postgres`, `@payloadcms/db-mongodb`
|
|
|
|
You can choose which database adapter you'd like to use for your project, and no matter which you choose, the entire data layer for Payload is contained within these packages. You can only use one at a time for any given project.
|
|
|
|
`@payloadcms/richtext-lexical`, `@payloadcms/richtext-slate`
|
|
|
|
Payload's Rich Text functionality is abstracted into separate packages and if you want to enable Rich Text in your project, you'll need to install one of these packages. We recommend Lexical for all new projects, and this is where Payload will focus its efforts on from this point, but Slate is still supported if you have already built with it.
|
|
|
|
Rich Text is entirely optional and you may not need it for your project.
|