Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Database

Payload interacts with your database via the database adapter that you choose. Right now, Payload officially supports two database adapters:

  1. MongoDB w/ Mongoose
  2. Postgres w/ Drizzle

We will be adding support for SQLite and MySQL in the near future using Drizzle ORM.

To use a specific database adapter, you need to install it and configure it according to its own specifications. Visit the documentation for your applicable database adapter to learn more.

Selecting a database

There are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.

When to use MongoDB

If your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as one document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.

You should prefer MongoDB if:

  • You prefer simplicity within your database
  • You don't want to deal with keeping production / staging databases in sync via DDL changes
  • Most (or everything) in your project is localized
  • You leverage a lot of array fields, block fields, or hasMany select fields and similar

When to use a relational DB

Many projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively "flat", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.

You should prefer a relational DB like Postgres if:

  • You are comfortable with migration workflows
  • You require enforced data consistency at the database level
  • You have a lot of relationships between collections and require relationships to be enforced

Differences in Payload features

It's important to note that almost everything Payload does is available in all of our officially supported database adapters, including localization, arrays, blocks, etc.

The only thing that is not supported in Postgres yet is the Point field, but that should be added soon.

It's up to you to choose which database you would like to use.

Configuration

To configure the database for your Payload application, an adapter can be assigned to config.db. This property is required within your Payload config.

Here's an example:

1
import { postgresAdapter } from '@payloadcms/db-postgres'
2
3
export default buildConfig({
4
// Your config goes here
5
collections: [
6
// Collections go here
7
],
8
// Here is where you pass your database adapter
9
// and the adapter will require options specific to itself
10
db: postgresAdapter({
11
pool: {
12
connectionString: process.env.DATABASE_URI,
13
}
14
}),
15
})

Collection Operations

To configure Collection database operations in your Payload application, your Collection config has methods that can override default database operations for that Collection.

The override methods receive arguments useful for augmenting operations such as Field data, the collection slug, and the req.

Here is an example:

1
import type { CollectionConfig } from 'payload/types'
2
3
export const Collection: CollectionConfig => {
4
return {
5
slug: 'collection-db-operations',
6
db: {
7
// Create a document in a custom db
8
create: async ({ collection, data, req }) => {
9
const doc = await fetch(`https://example.com/api/${collection}/create`, {
10
method: "POST",
11
body: JSON.stringify(data),
12
headers: {
13
'x-app-user': `payload_${req.payload.user}`,
14
'Content-Type': 'application/json'
15
}
16
}).then(response => response.json())
17
18
return doc
19
},
20
21
// Delete a document in a custom db
22
deleteOne: async ({ collection, data, req }) => {
23
const docs = await fetch(`https://example.com/api/${collection}/delete/${data.id}`, {
24
method: 'DELETE',
25
headers: {
26
'x-app-user': `payload_${req.payload.user}`
27
}
28
}).then(response => response.json())
29
30
return docs
31
},
32
33
// Delete many documents in a custom db
34
deleteMany: async ({ collection, data, req }) => {
35
const docs = await fetch(`https://example.com/api/${collection}/delete`, {
36
method: 'DELETE'
37
headers: {
38
'x-app-user': `payload_${req.payload.user}`
39
}
40
body: JSON.stringify(data),
41
}).then(response => response.json())
42
43
return docs
44
},
45
46
// Find documents in a custom db
47
find: async ({ collection, data, req, where, limit }) => {
48
const docs = await fetch(`https://example.com/api/${collection}/find`, {
49
headers: {
50
'x-app-user': `payload_${req.payload.user}`
51
}
52
body: JSON.stringify({data, where, limit}),
53
}).then(response => response.json())
54
55
return { docs }
56
},
57
58
// Find one document in a custom db
59
findOne: async ({ collection, data, req }) => {
60
const doc = await fetch(`https://example.com/api/${collection}/find/${data.id}`, {
61
headers: {
62
'x-app-user': `payload_${req.payload.user}`
63
}
64
}).then(response => response.json())
65
66
return doc
67
},
68
69
// Update one document in an custom db
70
updateOne: async ({ collection, data, req }) => {
71
const doc = await fetch(`https://example.com/api/${collection}/update/${data.id}`, {
72
method: 'PUT',
73
body: JSON.stringify(data),
74
headers: {
75
'x-app-user': `payload_${req.payload.user}`,
76
'Content-Type': 'application/json'
77
}
78
}).then(response => response.json())
79
80
return { ...doc, updated: true }
81
},
82
},
83
fields: [
84
{
85
name: 'name',
86
type: 'text',
87
},
88
],
89
}
90
}
Next

Migrations