41 Commits

Author SHA1 Message Date
Jessica Rynkar e4742054db fix: stricter input validation (#15868)
Misc input validation improvements, sanitizing path segments in both SQL
and JSON queries, standardizing the processing of column and JSON paths
across different adapters, and making adjustments to traversal and alias
generation to align behavior across components.
2026-03-16 10:48:24 +00:00
Jessica Rynkar 08226db60c fix: throw error for unknown query operators (#15739)
### What
Ensures unknown query operators are properly rejected during query
validation.

### Why
Previously, unrecognized operators in where clauses were silently
ignored. This could lead to unexpected behavior. Query validation should
fail-closed and only accept known operators.

### How
Added an `else` in validateQueryPaths.ts to push an error for any
operator not in the valid operator set.

Test added to `joins > int`
2026-02-24 14:54:50 +00:00
Alessio Gravili 0d9ec910a9 perf: up to 5000% faster permissions calculation (#14631)
In large projects, calculating the `permissions` object is one of the
slowest parts of Payload. This PR completely rewrites the permission
calculation system to make it significantly faster and more reliable.

The permissions object is calculated multiple times throughout Payload's
lifecycle: for the entire config every time we load or navigate to any
admin panel page, on every API endpoint call, and as part of query path
validation when calling any Payload operation.
This is done by `getAccessResults` without any document data, and in
large configs can be one of the slowest operations, noticeably slowing
down admin panel navigation.

After this rewrite, **speed improvements in permissions calculation
without data range from 5.3% (access-control test suite) to 74% (fields
test suite - our largest config)**. The larger the config, the more
noticeable these improvements become. These performance gains apply to
both the Payload Admin Panel and API requests.

---

The `permissions` object can also be calculated WITH document data,
which triggers evaluation of `Where` conditions in
collection/global-level access control and may fetch the document to
pass to access control functions.

This has been heavily optimized with **speed improvements for
permissions calculation with data ranging between 75% and 5138%** (if I
craft a collection config that excessively uses aspects this PR
improves) in the benchmarks. This optimization affects:

- Loading any document (collection/global edit view or drawer)
- Saving a document
- Loading the list view with query presets applied
- Bulk upload initialization (runs twice)

These improvements are most noticeable when navigating _to_ documents in
the admin panel.

## What's Fixed

In addition to performance improvements, the new function is now
cleaner, easier to understand and works more reliably. I specifically
wanna call out two issues that were fixed:

**Field access control data bug**: Added a new e2e test that was
previously failing - when saving a document, the data object passed to
field-level update access control functions had the wrong shape (or in
some cases did not exist at all). This caused fields to show incorrect
`readOnly` states after save. The new implementation properly passes
document data through all access control checks.

**Where query validation**: The old implementation sometimes skipped
validating `Where` queries for collections when `req.data` existed,
potentially granting incorrect access. The new version always validates
where queries correctly when `fetchData: true`.

**Global Where queries:**: Where queries were _never_ executed for
globals. Instead, [we were just checking if the global existed in
general](https://github.com/payloadcms/payload/blob/main/packages/payload/src/utilities/getEntityPolicies.ts#L59).
This PR ensures that `Where` queries returned from global access control
are respected.


## What's Faster (and Why)

**Where query caching:** When multiple operations return identical where
queries (common pattern), we now cache the result. Instead of 3-4
separate DB calls, we make 1 call and reuse it.

**Parallel execution:** All `Where` query evaluations and async access
control functions (for both collections and fields) now execute
concurrently with maximum parallelism, rather than sequentially.

**Optimized DB operations**: When evaluating returned `Where` queries,
we now use direct database `db.count()` queries instead of
`payload.find()` operations. This is much faster (especially on
Postgres).

**Synchronous tree traversal**: The entire field permission tree is now
built synchronously with all async work collected and executed in
parallel at the end, rather than cascading await calls through each
nesting level. This eliminates sequential bottlenecks in deeply nested
field structures.

## Benchmarks

### Admin Panel

- Fields test suite with access control added
- 1000 blocks added to the blocks collection. This is not as excessive
as you would expect:
- most Payload apps do not run on an M3 Max. CPU/DB is often much, much
slower
  - this is all local, with a local DB
- a lot of projects accumulate a huge amount of blocks if you multiply
blocks x block fields. They can definitely reach 1000 total blocks
- each block is simple - only one text field per block. In real
projects, blocks are usually a lot more complex

**Before:**


https://github.com/user-attachments/assets/37b55f02-6dbc-4005-9da6-d7cd4bfdc925

**After:**


https://github.com/user-attachments/assets/8dd796dd-66ed-4d32-a688-27b4169577c6

Branch:
https://github.com/payloadcms/payload/tree/fix/update-field-access-control-after-save-benchmarks

### Modified Access-Control Test Suite

`cd test && pnpm payload run access-control/benchmark-permissions.ts`

```md
📊 Benchmark 1: getAccessResults (all collections + globals)
──────────────────────────────────────────────────────────────────────
┌─────────┬───────────────────┬─────────┬───────────────────┬──────────┐
│ (index) │ Task Name         │ ops/sec │ Average Time (ms) │ Margin   │
├─────────┼───────────────────┼─────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (optimized)' │ '16.91' │ '59203.986'       │ '±0.52%' │
│ 1       │ 'OLD (previous)'  │ '16.07' │ '62323.953'       │ '±0.57%' │
└─────────┴───────────────────┴─────────┴───────────────────┴──────────┘
  ⚡ Speedup: +5.3% 🚀
  📊 DB Calls per operation (across all collections + globals):
     NEW: 0.0 total (0.0 data, 0.0 where)
     OLD: 0.0 total (0.0 data, 0.0 where)

📊 Benchmark 2: docAccessOperation (with fetchData)
──────────────────────────────────────────────────────────────────────
  Collection: where-cache-same (same where queries)

┌─────────┬────────────────────┬───────────┬───────────────────┬──────────┐
│ (index) │ Task Name          │ ops/sec   │ Average Time (ms) │ Margin   │
├─────────┼────────────────────┼───────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (with cache)' │ '2272.81' │ '442.319'         │ '±0.11%' │
│ 1       │ 'OLD (no cache)'   │ '1019.72' │ '983.347'         │ '±0.12%' │
└─────────┴────────────────────┴───────────┴───────────────────┴──────────┘
  ⚡ Speedup: +122.9% 🚀
  📊 DB Calls per operation:
     NEW: 2.0 total (1.0 data, 1.0 where)
     OLD: 4.0 total (1.0 data, 3.0 where)

📊 Benchmark 3: docAccessOperation (with data passed)
──────────────────────────────────────────────────────────────────────
  Collection: where-cache-same (same where queries, no DB fetch)

┌─────────┬────────────────────┬───────────┬───────────────────┬──────────┐
│ (index) │ Task Name          │ ops/sec   │ Average Time (ms) │ Margin   │
├─────────┼────────────────────┼───────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (with cache)' │ '4945.43' │ '203.252'         │ '±0.11%' │
│ 1       │ 'OLD (no cache)'   │ '1023.75' │ '979.898'         │ '±0.14%' │
└─────────┴────────────────────┴───────────┴───────────────────┴──────────┘
  ⚡ Speedup: +383.1% 🚀
  📊 DB Calls per operation:
     NEW: 1.0 total (0.0 data, 1.0 where)
     OLD: 4.0 total (1.0 data, 3.0 where)

📊 Benchmark 4: docAccessOperation (unique where queries)
──────────────────────────────────────────────────────────────────────
  Collection: where-cache-unique (unique where queries per operation)

┌─────────┬────────────────────┬───────────┬───────────────────┬──────────┐
│ (index) │ Task Name          │ ops/sec   │ Average Time (ms) │ Margin   │
├─────────┼────────────────────┼───────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (parallel)'   │ '1693.67' │ '599.091'         │ '±0.29%' │
│ 1       │ 'OLD (sequential)' │ '970.37'  │ '1036.268'        │ '±0.20%' │
└─────────┴────────────────────┴───────────┴───────────────────┴──────────┘
  ⚡ Speedup: +74.5% 🚀
  📊 DB Calls per operation:
     NEW: 4.0 total (1.0 data, 3.0 where)
     OLD: 4.0 total (1.0 data, 3.0 where)

📊 Benchmark 5: Complex Collection (async access, nested blocks, field access)
──────────────────────────────────────────────────────────────────────
  Collection: complex-content (stress test)

┌─────────┬────────────────────┬──────────┬───────────────────┬──────────┐
│ (index) │ Task Name          │ ops/sec  │ Average Time (ms) │ Margin   │
├─────────┼────────────────────┼──────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (optimized)'  │ '133.17' │ '7683.346'        │ '±0.83%' │
│ 1       │ 'OLD (sequential)' │ '72.86'  │ '13901.616'       │ '±0.82%' │
└─────────┴────────────────────┴──────────┴───────────────────┴──────────┘
  ⚡ Speedup: +82.8% 🚀
  📊 DB Calls per operation:
     NEW: 2.0 total (1.0 data, 1.0 where)
     OLD: 2.0 total (1.0 data, 1.0 where)

📊 Benchmark 6: Sync-Heavy Collection (same where, many sync field access)
──────────────────────────────────────────────────────────────────────
  Collection: sync-heavy (where cache + field access)

┌─────────┬────────────────────┬───────────┬───────────────────┬──────────┐
│ (index) │ Task Name          │ ops/sec   │ Average Time (ms) │ Margin   │
├─────────┼────────────────────┼───────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (with cache)' │ '3607.72' │ '279.559'         │ '±0.15%' │
│ 1       │ 'OLD (no cache)'   │ '68.87'   │ '15136.557'       │ '±1.44%' │
└─────────┴────────────────────┴───────────┴───────────────────┴──────────┘
  ⚡ Speedup: +5138.5% 🚀
  📊 DB Calls per operation:
     NEW: 1.0 total (0.0 data, 1.0 where)
     OLD: 5.0 total (1.0 data, 4.0 where)

══════════════════════════════════════════════════════════════════════
📈 Summary:
══════════════════════════════════════════════════════════════════════
  1. getAccessResults:                        (see above)
  2. docAccessOperation (with fetchData):     (see above)
  3. docAccessOperation (with data passed):   (see above)
  4. docAccessOperation (unique where):       (see above)
  5. Complex collection (async + nested):     (see above)
  6. Sync-heavy (where cache + fields):       (see above)
══════════════════════════════════════════════════════════════════════
```

### Fields Test Suite

`cd test && pnpm payload run fields/benchmark-getAccessResults.ts`

```md
📊 Benchmark: getAccessResults (all fields test collections + globals)
──────────────────────────────────────────────────────────────────────
┌─────────┬───────────────────┬───────────┬───────────────────┬──────────┐
│ (index) │ Task Name         │ ops/sec   │ Average Time (ms) │ Margin   │
├─────────┼───────────────────┼───────────┼───────────────────┼──────────┤
│ 0       │ 'NEW (optimized)' │ '2104.06' │ '478.382'         │ '±0.18%' │
│ 1       │ 'OLD (previous)'  │ '1208.14' │ '834.974'         │ '±0.21%' │
└─────────┴───────────────────┴───────────┴───────────────────┴──────────┘

  ⚡ Speedup: +74.2% 🚀
  📊 DB Calls per operation (across all collections + globals):
     NEW: 0.0 total (0.0 data, 0.0 where)
     OLD: 0.0 total (0.0 data, 0.0 where)
```
2025-11-20 11:06:12 -08:00
Jarrod Flesch 73ba4d1bb9 fix: unable to query versions on latest key (#13512)
Fixes https://github.com/payloadcms/payload/issues/13455

https://github.com/payloadcms/payload/pull/13297 Fixed a scoping issue,
but exposed a new issue where querying versioned documents by the
`latest` key would fail. This PR fixes the newly discoverable issue.
2025-08-19 11:42:02 -07:00
Jarrod Flesch 8d84352ee9 fix(next): catch list filter errors, prevent list view crash (#13297)
Catches list filter errors and prevents the list view from crashing when
attempting to search on fields the user does not have access to. Instead
just shows the default "no results found" message.
2025-07-29 11:30:07 -04:00
Alessio Gravili 053192c488 refactor: changed default exports to named exports in payload package (#12871)
This changes all remaining default exports to named exports in the
payload package and removes all unnecessary internal-only barrel export
files. => Less lines of code, less eslint warnings

![Screenshot 2025-06-19 at 14 02
23@2x](https://github.com/user-attachments/assets/bcbe2394-07b5-49b4-86c7-30243679bb61)
2025-06-24 04:38:02 +00:00
Sasha bc9b501e28 fix: querying virtual fields deeply with draft: true (#12868)
Fixes an issue when querying deeply new relationship virtual fields with
`draft: true`. Changes the method for `where` sanitization, before it
was done in `validateSearchParam` which didn't work with versions
properly, now there's a separate `sanitizeWhereQuery` function that does
this.
2025-06-23 22:18:49 -04:00
Sasha 9943b3508d fix: filtering joins in where by ID (#12804)
Fixes https://github.com/payloadcms/payload/issues/12768

Example:
```
const found_1 = await payload.find({
  collection: 'categories',
  where: { 'relatedPosts.id': { equals: post.id } },
})
```
or
```
const found_2 = await payload.find({
  collection: 'categories',
  where: { relatedPosts: { equals: post.id } },
})
```
2025-06-13 14:13:17 -04:00
Germán Jabloñski 53f8838830 chore: migrate to TypeScript strict in Payload package - #4/4 (#12733)
Important: An intentional effort is being made during migration to not
modify runtime behavior. This implies that there will be several
assertions, non-null assertions, and @ts-expect-error. This philosophy
applies only to migrating old code to TypeScript strict, not to writing
new code. For a more detailed justification for this reasoning, see
#11840 (comment).

In this PR, instead of following the approach of migrating a subset of
files, I'm migrating all files by disabling specific rules. The first
commits are named after the rule being disabled.

With this PR, the migration of the payload package is complete 🚀
2025-06-09 20:50:17 +00:00
Germán Jabloñski 6ec21a53ff chore: migrate to TypeScript strict in Payload package (enable strictNullChecks) - #3 (#12586)
Important: An intentional effort is being made during migration to not
modify runtime behavior. This implies that there will be several
assertions, non-null assertions, and @ts-expect-error. This philosophy
applies only to migrating old code to TypeScript strict, not to writing
new code. For a more detailed justification for this reasoning,
https://github.com/payloadcms/payload/pull/11840#discussion_r2021975897.

In this PR, instead of following the approach of migrating a subset of
files, I'm migrating all files by disabling a specific rule. In this
case, `strictNullChecks`.

`strictNullChecks` is a good rule to start the migration with because
it's easy to silence with non-null assertions or optional chainings.
Additionally, almost all ts strict errors are due to this rule.

This PR improves 200+ files, leaving only 68 remaining to migrate to
strict mode in the payload package.
2025-06-03 14:43:37 +00:00
Sasha 2b40e0f21f feat: polymorphic join querying by fields that don't exist in every collection (#12648)
This PR makes it possible to do polymorphic join querying by fields that
don't exist in all collections specified in `field.collection`, for
example:
```
const result = await payload.find({
  collection: 'payload-folders',
  joins: {
    documentsAndFolders: {
      where: {
        and: [
          {
            relationTo: {
              in: ['folderPoly1', 'folderPoly2'],
            },
          },
          {
            folderPoly2Title: { // this field exists only in the folderPoly2 collection, before it'd throw a query error.
              equals: 'Poly 2 Title',
            },
          },
        ],
      },
    },
  },
})
```

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-06-03 00:48:07 +03:00
Sasha 1c99f46e4f feat: queriable / sortable / useAsTitle virtual fields linked with a relationship field (#11805)
This PR adds an ability to specify a virtual field in this way
```js
{
  slug: 'posts',
  fields: [
    {
      name: 'title',
      type: 'text',
      required: true,
    },
  ],
},
{
  slug: 'virtual-relations',
  fields: [
    {
      name: 'postTitle',
      type: 'text',
      virtual: 'post.title',
    },
    {
      name: 'post',
      type: 'relationship',
      relationTo: 'posts',
    },
  ],
},
```

Then, every time you query `virtual-relations`, `postTitle` will be
automatically populated (even if using `depth: 0`) on the db level. This
field also, unlike `virtual: true` is available for querying / sorting /
`useAsTitle`.

Also, the field can be deeply nested to 2 or more relationships, for
example:
```
{
  name: 'postCategoryTitle',
  type: 'text',
  virtual: 'post.category.title',
},
```

Where the current collection has `post` - a relationship to `posts`, the
collection `posts` has `category` that's a relationship to `categories`
and finally `categories` has `title`.
2025-04-16 15:46:18 -04:00
Sasha b9ffbc6994 fix: querying by polymorphic join field relationTo with overrideAccess: false (#11999)
Previously, querying by polymorphic joins `relationTo` with
`overrideAccess: false` caused an error:
```
QueryError: The following paths cannot be queried: relationTo
```

As this field actually doesn't exist in the schema. Now, under condition
that the query comes from a polymorphic join we skip checking
`relationTo` field access.
2025-04-07 20:19:43 +00:00
Alessio Gravili 313ff047df perf: optimize permissions calculation with lots of blocks (#11236)
This PR optimizes permissions calculation for block references, by
calculating them only once per block reference config, instead of once
every single time the blocks are referenced.

This will lead to significant performance improvements in Payload
Configs with a lot of duplicative block references, as permissions are
calculated every time you navigate from page to page.

# Benchmarks

Tested using `pnpm dev benchmark-blocks`.

## Before - ~ 6 seconds


https://github.com/user-attachments/assets/85cac698-3120-414f-91d3-608a404a3a5f


## After - ~ 2 seconds


https://github.com/user-attachments/assets/0c3642f6-6001-41ae-a7cd-f30b24362e9b
2025-02-18 11:16:04 -05:00
Alessio Gravili e6fea1d132 fix: localized fields within block references were not handled properly if any parent is localized (#11207)
The `localized` properly was not stripped out of referenced block fields, if any parent was localized. For normal fields, this is done in sanitizeConfig. As the same referenced block config can be used in both a localized and non-localized config, we are not able to strip it out inside sanitizeConfig by modifying the block config.

Instead, this PR had to bring back tedious logic to handle it everywhere the `field.localized` property is accessed. For backwards-compatibility, we need to keep the existing sanitizeConfig logic. In 4.0, we should remove it to benefit from better test coverage of runtime field.localized handling - for now, this is done for our test suite using the `PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY` flag.
2025-02-17 19:50:32 +00:00
Alessio Gravili 4c8cafd6a6 perf: deduplicate blocks used in multiple places using new config.blocks property (#10905)
If you have multiple blocks that are used in multiple places, this can quickly blow up the size of your Payload Config. This will incur a performance hit, as more data is
1.  sent to the client (=> bloated `ClientConfig` and large initial html) and
2. processed on the server (permissions are calculated every single time you navigate to a page - this iterates through all blocks you have defined, even if they're duplicative)

This can be optimized by defining your block **once** in your Payload Config, and just referencing the block slug whenever it's used, instead of passing the entire block config. To do this, the block can be defined in the `blocks` array of the Payload Config. The slug can then be passed to the `blockReferences` array in the Blocks Field - the `blocks` array has to be empty for compatibility reasons.

```ts
import { buildConfig } from 'payload'
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'

// Payload Config
const config = buildConfig({
  // Define the block once
  blocks: [
    {
      slug: 'TextBlock',
      fields: [
        {
          name: 'text',
          type: 'text',
        },
      ],
    },
  ],
  collections: [
    {
      slug: 'collection1',
      fields: [
        {
          name: 'content',
          type: 'blocks',
          // Reference the block by slug
          blockReferences: ['TextBlock'],
          blocks: [], // Required to be empty, for compatibility reasons
        },
      ],
    },
     {
      slug: 'collection2',
      fields: [
        {
          name: 'editor',
          type: 'richText',
          editor: lexicalEditor({
            BlocksFeature({
              // Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
              blocks: ['TextBlock'],
            })
          })
        },
      ],
    },
  ],
})
```

## v4.0 Plans

In 4.0, we will remove the `blockReferences` property, and allow string block references to be passed directly to the blocks `property`. Essentially, we'd remove the `blocks` property and rename `blockReferences` to `blocks`.

The reason we opted to a new property in this PR is to avoid breaking changes. Allowing strings to be passed to the `blocks` property will prevent plugins that iterate through fields / blocks from compiling.

## PR Changes

- Testing: This PR introduces a plugin that automatically converts blocks to block references. This is done in the fields__blocks test suite, to run our existing test suite using block references.

- Block References support: Most changes are similar. Everywhere we iterate through blocks, we have to now do the following:
1. Check if `field.blockReferences` is provided. If so, only iterate through that.
2. Check if the block is an object (= actual block), or string
3. If it's a string, pull the actual block from the Payload Config or from `payload.blocks`.

The exception is config sanitization and block type generations. This PR optimizes them so that each block is only handled once, instead of every time the block is referenced.

## Benchmarks

60 Block fields, each block field having the same 600 Blocks.

### Before:
**Initial HTML:** 195 kB
**Generated types:** takes 11 minutes, 461,209 lines

https://github.com/user-attachments/assets/11d49a4e-5414-4579-8050-e6346e552f56

### After:
**Initial HTML:** 73.6 kB
**Generated types:** takes 2 seconds, 35,810 lines

https://github.com/user-attachments/assets/3eab1a99-6c29-489d-add5-698df67780a3

### After Permissions Optimization (follow-up PR)
Initial HTML: 73.6 kB

https://github.com/user-attachments/assets/a909202e-45a8-4bf6-9a38-8c85813f1312


## Future Plans

1. This PR does not yet deduplicate block references during permissions calculation. We'll optimize that in a separate PR, as this one is already large enough
2. The same optimization can be done to deduplicate fields. One common use-case would be link field groups that may be referenced in multiple entities, outside of blocks. We might explore adding a new `fieldReferences` property, that allows you to reference those same `config.blocks`.
2025-02-14 00:08:20 +00:00
Germán Jabloñski 6eee787493 chore: add typescript-strict-plugin to the payload package for incremental file-by-file migration [skip lint] (#11133)
### What?

Implement the
[typescript-strict-plugin](https://github.com/allegro/typescript-strict-plugin)
plugin in the payload (core) package.

### Why?

1. One strategy for incremental migration is to enable strictness rules
in tsconfig, fix some errors, and push them without committing the
changes to tsconfig.json. However, this is not feasible for a package as
large as Payload that has over 1000 typescript errors. Until the work is
done, new contributions would undo the work being done.
2. Even if no migration work is done after this PR, this change already
improves the strictness of the package. 89 of the 311 files within the
package already satisfy strict mode. This PR only adds a comment
`@ts-strict-ignore` to files that had at least one compilation error.
This way, the propagation of errors in those files is stopped.
3. New files created in the package are strict by default (this was the
main improvement in version 2 of `typescript-strict-plugin`).

I recommend starting the migration with this package because it is the
one that almost all the others depend on. Once we finish this package,
we can repeat the same strategy on another one, or use the strategy I
mentioned in point 1 if the package is small.

### Note

If you don't see errors in the IDE when you uncomment `//
@ts-strict-ignore`, try restarting the typescript server or VSCode


### How to contribute to the migration ❤️

1. Remove `// @ts-strict-ignore` comments from 1 or more files
2. Fix the pending errors (they should appear in your IDE's intellisense
or when running `cd packages/payload` + `pnpm build:types`
3. Submit your PR!

Important: You don't need to fix everything at once! Furthermore, I
recommend breaking this down into very small PRs to trace potential
issues later if there are any. So if you have 5 minutes, tackle a small
file—every bit counts! 🤗
2025-02-12 11:06:03 -05:00
Alessio Gravili 91ed882d62 perf: remove deepCopying in sanitizeJoinQuery, optimize flattenWhereToOperators (#10663) 2025-01-18 18:34:27 -07:00
Alessio Gravili 42382b6f6f perf: operations performance optimizations (#10609)
- reduces unnecessary shallow copying within operations by removing
unnecessary spreads or .map()'s
- removes unnecessary `deleteMany` call in `deleteUserPreferences` for
auth-enabled collections
- replaces all instances of `validOperators.includes` with
`validOperatorMap[]`. O(n) => O(1)
- optimizes the `sanitizeInternalFields` function. Previously, it was
doing a **lot** of shallow copying
2025-01-16 17:07:35 +00:00
Sasha cae300e8e3 perf: flattenedFields collection/global property, remove deep copying in validateQueryPaths (#9299)
### What?
Improves querying performance of the Local API, reduces copying overhead

### How?

Adds `flattenedFields` property for sanitized collection/global config
which contains fields in database schema structure.
For example, It removes rows / collapsible / unnamed tabs and merges
them to the parent `fields`, ignores UI fields, named tabs are added as
`type: 'tab'`.
This simplifies code in places like Drizzle `transform/traverseFields`.
Also, now we can avoid calling `flattenTopLevelFields` which adds some
overhead and use `collection.flattenedFields` / `field.flattenedFields`.

By refactoring `configToJSONSchema.ts` with `flattenedFields` it also
1. Fixes https://github.com/payloadcms/payload/issues/9467
2. Fixes types for UI fields were generated for `select`



Removes this deep copying for each `where` query constraint
https://github.com/payloadcms/payload/blob/58ac784425b411fc28c91dbb3e7df06cc26b6320/packages/payload/src/database/queryValidation/validateQueryPaths.ts#L69-L73
which potentially can add overhead if you have a large collection/global
config

UPD:
The overhead is even much more than in the benchmark below if you have
Lexical with blocks.

Benchmark in `relationships/int.spec.ts`:
```ts
const now = Date.now()
for (let i = 0; i < 10; i++) {
  const now = Date.now()
  for (let i = 0; i < 300; i++) {
    const query = await payload.find({
      collection: 'chained',
      where: {
        'relation.relation.name': {
          equals: 'third',
        },
        and: [
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
          {
            'relation.relation.name': {
              equals: 'third',
            },
          },
        ],
      },
    })
  }

  payload.logger.info(`#${i + 1} ${Date.now() - now}`)
}
payload.logger.info(`Total ${Date.now() - now}`)
```

Before:
```
[02:11:48] INFO: #1 3682
[02:11:50] INFO: #2 2199
[02:11:54] INFO: #3 3483
[02:11:56] INFO: #4 2516
[02:11:59] INFO: #5 2467
[02:12:01] INFO: #6 1987
[02:12:03] INFO: #7 1986
[02:12:05] INFO: #8 2375
[02:12:07] INFO: #9 2040
[02:12:09] INFO: #10 1920
    [PASS] Relationships > Querying > Nested Querying > should allow querying two levels deep (24667ms)
[02:12:09] INFO: Total 24657
```

After:
```
[02:12:36] INFO: #1 2113
[02:12:38] INFO: #2 1854
[02:12:40] INFO: #3 1700
[02:12:42] INFO: #4 1797
[02:12:44] INFO: #5 2121
[02:12:46] INFO: #6 1774
[02:12:47] INFO: #7 1670
[02:12:49] INFO: #8 1610
[02:12:50] INFO: #9 1596
[02:12:52] INFO: #10 1576
    [PASS] Relationships > Querying > Nested Querying > should allow querying two levels deep (17828ms)
[02:12:52] INFO: Total 17818
```
2024-11-25 10:28:07 -05:00
Sasha 5b97ac1a67 fix: querying relationships by id path with REST (#9013)
### What?
Fixes the issue with querying by `id` from REST / `overrideAccess:
false`.
For example, this didn't work:

`/api/loans?where[book.bibliography.id][equals]=67224d74257b3f2acddc75f4`
```
QueryError: The following path cannot be queried: id
```

### Why?
We support this syntax within the Local API.

### How?
Now, for simplicity we sanitize everything like
`relation.otherRelation.id` to `relation.otherRelation`

Fixes https://github.com/payloadcms/payload/issues/9008
2024-11-04 17:57:41 +02:00
Sasha bf48af411d feat: add virtual property to the fields config (#7621)
## Description

Adds `virtual` property to the fields config. Providing `true`
completely disables the field in the DB, which is useful for [Virtual
Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges)
Disables abillity to query by a field with `virtual: true`.
Currently, they bloat the DB with unused tables / columns, which may as
well introduce additional joins.
Discussion https://github.com/payloadcms/payload/discussions/6270
Prev PR (this one contains only this feature):
https://github.com/payloadcms/payload/pull/6983

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-09-17 10:40:54 -04:00
Paul d2571e10d6 feat: upload hasmany (#7796)
Supports `hasMany` upload fields, similar to how `hasMany` works in
other fields, i.e.:

```ts
{
  type: 'upload',
  relationTo: 'media',
  hasMany: true
}
```

---------

Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-08-21 20:44:04 -04:00
Alessio Gravili 83fd4c6622 chore: run lint and prettier on entire codebase 2024-07-11 15:27:01 -04:00
Jarrod Flesch 0711f880ff chore!: simplify api handler (#6910)
Removes PayloadRequestWithData in favour of just PayloadRequest with
optional types for `data` and `locale`

`addDataAndFileToRequest` and `addLocalesToRequestFromData` now takes in
a single argument instead of an object

```ts
// before
await addDataAndFileToRequest({ request: req })
addLocalesToRequestFromData({ request: req })

// current
await addDataAndFileToRequest(req)
addLocalesToRequestFromData(req)
```

---------

Co-authored-by: Paul Popus <paul@nouance.io>
2024-07-02 09:47:03 -04:00
Elliot DeNolf 739dfc1434 chore: convert all errors to named exports (#6061) 2024-04-26 13:24:18 -04:00
Jarrod Flesch 22c53392a3 chore: improves types for payloadRequest (#6012) 2024-04-25 10:23:03 -04:00
James 96012b26b7 chore: properly isolates req in parallel requests 2024-04-04 18:00:37 -04:00
James 5d57572694 chore: work to add consistency to fields e2e 2024-04-04 14:41:58 -04:00
Alessio Gravili 50c7269315 chore: replace .d.ts type imports with .js imports, as .d.ts imports break usage checks in the IDE 2024-03-07 16:08:49 -05:00
James 9674f4e739 chore: esm-ify payload 2024-03-06 14:48:02 -05:00
Jarrod Flesch b651b5cb48 chore: start to remove reliance on express specific functionality 2023-12-08 08:51:23 -05:00
Patrik 4b5453e8e5 fix: simplifies query validation and fixes nested relationship fields (#4391) 2023-12-06 08:47:34 -05:00
Jarrod Flesch fe888b5f6c fix: query validation on relationship fields (#4353) 2023-12-01 16:03:58 -05:00
Jarrod Flesch cd07873fc5 fix(db-postgres): allow for nested block fields to be queried (#4237)
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2023-11-29 11:03:57 -05:00
Jarrod Flesch 36576f152a fix: field paths being mutated if they ended with the req.locale (#3936) 2023-10-31 08:53:00 -04:00
James e437b41950 chore: misc bugs, types pattern 2023-09-24 10:51:21 -07:00
Alessio Gravili a81401cf77 feat: breaking: richtext adapter (#3311)
BREAKING: requires user to install @payloacms-richtext-slate and specify a `config.editor` property

* chore: move slate stuff into packages/richtext-slate

* chore: fieldTypes stuff

* chore: fix richtext-slate tsconfig

* chore: add clean:unix command

* chore: fix up things

* chore: undo subpath imports being hoisted up

* chore: fix incorrect imports

* chore: improve AdapterArguments type

* chore: remove unused richTextToHTML and stringifyRichText files

* fix: core-dev scss imports

* chore: fix publishConfig exports for richtext-slate

* chore: adjust joi schema for richtext field

* chore: various fixes

* chore: handle afterRead population in richText adapter

* chore: handle more after-read promise stuff

* chore: fix joi validation

* chore: add richtext adapter to tests

* chore: merge adapter props with field props

* chore: index.tsx => index.ts

* chore: rename `adapter` to `editor`

* chore: fix e2e tests not running due to importing a constant from a file (`Tabs`) which imports createSlate.

This fails because createSlate imports React components.

* chore: remove unnecessary import

* chore: improve various typings

* chore: improve typings for List view Cell components

* feat: richText adapter cell component

* chore: add missing types packages for packages/richtext-slate

* chore: add new adapter interface properties to joi schema

* chore: withMergedProps utility which replaces getSlateCellComponent and getSlateFieldComponent

* feat: added config.defaultEditor property which is now required. field.editor is no longer required and overrides config.defaultEditor

* docs: mention editor and defaultEditor property in the docs

* chore: fix incorrectly formatted JSX in docs files breaking mdx parser

* chore: fix various errors

* chore: auto-generated pointer files
2023-09-19 11:03:31 +02:00
James 5292f41a3b chore: rectifies test issues 2023-09-15 17:12:13 -04:00
Alessio Gravili ae7d6f97d2 chore: formatting and linting (#3261)
* chore: lint packages/payload

* chore: lint packages/db-postgres

* chore: lint packages/db-mongodb

* chore: update eslintrc exclusion rules

* chore: update eslintrc exclusion rules

* chore: lint misc files

* chore: run prettier through packages

* chore: run eslint on payload again

* chore: prettier misc files

* chore: prettier docs
2023-09-01 17:39:44 +02:00
Alessio Gravili a67278b29f chore: move to monorepo structure 2023-08-23 12:20:30 +02:00