diff --git a/docusaurus/docs/cms/api/document-service.md b/docusaurus/docs/cms/api/document-service.md
index 0d487a5d63..5785a138c3 100644
--- a/docusaurus/docs/cms/api/document-service.md
+++ b/docusaurus/docs/cms/api/document-service.md
@@ -109,6 +109,7 @@ Syntax: `findOne(parameters: Params) => Document`
{ name: 'documentId', type: 'ID', required: true, description: 'Document id' },
{ name: 'locale', type: 'String or undefined', required: false, description: 'Locale of the document to find. Defaults to the default locale. See locale docs.' },
{ name: 'status', type: "'published' | 'draft'", required: false, description: 'If Draft & Publish is enabled: publication status. Can be published or draft. Default: draft. See status docs.' },
+ { name: 'publicationFilter', type: 'String', required: false, description: 'If Draft & Publish is enabled: select documents by how their draft and published versions relate, before applying status. See publicationFilter docs.' },
{ name: 'fields', type: 'Object', required: false, description: 'Select fields to return. Defaults to all fields (except those not populated by default).' },
{ name: 'populate', type: 'Object', required: false, description: 'Populate results with additional fields. Default: null.' },
]}
@@ -147,6 +148,7 @@ Syntax: `findFirst(parameters: Params) => Document`
params={[
{ name: 'locale', type: 'String or undefined', required: false, description: 'Locale of the documents to find. Defaults to the default locale. See locale docs.' },
{ name: 'status', type: "'published' | 'draft'", required: false, description: 'If Draft & Publish is enabled: publication status. Can be published or draft. Default: draft. See status docs.' },
+ { name: 'publicationFilter', type: 'String', required: false, description: 'If Draft & Publish is enabled: select documents by how their draft and published versions relate, before applying status. See publicationFilter docs.' },
{ name: 'filters', type: 'Object', required: false, description: 'Filters to use. Default: null.' },
{ name: 'fields', type: 'Object', required: false, description: 'Select fields to return. Defaults to all fields (except those not populated by default).' },
{ name: 'populate', type: 'Object', required: false, description: 'Populate results with additional fields. Default: null.' },
@@ -210,6 +212,7 @@ Syntax: `findMany(parameters: Params) => Document[]`
params={[
{ name: 'locale', type: 'String or undefined', required: false, description: 'Locale of the documents to find. Defaults to the default locale. See locale docs.' },
{ name: 'status', type: "'published' | 'draft'", required: false, description: 'If Draft & Publish is enabled: publication status. Can be published or draft. Default: draft. See status docs.' },
+ { name: 'publicationFilter', type: 'String', required: false, description: 'If Draft & Publish is enabled: select documents by how their draft and published versions relate, before applying status. See publicationFilter docs.' },
{ name: 'filters', type: 'Object', required: false, description: 'Filters to use. Default: null.' },
{ name: 'fields', type: 'Object', required: false, description: 'Select fields to return. Defaults to all fields (except those not populated by default).' },
{ name: 'populate', type: 'Object', required: false, description: 'Populate results with additional fields. Default: null.' },
@@ -606,6 +609,7 @@ Syntax: `count(parameters: Params) => number`
params={[
{ name: 'locale', type: 'String or null', required: false, description: 'Locale of the documents to count. Defaults to the default locale. See locale docs.' },
{ name: 'status', type: "'published' | 'draft'", required: false, description: 'If Draft & Publish is enabled: publication status. published to count only published documents, draft to count draft documents (returns all documents). Default: draft. See status docs.' },
+ { name: 'publicationFilter', type: 'String', required: false, description: 'If Draft & Publish is enabled: select documents by how their draft and published versions relate, before applying status. See publicationFilter docs.' },
{ name: 'filters', type: 'Object', required: false, description: 'Filters to use. Default: null.' },
]}
codeTabs={[
@@ -633,7 +637,7 @@ strapi.documents('api::restaurant.restaurant').count({ filters: { name: { $start
:::note
Since published documents necessarily also have a draft counterpart, a published document is still counted as having a draft version.
-This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. There currently is no way to prevent already published documents from being counted.
+This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. To count only never-published drafts, pass a [`publicationFilter`](/cms/api/document-service/publication-filter) value such as `'never-published'` or `'never-published-document'`.
:::
diff --git a/docusaurus/docs/cms/api/document-service/publication-filter.md b/docusaurus/docs/cms/api/document-service/publication-filter.md
new file mode 100644
index 0000000000..51a37b1c47
--- /dev/null
+++ b/docusaurus/docs/cms/api/document-service/publication-filter.md
@@ -0,0 +1,557 @@
+---
+title: Using publicationFilter with the Document Service API
+description: Use the publicationFilter parameter with Strapi's Document Service API to query documents by the relationship between their draft and published versions, such as never-published or modified documents.
+displayed_sidebar: cmsSidebar
+sidebar_label: Publication filter
+tags:
+- API
+- Content API
+- count()
+- Document Service API
+- Draft & Publish
+- findMany()
+- findFirst()
+- findOne()
+- publicationFilter
+- status
+---
+
+# Document Service API: `publicationFilter`
+
+
+
+Use the optional `publicationFilter` parameter to query documents by the relationship between their draft and published versions, for example drafts that were never published, or entries modified since they were last published. It works with `findOne()`, `findFirst()`, `findMany()`, and `count()`, and combines with other query parameters. `status` still decides whether you get the draft or the published version.
+
+
+
+The `publicationFilter` is a parameter that, combined with [the `status` parameter](/cms/api/document-service/status), can help you cover complex queries to find exactly what you need with the [Document Service API](/cms/api/document-service).
+
+While `status` answers "do I want the draft or the published version?", the `publicationFilter` parameter answers a different question: "which documents do I want, based on how their draft and published versions relate?". This is useful for example to find drafts that were never published, or entries whose draft has unsaved changes compared to what is live.
+
+:::prerequisites
+The [Draft & Publish](/cms/features/draft-and-publish) feature must be enabled on the content-type. If Draft & Publish is disabled, `publicationFilter` has no effect.
+:::
+
+## Available values {#values}
+
+`publicationFilter` accepts one of the following values:
+
+| Value | Selects |
+| ----- | ------- |
+| `never-published` | Documents never published in a given locale |
+| `never-published-document` | Documents never published in any locale |
+| `modified` | Documents whose draft was edited since it was last published |
+| `unmodified` | Documents whose draft has not changed since it was last published |
+| `published-without-draft` | Published documents with no draft counterpart |
+| `published-with-draft` | Published documents that also have a draft |
+| `has-published-version` | Documents that have both a draft and a published version |
+| `has-published-version-document` | Documents published in at least one locale
(useful when [i18n](/cms/features/internationalization) is enabled) |
+
+For detailed examples of how to use the `publicationFilter` values, including with the `status` parameter, see the [possible use cases](#use-cases) table.
+
+:::note
+* Unknown values raise a validation error.
+* Values ending in `-document` consider all locales of a document, which matters when [Internationalization (i18n)](/cms/features/internationalization) is enabled: for example, `never-published-document` excludes a document as soon as one of its locales is published. All other values consider one locale at a time. Without i18n, both variants behave the same.
+:::
+
+:::caution Caution: Different default behaviors for different APIs
+The Document Service API returns draft versions of documents when `status` is omitted, while REST and GraphQL return the published ones instead, so REST API queries need an explicit `status` (see [REST API: `publicationFilter`](/cms/api/rest/publication-filter)).
+:::
+
+## Possible use cases {#use-cases}
+
+The following table lists many possible use cases, illustrating how the `status` and `publicationFilter` parameters can be combined to find exactly what you need with the Document Service API. Click a use case to jump to a complete example:
+
+| I want to… | Use `status` as… | Use `publicationFilter` as… |
+| ---------- | ------------------ | ----------------------------- |
+| [Find never published drafts](#never-published) | `draft` | `never-published` |
+| [Find drafts never published in any locale](#never-published-document) | `draft` | `never-published-document` |
+| [Find modified documents](#modified) | `draft` or `published` | `modified` |
+| [Find unmodified documents](#unmodified) | `draft` or `published` | `unmodified` |
+| [Find published documents without a draft](#published-without-draft) | `published` | `published-without-draft` |
+| [Find published documents with a draft](#published-with-draft) | `published` | `published-with-draft` |
+| [Find documents with a published version](#has-published-version) | `draft` or `published` | `has-published-version` |
+| [Find documents published in at least one locale](#has-published-version-document) | `draft` or `published` | `has-published-version-document` |
+| [Use with `findOne()` and `findFirst()`](#find-one-find-first) | `draft` or `published` | any value |
+| [Count only matching documents](#count) | `draft` or `published` | any value |
+
+:::note
+Pairing a value with the opposite `status` from the table above is valid but returns nothing rather than an error: for example, `never-published` with `status: 'published'` returns an empty result, because these documents have no published version yet.
+:::
+
+## Examples
+
+The following section lists the most common use cases summed up in the [table](#use-cases) above.
+
+### Find never published drafts {#never-published}
+
+One of the most common use cases is to find the drafts that have never been published. To do so, pass `status: 'draft'` and `publicationFilter: 'never-published'`.
+
+This parameter combination works only on a given locale; to find these documents across all locales, [use `never-published-document`](#never-published-document) instead.
+
+
+
+### Find drafts never published in any locale {#never-published-document}
+
+`publicationFilter: never-published-document` returns documents that have never been published in any locale. It looks at the whole document across all its locales, not one locale at a time. To find these documents for a given locale only, [use `never-published`](#never-published) instead.
+
+A document counts as published as soon as one of its locales is published: the document is then left out, even the locales that only exist as a draft. The example below returns the draft versions of documents that were never published anywhere:
+
+
+
+### Find modified documents {#modified}
+
+`publicationFilter: modified` selects documents whose draft has modified but unpublished changes. `status` then decides which version of those documents you get back.
+
+For instance, with `status: 'draft'`, the query returns the draft versions:
+
+
+
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
+
+
+
+### Find unmodified documents {#unmodified}
+
+`publicationFilter: unmodified` selects documents whose draft has not changed since it was last published. `status` then decides which version of those documents you get back.
+
+For instance, with `status: 'draft'`, the query returns the draft versions:
+
+
+
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
+
+
+
+### Find published documents without a draft {#published-without-draft}
+
+`publicationFilter: published-without-draft` selects published documents that have no draft counterpart.
+
+`published-without-draft` must be paired with `status: 'published'`:
+
+
+
+### Find published documents with a draft {#published-with-draft}
+
+`publicationFilter: published-with-draft` selects published documents that also have a draft. Unlike `published-without-draft`, it keeps only the published documents that still have a draft counterpart.
+
+`published-with-draft` must be paired with `status: 'published'`:
+
+
+
+### Find documents with a published version {#has-published-version}
+
+`publicationFilter: has-published-version` selects documents that have both a draft and a published version for the same locale. `status` then decides which version of those documents you get back. Unlike `published-without-draft`, it excludes published documents that have no draft counterpart.
+
+For instance, with `status: 'draft'`, the query returns the draft versions:
+
+
+
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
+
+
+
+### Find documents published in at least one locale {#has-published-version-document}
+
+`publicationFilter: has-published-version-document` considers all locales, so it matches a document as soon as one of its locales is published. With `status: 'draft'`, it returns the draft versions of every locale of those documents, including locales that were never published themselves:
+
+
+
+### Use with `findOne()` and `findFirst()` {#find-one-find-first}
+
+If the requested document (and locale, when applicable) does not match the filter, `findOne()` and `findFirst()` return `null` even when the `documentId` exists:
+
+
+
+### Count only matching documents {#count}
+
+Without `publicationFilter`, `count({ status: 'draft' })` counts every draft version, including drafts whose document already has a published version. Add `publicationFilter` to count only the documents that match a given value (see the [`status` documentation](/cms/api/document-service/status#count)):
+
+
+
+## Combination with other parameters {#combine}
+
+`publicationFilter` is combined with other query parameters as a logical `AND`, including [`filters`](/cms/api/document-service/filters) and [`populate`](/cms/api/document-service/populate). When populating draft & publish relations, nested queries inherit the same filter logic.
+
+## Content Manager mapping {#content-manager}
+
+In the Content Manager, the **Draft (never published)** list filter maps to `status: 'draft'` and `publicationFilter: 'never-published-document'` (document-scoped, not the per-locale `never-published`).
+
diff --git a/docusaurus/docs/cms/api/document-service/status.md b/docusaurus/docs/cms/api/document-service/status.md
index a7d82ec2e4..5a305e6641 100644
--- a/docusaurus/docs/cms/api/document-service/status.md
+++ b/docusaurus/docs/cms/api/document-service/status.md
@@ -36,6 +36,8 @@ By default the [Document Service API](/cms/api/document-service) returns the dra
Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter.
:::
+To select documents by how their draft and published versions relate (never-published, modified, and others), see [Document Service API: `publicationFilter`](/cms/api/document-service/publication-filter).
+
## Get the published version with `findOne()` {#find-one}
25, max 100' },
{ name: 'locale', type: 'string', required: false, description: 'Locale of the documents to fetch. See locale.' },
{ name: 'status', type: 'string', required: false, description: 'published or draft. See status.' },
+ { name: 'publicationFilter', type: 'string', required: false, description: 'Query documents by the relationship between their draft and published versions. See publicationFilter.' },
]}
codePath="/api/restaurants"
codePathHighlights={['restaurants']}
diff --git a/docusaurus/docs/cms/api/rest/filters.md b/docusaurus/docs/cms/api/rest/filters.md
index bcbf8f2635..7ec5847760 100644
--- a/docusaurus/docs/cms/api/rest/filters.md
+++ b/docusaurus/docs/cms/api/rest/filters.md
@@ -89,7 +89,8 @@ By default, the filters can only be used from `find` endpoints generated by the
+
+Add the optional `publicationFilter` query parameter to query documents by the relationship between their draft and published versions, for example documents that were never published, or documents modified since they were last published. It combines with other query parameters, and `status` still decides whether you get the draft or the published version.
+
+
+
+The `publicationFilter` is a query parameter that, combined with [the `status` parameter](/cms/api/rest/status), can help you cover complex queries to find exactly what you need with the [REST API](/cms/api/rest).
+
+While `status` answers "do I want the draft or the published version?", the `publicationFilter` parameter answers a different question: "which documents do I want, based on how their draft and published versions relate?". This is useful for example to find documents that were never published, or documents whose draft has unsaved changes compared to what is live.
+
+The underlying model behind how `publicationFilter` works is handled on the back-end server by the [Document Service API](/cms/api/document-service/publication-filter). The present page follows the exact same structure and explanations, but with examples tailored for the REST API, so you don't have to jump between 2 different pages.
+
+:::prerequisites
+The [Draft & Publish](/cms/features/draft-and-publish) feature must be enabled on the content-type. If Draft & Publish is disabled, `publicationFilter` has no effect.
+:::
+
+## Available values {#values}
+
+`publicationFilter` accepts one of the following values:
+
+| Value | Selects |
+| ----- | ------- |
+| `never-published` | Documents never published in a given locale |
+| `never-published-document` | Documents never published in any locale |
+| `modified` | Documents whose draft was edited since it was last published |
+| `unmodified` | Documents whose draft has not changed since it was last published |
+| `published-without-draft` | Published documents with no draft counterpart |
+| `published-with-draft` | Published documents that also have a draft |
+| `has-published-version` | Documents that have both a draft and a published version |
+| `has-published-version-document` | Documents published in at least one locale
(useful when [i18n](/cms/features/internationalization) is enabled) |
+
+For detailed examples of how to use the `publicationFilter` values, including with the `status` parameter, see the [possible use cases](#use-cases) table.
+
+:::note
+* Unknown values return an HTTP `400` error.
+* Values ending in `-document` consider all locales of a document, which matters when [Internationalization (i18n)](/cms/features/internationalization) is enabled: for example, `never-published-document` excludes a document as soon as one of its locales is published. All other values consider one locale at a time. Without i18n, both variants behave the same.
+:::
+
+:::caution Caution: Different default behaviors for different APIs
+The REST API returns published versions of documents when `status` is omitted, so queries for draft-only values such as `never-published` need an explicit `status=draft`. The Document Service API returns draft versions instead (see [Document Service API: `publicationFilter`](/cms/api/document-service/publication-filter)).
+:::
+
+## Possible use cases {#use-cases}
+
+The following table lists many possible use cases, illustrating how the `status` and `publicationFilter` parameters can be combined to find exactly what you need with the REST API. Click a use case to jump to a complete example:
+
+| I want to… | Use `status` as… | Use `publicationFilter` as… |
+| ---------- | ------------------ | ----------------------------- |
+| [Find never published drafts](#never-published) | `draft` | `never-published` |
+| [Find drafts never published in any locale](#never-published-document) | `draft` | `never-published-document` |
+| [Find modified documents](#modified) | `draft` or `published` | `modified` |
+| [Find unmodified documents](#unmodified) | `draft` or `published` | `unmodified` |
+| [Find published documents without a draft](#published-without-draft) | `published` | `published-without-draft` |
+| [Find published documents with a draft](#published-with-draft) | `published` | `published-with-draft` |
+| [Find documents with a published version](#has-published-version) | `draft` or `published` | `has-published-version` |
+| [Find documents published in at least one locale](#has-published-version-document) | `draft` or `published` | `has-published-version-document` |
+
+:::note
+Pairing a value with the opposite `status` from the table above is valid but returns nothing rather than an error: for example, `never-published` with `status=published` returns an empty result, because these documents have no published version yet.
+:::
+
+## Examples
+
+The following section lists the most common use cases summed up in the [table](#use-cases) above.
+
+### Find never published drafts {#never-published}
+
+One of the most common use cases is to find the drafts that have never been published. To do so, pass `status=draft` and `publicationFilter=never-published`.
+
+This parameter combination works only on a given locale; to find these documents across all locales, [use `never-published-document`](#never-published-document) instead.
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published',
+ }, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find drafts never published in any locale {#never-published-document}
+
+`publicationFilter=never-published-document` returns documents that have never been published in any locale. It looks at the whole document across all its locales, not one locale at a time. To find these documents for a given locale only, [use `never-published`](#never-published) instead.
+
+A document counts as published as soon as one of its locales is published: the document is then left out, even the locales that only exist as a draft. The example below returns the draft versions of documents that were never published anywhere:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published-document',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "d41r46wac4xix5vpba7561at",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find modified documents {#modified}
+
+`publicationFilter=modified` selects documents whose draft has modified but unpublished changes. `status` then decides which version of those documents you get back.
+
+For instance, with `status=draft`, the query returns the draft versions:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant (updated)",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find unmodified documents {#unmodified}
+
+`publicationFilter=unmodified` selects documents whose draft has not changed since it was last published. `status` then decides which version of those documents you get back.
+
+For instance, with `status=draft`, the query returns the draft versions:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find published documents without a draft {#published-without-draft}
+
+`publicationFilter=published-without-draft` selects published documents that have no draft counterpart. It describes published rows, so REST returns them with the default `status=published`:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'published-without-draft',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "j0klm1n2o3p4q5r6s7t8u9v",
+ "name": "Legacy Restaurant",
+ "publishedAt": "2024-01-10T09:15:00.000Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find published documents with a draft {#published-with-draft}
+
+`publicationFilter=published-with-draft` selects published documents that also have a draft. It describes published rows, so REST returns them with the default `status=published`:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'published-with-draft',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find documents with a published version {#has-published-version}
+
+`publicationFilter=has-published-version` selects documents that have both a draft and a published version for the same locale. `status` then decides which version of those documents you get back. Unlike `published-without-draft`, it excludes published documents that have no draft counterpart.
+
+For instance, with `status=draft`, the query returns the draft versions:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'has-published-version',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+### Find documents published in at least one locale {#has-published-version-document}
+
+`publicationFilter=has-published-version-document` considers all locales, so it matches a document as soon as one of its locales is published. With `status=draft`, it returns the draft versions of every locale of those documents, including locales that were never published themselves:
+
+'`
+ },
+ {
+ label: "JavaScript",
+ code: `const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version-document',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);`
+ }
+ ]}
+ responses={[
+ {
+ status: 200,
+ statusText: "OK",
+ body: `{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}`
+ }
+ ]}
+/>
+
+## Combine with other parameters {#combine}
+
+`publicationFilter` can be combined with [`filters`](/cms/api/rest/filters), [`locale`](/cms/api/rest/locale), [`populate`](/cms/api/rest/populate-select), and other [REST parameters](/cms/api/rest/parameters). All conditions are applied together.
diff --git a/docusaurus/docs/cms/api/rest/sort-pagination.md b/docusaurus/docs/cms/api/rest/sort-pagination.md
index ccba21f8e9..d9eceaa638 100644
--- a/docusaurus/docs/cms/api/rest/sort-pagination.md
+++ b/docusaurus/docs/cms/api/rest/sort-pagination.md
@@ -57,7 +57,8 @@ You can sort by multiple fields by passing fields in a `sort` array.
'`,
+ },
{
label: 'JavaScript',
code: `const qs = require('qs');
const query = qs.stringify({
- status: 'draft',
+ status: 'draft',
}, {
- encodeValuesOnly: true, // prettify URL
+ encodeValuesOnly: true, // prettify URL
});
await request(\`/api/articles?\${query}\`);`,
diff --git a/docusaurus/docs/cms/features/draft-and-publish.md b/docusaurus/docs/cms/features/draft-and-publish.md
index 9f72034c4e..a08e29a1fe 100644
--- a/docusaurus/docs/cms/features/draft-and-publish.md
+++ b/docusaurus/docs/cms/features/draft-and-publish.md
@@ -185,16 +185,19 @@ To unpublish several entries at the same time:
### Usage with APIs
-Draft or published content can be requested, created, updated, and deleted using the `status` parameter through the various front-end APIs accessible from [Strapi's Content API](/cms/api/content-api):
+Draft or published content can be requested, created, updated, and deleted using the `status` parameter through the various front-end APIs accessible from [Strapi's Content API](/cms/api/content-api). To query documents by the relationship between their draft and published versions, such as never-published or modified documents, use the `publicationFilter` parameter (REST and GraphQL) or the equivalent Document Service API option.
+
+
-On the back-end server of Strapi, the Document Service API can also be used to interact with localized content:
+On the back-end server of Strapi, the Document Service API can also query and manage draft and published content:
+
diff --git a/docusaurus/sidebars.js b/docusaurus/sidebars.js
index 1b257ab057..b522c08805 100644
--- a/docusaurus/sidebars.js
+++ b/docusaurus/sidebars.js
@@ -202,6 +202,7 @@ const sidebars = {
'cms/api/rest/filters',
'cms/api/rest/locale',
'cms/api/rest/status',
+ 'cms/api/rest/publication-filter',
'cms/api/rest/populate-select',
'cms/api/rest/relations',
'cms/api/rest/sort-pagination',
@@ -234,6 +235,7 @@ const sidebars = {
'cms/api/document-service/populate',
'cms/api/document-service/sort-pagination',
'cms/api/document-service/status',
+ 'cms/api/document-service/publication-filter',
],
},
],
diff --git a/docusaurus/static/llms-code.txt b/docusaurus/static/llms-code.txt
index d26aaa78da..1101e292cc 100644
--- a/docusaurus/static/llms-code.txt
+++ b/docusaurus/static/llms-code.txt
@@ -4560,19 +4560,20 @@ File path: N/A
-# Using Sort & Pagination with the Document Service API
-Source: https://docs.strapi.io/cms/api/document-service/sort-pagination
+# Using publicationFilter with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/publication-filter
-## Sort on a single field
-Description: GET strapi.documents().findMany() — Sort on a single field
-(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-a-single-field)
+## Find never published drafts
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#never-published)
Language: JavaScript
File path: N/A
```javascript
-const documents = await strapi.documents("api::article.article").findMany({
- sort: "title:asc",
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'never-published',
});
```
@@ -4581,32 +4582,29 @@ File path: N/A
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "New Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
-## Sort on multiple fields
-Description: GET strapi.documents().findMany() — Sort on multiple fields
-(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-multiple-fields)
+## Find drafts never published in any locale
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#never-published-document)
Language: JavaScript
File path: N/A
```javascript
-const documents = await strapi.documents("api::article.article").findMany({
- sort: [{ title: "asc" }, { slug: "desc" }],
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'never-published-document',
});
```
@@ -4615,33 +4613,40 @@ File path: N/A
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "d41r46wac4xix5vpba7561at",
+ name: "New Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
-## Pagination
-Description: GET strapi.documents().findMany() — Pagination
-(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#pagination)
+## Find modified documents
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#modified)
Language: JavaScript
File path: N/A
```javascript
-const documents = await strapi.documents("api::article.article").findMany({
- limit: 10,
- start: 0,
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'modified',
+});
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```javascript
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'modified',
});
```
@@ -4650,37 +4655,46 @@ File path: N/A
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant (updated)",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
+---
+Language: JSON
+File path: N/A
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
+```
-# Using Draft & Publish with the Document Service API
-Source: https://docs.strapi.io/cms/api/document-service/status
-## count() only draft or published versions
-Description: To take into account only draft or published versions of documents while counting documents with the Document Service API, pass the corresponding status parameter:
-(Source: https://docs.strapi.io/cms/api/document-service/status#count)
+## Find unmodified documents
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#unmodified)
Language: JavaScript
File path: N/A
-```js
-// Count draft documents (also actually includes published documents)
-const draftsCount = await strapi.documents("api::restaurant.restaurant").count({
- status: 'draft'
+```javascript
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'unmodified',
});
```
@@ -4688,52 +4702,58 @@ const draftsCount = await strapi.documents("api::restaurant.restaurant").count({
Language: JavaScript
File path: N/A
-```js
-// Count only published documents
-const publishedCount = await strapi.documents("api::restaurant.restaurant").count({
- status: 'published'
+```javascript
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'unmodified',
});
```
-
-## Get the published version with `findOne()`
-Description: GET strapi.documents().findOne() — findOne() with status:
-(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findone)
-
-Language: JavaScript
+Language: JSON
File path: N/A
-```javascript
-await strapi.documents('api::restaurant.restaurant').findOne({
- documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
- status: 'published'
-});
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
```
+---
Language: JSON
File path: N/A
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
- locale: "en", // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
-## Get the published version with `findFirst()`
-Description: GET strapi.documents().findFirst() — findFirst() with status:
-(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findfirst)
+## Find published documents without a draft
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#published-without-draft)
Language: JavaScript
File path: N/A
```javascript
-const document = await strapi.documents("api::restaurant.restaurant").findFirst({
- status: 'published',
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'published-without-draft',
});
```
@@ -4741,26 +4761,30 @@ Language: JSON
File path: N/A
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
- locale: "en", // default locale
+[
+ {
+ documentId: "j0klm1n2o3p4q5r6s7t8u9v",
+ name: "Legacy Restaurant",
+ publishedAt: "2024-01-10T09:15:00.000Z",
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
-## Get the published version with `findMany()`
-Description: GET strapi.documents().findMany() — findMany() with status:
-(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findmany)
+## Find published documents with a draft
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#published-with-draft)
Language: JavaScript
File path: N/A
```javascript
-const documents = await strapi.documents("api::restaurant.restaurant").findMany({
- status: 'published'
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'published-with-draft',
});
```
@@ -4769,406 +4793,472 @@ File path: N/A
```json
[
- {
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
- locale: "en", // default locale
- // …
- }
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
// …
]
```
-## Create a draft and publish it
-Description: GET strapi.documents().create() — create() with status:
-(Source: https://docs.strapi.io/cms/api/document-service/status#create)
+## Find documents with a published version
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#has-published-version)
Language: JavaScript
File path: N/A
```javascript
-await strapi.documents('api::restaurant.restaurant').create({
- data: {
- name: "New Restaurant",
- },
- status: 'published',
-})
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'has-published-version',
+});
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```javascript
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'has-published-version',
+});
```
Language: JSON
File path: N/A
```json
-{
- documentId: "d41r46wac4xix5vpba7561at",
- name: "New Restaurant",
- publishedAt: "2024-03-14T17:29:03.399Z",
- locale: "en" // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
+---
+Language: JSON
+File path: N/A
+
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
+```
-## Update a draft and publish it
-Description: GET strapi.documents().update() — update() with status:
-(Source: https://docs.strapi.io/cms/api/document-service/status#update)
+
+## Find documents published in at least one locale
+Description: GET strapi.documents().findMany() — findMany() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#has-published-version-document)
Language: JavaScript
File path: N/A
```javascript
-await strapi.documents('api::restaurant.restaurant').update({
- documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
- data: {
- name: "Biscotte Restaurant (closed)",
- },
- status: 'published',
-})
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'has-published-version-document',
+});
```
Language: JSON
File path: N/A
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant (closed)",
- publishedAt: "2024-03-14T17:29:03.399Z",
- locale: "en" // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // published in at least one locale
+ // …
+ }
// …
-}
+]
```
-
-# Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service
-
-## Basic usage
-Description: The Entity Service is available through strapi.entityService:
-(Source: https://docs.strapi.io/cms/api/entity-service#basic-usage)
+## Use with `findOne()` and `findFirst()`
+Description: GET strapi.documents().findOne() — findOne() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#use-with-findone-and-findfirst)
Language: JavaScript
File path: N/A
-```js
-const entry = await strapi.entityService.findOne('api::article.article', 1, {
- populate: { someRelation: true },
+```javascript
+await strapi.documents('api::restaurant.restaurant').findOne({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ status: 'draft',
+ publicationFilter: 'never-published',
});
```
+Language: JSON
+File path: N/A
+```json
+null // the documentId exists, but the document does not match never-published
+```
-# Components and Dynamic Zones
-Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones
-## Creation
-Description: A component can be created while creating an entry with the Entity Service API:
-(Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#creation)
+## Count only matching documents
+Description: GET strapi.documents().count() — count() with publicationFilter:
+(Source: https://docs.strapi.io/cms/api/document-service/publication-filter#count)
Language: JavaScript
File path: N/A
-```js
-strapi.entityService.create('api::article.article', {
- data: {
- myComponent: {
- foo: 'bar',
- },
- },
-});
+```javascript
+const neverPublishedCount = await strapi
+ .documents('api::restaurant.restaurant')
+ .count({
+ status: 'draft',
+ publicationFilter: 'never-published',
+ });
```
-Language: JavaScript
+Language: JSON
File path: N/A
-```js
-strapi.entityService.create('api::article.article', {
- data: {
- myDynamicZone: [
- {
- __component: 'compo.type',
- foo: 'bar',
- },
- {
- __component: 'compo.type2',
- foo: 'bar',
- },
- ],
- },
-});
+```json
+12 // the number of never-published drafts
```
-## Update
-Description: A component can be updated while updating an entry with the Entity Service API.
-(Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#update)
+
+# Using Sort & Pagination with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/sort-pagination
+
+## Sort on a single field
+Description: GET strapi.documents().findMany() — Sort on a single field
+(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-a-single-field)
Language: JavaScript
File path: N/A
-```js
-strapi.entityService.update('api::article.article', 1, {
- data: {
- myComponent: {
- id: 1, // will update component with id: 1 (if not specified, would have deleted it and created a new one)
- foo: 'bar',
- },
- },
+```javascript
+const documents = await strapi.documents("api::article.article").findMany({
+ sort: "title:asc",
});
```
-Language: JavaScript
+Language: JSON
File path: N/A
-```js
-strapi.entityService.update('api::article.article', 1, {
- data: {
- myDynamicZone: [
- {
- // will update
- id: 2,
- __component: 'compo.type',
- foo: 'bar',
- },
- {
- // will add a new & delete old ones
- __component: 'compo.type2',
- foo: 'bar2',
- },
- ],
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
},
-});
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
```
-
-# CRUD operations
-Source: https://docs.strapi.io/cms/api/entity-service/crud
-
-## Example
-Description: | Parameter | Description | Type | | ---------- | --------------- | --------------- | | fields | Attributes to return | String[] | | populate | Relations, components and dynamic zones to populate | PopulateParameter | | locale | Locale code (for example fr-FR) when the Internationalization plugin is enabled.
-(Source: https://docs.strapi.io/cms/api/entity-service/crud#example)
+## Sort on multiple fields
+Description: GET strapi.documents().findMany() — Sort on multiple fields
+(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-multiple-fields)
Language: JavaScript
File path: N/A
-```js
-const entry = await strapi.entityService.findOne('api::article.article', 1, {
- fields: ['title', 'description'],
- populate: { category: true },
+```javascript
+const documents = await strapi.documents("api::article.article").findMany({
+ sort: [{ title: "asc" }, { slug: "desc" }],
});
```
-Language: JavaScript
+Language: JSON
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- fields: ['title', 'description'],
- filters: { title: 'Hello World' },
- sort: { createdAt: 'DESC' },
- populate: { category: true },
-});
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
+ },
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
```
+
+## Pagination
+Description: GET strapi.documents().findMany() — Pagination
+(Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#pagination)
+
Language: JavaScript
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- publicationState: 'preview',
- filters: {
- publishedAt: {
- $null: true,
- },
- },
+```javascript
+const documents = await strapi.documents("api::article.article").findMany({
+ limit: 10,
+ start: 0,
});
+```
-:::
+Language: JSON
+File path: N/A
-## create()
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
+ },
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
+```
-Creates one entry and returns it
-Syntax: `create(uid: string, parameters: Params)` ⇒ `Entry`
-### Parameters
+# Using Draft & Publish with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/status
-| Parameter | Description | Type |
-| ---------- | ----------- | ---------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Creates the entry for that locale. | `string` |
-| `data` | Input data | `Object` |
+## count() only draft or published versions
+Description: To take into account only draft or published versions of documents while counting documents with the Document Service API, pass the corresponding status parameter:
+(Source: https://docs.strapi.io/cms/api/document-service/status#count)
-
+Language: JavaScript
+File path: N/A
-### Example
+```js
+// Count draft documents (also actually includes published documents)
+const draftsCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'draft'
+});
```
+---
Language: JavaScript
File path: N/A
+```js
+// Count only published documents
+const publishedCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'published'
+});
```
-## update()
-Updates one entry and returns it.
+## Get the published version with `findOne()`
+Description: GET strapi.documents().findOne() — findOne() with status:
+(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findone)
-:::note
-`update()` only performs a partial update, so existing fields that are not included won't be replaced.
-:::
+Language: JavaScript
+File path: N/A
-Syntax: `update(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+```javascript
+await strapi.documents('api::restaurant.restaurant').findOne({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ status: 'published'
+});
+```
-
+Language: JSON
+File path: N/A
-### Parameters
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+}
+```
-| Parameter | Description | Type |
-| ---------- | ------------- | ---------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Updates the matching localized variant. | `string` |
-| `data` | Input data | `object` |
-### Example
-```
+## Get the published version with `findFirst()`
+Description: GET strapi.documents().findFirst() — findFirst() with status:
+(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findfirst)
Language: JavaScript
File path: N/A
+```javascript
+const document = await strapi.documents("api::restaurant.restaurant").findFirst({
+ status: 'published',
+});
```
-## delete()
+Language: JSON
+File path: N/A
-Deletes one entry and returns it.
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+}
+```
-Syntax: `delete(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
-### Parameters
+## Get the published version with `findMany()`
+Description: GET strapi.documents().findMany() — findMany() with status:
+(Source: https://docs.strapi.io/cms/api/document-service/status#get-the-published-version-with-findmany)
-| Parameter | Description | Type |
-| ---------- | --------- | -------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Deletes the localized variant that matches this locale. | `string` |
+Language: JavaScript
+File path: N/A
-### Example
+```javascript
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'published'
+});
```
+Language: JSON
+File path: N/A
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
+```
-# Filtering with the Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service/filter
-## $and
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#and)
+## Create a draft and publish it
+Description: GET strapi.documents().create() — create() with status:
+(Source: https://docs.strapi.io/cms/api/document-service/status#create)
Language: JavaScript
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $and: [
- {
- title: 'Hello World',
- },
- {
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
- ],
+```javascript
+await strapi.documents('api::restaurant.restaurant').create({
+ data: {
+ name: "New Restaurant",
},
-});
+ status: 'published',
+})
```
-Language: JavaScript
+Language: JSON
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: 'Hello World',
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
-});
+```json
+{
+ documentId: "d41r46wac4xix5vpba7561at",
+ name: "New Restaurant",
+ publishedAt: "2024-03-14T17:29:03.399Z",
+ locale: "en" // default locale
+ // …
+}
```
-## $or
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#or)
+## Update a draft and publish it
+Description: GET strapi.documents().update() — update() with status:
+(Source: https://docs.strapi.io/cms/api/document-service/status#update)
Language: JavaScript
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $or: [
- {
- title: 'Hello World',
- },
- {
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
- ],
+```javascript
+await strapi.documents('api::restaurant.restaurant').update({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ data: {
+ name: "Biscotte Restaurant (closed)",
},
-});
+ status: 'published',
+})
```
-
-## $not
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#not)
-
-Language: JavaScript
+Language: JSON
File path: N/A
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $not: {
- title: 'Hello World',
- },
- },
-});
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant (closed)",
+ publishedAt: "2024-03-14T17:29:03.399Z",
+ locale: "en" // default locale
+ // …
+}
```
----
+
+
+# Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service
+
+## Basic usage
+Description: The Entity Service is available through strapi.entityService:
+(Source: https://docs.strapi.io/cms/api/entity-service#basic-usage)
+
Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $not: {
- $contains: 'Hello World',
- },
- },
- },
+const entry = await strapi.entityService.findOne('api::article.article', 1, {
+ populate: { someRelation: true },
});
```
-## $eq
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#eq)
+
+# Components and Dynamic Zones
+Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones
+
+## Creation
+Description: A component can be created while creating an entry with the Entity Service API:
+(Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#creation)
Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $eq: 'Hello World',
+strapi.entityService.create('api::article.article', {
+ data: {
+ myComponent: {
+ foo: 'bar',
},
},
});
@@ -5178,82 +5268,92 @@ Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: 'Hello World',
+strapi.entityService.create('api::article.article', {
+ data: {
+ myDynamicZone: [
+ {
+ __component: 'compo.type',
+ foo: 'bar',
+ },
+ {
+ __component: 'compo.type2',
+ foo: 'bar',
+ },
+ ],
},
});
```
-## $eqi
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#eqi)
+## Update
+Description: A component can be updated while updating an entry with the Entity Service API.
+(Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#update)
Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $eqi: 'HELLO World',
+strapi.entityService.update('api::article.article', 1, {
+ data: {
+ myComponent: {
+ id: 1, // will update component with id: 1 (if not specified, would have deleted it and created a new one)
+ foo: 'bar',
},
},
});
```
-
-## $ne
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#ne)
-
Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $ne: 'ABCD',
- },
+strapi.entityService.update('api::article.article', 1, {
+ data: {
+ myDynamicZone: [
+ {
+ // will update
+ id: 2,
+ __component: 'compo.type',
+ foo: 'bar',
+ },
+ {
+ // will add a new & delete old ones
+ __component: 'compo.type2',
+ foo: 'bar2',
+ },
+ ],
},
});
```
-## $nei
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#nei)
+
+# CRUD operations
+Source: https://docs.strapi.io/cms/api/entity-service/crud
+
+## Example
+Description: | Parameter | Description | Type | | ---------- | --------------- | --------------- | | fields | Attributes to return | String[] | | populate | Relations, components and dynamic zones to populate | PopulateParameter | | locale | Locale code (for example fr-FR) when the Internationalization plugin is enabled.
+(Source: https://docs.strapi.io/cms/api/entity-service/crud#example)
Language: JavaScript
File path: N/A
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $nei: 'abcd',
- },
- },
+const entry = await strapi.entityService.findOne('api::article.article', 1, {
+ fields: ['title', 'description'],
+ populate: { category: true },
});
```
-
-## $in
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#in)
-
Language: JavaScript
File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $in: ['Hello', 'Hola', 'Bonjour'],
- },
- },
+ fields: ['title', 'description'],
+ filters: { title: 'Hello World' },
+ sort: { createdAt: 'DESC' },
+ populate: { category: true },
});
```
@@ -5262,34 +5362,95 @@ File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
+ publicationState: 'preview',
filters: {
- title: ['Hello', 'Hola', 'Bonjour'],
+ publishedAt: {
+ $null: true,
+ },
},
});
-```
+:::
-## $notIn
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#notin)
+## create()
-Language: JavaScript
-File path: N/A
+Creates one entry and returns it
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $notIn: ['Hello', 'Hola', 'Bonjour'],
- },
- },
-});
+Syntax: `create(uid: string, parameters: Params)` ⇒ `Entry`
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | ----------- | ---------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Creates the entry for that locale. | `string` |
+| `data` | Input data | `Object` |
+
+
+
+### Example
```
+Language: JavaScript
+File path: N/A
+
+```
-## $lt
+## update()
+
+Updates one entry and returns it.
+
+:::note
+`update()` only performs a partial update, so existing fields that are not included won't be replaced.
+:::
+
+Syntax: `update(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+
+
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | ------------- | ---------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Updates the matching localized variant. | `string` |
+| `data` | Input data | `object` |
+
+### Example
+```
+
+Language: JavaScript
+File path: N/A
+
+```
+
+## delete()
+
+Deletes one entry and returns it.
+
+Syntax: `delete(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | --------- | -------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Deletes the localized variant that matches this locale. | `string` |
+
+### Example
+```
+
+
+
+# Filtering with the Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service/filter
+
+## $and
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#lt)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#and)
Language: JavaScript
File path: N/A
@@ -5297,35 +5458,34 @@ File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $lt: 10,
- },
+ $and: [
+ {
+ title: 'Hello World',
+ },
+ {
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
+ },
+ ],
},
});
```
-
-## $lte
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#lte)
-
Language: JavaScript
File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $lte: 10,
- },
+ title: 'Hello World',
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
},
});
```
-## $gt
+## $or
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#gt)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#or)
Language: JavaScript
File path: N/A
@@ -5333,17 +5493,22 @@ File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $gt: 5,
- },
+ $or: [
+ {
+ title: 'Hello World',
+ },
+ {
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
+ },
+ ],
},
});
```
-## $gte
+## $not
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#gte)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#not)
Language: JavaScript
File path: N/A
@@ -5351,35 +5516,33 @@ File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $gte: 5,
+ $not: {
+ title: 'Hello World',
},
},
});
```
-
-## $between
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#between)
-
+---
Language: JavaScript
File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $between: [1, 20],
+ title: {
+ $not: {
+ $contains: 'Hello World',
+ },
},
},
});
```
-## $contains
+## $eq
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#contains)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#eq)
Language: JavaScript
File path: N/A
@@ -5388,34 +5551,27 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $contains: 'Hello',
+ $eq: 'Hello World',
},
},
});
```
-
-## $notContains
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontains)
-
Language: JavaScript
File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- title: {
- $notContains: 'Hello',
- },
+ title: 'Hello World',
},
});
```
-## $containsi
+## $eqi
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#containsi)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#eqi)
Language: JavaScript
File path: N/A
@@ -5424,16 +5580,16 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $containsi: 'hello',
+ $eqi: 'HELLO World',
},
},
});
```
-## $notContainsi
+## $ne
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontainsi)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#ne)
Language: JavaScript
File path: N/A
@@ -5442,16 +5598,16 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $notContainsi: 'hello',
+ $ne: 'ABCD',
},
},
});
```
-## $startsWith
+## $nei
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#startswith)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#nei)
Language: JavaScript
File path: N/A
@@ -5460,16 +5616,16 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $startsWith: 'ABCD',
+ $nei: 'abcd',
},
},
});
```
-## $endsWith
+## $in
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#endswith)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#in)
Language: JavaScript
File path: N/A
@@ -5478,34 +5634,27 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $endsWith: 'ABCD',
+ $in: ['Hello', 'Hola', 'Bonjour'],
},
},
});
```
-
-## $null
-Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#null)
-
Language: JavaScript
File path: N/A
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- title: {
- $null: true,
- },
+ title: ['Hello', 'Hola', 'Bonjour'],
},
});
```
-## $notNull
+## $notIn
Description: Example
-(Source: https://docs.strapi.io/cms/api/entity-service/filter#notnull)
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#notin)
Language: JavaScript
File path: N/A
@@ -5514,87 +5663,321 @@ File path: N/A
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $notNull: true,
+ $notIn: ['Hello', 'Hola', 'Bonjour'],
},
},
});
```
-
-# Ordering & Pagination with the Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service/order-pagination
-
-## Single
-Description: as a string to sort with the default ascending order, or - as an object to define both the field name and the order (i.e.
-(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#single)
+## $lt
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#lt)
Language: JavaScript
File path: N/A
```js
-strapi.entityService.findMany('api::article.article', {
- sort: 'id',
-});
-
-// single with direction
-strapi.entityService.findMany('api::article.article', {
- sort: { id: 'desc' },
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $lt: 10,
+ },
+ },
});
```
-## Multiple
-Description: as an array of strings to sort multiple fields using the default ascending order, or - as an array of objects to define both the field name and the order (i.e.
-(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#multiple)
+## $lte
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#lte)
Language: JavaScript
File path: N/A
```js
-strapi.entityService.findMany('api::article.article', {
- sort: ['publishDate', 'name'],
-});
-
-// multiple with direction
-strapi.entityService.findMany('api::article.article', {
- sort: [{ title: 'asc' }, { publishedAt: 'desc' }],
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $lte: 10,
+ },
+ },
});
```
-## Relational ordering
-Description: Fields can also be sorted based on fields from relations:
-(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#relational-ordering)
+## $gt
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#gt)
Language: JavaScript
File path: N/A
```js
-strapi.entityService.findMany('api::article.article', {
- sort: {
- author: {
- name: 'asc',
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $gt: 5,
},
},
});
```
-## Pagination
-Description: To paginate results returned by the Entity Service API, you can use the start and limit parameters:
-(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#pagination)
+## $gte
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#gte)
Language: JavaScript
File path: N/A
```js
-strapi.entityService.findMany('api::article.article', {
- start: 10,
- limit: 15,
-});
-```
-
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $gte: 5,
+ },
+ },
+});
+```
+
+
+## $between
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#between)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $between: [1, 20],
+ },
+ },
+});
+```
+
+
+## $contains
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#contains)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $contains: 'Hello',
+ },
+ },
+});
+```
+
+
+## $notContains
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontains)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notContains: 'Hello',
+ },
+ },
+});
+```
+
+
+## $containsi
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#containsi)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $containsi: 'hello',
+ },
+ },
+});
+```
+
+
+## $notContainsi
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontainsi)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notContainsi: 'hello',
+ },
+ },
+});
+```
+
+
+## $startsWith
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#startswith)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $startsWith: 'ABCD',
+ },
+ },
+});
+```
+
+
+## $endsWith
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#endswith)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $endsWith: 'ABCD',
+ },
+ },
+});
+```
+
+
+## $null
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#null)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $null: true,
+ },
+ },
+});
+```
+
+
+## $notNull
+Description: Example
+(Source: https://docs.strapi.io/cms/api/entity-service/filter#notnull)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notNull: true,
+ },
+ },
+});
+```
+
+
+
+# Ordering & Pagination with the Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service/order-pagination
+
+## Single
+Description: as a string to sort with the default ascending order, or - as an object to define both the field name and the order (i.e.
+(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#single)
+
+Language: JavaScript
+File path: N/A
+
+```js
+strapi.entityService.findMany('api::article.article', {
+ sort: 'id',
+});
+
+// single with direction
+strapi.entityService.findMany('api::article.article', {
+ sort: { id: 'desc' },
+});
+```
+
+
+## Multiple
+Description: as an array of strings to sort multiple fields using the default ascending order, or - as an array of objects to define both the field name and the order (i.e.
+(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#multiple)
+
+Language: JavaScript
+File path: N/A
+
+```js
+strapi.entityService.findMany('api::article.article', {
+ sort: ['publishDate', 'name'],
+});
+
+// multiple with direction
+strapi.entityService.findMany('api::article.article', {
+ sort: [{ title: 'asc' }, { publishedAt: 'desc' }],
+});
+```
+
+
+## Relational ordering
+Description: Fields can also be sorted based on fields from relations:
+(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#relational-ordering)
+
+Language: JavaScript
+File path: N/A
+
+```js
+strapi.entityService.findMany('api::article.article', {
+ sort: {
+ author: {
+ name: 'asc',
+ },
+ },
+});
+```
+
+
+## Pagination
+Description: To paginate results returned by the Entity Service API, you can use the start and limit parameters:
+(Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#pagination)
+
+Language: JavaScript
+File path: N/A
+
+```js
+strapi.entityService.findMany('api::article.article', {
+ start: 10,
+ limit: 15,
+});
+```
+
Language: JavaScript
File path: N/A
@@ -6053,19 +6436,51 @@ query Query($status: PublicationStatus) {
```
-## Create a new document
-Description: The following example creates a new document for the "Restaurant" content-type and returns its name and documentId:
-(Source: https://docs.strapi.io/cms/api/graphql#create-a-new-document)
+## Filter with publicationFilter
+Description: :::caution When status is omitted, GraphQL defaults to PUBLISHED before applying publicationFilter (same as REST).
+(Source: https://docs.strapi.io/cms/api/graphql#publication-filter)
Language: GRAPHQL
-File path: N/A
+File path: Example:
```graphql
-mutation CreateRestaurant($data: RestaurantInput!) {
- createRestaurant(data: {
- name: "Pizzeria Arrivederci"
- }) {
- name
+query Query($status: PublicationStatus, $publicationFilter: PublicationFilter) {
+ restaurants(status: DRAFT, publicationFilter: NEVER_PUBLISHED) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+---
+Language: GRAPHQL
+File path: Example:
+
+```graphql
+query Query {
+ restaurants(publicationFilter: MODIFIED) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+
+## Create a new document
+Description: The following example creates a new document for the "Restaurant" content-type and returns its name and documentId:
+(Source: https://docs.strapi.io/cms/api/graphql#create-a-new-document)
+
+Language: GRAPHQL
+File path: N/A
+
+```graphql
+mutation CreateRestaurant($data: RestaurantInput!) {
+ createRestaurant(data: {
+ name: "Pizzeria Arrivederci"
+ }) {
+ name
documentId
}
}
@@ -8289,7 +8704,7 @@ const response = await fetch(
Source: https://docs.strapi.io/cms/api/rest/filters
## Example: Find users having 'John' as a first name
-Description: GET /api/users — Find users having
+Description: GET /api/users?filters[username][$eq]=John — Find users having
(Source: https://docs.strapi.io/cms/api/rest/filters#example-find-users-having-john-as-a-first-name)
Language: Bash
@@ -8349,7 +8764,7 @@ File path: N/A
## Example: Find multiple restaurants with ids 3, 6,8
-Description: GET /api/restaurants — Find multiple restaurants with ids 3, 6, 8
+Description: GET /api/restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8 — Find multiple restaurants with ids 3, 6, 8
(Source: https://docs.strapi.io/cms/api/rest/filters#example-find-multiple-restaurants-with-ids-3-6-8)
Language: Bash
@@ -8406,7 +8821,7 @@ File path: N/A
## Complex filtering
-Description: GET /api/books — Find books with 2 possible dates and a specific author
+Description: GET /api/books?filters[$and][0][$or][0][date][$eq]=2020-01-01&filters[$and][0][$or][1][date][$eq]=2020-01-02&filters[$and][1][author][name][$eq]=Kai%20doe — Find books with 2 possible dates and a specific author
(Source: https://docs.strapi.io/cms/api/rest/filters#complex-filtering)
Language: Bash
@@ -8480,7 +8895,7 @@ File path: N/A
## Deep filtering
-Description: GET /api/restaurants — Find restaurants owned by a chef who belongs to a 5-star restaurant
+Description: GET /api/restaurants?filters[chef][restaurants][stars][$eq]=5 — Find restaurants owned by a chef who belongs to a 5-star restaurant
(Source: https://docs.strapi.io/cms/api/rest/filters#deep-filtering)
Language: Bash
@@ -9797,123 +10212,646 @@ Language: Bash
File path: N/A
```bash
-GET /api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
+GET /api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
+```
+
+
+## Single types
+Description: GET /api/homepage?locale=fr — Get a document in a specific locale (single type)
+(Source: https://docs.strapi.io/cms/api/rest/locale#get-one-single-type)
+
+Language: Bash
+File path: N/A
+
+```bash
+GET /api/homepage?locale=fr
+```
+
+
+## For the default locale
+Description: POST /api/restaurants — Create a document for the default locale
+(Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-default-locale)
+
+Language: Bash
+File path: N/A
+
+```bash
+POST http://localhost:1337/api/restaurants
+
+{
+ "data": {
+ "Name": "Oplato"
+ }
+}
+```
+
+
+## For a specific locale
+Description: POST /api/restaurants?locale=fr — Create a document for a specific locale
+(Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-specific-locale)
+
+Language: Bash
+File path: N/A
+
+```bash
+POST http://localhost:1337/api/restaurants?locale=fr
+
+{
+ "data": {
+ "Name": "She's Cake"
+ }
+}
+```
+
+
+## In a collection type
+Description: PUT /api/restaurants/:documentId?locale=fr — Create or update a locale version (collection type)
+(Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-collection-type)
+
+Language: Bash
+File path: N/A
+
+```bash
+PUT http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
+
+{
+ "data": {
+ "Name": "She's Cake in French"
+ }
+}
+```
+
+---
+Language: Bash
+File path: N/A
+
+```bash
+DELETE /api/restaurants/abcdefghijklmno456?locale=fr
+```
+
+
+## In a single type
+Description: PUT /api/homepage?locale=fr — Create or update a locale version (single type)
+(Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-single-type)
+
+Language: Bash
+File path: N/A
+
+```bash
+PUT http://localhost:1337/api/homepage?locale=fr
+
+{
+ "data": {
+ "Title": "Page d'accueil"
+ }
+}
+```
+
+---
+Language: Bash
+File path: N/A
+
+```bash
+DELETE /api/homepage?locale=fr
+```
+
+
+
+# Populate and Select
+Source: https://docs.strapi.io/cms/api/rest/populate-select
+
+## Field selection
+Description: GET /api/restaurants?fields[0]=name&fields[1]=description — Return only name and description fields
+(Source: https://docs.strapi.io/cms/api/rest/populate-select#field-selection)
+
+Language: Bash
+File path: N/A
+
+```bash
+GET /api/restaurants?fields[0]=name&fields[1]=description
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ fields: ['name', 'description'],
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
+ }
+);
+
+await request(`/api/users?${query}`);
+```
+
+
+## Populate with field selection
+Description: GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url — Populate with field selection
+(Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-field-selection)
+
+Language: Bash
+File path: N/A
+
+```bash
+GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ fields: ['title', 'slug'],
+ populate: {
+ headerImage: {
+ fields: ['name', 'url'],
+ },
+ },
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
+ }
+);
+
+await request(`/api/articles?${query}`);
+```
+
+
+## Populate with filtering
+Description: GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars — Populate with filtering
+(Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-filtering)
+
+Language: Bash
+File path: N/A
+
+```bash
+GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ populate: {
+ categories: {
+ sort: ['name:asc'],
+ filters: {
+ name: {
+ $eq: 'Cars',
+ },
+ },
+ },
+ },
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
+ }
+);
+
+await request(`/api/articles?${query}`);
+```
+
+
+
+# REST API: publicationFilter
+Source: https://docs.strapi.io/cms/api/rest/publication-filter
+
+## Find never published drafts
+Description: GET /api/restaurants?status=draft&publicationFilter=never-published — Get draft restaurants that have never been published for their locale
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#never-published)
+
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published' \\
+ -H 'Authorization: Bearer '
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published',
+ }, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
+```
+
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+## Find drafts never published in any locale
+Description: GET /api/restaurants?status=draft&publicationFilter=never-published-document — Get drafts of restaurants never published in any locale
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#never-published-document)
+
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published-document' \\
+ -H 'Authorization: Bearer '
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published-document',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
+```
+
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "d41r46wac4xix5vpba7561at",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+## Find modified documents
+Description: GET /api/restaurants?status=draft&publicationFilter=modified — Get the draft versions of modified restaurants
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#modified)
+
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=modified' \\
+ -H 'Authorization: Bearer '
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
+```
+
+---
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/restaurants?publicationFilter=modified' \\
+ -H 'Authorization: Bearer '
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
+```
+
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant (updated)",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+## Find unmodified documents
+Description: GET /api/restaurants?status=draft&publicationFilter=unmodified — Get the draft versions of unmodified restaurants
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#unmodified)
+
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=unmodified' \\
+ -H 'Authorization: Bearer '
```
+---
+Language: JavaScript
+File path: N/A
-## Single types
-Description: GET /api/homepage?locale=fr — Get a document in a specific locale (single type)
-(Source: https://docs.strapi.io/cms/api/rest/locale#get-one-single-type)
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
+```
+---
Language: Bash
File path: N/A
```bash
-GET /api/homepage?locale=fr
+curl 'http://localhost:1337/api/restaurants?publicationFilter=unmodified' \\
+ -H 'Authorization: Bearer '
```
+---
+Language: JavaScript
+File path: N/A
-## For the default locale
-Description: POST /api/restaurants — Create a document for the default locale
-(Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-default-locale)
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
-Language: Bash
-File path: N/A
+await request(`/api/restaurants?${query}`);
+```
-```bash
-POST http://localhost:1337/api/restaurants
+Language: JSON
+File path: N/A
+```json
{
- "data": {
- "Name": "Oplato"
- }
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
}
```
-
-## For a specific locale
-Description: POST /api/restaurants?locale=fr — Create a document for a specific locale
-(Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-specific-locale)
-
-Language: Bash
+---
+Language: JSON
File path: N/A
-```bash
-POST http://localhost:1337/api/restaurants?locale=fr
-
+```json
{
- "data": {
- "Name": "She's Cake"
- }
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
}
```
-## In a collection type
-Description: PUT /api/restaurants/:documentId?locale=fr — Create or update a locale version (collection type)
-(Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-collection-type)
+## Find published documents without a draft
+Description: GET /api/restaurants?publicationFilter=published-without-draft — Get published restaurants with no draft for the same locale
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#published-without-draft)
Language: Bash
File path: N/A
```bash
-PUT http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
-
-{
- "data": {
- "Name": "She's Cake in French"
- }
-}
+curl 'http://localhost:1337/api/restaurants?publicationFilter=published-without-draft' \\
+ -H 'Authorization: Bearer '
```
---
-Language: Bash
+Language: JavaScript
File path: N/A
-```bash
-DELETE /api/restaurants/abcdefghijklmno456?locale=fr
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'published-without-draft',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
```
+Language: JSON
+File path: N/A
-## In a single type
-Description: PUT /api/homepage?locale=fr — Create or update a locale version (single type)
-(Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-single-type)
+```json
+{
+ "data": [
+ {
+ "documentId": "j0klm1n2o3p4q5r6s7t8u9v",
+ "name": "Legacy Restaurant",
+ "publishedAt": "2024-01-10T09:15:00.000Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+## Find published documents with a draft
+Description: GET /api/restaurants?publicationFilter=published-with-draft — Get published restaurants that also have a draft for the same locale
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#published-with-draft)
Language: Bash
File path: N/A
```bash
-PUT http://localhost:1337/api/homepage?locale=fr
-
-{
- "data": {
- "Title": "Page d'accueil"
- }
-}
+curl 'http://localhost:1337/api/restaurants?publicationFilter=published-with-draft' \\
+ -H 'Authorization: Bearer '
```
---
-Language: Bash
+Language: JavaScript
File path: N/A
-```bash
-DELETE /api/homepage?locale=fr
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'published-with-draft',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/restaurants?${query}`);
```
+Language: JSON
+File path: N/A
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
-# Populate and Select
-Source: https://docs.strapi.io/cms/api/rest/populate-select
-## Field selection
-Description: GET /api/restaurants — Return only name and description fields
-(Source: https://docs.strapi.io/cms/api/rest/populate-select#field-selection)
+## Find documents with a published version
+Description: GET /api/restaurants?status=draft&publicationFilter=has-published-version — Get the draft versions of restaurants that also have a published version
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#has-published-version)
Language: Bash
File path: N/A
```bash
-GET /api/restaurants?fields[0]=name&fields[1]=description
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version' \\
+ -H 'Authorization: Bearer '
```
---
@@ -9922,28 +10860,23 @@ File path: N/A
```js
const qs = require('qs');
-const query = qs.stringify(
- {
- fields: ['name', 'description'],
- },
- {
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(`/api/users?${query}`);
+await request(`/api/restaurants?${query}`);
```
-
-## Populate with field selection
-Description: GET /api/articles — Populate with field selection
-(Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-field-selection)
-
+---
Language: Bash
File path: N/A
```bash
-GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url
+curl 'http://localhost:1337/api/restaurants?publicationFilter=has-published-version' \\
+ -H 'Authorization: Bearer '
```
---
@@ -9952,33 +10885,75 @@ File path: N/A
```js
const qs = require('qs');
-const query = qs.stringify(
- {
- fields: ['title', 'slug'],
- populate: {
- headerImage: {
- fields: ['name', 'url'],
- },
- },
- },
- {
+const query = qs.stringify({
+ publicationFilter: 'has-published-version',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(`/api/articles?${query}`);
+await request(`/api/restaurants?${query}`);
```
+Language: JSON
+File path: N/A
-## Populate with filtering
-Description: GET /api/articles — Populate with filtering
-(Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-filtering)
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+## Find documents published in at least one locale
+Description: GET /api/restaurants?status=draft&publicationFilter=has-published-version-document — Get the drafts of restaurants published in at least one locale
+(Source: https://docs.strapi.io/cms/api/rest/publication-filter#has-published-version-document)
Language: Bash
File path: N/A
```bash
-GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version-document' \\
+ -H 'Authorization: Bearer '
```
---
@@ -9987,25 +10962,38 @@ File path: N/A
```js
const qs = require('qs');
-const query = qs.stringify(
- {
- populate: {
- categories: {
- sort: ['name:asc'],
- filters: {
- name: {
- $eq: 'Cars',
- },
- },
- },
- },
- },
- {
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version-document',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(`/api/articles?${query}`);
+await request(`/api/restaurants?${query}`);
+```
+
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
```
@@ -10316,7 +11304,7 @@ File path: N/A
Source: https://docs.strapi.io/cms/api/rest/sort-pagination
## Example: Sort using 2 fields
-Description: GET /api/restaurants — Sort using 2 fields
+Description: GET /api/restaurants?sort[0]=Description&sort[1]=Name — Sort using 2 fields
(Source: https://docs.strapi.io/cms/api/rest/sort-pagination#example-sort-using-2-fields)
Language: Bash
@@ -10391,7 +11379,7 @@ File path: N/A
## Example: Sort using 2 fields and set the order
-Description: GET /api/restaurants — Sort using 2 fields and set the order
+Description: GET /api/restaurants?sort[0]=Description:asc&sort[1]=Name:desc — Sort using 2 fields and set the order
(Source: https://docs.strapi.io/cms/api/rest/sort-pagination#example-sort-using-2-fields-and-set-the-order)
Language: Bash
@@ -10466,7 +11454,7 @@ File path: N/A
## Pagination by page
-Description: GET /api/articles — Pagination by page
+Description: GET /api/articles?pagination[page]=1&pagination[pageSize]=10 — Pagination by page
(Source: https://docs.strapi.io/cms/api/rest/sort-pagination#pagination-by-page)
Language: Bash
@@ -10515,7 +11503,7 @@ File path: N/A
## Pagination by offset
-Description: GET /api/articles — Pagination by offset
+Description: GET /api/articles?pagination[start]=0&pagination[limit]=10 — Pagination by offset
(Source: https://docs.strapi.io/cms/api/rest/sort-pagination#pagination-by-offset)
Language: Bash
@@ -10570,15 +11558,24 @@ Source: https://docs.strapi.io/cms/api/rest/status
Description: GET /api/articles?status=draft — Get draft versions of restaurants
(Source: https://docs.strapi.io/cms/api/rest/status#status)
+Language: Bash
+File path: N/A
+
+```bash
+curl 'http://localhost:1337/api/articles?status=draft' \\
+ -H 'Authorization: Bearer '
+```
+
+---
Language: JavaScript
File path: N/A
```js
const qs = require('qs');
const query = qs.stringify({
- status: 'draft',
+ status: 'draft',
}, {
- encodeValuesOnly: true, // prettify URL
+ encodeValuesOnly: true, // prettify URL
});
await request(`/api/articles?${query}`);
diff --git a/docusaurus/static/llms-full.txt b/docusaurus/static/llms-full.txt
index d8920cf35e..71a9e4b77c 100644
--- a/docusaurus/static/llms-full.txt
+++ b/docusaurus/static/llms-full.txt
@@ -4675,6 +4675,7 @@ Find a document matching the passed documentId and parameters. If only a documen
- `documentId` (ID, required): Document id
- `locale` (String or undefined): Locale of the document to find. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#find-one).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: publication status. Can be `published` or `draft`. Default: `draft`. See status docs (/cms/api/document-service/status#find-one).
+- `publicationFilter` (String): If Draft & Publish (/cms/features/draft-and-publish) is enabled: select documents by how their draft and published versions relate, before applying `status`. See publicationFilter docs (/cms/api/document-service/publication-filter).
- `fields` (Object): Select fields (/cms/api/document-service/fields#findone) to return. Defaults to all fields (except those not populated by default).
- `populate` (Object): Populate (/cms/api/document-service/populate) results with additional fields. Default: `null`.
@@ -4706,6 +4707,7 @@ Find the first document matching the parameters. By default, findFirst() returns
**Parameters:**
- `locale` (String or undefined): Locale of the documents to find. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#find-first).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: publication status. Can be `published` or `draft`. Default: `draft`. See status docs (/cms/api/document-service/status#find-first).
+- `publicationFilter` (String): If Draft & Publish (/cms/features/draft-and-publish) is enabled: select documents by how their draft and published versions relate, before applying `status`. See publicationFilter docs (/cms/api/document-service/publication-filter).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#findfirst) to return. Defaults to all fields (except those not populated by default).
- `populate` (Object): Populate (/cms/api/document-service/populate) results with additional fields. Default: `null`.
@@ -4761,6 +4763,7 @@ Find documents matching the parameters. When no parameter is passed, findMany()
**Parameters:**
- `locale` (String or undefined): Locale of the documents to find. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#find-many).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: publication status. Can be `published` or `draft`. Default: `draft`. See status docs (/cms/api/document-service/status#find-many).
+- `publicationFilter` (String): If Draft & Publish (/cms/features/draft-and-publish) is enabled: select documents by how their draft and published versions relate, before applying `status`. See publicationFilter docs (/cms/api/document-service/publication-filter).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#findmany) to return. Defaults to all fields (except those not populated by default).
- `populate` (Object): Populate (/cms/api/document-service/populate) results with additional fields. Default: `null`.
@@ -5096,6 +5099,7 @@ Count how many documents match the parameters. If no parameter is passed, the co
**Parameters:**
- `locale` (String or null): Locale of the documents to count. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#count).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: publication status. `published` to count only published documents, `draft` to count draft documents (returns all documents). Default: `draft`. See status docs (/cms/api/document-service/status#count).
+- `publicationFilter` (String): If Draft & Publish (/cms/features/draft-and-publish) is enabled: select documents by how their draft and published versions relate, before applying `status`. See publicationFilter docs (/cms/api/document-service/publication-filter).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
**Generic example:**
@@ -5121,7 +5125,7 @@ strapi.documents('api::restaurant.restaurant').count({ filters: { name: { $start
:::note
Since published documents necessarily also have a draft counterpart, a published document is still counted as having a draft version.
-This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. There currently is no way to prevent already published documents from being counted.
+This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. To count only never-published drafts, pass a [`publicationFilter`](/cms/api/document-service/publication-filter) value such as `'never-published'` or `'never-published-document'`.
:::
@@ -7050,191 +7054,273 @@ strapi.documents("api::article.article").delete({
-# Using Sort & Pagination with the Document Service API
-Source: https://docs.strapi.io/cms/api/document-service/sort-pagination
+# Using publicationFilter with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/publication-filter
-# Document Service API: Sorting and paginating results
+# Document Service API: `publicationFilter`
-Use the Document Service API's `sort` and pagination parameters to order query results by single or multiple fields and control result limits with `limit` and `start`.
+Use the optional `publicationFilter` parameter to query documents by the relationship between their draft and published versions, for example drafts that were never published, or entries modified since they were last published. It works with `findOne()`, `findFirst()`, `findMany()`, and `count()`, and combines with other query parameters. `status` still decides whether you get the draft or the published version.
-The [Document Service API](/cms/api/document-service) offers the ability to sort and paginate query results.
+The `publicationFilter` is a parameter that, combined with [the `status` parameter](/cms/api/document-service/status), can help you cover complex queries to find exactly what you need with the [Document Service API](/cms/api/document-service).
-## Sort
+While `status` answers "do I want the draft or the published version?", the `publicationFilter` parameter answers a different question: "which documents do I want, based on how their draft and published versions relate?". This is useful for example to find drafts that were never published, or entries whose draft has unsaved changes compared to what is live.
-To sort results returned by the Document Service API, include the `sort` parameter with queries.
+:::prerequisites
+The [Draft & Publish](/cms/features/draft-and-publish) feature must be enabled on the content-type. If Draft & Publish is disabled, `publicationFilter` has no effect.
+:::
-### Sort on a single field
+## Available values {#values}
-#### GET strapi.documents().findMany() — Sort on a single field
+`publicationFilter` accepts one of the following values:
-Sort results based on a single field using a string value.
+| Value | Selects |
+| ----- | ------- |
+| `never-published` | Documents never published in a given locale |
+| `never-published-document` | Documents never published in any locale |
+| `modified` | Documents whose draft was edited since it was last published |
+| `unmodified` | Documents whose draft has not changed since it was last published |
+| `published-without-draft` | Published documents with no draft counterpart |
+| `published-with-draft` | Published documents that also have a draft |
+| `has-published-version` | Documents that have both a draft and a published version |
+| `has-published-version-document` | Documents published in at least one locale
(useful when [i18n](/cms/features/internationalization) is enabled) |
+
+For detailed examples of how to use the `publicationFilter` values, including with the `status` parameter, see the [possible use cases](#use-cases) table.
+
+:::note
+* Unknown values raise a validation error.
+* Values ending in `-document` consider all locales of a document, which matters when [Internationalization (i18n)](/cms/features/internationalization) is enabled: for example, `never-published-document` excludes a document as soon as one of its locales is published. All other values consider one locale at a time. Without i18n, both variants behave the same.
+:::
+
+:::caution Caution: Different default behaviors for different APIs
+The Document Service API returns draft versions of documents when `status` is omitted, while REST and GraphQL return the published ones instead, so REST API queries need an explicit `status` (see [REST API: `publicationFilter`](/cms/api/rest/publication-filter)).
+:::
+
+## Possible use cases {#use-cases}
+
+The following table lists many possible use cases, illustrating how the `status` and `publicationFilter` parameters can be combined to find exactly what you need with the Document Service API. Click a use case to jump to a complete example:
+
+| I want to… | Use `status` as… | Use `publicationFilter` as… |
+| ---------- | ------------------ | ----------------------------- |
+| [Find never published drafts](#never-published) | `draft` | `never-published` |
+| [Find drafts never published in any locale](#never-published-document) | `draft` | `never-published-document` |
+| [Find modified documents](#modified) | `draft` or `published` | `modified` |
+| [Find unmodified documents](#unmodified) | `draft` or `published` | `unmodified` |
+| [Find published documents without a draft](#published-without-draft) | `published` | `published-without-draft` |
+| [Find published documents with a draft](#published-with-draft) | `published` | `published-with-draft` |
+| [Find documents with a published version](#has-published-version) | `draft` or `published` | `has-published-version` |
+| [Find documents published in at least one locale](#has-published-version-document) | `draft` or `published` | `has-published-version-document` |
+| [Use with `findOne()` and `findFirst()`](#find-one-find-first) | `draft` or `published` | any value |
+| [Count only matching documents](#count) | `draft` or `published` | any value |
+
+:::note
+Pairing a value with the opposite `status` from the table above is valid but returns nothing rather than an error: for example, `never-published` with `status: 'published'` returns an empty result, because these documents have no published version yet.
+:::
+
+## Examples
+
+The following section lists the most common use cases summed up in the [table](#use-cases) above.
+
+### Find never published drafts {#never-published}
+
+One of the most common use cases is to find the drafts that have never been published. To do so, pass `status: 'draft'` and `publicationFilter: 'never-published'`.
+
+This parameter combination works only on a given locale; to find these documents across all locales, [use `never-published-document`](#never-published-document) instead.
+
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
+
+Return the drafts that have never been published for their locale.
**JavaScript:**
```
-const documents = await strapi.documents("api::article.article").findMany({
- sort: "title:asc",
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'never-published',
});
```
**Response 200 OK:**
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "New Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
-### Sort on multiple fields
+### Find drafts never published in any locale {#never-published-document}
-#### GET strapi.documents().findMany() — Sort on multiple fields
+`publicationFilter: never-published-document` returns documents that have never been published in any locale. It looks at the whole document across all its locales, not one locale at a time. To find these documents for a given locale only, [use `never-published`](#never-published) instead.
-Sort results on multiple fields by passing an array of sort objects.
+A document counts as published as soon as one of its locales is published: the document is then left out, even the locales that only exist as a draft. The example below returns the draft versions of documents that were never published anywhere:
+
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
+
+Return the drafts of documents never published in any locale.
**JavaScript:**
```
-const documents = await strapi.documents("api::article.article").findMany({
- sort: [{ title: "asc" }, { slug: "desc" }],
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'never-published-document',
});
```
**Response 200 OK:**
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "d41r46wac4xix5vpba7561at",
+ name: "New Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
-## Pagination
+### Find modified documents {#modified}
-#### GET strapi.documents().findMany() — Pagination
+`publicationFilter: modified` selects documents whose draft has modified but unpublished changes. `status` then decides which version of those documents you get back.
-Paginate results using the limit and start parameters.
+For instance, with `status: 'draft'`, the query returns the draft versions:
+
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
+
+Return the draft versions of documents with unpublished changes.
**JavaScript:**
```
-const documents = await strapi.documents("api::article.article").findMany({
- limit: 10,
- start: 0,
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'modified',
});
```
**Response 200 OK:**
```json
[
- {
- "documentId": "cjld2cjxh0000qzrmn831i7rn",
- "title": "Test Article",
- "slug": "test-article",
- "body": "Test 1"
- },
- {
- "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
- "title": "Test Article 2",
- "slug": "test-article-2",
- "body": "Test 2"
- }
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant (updated)",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
+ // …
]
```
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-# Using Draft & Publish with the Document Service API
-Source: https://docs.strapi.io/cms/api/document-service/status
-
-# Document Service API: Usage with Draft & Publish
+Return the currently live versions of documents with unpublished changes.
-Use the `status` parameter with the Document Service API to retrieve published or draft versions of documents, count documents by status, and directly publish documents during creation or updates.
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'modified',
+});
+```
-By default the [Document Service API](/cms/api/document-service) returns the draft version of a document when the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled. This page describes how to use the `status` parameter to:
+**Response 200 OK:**
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
+```
-- return the published version of a document,
-- count documents depending on their status,
-- and directly publish a document while creating it or updating it.
+### Find unmodified documents {#unmodified}
-:::note
-Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter.
-:::
+`publicationFilter: unmodified` selects documents whose draft has not changed since it was last published. `status` then decides which version of those documents you get back.
-## Get the published version with `findOne()` {#find-one}
+For instance, with `status: 'draft'`, the query returns the draft versions:
-#### GET strapi.documents().findOne() — findOne() with status:
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-Return the published version of a specific document.
+Return the draft versions of documents unchanged since their last publication.
**JavaScript:**
```
-await strapi.documents('api::restaurant.restaurant').findOne({
- documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
- status: 'published'
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'unmodified',
});
```
**Response 200 OK:**
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
- locale: "en", // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
-## Get the published version with `findFirst()` {#find-first}
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
-#### GET strapi.documents().findFirst() — findFirst() with status:
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-Return the published version of the first matching document.
+Return the currently live versions of documents unchanged since their last publication.
**JavaScript:**
```
-const document = await strapi.documents("api::restaurant.restaurant").findFirst({
- status: 'published',
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'unmodified',
});
```
**Response 200 OK:**
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
- locale: "en", // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
-## Get the published version with `findMany()` {#find-many}
+### Find published documents without a draft {#published-without-draft}
-#### GET strapi.documents().findMany() — findMany() with status:
+`publicationFilter: published-without-draft` selects published documents that have no draft counterpart.
-Return the published versions of all matching documents.
+`published-without-draft` must be paired with `status: 'published'`:
+
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
+
+Return published documents with no matching draft version for the same locale.
**JavaScript:**
```
-const documents = await strapi.documents("api::restaurant.restaurant").findMany({
- status: 'published'
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'published-without-draft',
});
```
@@ -7242,9 +7328,9 @@ const documents = await strapi.documents("api::restaurant.restaurant").findMany(
```json
[
{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant",
- publishedAt: "2024-03-14T15:40:45.330Z",
+ documentId: "j0klm1n2o3p4q5r6s7t8u9v",
+ name: "Legacy Restaurant",
+ publishedAt: "2024-01-10T09:15:00.000Z",
locale: "en", // default locale
// …
}
@@ -7252,745 +7338,897 @@ const documents = await strapi.documents("api::restaurant.restaurant").findMany(
]
```
-## `count()` only draft or published versions {#count}
+### Find published documents with a draft {#published-with-draft}
-To take into account only draft or published versions of documents while [counting documents](/cms/api/document-service#count) with the Document Service API, pass the corresponding `status` parameter:
+`publicationFilter: published-with-draft` selects published documents that also have a draft. Unlike `published-without-draft`, it keeps only the published documents that still have a draft counterpart.
-```js
-// Count draft documents (also actually includes published documents)
-const draftsCount = await strapi.documents("api::restaurant.restaurant").count({
- status: 'draft'
+`published-with-draft` must be paired with `status: 'published'`:
+
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
+
+Return published documents that also have a matching draft version for the same locale.
+
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'published-with-draft',
});
```
-```js
-// Count only published documents
-const publishedCount = await strapi.documents("api::restaurant.restaurant").count({
- status: 'published'
-});
+**Response 200 OK:**
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
```
-:::note
-Since published documents necessarily also have a draft counterpart, a published document is still counted as having a draft version.
+### Find documents with a published version {#has-published-version}
-This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. There currently is no way to prevent already published documents from being counted.
-:::
+`publicationFilter: has-published-version` selects documents that have both a draft and a published version for the same locale. `status` then decides which version of those documents you get back. Unlike `published-without-draft`, it excludes published documents that have no draft counterpart.
-## Create a draft and publish it {#create}
+For instance, with `status: 'draft'`, the query returns the draft versions:
-#### GET strapi.documents().create() — create() with status:
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-Create a new document and immediately publish it.
+Return the draft versions of documents that also have a published version for the same locale.
**JavaScript:**
```
-await strapi.documents('api::restaurant.restaurant').create({
- data: {
- name: "New Restaurant",
- },
- status: 'published',
-})
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'has-published-version',
+});
```
**Response 200 OK:**
```json
-{
- documentId: "d41r46wac4xix5vpba7561at",
- name: "New Restaurant",
- publishedAt: "2024-03-14T17:29:03.399Z",
- locale: "en" // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
-## Update a draft and publish it {#update}
+
+With `status: 'published'`, the same query returns the currently live version of those documents instead:
-#### GET strapi.documents().update() — update() with status:
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-Update an existing document and immediately publish it.
+Return the currently live versions of documents that also have a published version for the same locale.
**JavaScript:**
```
-await strapi.documents('api::restaurant.restaurant').update({
- documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
- data: {
- name: "Biscotte Restaurant (closed)",
- },
- status: 'published',
-})
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'published',
+ publicationFilter: 'has-published-version',
+});
```
**Response 200 OK:**
```json
-{
- documentId: "a1b2c3d4e5f6g7h8i9j0klm",
- name: "Biscotte Restaurant (closed)",
- publishedAt: "2024-03-14T17:29:03.399Z",
- locale: "en" // default locale
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
// …
-}
+]
```
+### Find documents published in at least one locale {#has-published-version-document}
+`publicationFilter: has-published-version-document` considers all locales, so it matches a document as soon as one of its locales is published. With `status: 'draft'`, it returns the draft versions of every locale of those documents, including locales that were never published themselves:
-# Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service
-
-# Entity Service API
+#### GET strapi.documents().findMany() — findMany() with publicationFilter:
-The Entity Service API is a backend layer that handles complex content structures like components and dynamic zones, providing CRUD operations, filtering, populating relations, and pagination via `strapi.entityService`.
+Return the draft versions of documents published in at least one locale.
-:::caution
-The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
-:::
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').findMany({
+ status: 'draft',
+ publicationFilter: 'has-published-version-document',
+});
+```
-:::prerequisites
-Before diving deeper into the Entity Service API documentation, it is recommended that you read the following introductions:
-- the [backend customization introduction](/cms/backend-customization),
-- and the [Content APIs introduction](/cms/api/content-api).
-:::
+**Response 200 OK:**
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null,
+ locale: "en", // published in at least one locale
+ // …
+ }
+ // …
+]
+```
-The Strapi backend provides an Entity Service API, built on top of the [Query Engine API](/cms/api/query-engine/). The Entity Service is the layer that handles Strapi's complex content structures like [components](/cms/backend-customization/models#components-json) and [dynamic zones](/cms/backend-customization/models#dynamic-zones), and uses the Query Engine API under the hood to execute database queries.
+### Use with `findOne()` and `findFirst()` {#find-one-find-first}
-:::strapi Entity Service API vs. Query Engine API
+If the requested document (and locale, when applicable) does not match the filter, `findOne()` and `findFirst()` return `null` even when the `documentId` exists:
-Strapi v4 offers several layers to interact with the backend and build your queries:
+#### GET strapi.documents().findOne() — findOne() with publicationFilter:
-* The Document Service API is the recommended API to interact with your application's database. The Document Service is the layer that handles Strapi's document model and the complex content structures like components and dynamic zones, which the lower-level layers are not aware of.
-* The Query Engine API interacts with the database layer at a lower level and is used under the hood to execute database queries. It gives unrestricted internal access to the database layer, but should be used only if the Document Service API does not cover your use case.
-* If you need direct access to `knex` functions, use `strapi.db.connection`.
+Return the document only if it matches the filter, null otherwise.
-:::
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').findOne({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ status: 'draft',
+ publicationFilter: 'never-published',
+});
+```
-:::info Disambiguation: Services vs. Entity Service
-While [services](/cms/backend-customization/services) can use the Entity Service API, services and the Entity Service API are not directly related. You can find more information about the core elements of the Strapi back end in the [back-end customization](/cms/backend-customization) documentation.
-:::
+**Response 200 OK:**
+```json
+null // the documentId exists, but the document does not match never-published
+```
-## Basic usage
+### Count only matching documents {#count}
-The Entity Service is available through `strapi.entityService`:
+Without `publicationFilter`, `count({ status: 'draft' })` counts every draft version, including drafts whose document already has a published version. Add `publicationFilter` to count only the documents that match a given value (see the [`status` documentation](/cms/api/document-service/status#count)):
-```js
-const entry = await strapi.entityService.findOne('api::article.article', 1, {
- populate: { someRelation: true },
-});
+#### GET strapi.documents().count() — count() with publicationFilter:
+
+Count only the documents that match a given value.
+
+**JavaScript:**
+```
+const neverPublishedCount = await strapi
+ .documents('api::restaurant.restaurant')
+ .count({
+ status: 'draft',
+ publicationFilter: 'never-published',
+ });
```
-## Available operations
+**Response 200 OK:**
+```json
+12 // the number of never-published drafts
+```
-The Entity Service API allows the following operations on entities:
+## Combination with other parameters {#combine}
-- [CRUD operations](/cms/api/entity-service/crud): Create, read, update, and delete entities with the Entity Service API.
-- [Filters](/cms/api/entity-service/filter): Get exactly what you need by filtering entities with your Entity Service API queries.
-- [Populate](/cms/api/entity-service/populate): Get additional data with your Entity Service API queries by populating relations.
-- [Order & Pagination](/cms/api/entity-service/order-pagination): Sort and paginate the results of your Entity Service API queries.
-- [Components/Dynamic Zones](/cms/api/entity-service/components-dynamic-zones): Create and update components and dynamic zones with your Entity Service API queries.
+`publicationFilter` is combined with other query parameters as a logical `AND`, including [`filters`](/cms/api/document-service/filters) and [`populate`](/cms/api/document-service/populate). When populating draft & publish relations, nested queries inherit the same filter logic.
+## Content Manager mapping {#content-manager}
+In the Content Manager, the **Draft (never published)** list filter maps to `status: 'draft'` and `publicationFilter: 'never-published-document'` (document-scoped, not the per-locale `never-published`).
-# Components and Dynamic Zones
-Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones
-# Creating components and dynamic zones with the Entity Service API
-Use the Entity Service API to create and update components and dynamic zones while creating or updating entries. Components are single objects while dynamic zones are lists of components with a `__component` type identifier.
+# Using Sort & Pagination with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/sort-pagination
-:::caution
-The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
-:::
+# Document Service API: Sorting and paginating results
-The [Entity Service](/cms/api/entity-service) is the layer that handles [components](/cms/backend-customization/models#components-json) and [dynamic zones](/cms/backend-customization/models#dynamic-zones) logic. With the Entity Service API, components and dynamic zones can be [created](#creation) and [updated](#update) while creating or updating entries.
+Use the Document Service API's `sort` and pagination parameters to order query results by single or multiple fields and control result limits with `limit` and `start`.
-## Creation
+The [Document Service API](/cms/api/document-service) offers the ability to sort and paginate query results.
-A [component](/cms/backend-customization/models#components-json) can be created while creating an entry with the Entity Service API:
+## Sort
-```js
-strapi.entityService.create('api::article.article', {
- data: {
- myComponent: {
- foo: 'bar',
- },
- },
+To sort results returned by the Document Service API, include the `sort` parameter with queries.
+
+### Sort on a single field
+
+#### GET strapi.documents().findMany() — Sort on a single field
+
+Sort results based on a single field using a string value.
+
+**JavaScript:**
+```
+const documents = await strapi.documents("api::article.article").findMany({
+ sort: "title:asc",
});
```
-A [dynamic zone](/cms/backend-customization/models#dynamic-zones) (i.e. a list of components) can be created while creating an entry with the Entity Service API:
-
-```js
-strapi.entityService.create('api::article.article', {
- data: {
- myDynamicZone: [
- {
- __component: 'compo.type',
- foo: 'bar',
- },
- {
- __component: 'compo.type2',
- foo: 'bar',
- },
- ],
+**Response 200 OK:**
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
},
-});
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
```
-## Update
+### Sort on multiple fields
-A [component](/cms/backend-customization/models#components-json) can be updated while updating an entry with the Entity Service API. If a component `id` is specified, the component is updated, otherwise the old one is deleted and a new one is created:
+#### GET strapi.documents().findMany() — Sort on multiple fields
-```js
-strapi.entityService.update('api::article.article', 1, {
- data: {
- myComponent: {
- id: 1, // will update component with id: 1 (if not specified, would have deleted it and created a new one)
- foo: 'bar',
- },
- },
+Sort results on multiple fields by passing an array of sort objects.
+
+**JavaScript:**
+```
+const documents = await strapi.documents("api::article.article").findMany({
+ sort: [{ title: "asc" }, { slug: "desc" }],
});
```
-A [dynamic zone](/cms/backend-customization/models#dynamic-zones) (i.e. a list of components) can be updated while updating an entry with the Entity Service API. If a component `id` is specified, the component is updated, otherwise the old one is deleted and a new one is created:
-
-```js
-strapi.entityService.update('api::article.article', 1, {
- data: {
- myDynamicZone: [
- {
- // will update
- id: 2,
- __component: 'compo.type',
- foo: 'bar',
- },
- {
- // will add a new & delete old ones
- __component: 'compo.type2',
- foo: 'bar2',
- },
- ],
+**Response 200 OK:**
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
},
-});
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
```
+## Pagination
+#### GET strapi.documents().findMany() — Pagination
-# CRUD operations
-Source: https://docs.strapi.io/cms/api/entity-service/crud
+Paginate results using the limit and start parameters.
-# CRUD operations with the Entity Service API
+**JavaScript:**
+```
+const documents = await strapi.documents("api::article.article").findMany({
+ limit: 10,
+ start: 0,
+});
+```
-The Entity Service API performs CRUD operations on content through `findOne()`, `findMany()`, `create()`, `update()`, and `delete()` methods, supporting filtering, pagination, relations, and localization.
+**Response 200 OK:**
+```json
+[
+ {
+ "documentId": "cjld2cjxh0000qzrmn831i7rn",
+ "title": "Test Article",
+ "slug": "test-article",
+ "body": "Test 1"
+ },
+ {
+ "documentId": "cjld2cjxh0001qzrm5q1j5q7m",
+ "title": "Test Article 2",
+ "slug": "test-article-2",
+ "body": "Test 2"
+ }
+]
+```
-:::caution
-The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
-:::
-The [Entity Service API](/cms/api/entity-service) is built on top of the the [Query Engine API](/cms/api/query-engine) and uses it to perform CRUD operations on entities.
-The `uid` parameter used in function calls for this API is a `string` built with the following format: `[category]::[content-type]` where `category` is one of: `admin`, `plugin` or `api`.
+# Using Draft & Publish with the Document Service API
+Source: https://docs.strapi.io/cms/api/document-service/status
-Examples:
-- A correct `uid` to get users of the Strapi admin panel is `admin::user`.
-- A possible `uid` for the Upload plugin could be `plugin::upload.file`.
-- As the `uid`s for user-defined custom content-types follow the `api::[content-type]` syntax, if a content-type `article` exists, it is referenced by `api::article.article`.
+# Document Service API: Usage with Draft & Publish
-:::tip
-Run the [`strapi content-types:list`](/cms/cli#strapi-content-typeslist) command in a terminal to display all possible content-types' `uid`s for a specific Strapi instance.
-:::
+Use the `status` parameter with the Document Service API to retrieve published or draft versions of documents, count documents by status, and directly publish documents during creation or updates.
-## findOne()
+By default the [Document Service API](/cms/api/document-service) returns the draft version of a document when the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled. This page describes how to use the `status` parameter to:
-Finds the first entry matching the parameters.
+- return the published version of a document,
+- count documents depending on their status,
+- and directly publish a document while creating it or updating it.
-Syntax: `findOne(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+:::note
+Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter.
+:::
-### Parameters
+To select documents by how their draft and published versions relate (never-published, modified, and others), see [Document Service API: `publicationFilter`](/cms/api/document-service/publication-filter).
-| Parameter | Description | Type |
-| ---------- | --------------- | --------------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code (for example `fr-FR`) when the Internationalization plugin is enabled. Targets the localized variant instead of the default locale. | `string` |
+## Get the published version with `findOne()` {#find-one}
-### Example
+#### GET strapi.documents().findOne() — findOne() with status:
-```js
-const entry = await strapi.entityService.findOne('api::article.article', 1, {
- fields: ['title', 'description'],
- populate: { category: true },
+Return the published version of a specific document.
+
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').findOne({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ status: 'published'
});
```
-## findMany()
-
-Finds entries matching the parameters.
-
-Syntax: `findMany(uid: string, parameters: Params)` ⇒ `Entry[]`
-
-### Parameters
-
-| Parameter | Description | Type |
-| ----------- | ------ | -------------- |
-| `fields` | Attributes to return | `String[]` |
-| `filters` | [Filters](/cms/api/entity-service/filter) to use | [`FiltersParameters`](/cms/api/entity-service/filter) |
-| `start` | Number of entries to skip (see [pagination](/cms/api/entity-service/order-pagination#pagination)) | `Number` |
-| `limit` | Number of entries to return (see [pagination](/cms/api/entity-service/order-pagination#pagination)) | `Number` |
-| `sort` | [Order](/cms/api/entity-service/order-pagination) definition | [`OrderByParameter`](/cms/api/entity-service/order-pagination) |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `publicationState` | Publication state, can be:- `live` to return only published entries
- `preview` to return both draft entries & published entries (default)
| `PublicationStateParameter` |
-| `locale` | Locale code when the Internationalization plugin is enabled. Restricts results to that locale (omit for the default locale). | `string` |
-
-### Example
-
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- fields: ['title', 'description'],
- filters: { title: 'Hello World' },
- sort: { createdAt: 'DESC' },
- populate: { category: true },
-});
+**Response 200 OK:**
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+}
```
-
+## Get the published version with `findFirst()` {#find-first}
-:::tip
-To retrieve only draft entries, combine the `preview` publication state and the `publishedAt` fields:
+#### GET strapi.documents().findFirst() — findFirst() with status:
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- publicationState: 'preview',
- filters: {
- publishedAt: {
- $null: true,
- },
- },
+Return the published version of the first matching document.
+
+**JavaScript:**
+```
+const document = await strapi.documents("api::restaurant.restaurant").findFirst({
+ status: 'published',
});
+```
-:::
+**Response 200 OK:**
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+}
+```
-## create()
+## Get the published version with `findMany()` {#find-many}
-Creates one entry and returns it
+#### GET strapi.documents().findMany() — findMany() with status:
-Syntax: `create(uid: string, parameters: Params)` ⇒ `Entry`
+Return the published versions of all matching documents.
-### Parameters
+**JavaScript:**
+```
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'published'
+});
+```
-| Parameter | Description | Type |
-| ---------- | ----------- | ---------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Creates the entry for that locale. | `string` |
-| `data` | Input data | `Object` |
+**Response 200 OK:**
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: "2024-03-14T15:40:45.330Z",
+ locale: "en", // default locale
+ // …
+ }
+ // …
+]
+```
-:::tip
-In the `data` object, relations can be managed with the `connect`, `disconnect`, and `set` parameters using the syntax described for the REST API (see [managing relations](/cms/api/rest/relations)).
-:::
+## `count()` only draft or published versions {#count}
-### Example
+To take into account only draft or published versions of documents while [counting documents](/cms/api/document-service#count) with the Document Service API, pass the corresponding `status` parameter:
```js
-const entry = await strapi.entityService.create('api::article.article', {
- data: {
- title: 'My Article',
- },
+// Count draft documents (also actually includes published documents)
+const draftsCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'draft'
});
```
-## update()
-
-Updates one entry and returns it.
+```js
+// Count only published documents
+const publishedCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'published'
+});
+```
:::note
-`update()` only performs a partial update, so existing fields that are not included won't be replaced.
-:::
-
-Syntax: `update(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+Since published documents necessarily also have a draft counterpart, a published document is still counted as having a draft version.
-:::tip
-In the `data` object, relations can be managed with the `connect`, `disconnect`, and `set` parameters using the syntax described for the REST API (see [managing relations](/cms/api/rest/relations)).
+This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. To count only never-published drafts, [pass a `publicationFilter` value](/cms/api/document-service/publication-filter) such as `'never-published'` or `'never-published-document'`.
:::
-### Parameters
+## Create a draft and publish it {#create}
-| Parameter | Description | Type |
-| ---------- | ------------- | ---------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Updates the matching localized variant. | `string` |
-| `data` | Input data | `object` |
+#### GET strapi.documents().create() — create() with status:
-### Example
+Create a new document and immediately publish it.
-```js
-const entry = await strapi.entityService.update('api::article.article', 1, {
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').create({
data: {
- title: 'xxx',
+ name: "New Restaurant",
},
-});
+ status: 'published',
+})
```
-## delete()
-
-Deletes one entry and returns it.
+**Response 200 OK:**
+```json
+{
+ documentId: "d41r46wac4xix5vpba7561at",
+ name: "New Restaurant",
+ publishedAt: "2024-03-14T17:29:03.399Z",
+ locale: "en" // default locale
+ // …
+}
+```
-Syntax: `delete(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+## Update a draft and publish it {#update}
-### Parameters
+#### GET strapi.documents().update() — update() with status:
-| Parameter | Description | Type |
-| ---------- | --------- | -------- |
-| `fields` | Attributes to return | `String[]` |
-| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
-| `locale` | Locale code when the Internationalization plugin is enabled. Deletes the localized variant that matches this locale. | `string` |
+Update an existing document and immediately publish it.
-### Example
+**JavaScript:**
+```
+await strapi.documents('api::restaurant.restaurant').update({
+ documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
+ data: {
+ name: "Biscotte Restaurant (closed)",
+ },
+ status: 'published',
+})
+```
-```js
-const entry = await strapi.entityService.delete('api::article.article', 1);
+**Response 200 OK:**
+```json
+{
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant (closed)",
+ publishedAt: "2024-03-14T17:29:03.399Z",
+ locale: "en" // default locale
+ // …
+}
```
-# Filtering with the Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service/filter
+# Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service
-# Filtering with the Entity Service API
+# Entity Service API
-Filter Entity Service API query results using logical operators (`$and`, `$or`, `$not`) and attribute operators (`$eq`, `$contains`, `$gt`, `$between`, etc.) with the `filters` parameter in `findMany()`.
+The Entity Service API is a backend layer that handles complex content structures like components and dynamic zones, providing CRUD operations, filtering, populating relations, and pagination via `strapi.entityService`.
:::caution
The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
:::
-The [Entity Service API](/cms/api/entity-service) offers the ability to filter results found with its [findMany()](/cms/api/entity-service/crud#findmany) method.
+:::prerequisites
+Before diving deeper into the Entity Service API documentation, it is recommended that you read the following introductions:
+- the [backend customization introduction](/cms/backend-customization),
+- and the [Content APIs introduction](/cms/api/content-api).
+:::
-Results are filtered with the `filters` parameter that accepts [logical operators](#logical-operators) and [attribute operators](#attribute-operators). Every operator should be prefixed with `$`.
+The Strapi backend provides an Entity Service API, built on top of the [Query Engine API](/cms/api/query-engine/). The Entity Service is the layer that handles Strapi's complex content structures like [components](/cms/backend-customization/models#components-json) and [dynamic zones](/cms/backend-customization/models#dynamic-zones), and uses the Query Engine API under the hood to execute database queries.
-:::strapi Deep filtering with the various APIs
-For examples of how to deep filter with the various APIs, please refer to [this blog article](https://strapi.io/blog/deep-filtering-alpha-26).
-:::
+:::strapi Entity Service API vs. Query Engine API
-## Logical operators
+Strapi v4 offers several layers to interact with the backend and build your queries:
-### `$and`
+* The Document Service API is the recommended API to interact with your application's database. The Document Service is the layer that handles Strapi's document model and the complex content structures like components and dynamic zones, which the lower-level layers are not aware of.
+* The Query Engine API interacts with the database layer at a lower level and is used under the hood to execute database queries. It gives unrestricted internal access to the database layer, but should be used only if the Document Service API does not cover your use case.
+* If you need direct access to `knex` functions, use `strapi.db.connection`.
-All nested conditions must be `true`.
+:::
-**Example**
+:::info Disambiguation: Services vs. Entity Service
+While [services](/cms/backend-customization/services) can use the Entity Service API, services and the Entity Service API are not directly related. You can find more information about the core elements of the Strapi back end in the [back-end customization](/cms/backend-customization) documentation.
+:::
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $and: [
- {
- title: 'Hello World',
- },
- {
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
- ],
- },
-});
-```
+## Basic usage
-`$and` will be used implicitly when passing an object with nested conditions:
+The Entity Service is available through `strapi.entityService`:
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: 'Hello World',
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
+const entry = await strapi.entityService.findOne('api::article.article', 1, {
+ populate: { someRelation: true },
});
```
-### `$or`
+## Available operations
-One or many nested conditions must be `true`.
+The Entity Service API allows the following operations on entities:
-**Example**
+- [CRUD operations](/cms/api/entity-service/crud): Create, read, update, and delete entities with the Entity Service API.
+- [Filters](/cms/api/entity-service/filter): Get exactly what you need by filtering entities with your Entity Service API queries.
+- [Populate](/cms/api/entity-service/populate): Get additional data with your Entity Service API queries by populating relations.
+- [Order & Pagination](/cms/api/entity-service/order-pagination): Sort and paginate the results of your Entity Service API queries.
+- [Components/Dynamic Zones](/cms/api/entity-service/components-dynamic-zones): Create and update components and dynamic zones with your Entity Service API queries.
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $or: [
- {
- title: 'Hello World',
- },
- {
- createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
- },
- ],
- },
-});
-```
-
-### `$not`
-
-Negates the nested conditions.
-
-**Example**
-
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- $not: {
- title: 'Hello World',
- },
- },
-});
-```
-:::note
-`$not` can be used as:
-- a logical operator (e.g. in `filters: { $not: { // conditions… }}`)
-- [an attribute operator](#not-1) (e.g. in `filters: { attribute-name: $not: { … } }`).
-:::
+# Components and Dynamic Zones
+Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones
-:::tip
-`$and`, `$or` and `$not` operators are nestable inside of another `$and`, `$or` or `$not` operator.
-:::
+# Creating components and dynamic zones with the Entity Service API
-## Attribute Operators
+Use the Entity Service API to create and update components and dynamic zones while creating or updating entries. Components are single objects while dynamic zones are lists of components with a `__component` type identifier.
:::caution
-Using these operators may give different results depending on the database's implementation, as the comparison is handled by the database and not by Strapi.
+The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
:::
-### `$not`
+The [Entity Service](/cms/api/entity-service) is the layer that handles [components](/cms/backend-customization/models#components-json) and [dynamic zones](/cms/backend-customization/models#dynamic-zones) logic. With the Entity Service API, components and dynamic zones can be [created](#creation) and [updated](#update) while creating or updating entries.
-Negates the nested condition(s).
+## Creation
-**Example**
+A [component](/cms/backend-customization/models#components-json) can be created while creating an entry with the Entity Service API:
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $not: {
- $contains: 'Hello World',
- },
+strapi.entityService.create('api::article.article', {
+ data: {
+ myComponent: {
+ foo: 'bar',
},
},
});
```
-### `$eq`
-
-Attribute equals input value.
-
-**Example**
+A [dynamic zone](/cms/backend-customization/models#dynamic-zones) (i.e. a list of components) can be created while creating an entry with the Entity Service API:
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $eq: 'Hello World',
- },
+strapi.entityService.create('api::article.article', {
+ data: {
+ myDynamicZone: [
+ {
+ __component: 'compo.type',
+ foo: 'bar',
+ },
+ {
+ __component: 'compo.type2',
+ foo: 'bar',
+ },
+ ],
},
});
```
-`$eq` can be omitted:
+## Update
+
+A [component](/cms/backend-customization/models#components-json) can be updated while updating an entry with the Entity Service API. If a component `id` is specified, the component is updated, otherwise the old one is deleted and a new one is created:
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: 'Hello World',
+strapi.entityService.update('api::article.article', 1, {
+ data: {
+ myComponent: {
+ id: 1, // will update component with id: 1 (if not specified, would have deleted it and created a new one)
+ foo: 'bar',
+ },
},
});
```
-### `$eqi`
-
-Attribute equals input value (case-insensitive).
-
-**Example**
+A [dynamic zone](/cms/backend-customization/models#dynamic-zones) (i.e. a list of components) can be updated while updating an entry with the Entity Service API. If a component `id` is specified, the component is updated, otherwise the old one is deleted and a new one is created:
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $eqi: 'HELLO World',
- },
+strapi.entityService.update('api::article.article', 1, {
+ data: {
+ myDynamicZone: [
+ {
+ // will update
+ id: 2,
+ __component: 'compo.type',
+ foo: 'bar',
+ },
+ {
+ // will add a new & delete old ones
+ __component: 'compo.type2',
+ foo: 'bar2',
+ },
+ ],
},
});
```
-### `$ne`
-
-Attribute does not equal input value.
-**Example**
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $ne: 'ABCD',
- },
- },
-});
-```
+# CRUD operations
+Source: https://docs.strapi.io/cms/api/entity-service/crud
-### `$nei`
+# CRUD operations with the Entity Service API
-Attribute does not equal input value (case-insensitive).
+The Entity Service API performs CRUD operations on content through `findOne()`, `findMany()`, `create()`, `update()`, and `delete()` methods, supporting filtering, pagination, relations, and localization.
-**Example**
+:::caution
+The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
+:::
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $nei: 'abcd',
- },
- },
-});
-```
+The [Entity Service API](/cms/api/entity-service) is built on top of the the [Query Engine API](/cms/api/query-engine) and uses it to perform CRUD operations on entities.
-### `$in`
+The `uid` parameter used in function calls for this API is a `string` built with the following format: `[category]::[content-type]` where `category` is one of: `admin`, `plugin` or `api`.
-Attribute is contained in the input list.
+Examples:
+- A correct `uid` to get users of the Strapi admin panel is `admin::user`.
+- A possible `uid` for the Upload plugin could be `plugin::upload.file`.
+- As the `uid`s for user-defined custom content-types follow the `api::[content-type]` syntax, if a content-type `article` exists, it is referenced by `api::article.article`.
-**Example**
+:::tip
+Run the [`strapi content-types:list`](/cms/cli#strapi-content-typeslist) command in a terminal to display all possible content-types' `uid`s for a specific Strapi instance.
+:::
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $in: ['Hello', 'Hola', 'Bonjour'],
- },
- },
-});
-```
+## findOne()
-`$in` can be omitted when passing an array of values:
+Finds the first entry matching the parameters.
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: ['Hello', 'Hola', 'Bonjour'],
- },
-});
-```
+Syntax: `findOne(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
-### `$notIn`
+### Parameters
-Attribute is not contained in the input list.
+| Parameter | Description | Type |
+| ---------- | --------------- | --------------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code (for example `fr-FR`) when the Internationalization plugin is enabled. Targets the localized variant instead of the default locale. | `string` |
-**Example**
+### Example
```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- title: {
- $notIn: ['Hello', 'Hola', 'Bonjour'],
- },
- },
+const entry = await strapi.entityService.findOne('api::article.article', 1, {
+ fields: ['title', 'description'],
+ populate: { category: true },
});
```
-### `$lt`
-
-Attribute is less than the input value.
+## findMany()
-**Example**
+Finds entries matching the parameters.
-```js
-const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- rating: {
- $lt: 10,
- },
- },
-});
-```
+Syntax: `findMany(uid: string, parameters: Params)` ⇒ `Entry[]`
-### `$lte`
+### Parameters
-Attribute is less than or equal to the input value.
+| Parameter | Description | Type |
+| ----------- | ------ | -------------- |
+| `fields` | Attributes to return | `String[]` |
+| `filters` | [Filters](/cms/api/entity-service/filter) to use | [`FiltersParameters`](/cms/api/entity-service/filter) |
+| `start` | Number of entries to skip (see [pagination](/cms/api/entity-service/order-pagination#pagination)) | `Number` |
+| `limit` | Number of entries to return (see [pagination](/cms/api/entity-service/order-pagination#pagination)) | `Number` |
+| `sort` | [Order](/cms/api/entity-service/order-pagination) definition | [`OrderByParameter`](/cms/api/entity-service/order-pagination) |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `publicationState` | Publication state, can be:- `live` to return only published entries
- `preview` to return both draft entries & published entries (default)
| `PublicationStateParameter` |
+| `locale` | Locale code when the Internationalization plugin is enabled. Restricts results to that locale (omit for the default locale). | `string` |
-**Example**
+### Example
```js
const entries = await strapi.entityService.findMany('api::article.article', {
- filters: {
- rating: {
- $lte: 10,
- },
- },
+ fields: ['title', 'description'],
+ filters: { title: 'Hello World' },
+ sort: { createdAt: 'DESC' },
+ populate: { category: true },
});
```
-### `$gt`
-
-Attribute is greater than the input value.
+
-**Example**
+:::tip
+To retrieve only draft entries, combine the `preview` publication state and the `publishedAt` fields:
```js
const entries = await strapi.entityService.findMany('api::article.article', {
+ publicationState: 'preview',
filters: {
- rating: {
- $gt: 5,
+ publishedAt: {
+ $null: true,
},
},
});
-```
-### `$gte`
+:::
-Attribute is greater than or equal to the input value.
+## create()
+
+Creates one entry and returns it
+
+Syntax: `create(uid: string, parameters: Params)` ⇒ `Entry`
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | ----------- | ---------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Creates the entry for that locale. | `string` |
+| `data` | Input data | `Object` |
+
+:::tip
+In the `data` object, relations can be managed with the `connect`, `disconnect`, and `set` parameters using the syntax described for the REST API (see [managing relations](/cms/api/rest/relations)).
+:::
+
+### Example
+
+```js
+const entry = await strapi.entityService.create('api::article.article', {
+ data: {
+ title: 'My Article',
+ },
+});
+```
+
+## update()
+
+Updates one entry and returns it.
+
+:::note
+`update()` only performs a partial update, so existing fields that are not included won't be replaced.
+:::
+
+Syntax: `update(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+
+:::tip
+In the `data` object, relations can be managed with the `connect`, `disconnect`, and `set` parameters using the syntax described for the REST API (see [managing relations](/cms/api/rest/relations)).
+:::
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | ------------- | ---------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Updates the matching localized variant. | `string` |
+| `data` | Input data | `object` |
+
+### Example
+
+```js
+const entry = await strapi.entityService.update('api::article.article', 1, {
+ data: {
+ title: 'xxx',
+ },
+});
+```
+
+## delete()
+
+Deletes one entry and returns it.
+
+Syntax: `delete(uid: string, id: ID, parameters: Params)` ⇒ `Entry`
+
+### Parameters
+
+| Parameter | Description | Type |
+| ---------- | --------- | -------- |
+| `fields` | Attributes to return | `String[]` |
+| `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) |
+| `locale` | Locale code when the Internationalization plugin is enabled. Deletes the localized variant that matches this locale. | `string` |
+
+### Example
+
+```js
+const entry = await strapi.entityService.delete('api::article.article', 1);
+```
+
+
+
+# Filtering with the Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service/filter
+
+# Filtering with the Entity Service API
+
+Filter Entity Service API query results using logical operators (`$and`, `$or`, `$not`) and attribute operators (`$eq`, `$contains`, `$gt`, `$between`, etc.) with the `filters` parameter in `findMany()`.
+
+:::caution
+The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
+:::
+
+The [Entity Service API](/cms/api/entity-service) offers the ability to filter results found with its [findMany()](/cms/api/entity-service/crud#findmany) method.
+
+Results are filtered with the `filters` parameter that accepts [logical operators](#logical-operators) and [attribute operators](#attribute-operators). Every operator should be prefixed with `$`.
+
+:::strapi Deep filtering with the various APIs
+For examples of how to deep filter with the various APIs, please refer to [this blog article](https://strapi.io/blog/deep-filtering-alpha-26).
+:::
+
+## Logical operators
+
+### `$and`
+
+All nested conditions must be `true`.
**Example**
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $gte: 5,
- },
+ $and: [
+ {
+ title: 'Hello World',
+ },
+ {
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
+ },
+ ],
},
});
```
-### `$between`
+`$and` will be used implicitly when passing an object with nested conditions:
-Attribute is between the 2 input values, boundaries included (e.g., `$between[1, 3]` will also return `1` and `3`).
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: 'Hello World',
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
+ },
+});
+```
+
+### `$or`
+
+One or many nested conditions must be `true`.
**Example**
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- rating: {
- $between: [1, 20],
- },
+ $or: [
+ {
+ title: 'Hello World',
+ },
+ {
+ createdAt: { $gt: '2021-11-17T14:28:25.843Z' },
+ },
+ ],
},
});
```
-### `$contains`
+### `$not`
-Attribute contains the input value (case-sensitive).
+Negates the nested conditions.
**Example**
```js
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
- title: {
- $contains: 'Hello',
+ $not: {
+ title: 'Hello World',
},
},
});
```
-### `$notContains`
+:::note
+`$not` can be used as:
-Attribute does not contain the input value (case-sensitive).
+- a logical operator (e.g. in `filters: { $not: { // conditions… }}`)
+- [an attribute operator](#not-1) (e.g. in `filters: { attribute-name: $not: { … } }`).
+:::
+
+:::tip
+`$and`, `$or` and `$not` operators are nestable inside of another `$and`, `$or` or `$not` operator.
+:::
+
+## Attribute Operators
+
+:::caution
+Using these operators may give different results depending on the database's implementation, as the comparison is handled by the database and not by Strapi.
+:::
+
+### `$not`
+
+Negates the nested condition(s).
**Example**
@@ -7998,15 +8236,17 @@ Attribute does not contain the input value (case-sensitive).
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $notContains: 'Hello',
+ $not: {
+ $contains: 'Hello World',
+ },
},
},
});
```
-### `$containsi`
+### `$eq`
-Attribute contains the input value. `$containsi` is not case-sensitive, while [$contains](#contains) is.
+Attribute equals input value.
**Example**
@@ -8014,15 +8254,25 @@ Attribute contains the input value. `$containsi` is not case-sensitive, while [$
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $containsi: 'hello',
+ $eq: 'Hello World',
},
},
});
```
-### `$notContainsi`
+`$eq` can be omitted:
-Attribute does not contain the input value. `$notContainsi` is not case-sensitive, while [$notContains](#notcontains) is.
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: 'Hello World',
+ },
+});
+```
+
+### `$eqi`
+
+Attribute equals input value (case-insensitive).
**Example**
@@ -8030,15 +8280,15 @@ Attribute does not contain the input value. `$notContainsi` is not case-sensitiv
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $notContainsi: 'hello',
+ $eqi: 'HELLO World',
},
},
});
```
-### `$startsWith`
+### `$ne`
-Attribute starts with input value.
+Attribute does not equal input value.
**Example**
@@ -8046,15 +8296,15 @@ Attribute starts with input value.
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $startsWith: 'ABCD',
+ $ne: 'ABCD',
},
},
});
```
-### `$endsWith`
+### `$nei`
-Attribute ends with input value.
+Attribute does not equal input value (case-insensitive).
**Example**
@@ -8062,15 +8312,15 @@ Attribute ends with input value.
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $endsWith: 'ABCD',
+ $nei: 'abcd',
},
},
});
```
-### `$null`
+### `$in`
-Attribute is `null`.
+Attribute is contained in the input list.
**Example**
@@ -8078,15 +8328,25 @@ Attribute is `null`.
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $null: true,
+ $in: ['Hello', 'Hola', 'Bonjour'],
},
},
});
```
-### `$notNull`
+`$in` can be omitted when passing an array of values:
-Attribute is not `null`.
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: ['Hello', 'Hola', 'Bonjour'],
+ },
+});
+```
+
+### `$notIn`
+
+Attribute is not contained in the input list.
**Example**
@@ -8094,23 +8354,231 @@ Attribute is not `null`.
const entries = await strapi.entityService.findMany('api::article.article', {
filters: {
title: {
- $notNull: true,
+ $notIn: ['Hello', 'Hola', 'Bonjour'],
},
},
});
```
+### `$lt`
+Attribute is less than the input value.
-# Ordering & Pagination with the Entity Service API
-Source: https://docs.strapi.io/cms/api/entity-service/order-pagination
+**Example**
-# Ordering and Paginating with the Entity Service API
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $lt: 10,
+ },
+ },
+});
+```
-Order and paginate Entity Service API query results using `sort`, `start`/`limit`, or `page`/`pageSize` parameters to control result ordering and retrieve specific data subsets.
+### `$lte`
-:::caution
-The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
+Attribute is less than or equal to the input value.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $lte: 10,
+ },
+ },
+});
+```
+
+### `$gt`
+
+Attribute is greater than the input value.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $gt: 5,
+ },
+ },
+});
+```
+
+### `$gte`
+
+Attribute is greater than or equal to the input value.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $gte: 5,
+ },
+ },
+});
+```
+
+### `$between`
+
+Attribute is between the 2 input values, boundaries included (e.g., `$between[1, 3]` will also return `1` and `3`).
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ rating: {
+ $between: [1, 20],
+ },
+ },
+});
+```
+
+### `$contains`
+
+Attribute contains the input value (case-sensitive).
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $contains: 'Hello',
+ },
+ },
+});
+```
+
+### `$notContains`
+
+Attribute does not contain the input value (case-sensitive).
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notContains: 'Hello',
+ },
+ },
+});
+```
+
+### `$containsi`
+
+Attribute contains the input value. `$containsi` is not case-sensitive, while [$contains](#contains) is.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $containsi: 'hello',
+ },
+ },
+});
+```
+
+### `$notContainsi`
+
+Attribute does not contain the input value. `$notContainsi` is not case-sensitive, while [$notContains](#notcontains) is.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notContainsi: 'hello',
+ },
+ },
+});
+```
+
+### `$startsWith`
+
+Attribute starts with input value.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $startsWith: 'ABCD',
+ },
+ },
+});
+```
+
+### `$endsWith`
+
+Attribute ends with input value.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $endsWith: 'ABCD',
+ },
+ },
+});
+```
+
+### `$null`
+
+Attribute is `null`.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $null: true,
+ },
+ },
+});
+```
+
+### `$notNull`
+
+Attribute is not `null`.
+
+**Example**
+
+```js
+const entries = await strapi.entityService.findMany('api::article.article', {
+ filters: {
+ title: {
+ $notNull: true,
+ },
+ },
+});
+```
+
+
+
+# Ordering & Pagination with the Entity Service API
+Source: https://docs.strapi.io/cms/api/entity-service/order-pagination
+
+# Ordering and Paginating with the Entity Service API
+
+Order and paginate Entity Service API query results using `sort`, `start`/`limit`, or `page`/`pageSize` parameters to control result ordering and retrieve specific data subsets.
+
+:::caution
+The Entity Service API is deprecated in Strapi v5. Please consider using the [Document Service API](/cms/api/document-service) instead.
:::
The [Entity Service API](/cms/api/entity-service) offers the ability to [order](#ordering) and [paginate](#pagination) results found with its [findMany()](/cms/api/entity-service/crud#findmany) method.
@@ -8646,6 +9114,51 @@ query Query($status: PublicationStatus) {
}
```
+### Filter with `publicationFilter` {#publication-filter}
+
+If the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled, you can add a `publicationFilter` argument to built-in collection and single-type queries. It filters documents by the [relationship between their draft and published versions](/cms/api/document-service/publication-filter): for example, drafts that were never published, or entries modified since they were last published. GraphQL exposes the same values as the REST API and the Document Service API through the `PublicationFilter` enum.
+
+`publicationFilter` selects the group of documents first; the `status` argument then decides whether each result returns its draft or published row.
+
+:::caution
+When `status` is omitted, GraphQL defaults to `PUBLISHED` before applying `publicationFilter` (same as REST). Draft-only values such as `NEVER_PUBLISHED` return no results unless you pass `status: DRAFT`.
+:::
+
+```graphql title="Example: Fetch never-published draft documents"
+query Query($status: PublicationStatus, $publicationFilter: PublicationFilter) {
+ restaurants(status: DRAFT, publicationFilter: NEVER_PUBLISHED) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+```graphql title="Example: Modified documents with default PUBLISHED status"
+query Query {
+ restaurants(publicationFilter: MODIFIED) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+Available enum values:
+
+| GraphQL enum | Document Service / REST value |
+| ------------ | ----------------------------- |
+| `NEVER_PUBLISHED` | `never-published` |
+| `HAS_PUBLISHED_VERSION` | `has-published-version` |
+| `MODIFIED` | `modified` |
+| `UNMODIFIED` | `unmodified` |
+| `NEVER_PUBLISHED_DOCUMENT` | `never-published-document` |
+| `HAS_PUBLISHED_VERSION_DOCUMENT` | `has-published-version-document` |
+| `PUBLISHED_WITHOUT_DRAFT` | `published-without-draft` |
+| `PUBLISHED_WITH_DRAFT` | `published-with-draft` |
+
+To learn more, see the [use cases and accepted values](/cms/api/document-service/publication-filter#values) on the Document Service API page.
+
## Mutations
Mutations in GraphQL are used to modify data (e.g. create, update, and delete data).
@@ -10971,6 +11484,7 @@ Returns a paginated list of documents. Supports filtering, sorting, field select
- `pagination[pageSize]` (integer): Items per page. Default `25`, max `100`
- `locale` (string): Locale of the documents to fetch. See locale (/cms/api/rest/locale).
- `status` (string): `published` or `draft`. See status (/cms/api/rest/status).
+- `publicationFilter` (string): Query documents by the relationship between their draft and published versions. See publicationFilter (/cms/api/rest/publication-filter).
**cURL:**
```
@@ -11388,7 +11902,7 @@ By default, the filters can only be used from `find` endpoints generated by the
## Example: Find users having 'John' as a first name
-#### GET /api/users — Find users having
+#### GET /api/users?filters[username][$eq]=John — Find users having
Use the $eq filter operator to find an exact match.
@@ -11442,7 +11956,7 @@ await request(\`/api/users?\${query}\`);
## Example: Find multiple restaurants with ids 3, 6,8
-#### GET /api/restaurants — Find multiple restaurants with ids 3, 6, 8
+#### GET /api/restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8 — Find multiple restaurants with ids 3, 6, 8
Use the $in filter operator with an array of values to find multiple exact values.
@@ -11493,7 +12007,7 @@ await request(\`/api/restaurants?\${query}\`);
## Complex filtering
-#### GET /api/books — Find books with 2 possible dates and a specific author
+#### GET /api/books?filters[$and][0][$or][0][date][$eq]=2020-01-01&filters[$and][0][$or][1][date][$eq]=2020-01-02&filters[$and][1][author][name][$eq]=Kai%20doe — Find books with 2 possible dates and a specific author
Combine $and and $or operators for complex filtering.
@@ -11578,7 +12092,7 @@ Querying your API with deep filters may cause performance issues. If one of you
For examples of how to deep filter with the various APIs, please refer to [this blog article](https://strapi.io/blog/deep-filtering-alpha-26).
:::
-#### GET /api/restaurants — Find restaurants owned by a chef who belongs to a 5-star restaurant
+#### GET /api/restaurants?filters[chef][restaurants][stars][$eq]=5 — Find restaurants owned by a chef who belongs to a 5-star restaurant
Use deep filtering to filter on a relation
@@ -13193,13 +13707,454 @@ The following table lists the new possible use cases added by i18n to the REST A
### `GET` Get all documents in a specific locale {#rest-get-all}
-#### GET /api/restaurants?locale=fr — Get all documents in a specific locale
+#### GET /api/restaurants?locale=fr — Get all documents in a specific locale
+
+Returns all documents for a given locale.
+
+**Request:**
+```
+GET http://localhost:1337/api/restaurants?locale=fr
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "id": 5,
+ "documentId": "h90lgohlzfpjf3bvan72mzll",
+ "Title": "Meilleures pizzas",
+ "Body": [
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "text": "On déguste les meilleures pizzas de la ville à la Pizzeria Arrivederci."
+ }
+ ]
+ }
+ ],
+ "createdAt": "2024-03-06T22:08:59.643Z",
+ "updatedAt": "2024-03-06T22:10:21.127Z",
+ "publishedAt": "2024-03-06T22:10:21.130Z",
+ "locale": "fr"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+### `GET` Get a document in a specific locale {#rest-get}
+
+To get a specific document in a given locale, add the `locale` parameter to the query:
+
+| Use case | Syntax format and link for more information |
+| -------------------- | ---------------------------------------------------------------------------------------------- |
+| In a collection type | [`GET /api/content-type-plural-name/document-id?locale=locale-code`](#get-one-collection-type) |
+| In a single type | [`GET /api/content-type-singular-name?locale=locale-code`](#get-one-single-type) |
+
+#### Collection types {#get-one-collection-type}
+
+To get a specific document in a collection type in a given locale, add the `locale` parameter to the query, after the `documentId`:
+
+#### GET /api/restaurants/:documentId?locale=fr — Get a document in a specific locale (collection type)
+
+Returns a specific document in a collection type for a given locale.
+
+**Request:**
+```
+GET /api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "id": 22,
+ "documentId": "lr5wju2og49bf820kj9kz8c3",
+ "Name": "Biscotte Restaurant",
+ "Description": [
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "text": "Bienvenue au restaurant Biscotte! Le Restaurant Biscotte propose une cuisine à base de produits frais et de qualité, souvent locaux, biologiques lorsque cela est possible, et toujours produits par des producteurs passionnés."
+ }
+ ]
+ }
+ ],
+ "locale": "fr"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 3
+ }
+ }
+}
+```
+
+#### Single types {#get-one-single-type}
+
+To get a specific single type document in a given locale, add the `locale` parameter to the query, after the single type name:
+
+#### GET /api/homepage?locale=fr — Get a document in a specific locale (single type)
+
+Returns a specific single type document for a given locale.
+
+**Request:**
+```
+GET /api/homepage?locale=fr
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": {
+ "id": 10,
+ "documentId": "ukbpbnu8kbutpn98rsanyi50",
+ "Title": "Page d'accueil",
+ "Body": null,
+ "createdAt": "2024-03-07T13:28:26.349Z",
+ "updatedAt": "2024-03-07T13:28:26.349Z",
+ "publishedAt": "2024-03-07T13:28:26.353Z",
+ "locale": "fr"
+ },
+ "meta": {}
+}
+```
+
+### `POST` Create a new localized document for a collection type {#rest-create}
+
+To create a localized document from scratch, send a POST request to the Content API. Depending on whether you want to create it for the default locale or for another locale, you might need to pass the `locale` parameter in the query.
+
+| Use case | Syntax format and link for more information |
+| ----------------------------- | --------------------------------------------------------------------------------------- |
+| Create for the default locale | [`POST /api/content-type-plural-name`](#rest-create-default-locale) |
+| Create for a specific locale | [`POST /api/content-type-plural-name?locale=fr`](#rest-create-specific-locale)
+
+#### For the default locale {#rest-create-default-locale}
+
+If no locale has been passed in the request body, the document is created using the default locale for the application:
+
+#### POST /api/restaurants — Create a document for the default locale
+
+Creates a new document using the default locale.
+
+**Request:**
+```
+POST http://localhost:1337/api/restaurants
+
+{
+ "data": {
+ "Name": "Oplato"
+ }
+}
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": {
+ "id": 13,
+ "documentId": "jae8klabhuucbkgfe2xxc5dj",
+ "Name": "Oplato",
+ "Description": null,
+ "createdAt": "2024-03-06T22:19:54.646Z",
+ "updatedAt": "2024-03-06T22:19:54.646Z",
+ "publishedAt": "2024-03-06T22:19:54.649Z",
+ "locale": "en"
+ },
+ "meta": {}
+}
+```
+
+#### For a specific locale {#rest-create-specific-locale}
+
+To create a localized entry for a locale different from the default one, add the `locale` parameter to the query URL of the POST request:
+
+#### POST /api/restaurants?locale=fr — Create a document for a specific locale
+
+Creates a new document for a specified locale.
+
+**Request:**
+```
+POST http://localhost:1337/api/restaurants?locale=fr
+
+{
+ "data": {
+ "Name": "She's Cake"
+ }
+}
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": {
+ "id": 15,
+ "documentId": "ldcmn698iams5nuaehj69j5o",
+ "Name": "She's Cake",
+ "Description": null,
+ "createdAt": "2024-03-06T22:21:18.373Z",
+ "updatedAt": "2024-03-06T22:21:18.373Z",
+ "publishedAt": "2024-03-06T22:21:18.378Z",
+ "locale": "fr"
+ },
+ "meta": {}
+}
+```
+
+### `PUT` Create a new, or update an existing, locale version for an existing document {#rest-update}
+
+With `PUT` requests sent to an existing document, you can:
+
+- create another locale version of the document,
+- or update an existing locale version of the document.
+
+Send the `PUT` request to the appropriate URL, adding the `locale=your-locale-code` parameter to the query URL and passing attributes in a `data` object in the request's body:
+
+| Use case | Syntax format and link for more information |
+| -------------------- | --------------------------------------------------------------------------------------- |
+| In a collection type | [`PUT /api/content-type-plural-name/document-id?locale=locale-code`](#rest-put-collection-type) |
+| In a single type | [`PUT /api/content-type-singular-name?locale=locale-code`](#rest-put-single-type) |
+
+:::caution
+When creating a localization for existing localized entries, the body of the request can only accept localized fields.
+:::
+
+:::tip
+The Content-Type should have the [`createLocalization` permission](/cms/features/rbac#collection-and-single-types) enabled, otherwise the request will return a `403: Forbidden` status.
+:::
+
+:::note
+It is not possible to change the locale of an existing localized entry. When updating a localized entry, if you set a `locale` attribute in the request body it will be ignored.
+:::
+
+#### In a collection type {#rest-put-collection-type}
+
+To create a new locale for an existing document in a collection type, add the `locale` parameter to the query, after the `documentId`, and pass data to the request's body:
+
+#### PUT /api/restaurants/:documentId?locale=fr — Create or update a locale version (collection type)
+
+Creates a French locale for an existing restaurant, or updates it if it already exists.
+
+**Request:**
+```
+PUT http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
+
+{
+ "data": {
+ "Name": "She's Cake in French"
+ }
+}
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": {
+ "id": 19,
+ "documentId": "lr5wju2og49bf820kj9kz8c3",
+ "Name": "She's Cake in French",
+ "Description": null,
+ "createdAt": "2024-03-07T12:13:09.551Z",
+ "updatedAt": "2024-03-07T12:13:09.551Z",
+ "publishedAt": "2024-03-07T12:13:09.554Z",
+ "locale": "fr"
+ },
+ "meta": {}
+}
+```
+
+#### In a single type {#rest-put-single-type}
+
+To create a new locale for an existing single type document, add the `locale` parameter to the query, after the single type name, and pass data to the request's body:
+
+#### PUT /api/homepage?locale=fr — Create or update a locale version (single type)
+
+Creates a French locale for an existing Homepage single type, or updates it if it already exists.
+
+**Request:**
+```
+PUT http://localhost:1337/api/homepage?locale=fr
+
+{
+ "data": {
+ "Title": "Page d'accueil"
+ }
+}
+```
+
+**Response 200 OK:**
+```json
+{
+ "data": {
+ "id": 10,
+ "documentId": "ukbpbnu8kbutpn98rsanyi50",
+ "Title": "Page d'accueil",
+ "Body": null,
+ "createdAt": "2024-03-07T13:28:26.349Z",
+ "updatedAt": "2024-03-07T13:28:26.349Z",
+ "publishedAt": "2024-03-07T13:28:26.353Z",
+ "locale": "fr"
+ },
+ "meta": {}
+}
+```
+
+
+
+### `DELETE` Delete a locale version of a document {#rest-delete}
+
+To delete a locale version of a document, send a `DELETE` request with the appropriate `locale` parameter.
+
+`DELETE` requests only send a 204 HTTP status code on success and do not return any data in the response body.
+
+#### In a collection type {#rest-delete-collection-type}
+
+To delete only a specific locale version of a document in a collection type, add the `locale` parameter to the query after the `documentId`:
+
+#### DELETE /api/restaurants/:documentId?locale=fr — Delete a locale version (collection type)
+
+Deletes a specific locale version of a document in a collection type.
+
+**Request:**
+```
+DELETE /api/restaurants/abcdefghijklmno456?locale=fr
+```
+
+**Response 200 No Content:**
+```json
+(response body)
+```
+
+#### In a single type {#rest-delete-single-type}
+
+To delete only a specific locale version of a single type document, add the `locale` parameter to the query after the single type name:
+
+#### DELETE /api/homepage?locale=fr — Delete a locale version (single type)
+
+Deletes a specific locale version of a single type document.
+
+**Request:**
+```
+DELETE /api/homepage?locale=fr
+```
+
+**Response 200 No Content:**
+```json
+(response body)
+```
+
+
+
+# Parameters
+Source: https://docs.strapi.io/cms/api/rest/parameters
+
+# REST API parameters
+
+REST API parameters filter, sort, paginate, and select fields and relations in Strapi queries. Use `filters`, `locale`, `populate`, `sort`, and `pagination` to refine your content requests.
+
+API parameters can be used with the [REST API](/cms/api/rest) to filter, sort, and paginate results and to select fields and relations to populate. Additionally, specific parameters related to optional Strapi features can be used, like the publication state and locale of a content-type.
+
+The following API parameters are available:
+
+| Operator | Type | Description |
+| ------------------ | ------------- | ----------------------------------------------------- |
+| `filters` | Object | [Filter the response](/cms/api/rest/filters) |
+| `locale` | String | [Select a locale](/cms/api/rest/locale) |
+| `status` | String | [Select the Draft & Publish status](/cms/api/rest/status) |
+| `publicationFilter` | String | [Select documents by how their draft and published versions relate](/cms/api/rest/publication-filter) |
+| `populate` | String or Object | [Populate relations, components, or dynamic zones](/cms/api/rest/populate-select#population) |
+| `fields` | Array | [Select only specific fields to display](/cms/api/rest/populate-select#field-selection) |
+| `sort` | String or Array | [Sort the response](/cms/api/rest/sort-pagination.md#sorting) |
+| `pagination` | Object | [Page through entries](/cms/api/rest/sort-pagination.md#pagination) |
+
+:::note
+Long bracket-encoded lists in a parameter (for example `populate` or `fields`) are limited by [`arrayLimit` on `strapi::query`](/cms/configurations/middlewares#query). See [Population](/cms/api/rest/populate-select#population).
+:::
+
+Query parameters use the [LHS bracket syntax](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets) (i.e. they are encoded using square brackets `[]`).
+
+:::tip
+A wide range of REST API parameters can be used and combined to query your content, which can result in long and complex query URLs.
👉 You can use Strapi's [interactive query builder](/cms/api/rest/interactive-query-builder) tool to build query URLs more conveniently. 🤗
+:::
+
+
+
+# Populate and Select
+Source: https://docs.strapi.io/cms/api/rest/populate-select
+
+# REST API: Population & Field Selection
+
+Use the `populate` parameter to include relations, media fields, components, and dynamic zones in REST API responses. Use the `fields` parameter to return only specific fields.
+
+The [REST API](/cms/api/rest) by default does not populate any relations, media fields, components, or dynamic zones. Use the [`populate` parameter](#population) to populate specific fields. Use the [`fields` parameter](#field-selection) to return only specific fields with the query results.
+
+:::tip
+
+Strapi takes advantage of the ability of [the `qs` library](https://github.com/ljharb/qs) to parse nested objects to create more complex queries.
+
+Use `qs` directly to generate complex queries instead of creating them manually. Examples in this documentation showcase how you can use `qs`.
+
+You can also use the [interactive query builder](/cms/api/rest/interactive-query-builder) if you prefer playing with our online tool instead of generating queries with `qs` on your machine.
+
+:::
+
+## Field selection
+
+Queries can accept a `fields` parameter to select only some fields. By default, the REST API only returns the following [types of fields](/cms/backend-customization/models#model-attributes):
+
+- string types: `string`, `text`, `richtext`, `enumeration`, `email`, `password`, and `uid`,
+- date types: `date`, `time`, `datetime`, and `timestamp`,
+- number types: `integer`, `biginteger`, `float`, and `decimal`,
+- generic types: `boolean`, `array`, and `JSON`.
+
+| Use case | Example parameter syntax |
+|-----------------------|---------------------------------------|
+| Select a single field | `fields=name` |
+| Select multiple fields| `fields[0]=name&fields[1]=description`|
+
+:::note
+Field selection does not work on relational, media, component, or dynamic zone fields. To populate these fields, use the [`populate` parameter](#population).
+:::
+
+#### GET /api/restaurants?fields[0]=name&fields[1]=description — Return only name and description fields
+
+Use the fields parameter to select only specific fields in the response.
-Returns all documents for a given locale.
+**URL:**
+```
+GET /api/restaurants?fields[0]=name&fields[1]=description
+```
-**Request:**
+**JavaScript:**
```
-GET http://localhost:1337/api/restaurants?locale=fr
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ fields: ['name', 'description'],
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
+ }
+);
+
+await request(\`/api/users?\${query}\`);
```
**Response 200 OK:**
@@ -13207,24 +14162,20 @@ GET http://localhost:1337/api/restaurants?locale=fr
{
"data": [
{
- "id": 5,
- "documentId": "h90lgohlzfpjf3bvan72mzll",
- "Title": "Meilleures pizzas",
- "Body": [
+ "id": 4,
+ "Name": "Pizzeria Arrivederci",
+ "Description": [
{
"type": "paragraph",
"children": [
{
"type": "text",
- "text": "On déguste les meilleures pizzas de la ville à la Pizzeria Arrivederci."
+ "text": "Specialized in pizza, we invite you to rediscover our classics, such as 4 Formaggi or Calzone, and our original creations such as Do Luigi or Nduja."
}
]
}
],
- "createdAt": "2024-03-06T22:08:59.643Z",
- "updatedAt": "2024-03-06T22:10:21.127Z",
- "publishedAt": "2024-03-06T22:10:21.130Z",
- "locale": "fr"
+ "documentId": "lr5wju2og49bf820kj9kz8c3"
}
],
"meta": {
@@ -13232,612 +14183,789 @@ GET http://localhost:1337/api/restaurants?locale=fr
"page": 1,
"pageSize": 25,
"pageCount": 1,
- "total": 1
+ "total": 4
}
}
}
```
-### `GET` Get a document in a specific locale {#rest-get}
+## Population
-To get a specific document in a given locale, add the `locale` parameter to the query:
+The REST API by default does not populate any type of fields, so it will not populate relations, media fields, components, or dynamic zones unless you pass a `populate` parameter to populate various field types. Populated relations always return full objects; the REST API currently cannot return just an array of IDs.
-| Use case | Syntax format and link for more information |
-| -------------------- | ---------------------------------------------------------------------------------------------- |
-| In a collection type | [`GET /api/content-type-plural-name/document-id?locale=locale-code`](#get-one-collection-type) |
-| In a single type | [`GET /api/content-type-singular-name?locale=locale-code`](#get-one-single-type) |
+:::prerequisites
+The `find` permission must be enabled for the content-types that are being populated. If a role does not have access to a content-type, the content-type will not be populated (see [Users & Permissions](/cms/features/users-permissions#editing-a-role) for additional information on how to enable `find` permissions for content-types).
+:::
-#### Collection types {#get-one-collection-type}
+You can use the `populate` parameter alone or [in combination with multiple operators](#combining-population-with-other-operators) for more control over the population.
-To get a specific document in a collection type in a given locale, add the `locale` parameter to the query, after the `documentId`:
+:::caution
+`populate=deep` plugins are [not recommended in Strapi](https://support.strapi.io/articles/8544110758-why-populate-deep-plugins-are-not-recommended-in-strapi).
+:::
-#### GET /api/restaurants/:documentId?locale=fr — Get a document in a specific locale (collection type)
+:::note
+Large `populate` lists in the query string (many `populate[0]`, `populate[1]`, … entries) are bounded by the query parser `arrayLimit` (default: `100`). To allow a longer list, raise `arrayLimit` on the [`strapi::query` middleware](/cms/configurations/middlewares#query). Higher values increase parsing cost per request.
+:::
-Returns a specific document in a collection type for a given locale.
+The following table lists populate use cases with example syntax. Each row links to the Understanding populate guide for details:
-**Request:**
-```
-GET /api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
-```
+| Use case | Example parameter syntax | Detailed explanations to read |
+|-----------| ---------------|-----------------------|
+| Populate everything, 1 level deep, including media fields, relations, components, and dynamic zones | `populate=*`| [Populate all relations and fields, 1 level deep](/cms/api/rest/guides/understanding-populate#populate-all-relations-and-fields-1-level-deep) |
+| Populate one relation,
1 level deep | `populate=a-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) |
+| Populate several relations,
1 level deep | `populate[0]=relation-name&populate[1]=another-relation-name&populate[2]=yet-another-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) |
+| Populate some relations, several levels deep | `populate[root-relation-name][populate][0]=nested-relation-name`| [Populate several levels deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-several-levels-deep-for-specific-relations) |
+| Populate a component | `populate[0]=component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) |
+| Populate a component and one of its nested components | `populate[0]=component-name&populate[1]=component-name.nested-component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) |
+| Populate a dynamic zone (only its first-level scalar fields) | `populate[0]=dynamic-zone-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) |
+| Populate a dynamic zone, including component-specific fields, nested components, and relations | `populate[dynamic-zone-name][on][component-category.component-name][populate][relation-name][populate][0]=field-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) |
-**Response 200 OK:**
-```json
-{
- "data": [
- {
- "id": 22,
- "documentId": "lr5wju2og49bf820kj9kz8c3",
- "Name": "Biscotte Restaurant",
- "Description": [
- {
- "type": "paragraph",
- "children": [
- {
- "type": "text",
- "text": "Bienvenue au restaurant Biscotte! Le Restaurant Biscotte propose une cuisine à base de produits frais et de qualité, souvent locaux, biologiques lorsque cela est possible, et toujours produits par des producteurs passionnés."
- }
- ]
- }
- ],
- "locale": "fr"
- }
- ],
- "meta": {
- "pagination": {
- "page": 1,
- "pageSize": 25,
- "pageCount": 1,
- "total": 3
- }
- }
-}
-```
+:::tip
+To build complex queries with multiple-level population, use the [interactive query builder](/cms/api/rest/interactive-query-builder) tool. For more detailed explanations and examples, see the [REST API guides](/cms/api/rest/guides/intro).
+:::
-#### Single types {#get-one-single-type}
+### Combining population with other operators
-To get a specific single type document in a given locale, add the `locale` parameter to the query, after the single type name:
+You can combine the `populate` operator with other operators such as [field selection](/cms/api/rest/populate-select#field-selection), [filters](/cms/api/rest/filters), and [sort](/cms/api/rest/sort-pagination) in the population queries.
-#### GET /api/homepage?locale=fr — Get a document in a specific locale (single type)
+:::note
+Top-level pagination parameters (e.g., `pagination[page]` and `pagination[pageSize]`) work alongside `populate` to paginate the main query results. However, you cannot apply pagination parameters directly to populated relations to limit the number of related entries returned within each result (nested pagination on relations is not supported in the REST API).
+:::
-Returns a specific single type document for a given locale.
+#### Populate with field selection
-**Request:**
+`fields` and `populate` can be combined.
+
+#### GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url — Populate with field selection
+
+Combine fields and populate parameters to select specific fields on both the main entry and its relations.
+
+**URL:**
```
-GET /api/homepage?locale=fr
+GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url
+```
+
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ fields: ['title', 'slug'],
+ populate: {
+ headerImage: {
+ fields: ['name', 'url'],
+ },
+ },
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
+ }
+);
+
+await request(\`/api/articles?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": {
- "id": 10,
- "documentId": "ukbpbnu8kbutpn98rsanyi50",
- "Title": "Page d'accueil",
- "Body": null,
- "createdAt": "2024-03-07T13:28:26.349Z",
- "updatedAt": "2024-03-07T13:28:26.349Z",
- "publishedAt": "2024-03-07T13:28:26.353Z",
- "locale": "fr"
- },
+ "data": [
+ {
+ "id": 1,
+ "documentId": "h90lgohlzfpjf3bvan72mzll",
+ "title": "Test Article",
+ "slug": "test-article",
+ "headerImage": {
+ "id": 1,
+ "documentId": "cf07g1dbusqr8mzmlbqvlegx",
+ "name": "17520.jpg",
+ "url": "/uploads/17520_73c601c014.jpg"
+ }
+ }
+ ],
"meta": {}
}
```
-### `POST` Create a new localized document for a collection type {#rest-create}
-
-To create a localized document from scratch, send a POST request to the Content API. Depending on whether you want to create it for the default locale or for another locale, you might need to pass the `locale` parameter in the query.
-
-| Use case | Syntax format and link for more information |
-| ----------------------------- | --------------------------------------------------------------------------------------- |
-| Create for the default locale | [`POST /api/content-type-plural-name`](#rest-create-default-locale) |
-| Create for a specific locale | [`POST /api/content-type-plural-name?locale=fr`](#rest-create-specific-locale)
-
-#### For the default locale {#rest-create-default-locale}
+#### Populate with filtering
-If no locale has been passed in the request body, the document is created using the default locale for the application:
+`filters` and `populate` can be combined.
-#### POST /api/restaurants — Create a document for the default locale
+#### GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars — Populate with filtering
-Creates a new document using the default locale.
+Combine populate with sort and filter parameters to refine which related entries are returned.
-**Request:**
+**URL:**
+```
+GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars
```
-POST http://localhost:1337/api/restaurants
-{
- "data": {
- "Name": "Oplato"
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify(
+ {
+ populate: {
+ categories: {
+ sort: ['name:asc'],
+ filters: {
+ name: {
+ $eq: 'Cars',
+ },
+ },
+ },
+ },
+ },
+ {
+ encodeValuesOnly: true, // prettify URL
}
-}
+);
+
+await request(\`/api/articles?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": {
- "id": 13,
- "documentId": "jae8klabhuucbkgfe2xxc5dj",
- "Name": "Oplato",
- "Description": null,
- "createdAt": "2024-03-06T22:19:54.646Z",
- "updatedAt": "2024-03-06T22:19:54.646Z",
- "publishedAt": "2024-03-06T22:19:54.649Z",
- "locale": "en"
- },
+ "data": [
+ {
+ "id": 1,
+ "documentId": "a1b2c3d4e5d6f7g8h9i0jkl",
+ "title": "Test Article",
+ "categories": {
+ "data": [
+ {
+ "id": 2,
+ "documentId": "jKd8djla9ndalk98hflj3",
+ "name": "Cars"
+ }
+ ]
+ }
+ }
+ ],
"meta": {}
}
```
-#### For a specific locale {#rest-create-specific-locale}
+:::note
+For many-to-many and other join-table relations, an explicit `sort` within a `populate` object overrides the default connect order. Omit `sort` to preserve the connect order (the order in which entries were associated).
+:::
-To create a localized entry for a locale different from the default one, add the `locale` parameter to the query URL of the POST request:
+:::tip Performance tip
+In production, always use explicit population instead of wildcards like `populate=*`. Limit population depth to 2-3 levels and consider centralizing population logic in route middlewares. See [Building High-Performance Strapi Applications](https://strapi.io/blog/building-high-performance-strapi-applications-common-pitfalls-and-best-practices) on the Strapi blog.
+:::
-#### POST /api/restaurants?locale=fr — Create a document for a specific locale
-Creates a new document for a specified locale.
-**Request:**
-```
-POST http://localhost:1337/api/restaurants?locale=fr
+# REST API: publicationFilter
+Source: https://docs.strapi.io/cms/api/rest/publication-filter
+
+# REST API: `publicationFilter`
+
+Add the optional `publicationFilter` query parameter to query documents by the relationship between their draft and published versions, for example documents that were never published, or documents modified since they were last published. It combines with other query parameters, and `status` still decides whether you get the draft or the published version.
+
+The `publicationFilter` is a query parameter that, combined with [the `status` parameter](/cms/api/rest/status), can help you cover complex queries to find exactly what you need with the [REST API](/cms/api/rest).
-{
- "data": {
- "Name": "She's Cake"
- }
-}
-```
+While `status` answers "do I want the draft or the published version?", the `publicationFilter` parameter answers a different question: "which documents do I want, based on how their draft and published versions relate?". This is useful for example to find documents that were never published, or documents whose draft has unsaved changes compared to what is live.
-**Response 200 OK:**
-```json
-{
- "data": {
- "id": 15,
- "documentId": "ldcmn698iams5nuaehj69j5o",
- "Name": "She's Cake",
- "Description": null,
- "createdAt": "2024-03-06T22:21:18.373Z",
- "updatedAt": "2024-03-06T22:21:18.373Z",
- "publishedAt": "2024-03-06T22:21:18.378Z",
- "locale": "fr"
- },
- "meta": {}
-}
-```
+The underlying model behind how `publicationFilter` works is handled on the back-end server by the [Document Service API](/cms/api/document-service/publication-filter). The present page follows the exact same structure and explanations, but with examples tailored for the REST API, so you don't have to jump between 2 different pages.
-### `PUT` Create a new, or update an existing, locale version for an existing document {#rest-update}
+:::prerequisites
+The [Draft & Publish](/cms/features/draft-and-publish) feature must be enabled on the content-type. If Draft & Publish is disabled, `publicationFilter` has no effect.
+:::
-With `PUT` requests sent to an existing document, you can:
+## Available values {#values}
-- create another locale version of the document,
-- or update an existing locale version of the document.
+`publicationFilter` accepts one of the following values:
-Send the `PUT` request to the appropriate URL, adding the `locale=your-locale-code` parameter to the query URL and passing attributes in a `data` object in the request's body:
+| Value | Selects |
+| ----- | ------- |
+| `never-published` | Documents never published in a given locale |
+| `never-published-document` | Documents never published in any locale |
+| `modified` | Documents whose draft was edited since it was last published |
+| `unmodified` | Documents whose draft has not changed since it was last published |
+| `published-without-draft` | Published documents with no draft counterpart |
+| `published-with-draft` | Published documents that also have a draft |
+| `has-published-version` | Documents that have both a draft and a published version |
+| `has-published-version-document` | Documents published in at least one locale
(useful when [i18n](/cms/features/internationalization) is enabled) |
-| Use case | Syntax format and link for more information |
-| -------------------- | --------------------------------------------------------------------------------------- |
-| In a collection type | [`PUT /api/content-type-plural-name/document-id?locale=locale-code`](#rest-put-collection-type) |
-| In a single type | [`PUT /api/content-type-singular-name?locale=locale-code`](#rest-put-single-type) |
+For detailed examples of how to use the `publicationFilter` values, including with the `status` parameter, see the [possible use cases](#use-cases) table.
-:::caution
-When creating a localization for existing localized entries, the body of the request can only accept localized fields.
+:::note
+* Unknown values return an HTTP `400` error.
+* Values ending in `-document` consider all locales of a document, which matters when [Internationalization (i18n)](/cms/features/internationalization) is enabled: for example, `never-published-document` excludes a document as soon as one of its locales is published. All other values consider one locale at a time. Without i18n, both variants behave the same.
:::
-:::tip
-The Content-Type should have the [`createLocalization` permission](/cms/features/rbac#collection-and-single-types) enabled, otherwise the request will return a `403: Forbidden` status.
+:::caution Caution: Different default behaviors for different APIs
+The REST API returns published versions of documents when `status` is omitted, so queries for draft-only values such as `never-published` need an explicit `status=draft`. The Document Service API returns draft versions instead (see [Document Service API: `publicationFilter`](/cms/api/document-service/publication-filter)).
:::
+## Possible use cases {#use-cases}
+
+The following table lists many possible use cases, illustrating how the `status` and `publicationFilter` parameters can be combined to find exactly what you need with the REST API. Click a use case to jump to a complete example:
+
+| I want to… | Use `status` as… | Use `publicationFilter` as… |
+| ---------- | ------------------ | ----------------------------- |
+| [Find never published drafts](#never-published) | `draft` | `never-published` |
+| [Find drafts never published in any locale](#never-published-document) | `draft` | `never-published-document` |
+| [Find modified documents](#modified) | `draft` or `published` | `modified` |
+| [Find unmodified documents](#unmodified) | `draft` or `published` | `unmodified` |
+| [Find published documents without a draft](#published-without-draft) | `published` | `published-without-draft` |
+| [Find published documents with a draft](#published-with-draft) | `published` | `published-with-draft` |
+| [Find documents with a published version](#has-published-version) | `draft` or `published` | `has-published-version` |
+| [Find documents published in at least one locale](#has-published-version-document) | `draft` or `published` | `has-published-version-document` |
+
:::note
-It is not possible to change the locale of an existing localized entry. When updating a localized entry, if you set a `locale` attribute in the request body it will be ignored.
+Pairing a value with the opposite `status` from the table above is valid but returns nothing rather than an error: for example, `never-published` with `status=published` returns an empty result, because these documents have no published version yet.
:::
-#### In a collection type {#rest-put-collection-type}
+## Examples
-To create a new locale for an existing document in a collection type, add the `locale` parameter to the query, after the `documentId`, and pass data to the request's body:
+The following section lists the most common use cases summed up in the [table](#use-cases) above.
-#### PUT /api/restaurants/:documentId?locale=fr — Create or update a locale version (collection type)
+### Find never published drafts {#never-published}
-Creates a French locale for an existing restaurant, or updates it if it already exists.
+One of the most common use cases is to find the drafts that have never been published. To do so, pass `status=draft` and `publicationFilter=never-published`.
-**Request:**
+This parameter combination works only on a given locale; to find these documents across all locales, [use `never-published-document`](#never-published-document) instead.
+
+#### GET /api/restaurants?status=draft&publicationFilter=never-published — Get draft restaurants that have never been published for their locale
+
+Return the drafts that have never been published for their locale.
+
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published' \\
+ -H 'Authorization: Bearer '
```
-PUT http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr
-{
- "data": {
- "Name": "She's Cake in French"
- }
-}
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published',
+ }, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": {
- "id": 19,
- "documentId": "lr5wju2og49bf820kj9kz8c3",
- "Name": "She's Cake in French",
- "Description": null,
- "createdAt": "2024-03-07T12:13:09.551Z",
- "updatedAt": "2024-03-07T12:13:09.551Z",
- "publishedAt": "2024-03-07T12:13:09.554Z",
- "locale": "fr"
- },
- "meta": {}
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
}
```
-#### In a single type {#rest-put-single-type}
+### Find drafts never published in any locale {#never-published-document}
-To create a new locale for an existing single type document, add the `locale` parameter to the query, after the single type name, and pass data to the request's body:
+`publicationFilter=never-published-document` returns documents that have never been published in any locale. It looks at the whole document across all its locales, not one locale at a time. To find these documents for a given locale only, [use `never-published`](#never-published) instead.
-#### PUT /api/homepage?locale=fr — Create or update a locale version (single type)
+A document counts as published as soon as one of its locales is published: the document is then left out, even the locales that only exist as a draft. The example below returns the draft versions of documents that were never published anywhere:
-Creates a French locale for an existing Homepage single type, or updates it if it already exists.
+#### GET /api/restaurants?status=draft&publicationFilter=never-published-document — Get drafts of restaurants never published in any locale
-**Request:**
+Return the drafts of documents never published in any locale.
+
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published-document' \\
+ -H 'Authorization: Bearer '
```
-PUT http://localhost:1337/api/homepage?locale=fr
-{
- "data": {
- "Title": "Page d'accueil"
- }
-}
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'never-published-document',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": {
- "id": 10,
- "documentId": "ukbpbnu8kbutpn98rsanyi50",
- "Title": "Page d'accueil",
- "Body": null,
- "createdAt": "2024-03-07T13:28:26.349Z",
- "updatedAt": "2024-03-07T13:28:26.349Z",
- "publishedAt": "2024-03-07T13:28:26.353Z",
- "locale": "fr"
- },
- "meta": {}
+ "data": [
+ {
+ "documentId": "d41r46wac4xix5vpba7561at",
+ "name": "New Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
}
```
-
-
-### `DELETE` Delete a locale version of a document {#rest-delete}
-
-To delete a locale version of a document, send a `DELETE` request with the appropriate `locale` parameter.
+### Find modified documents {#modified}
-`DELETE` requests only send a 204 HTTP status code on success and do not return any data in the response body.
+`publicationFilter=modified` selects documents whose draft has modified but unpublished changes. `status` then decides which version of those documents you get back.
-#### In a collection type {#rest-delete-collection-type}
+For instance, with `status=draft`, the query returns the draft versions:
-To delete only a specific locale version of a document in a collection type, add the `locale` parameter to the query after the `documentId`:
+#### GET /api/restaurants?status=draft&publicationFilter=modified — Get the draft versions of modified restaurants
-#### DELETE /api/restaurants/:documentId?locale=fr — Delete a locale version (collection type)
+Return the draft versions of documents with unpublished changes.
-Deletes a specific locale version of a document in a collection type.
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=modified' \\
+ -H 'Authorization: Bearer '
+```
-**Request:**
+**JavaScript:**
```
-DELETE /api/restaurants/abcdefghijklmno456?locale=fr
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);
```
-**Response 200 No Content:**
+**Response 200 OK:**
```json
-(response body)
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant (updated)",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
```
-#### In a single type {#rest-delete-single-type}
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
-To delete only a specific locale version of a single type document, add the `locale` parameter to the query after the single type name:
+#### GET /api/restaurants?publicationFilter=modified — Get the currently live version of modified restaurants
-#### DELETE /api/homepage?locale=fr — Delete a locale version (single type)
+Return the currently live versions of documents with unpublished changes.
-Deletes a specific locale version of a single type document.
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?publicationFilter=modified' \\
+ -H 'Authorization: Bearer '
+```
-**Request:**
+**JavaScript:**
```
-DELETE /api/homepage?locale=fr
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'modified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(\`/api/restaurants?\${query}\`);
```
-**Response 200 No Content:**
+**Response 200 OK:**
```json
-(response body)
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
```
+### Find unmodified documents {#unmodified}
+`publicationFilter=unmodified` selects documents whose draft has not changed since it was last published. `status` then decides which version of those documents you get back.
-# Parameters
-Source: https://docs.strapi.io/cms/api/rest/parameters
-
-# REST API parameters
+For instance, with `status=draft`, the query returns the draft versions:
-REST API parameters filter, sort, paginate, and select fields and relations in Strapi queries. Use `filters`, `locale`, `populate`, `sort`, and `pagination` to refine your content requests.
+#### GET /api/restaurants?status=draft&publicationFilter=unmodified — Get the draft versions of unmodified restaurants
-API parameters can be used with the [REST API](/cms/api/rest) to filter, sort, and paginate results and to select fields and relations to populate. Additionally, specific parameters related to optional Strapi features can be used, like the publication state and locale of a content-type.
+Return the draft versions of documents unchanged since their last publication.
-The following API parameters are available:
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=unmodified' \\
+ -H 'Authorization: Bearer '
+```
-| Operator | Type | Description |
-| ------------------ | ------------- | ----------------------------------------------------- |
-| `filters` | Object | [Filter the response](/cms/api/rest/filters) |
-| `locale` | String | [Select a locale](/cms/api/rest/locale) |
-| `status` | String | [Select the Draft & Publish status](/cms/api/rest/status) |
-| `populate` | String or Object | [Populate relations, components, or dynamic zones](/cms/api/rest/populate-select#population) |
-| `fields` | Array | [Select only specific fields to display](/cms/api/rest/populate-select#field-selection) |
-| `sort` | String or Array | [Sort the response](/cms/api/rest/sort-pagination.md#sorting) |
-| `pagination` | Object | [Page through entries](/cms/api/rest/sort-pagination.md#pagination) |
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
-:::note
-Long bracket-encoded lists in a parameter (for example `populate` or `fields`) are limited by [`arrayLimit` on `strapi::query`](/cms/configurations/middlewares#query). See [Population](/cms/api/rest/populate-select#population).
-:::
+await request(\`/api/restaurants?\${query}\`);
+```
-Query parameters use the [LHS bracket syntax](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets) (i.e. they are encoded using square brackets `[]`).
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
-:::tip
-A wide range of REST API parameters can be used and combined to query your content, which can result in long and complex query URLs.
👉 You can use Strapi's [interactive query builder](/cms/api/rest/interactive-query-builder) tool to build query URLs more conveniently. 🤗
-:::
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
+#### GET /api/restaurants?publicationFilter=unmodified — Get the currently live version of unmodified restaurants
+Return the currently live versions of documents unchanged since their last publication.
-# Populate and Select
-Source: https://docs.strapi.io/cms/api/rest/populate-select
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?publicationFilter=unmodified' \\
+ -H 'Authorization: Bearer '
+```
-# REST API: Population & Field Selection
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'unmodified',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
-Use the `populate` parameter to include relations, media fields, components, and dynamic zones in REST API responses. Use the `fields` parameter to return only specific fields.
+await request(\`/api/restaurants?\${query}\`);
+```
-The [REST API](/cms/api/rest) by default does not populate any relations, media fields, components, or dynamic zones. Use the [`populate` parameter](#population) to populate specific fields. Use the [`fields` parameter](#field-selection) to return only specific fields with the query results.
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
-:::tip
+### Find published documents without a draft {#published-without-draft}
-Strapi takes advantage of the ability of [the `qs` library](https://github.com/ljharb/qs) to parse nested objects to create more complex queries.
+`publicationFilter=published-without-draft` selects published documents that have no draft counterpart. It describes published rows, so REST returns them with the default `status=published`:
-Use `qs` directly to generate complex queries instead of creating them manually. Examples in this documentation showcase how you can use `qs`.
+#### GET /api/restaurants?publicationFilter=published-without-draft — Get published restaurants with no draft for the same locale
-You can also use the [interactive query builder](/cms/api/rest/interactive-query-builder) if you prefer playing with our online tool instead of generating queries with `qs` on your machine.
+Return published documents with no matching draft version for the same locale.
-:::
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?publicationFilter=published-without-draft' \\
+ -H 'Authorization: Bearer '
+```
-## Field selection
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ publicationFilter: 'published-without-draft',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
-Queries can accept a `fields` parameter to select only some fields. By default, the REST API only returns the following [types of fields](/cms/backend-customization/models#model-attributes):
+await request(\`/api/restaurants?\${query}\`);
+```
-- string types: `string`, `text`, `richtext`, `enumeration`, `email`, `password`, and `uid`,
-- date types: `date`, `time`, `datetime`, and `timestamp`,
-- number types: `integer`, `biginteger`, `float`, and `decimal`,
-- generic types: `boolean`, `array`, and `JSON`.
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "documentId": "j0klm1n2o3p4q5r6s7t8u9v",
+ "name": "Legacy Restaurant",
+ "publishedAt": "2024-01-10T09:15:00.000Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
-| Use case | Example parameter syntax |
-|-----------------------|---------------------------------------|
-| Select a single field | `fields=name` |
-| Select multiple fields| `fields[0]=name&fields[1]=description`|
+### Find published documents with a draft {#published-with-draft}
-:::note
-Field selection does not work on relational, media, component, or dynamic zone fields. To populate these fields, use the [`populate` parameter](#population).
-:::
+`publicationFilter=published-with-draft` selects published documents that also have a draft. It describes published rows, so REST returns them with the default `status=published`:
-#### GET /api/restaurants — Return only name and description fields
+#### GET /api/restaurants?publicationFilter=published-with-draft — Get published restaurants that also have a draft for the same locale
-Use the fields parameter to select only specific fields in the response.
+Return published documents that also have a matching draft version for the same locale.
-**URL:**
+**cURL:**
```
-GET /api/restaurants?fields[0]=name&fields[1]=description
+curl 'http://localhost:1337/api/restaurants?publicationFilter=published-with-draft' \\
+ -H 'Authorization: Bearer '
```
**JavaScript:**
```
const qs = require('qs');
-const query = qs.stringify(
- {
- fields: ['name', 'description'],
- },
- {
+const query = qs.stringify({
+ publicationFilter: 'published-with-draft',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(\`/api/users?\${query}\`);
+await request(\`/api/restaurants?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": [
- {
- "id": 4,
- "Name": "Pizzeria Arrivederci",
- "Description": [
- {
- "type": "paragraph",
- "children": [
- {
- "type": "text",
- "text": "Specialized in pizza, we invite you to rediscover our classics, such as 4 Formaggi or Calzone, and our original creations such as Do Luigi or Nduja."
- }
- ]
- }
- ],
- "documentId": "lr5wju2og49bf820kj9kz8c3"
- }
- ],
- "meta": {
- "pagination": {
- "page": 1,
- "pageSize": 25,
- "pageCount": 1,
- "total": 4
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
}
- }
}
```
-## Population
-
-The REST API by default does not populate any type of fields, so it will not populate relations, media fields, components, or dynamic zones unless you pass a `populate` parameter to populate various field types. Populated relations always return full objects; the REST API currently cannot return just an array of IDs.
-
-:::prerequisites
-The `find` permission must be enabled for the content-types that are being populated. If a role does not have access to a content-type, the content-type will not be populated (see [Users & Permissions](/cms/features/users-permissions#editing-a-role) for additional information on how to enable `find` permissions for content-types).
-:::
-
-You can use the `populate` parameter alone or [in combination with multiple operators](#combining-population-with-other-operators) for more control over the population.
-
-:::caution
-`populate=deep` plugins are [not recommended in Strapi](https://support.strapi.io/articles/8544110758-why-populate-deep-plugins-are-not-recommended-in-strapi).
-:::
+### Find documents with a published version {#has-published-version}
-:::note
-Large `populate` lists in the query string (many `populate[0]`, `populate[1]`, … entries) are bounded by the query parser `arrayLimit` (default: `100`). To allow a longer list, raise `arrayLimit` on the [`strapi::query` middleware](/cms/configurations/middlewares#query). Higher values increase parsing cost per request.
-:::
+`publicationFilter=has-published-version` selects documents that have both a draft and a published version for the same locale. `status` then decides which version of those documents you get back. Unlike `published-without-draft`, it excludes published documents that have no draft counterpart.
-The following table lists populate use cases with example syntax. Each row links to the Understanding populate guide for details:
+For instance, with `status=draft`, the query returns the draft versions:
-| Use case | Example parameter syntax | Detailed explanations to read |
-|-----------| ---------------|-----------------------|
-| Populate everything, 1 level deep, including media fields, relations, components, and dynamic zones | `populate=*`| [Populate all relations and fields, 1 level deep](/cms/api/rest/guides/understanding-populate#populate-all-relations-and-fields-1-level-deep) |
-| Populate one relation,
1 level deep | `populate=a-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) |
-| Populate several relations,
1 level deep | `populate[0]=relation-name&populate[1]=another-relation-name&populate[2]=yet-another-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) |
-| Populate some relations, several levels deep | `populate[root-relation-name][populate][0]=nested-relation-name`| [Populate several levels deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-several-levels-deep-for-specific-relations) |
-| Populate a component | `populate[0]=component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) |
-| Populate a component and one of its nested components | `populate[0]=component-name&populate[1]=component-name.nested-component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) |
-| Populate a dynamic zone (only its first-level scalar fields) | `populate[0]=dynamic-zone-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) |
-| Populate a dynamic zone, including component-specific fields, nested components, and relations | `populate[dynamic-zone-name][on][component-category.component-name][populate][relation-name][populate][0]=field-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) |
+#### GET /api/restaurants?status=draft&publicationFilter=has-published-version — Get the draft versions of restaurants that also have a published version
-:::tip
-To build complex queries with multiple-level population, use the [interactive query builder](/cms/api/rest/interactive-query-builder) tool. For more detailed explanations and examples, see the [REST API guides](/cms/api/rest/guides/intro).
-:::
+Return the draft versions of documents that also have a published version for the same locale.
-### Combining population with other operators
+**cURL:**
+```
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version' \\
+ -H 'Authorization: Bearer '
+```
-You can combine the `populate` operator with other operators such as [field selection](/cms/api/rest/populate-select#field-selection), [filters](/cms/api/rest/filters), and [sort](/cms/api/rest/sort-pagination) in the population queries.
+**JavaScript:**
+```
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version',
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
-:::note
-Top-level pagination parameters (e.g., `pagination[page]` and `pagination[pageSize]`) work alongside `populate` to paginate the main query results. However, you cannot apply pagination parameters directly to populated relations to limit the number of related entries returned within each result (nested pagination on relations is not supported in the REST API).
-:::
+await request(\`/api/restaurants?\${query}\`);
+```
-#### Populate with field selection
+**Response 200 OK:**
+```json
+{
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
-`fields` and `populate` can be combined.
+
+With `status=published` (the REST default), the same query returns the currently live version of those documents instead:
-#### GET /api/articles — Populate with field selection
+#### GET /api/restaurants?publicationFilter=has-published-version — Get the currently live version of restaurants that also have a draft
-Combine fields and populate parameters to select specific fields on both the main entry and its relations.
+Return the currently live versions of documents that also have a published version for the same locale.
-**URL:**
+**cURL:**
```
-GET /api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url
+curl 'http://localhost:1337/api/restaurants?publicationFilter=has-published-version' \\
+ -H 'Authorization: Bearer '
```
**JavaScript:**
```
const qs = require('qs');
-const query = qs.stringify(
- {
- fields: ['title', 'slug'],
- populate: {
- headerImage: {
- fields: ['name', 'url'],
- },
- },
- },
- {
+const query = qs.stringify({
+ publicationFilter: 'has-published-version',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(\`/api/articles?\${query}\`);
+await request(\`/api/restaurants?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": [
- {
- "id": 1,
- "documentId": "h90lgohlzfpjf3bvan72mzll",
- "title": "Test Article",
- "slug": "test-article",
- "headerImage": {
- "id": 1,
- "documentId": "cf07g1dbusqr8mzmlbqvlegx",
- "name": "17520.jpg",
- "url": "/uploads/17520_73c601c014.jpg"
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": "2024-03-14T15:40:45.330Z",
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
}
}
- ],
- "meta": {}
}
```
-#### Populate with filtering
+### Find documents published in at least one locale {#has-published-version-document}
-`filters` and `populate` can be combined.
+`publicationFilter=has-published-version-document` considers all locales, so it matches a document as soon as one of its locales is published. With `status=draft`, it returns the draft versions of every locale of those documents, including locales that were never published themselves:
-#### GET /api/articles — Populate with filtering
+#### GET /api/restaurants?status=draft&publicationFilter=has-published-version-document — Get the drafts of restaurants published in at least one locale
-Combine populate with sort and filter parameters to refine which related entries are returned.
+Return the draft versions of documents published in at least one locale.
-**URL:**
+**cURL:**
```
-GET /api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars
+curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version-document' \\
+ -H 'Authorization: Bearer '
```
**JavaScript:**
```
const qs = require('qs');
-const query = qs.stringify(
- {
- populate: {
- categories: {
- sort: ['name:asc'],
- filters: {
- name: {
- $eq: 'Cars',
- },
- },
- },
- },
- },
- {
+const query = qs.stringify({
+ status: 'draft',
+ publicationFilter: 'has-published-version-document',
+}, {
encodeValuesOnly: true, // prettify URL
- }
-);
+});
-await request(\`/api/articles?\${query}\`);
+await request(\`/api/restaurants?\${query}\`);
```
**Response 200 OK:**
```json
{
- "data": [
- {
- "id": 1,
- "documentId": "a1b2c3d4e5d6f7g8h9i0jkl",
- "title": "Test Article",
- "categories": {
- "data": [
- {
- "id": 2,
- "documentId": "jKd8djla9ndalk98hflj3",
- "name": "Cars"
- }
- ]
+ "data": [
+ {
+ "documentId": "a1b2c3d4e5f6g7h8i9j0klm",
+ "name": "Biscotte Restaurant",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
}
}
- ],
- "meta": {}
}
```
-:::note
-For many-to-many and other join-table relations, an explicit `sort` within a `populate` object overrides the default connect order. Omit `sort` to preserve the connect order (the order in which entries were associated).
-:::
+## Combine with other parameters {#combine}
-:::tip Performance tip
-In production, always use explicit population instead of wildcards like `populate=*`. Limit population depth to 2-3 levels and consider centralizing population logic in route middlewares. See [Building High-Performance Strapi Applications](https://strapi.io/blog/building-high-performance-strapi-applications-common-pitfalls-and-best-practices) on the Strapi blog.
-:::
+`publicationFilter` can be combined with [`filters`](/cms/api/rest/filters), [`locale`](/cms/api/rest/locale), [`populate`](/cms/api/rest/populate-select), and other [REST parameters](/cms/api/rest/parameters). All conditions are applied together.
@@ -14257,7 +15385,7 @@ The sorting order can be defined with:
You can sort by multiple fields by passing fields in a `sort` array.
-#### GET /api/restaurants — Sort using 2 fields
+#### GET /api/restaurants?sort[0]=Description&sort[1]=Name — Sort using 2 fields
Sort results by Description and Name fields.
@@ -14328,7 +15456,7 @@ await request(\`/api/restaurants?\${query}\`);
Using the `sort` parameter and defining `:asc` or `:desc` on sorted fields, you can get results sorted in a particular order.
-#### GET /api/restaurants — Sort using 2 fields and set the order
+#### GET /api/restaurants?sort[0]=Description:asc&sort[1]=Name:desc — Sort using 2 fields and set the order
Sort results by Description ascending and Name descending.
@@ -14416,7 +15544,7 @@ To paginate results by page, use the following parameters:
| `pagination[pageSize]` | Integer | Page size | 25 |
| `pagination[withCount]` | Boolean | Adds the total numbers of entries and the number of pages to the response | True |
-#### GET /api/articles — Pagination by page
+#### GET /api/articles?pagination[page]=1&pagination[pageSize]=10 — Pagination by page
Return only 10 entries on page 1.
@@ -14471,7 +15599,7 @@ To paginate results by offset, use the following parameters:
The default and maximum values for `pagination[limit]` can be [configured in the `./config/api.js`](/cms/configurations/api) file with the `api.rest.defaultLimit` and `api.rest.maxLimit` keys.
:::
-#### GET /api/articles — Pagination by offset
+#### GET /api/articles?pagination[start]=0&pagination[limit]=10 — Pagination by offset
Return only the first 10 entries using offset.
@@ -14539,19 +15667,27 @@ In the response data, the `publishedAt` field is `null` for drafts.
Since published versions are returned by default, passing no status parameter is equivalent to passing `status=published`.
:::
+To select documents by how their draft and published versions relate (never-published, modified, and others), see [REST API: `publicationFilter`](/cms/api/rest/publication-filter).
+
#### GET /api/articles?status=draft — Get draft versions of restaurants
Returns draft versions of documents by passing the status=draft query parameter.
+**cURL:**
+```
+curl 'http://localhost:1337/api/articles?status=draft' \\
+ -H 'Authorization: Bearer '
+```
+
**JavaScript:**
```
const qs = require('qs');
const query = qs.stringify({
- status: 'draft',
+ status: 'draft',
}, {
- encodeValuesOnly: true, // prettify URL
+ encodeValuesOnly: true, // prettify URL
});
await request(\`/api/articles?\${query}\`);
@@ -32099,14 +33235,17 @@ To unpublish several entries at the same time:
### Usage with APIs
-Draft or published content can be requested, created, updated, and deleted using the `status` parameter through the various front-end APIs accessible from [Strapi's Content API](/cms/api/content-api):
+Draft or published content can be requested, created, updated, and deleted using the `status` parameter through the various front-end APIs accessible from [Strapi's Content API](/cms/api/content-api). To query documents by the relationship between their draft and published versions, such as never-published or modified documents, use the `publicationFilter` parameter (REST and GraphQL) or the equivalent Document Service API option.
- [REST API](/cms/api/rest/status): Learn how to use the status parameter with the REST API.
+- [REST API: publicationFilter](/cms/api/rest/publication-filter): Query never-published, modified, and other publication-related groups of documents.
- [GraphQL API](/cms/api/graphql#status): Learn how to use the status parameter with GraphQL API.
+- [GraphQL API: publicationFilter](/cms/api/graphql#publication-filter): Query never-published, modified, and more with the PublicationFilter enum.
-On the back-end server of Strapi, the Document Service API can also be used to interact with localized content:
+On the back-end server of Strapi, the Document Service API can also query and manage draft and published content:
- [Document Service API](/cms/api/document-service/status): Learn how to use the status parameter with the Document Service API.
+- [Document Service API: publicationFilter](/cms/api/document-service/publication-filter): Query never-published, modified, and more from server-side code.
diff --git a/docusaurus/static/llms.txt b/docusaurus/static/llms.txt
index a0ce549e4a..7cfb058b14 100644
--- a/docusaurus/static/llms.txt
+++ b/docusaurus/static/llms.txt
@@ -44,6 +44,7 @@
- [Using the locale parameter with the Document Service API](https://docs.strapi.io/cms/api/document-service/locale.md): The locale parameter in the Document Service API lets you query, create, update, delete, publish, and unpublish documents for specific language versions using methods like findOne(), findMany(), update(), and delete().
- [Extending the Document Service behavior](https://docs.strapi.io/cms/api/document-service/middlewares.md): Document Service middlewares allow you to perform actions before and after Document Service methods run by registering middleware functions via strapi.documents.use() with access to content type context and method parameters.
- [Using Populate with the Document Service API](https://docs.strapi.io/cms/api/document-service/populate.md): Use the populate parameter with the Document Service API to explicitly load relations, media fields, components, and dynamic zones at one or multiple levels deep, and within create(), update(), publish(), and delete() operations.
+- [Using publicationFilter with the Document Service API](https://docs.strapi.io/cms/api/document-service/publication-filter.md): Use the optional publicationFilter parameter to query documents by the relationship between their draft and published versions, for example drafts that were never published, or entries modified since they were last published. It works with findOne(), findFirst(), findMany(), and count(), and combines with other query parameters. status still decides whether you get the draft or the published version.
- [Using Sort & Pagination with the Document Service API](https://docs.strapi.io/cms/api/document-service/sort-pagination.md): Use the Document Service API's sort and pagination parameters to order query results by single or multiple fields and control result limits with limit and start.
- [Using Draft & Publish with the Document Service API](https://docs.strapi.io/cms/api/document-service/status.md): Use the status parameter with the Document Service API to retrieve published or draft versions of documents, count documents by status, and directly publish documents during creation or updates.
- [Entity Service API](https://docs.strapi.io/cms/api/entity-service.md): The Entity Service API is a backend layer that handles complex content structures like components and dynamic zones, providing CRUD operations, filtering, populating relations, and pagination via strapi.entityService.
@@ -72,6 +73,7 @@
- [Locale](https://docs.strapi.io/cms/api/rest/locale.md): The locale REST API parameter retrieves and manages documents in specific languages, defaulting to the application's default locale. Use it to fetch, create, update, and delete locale-specific versions of documents in both collection and single types.
- [Parameters](https://docs.strapi.io/cms/api/rest/parameters.md): REST API parameters filter, sort, paginate, and select fields and relations in Strapi queries. Use filters, locale, populate, sort, and pagination to refine your content requests.
- [Populate and Select](https://docs.strapi.io/cms/api/rest/populate-select.md): Use the populate parameter to include relations, media fields, components, and dynamic zones in REST API responses. Use the fields parameter to return only specific fields.
+- [REST API: publicationFilter](https://docs.strapi.io/cms/api/rest/publication-filter.md): Add the optional publicationFilter query parameter to query documents by the relationship between their draft and published versions, for example documents that were never published, or documents modified since they were last published. It combines with other query parameters, and status still decides whether you get the draft or the published version.
- [Relations](https://docs.strapi.io/cms/api/rest/relations.md): Use connect, disconnect, and set parameters in REST and GraphQL API requests to manage relations between content-types. Reorder relations using positional arguments like before, after, start, or end.
- [Sort and Pagination](https://docs.strapi.io/cms/api/rest/sort-pagination.md): Sort REST API results on one or multiple fields with :asc or :desc syntax, and paginate using either page-based or offset-based parameters.
- [Status](https://docs.strapi.io/cms/api/rest/status.md): The REST API's status parameter filters documents by their publication state, returning either published versions (default) or drafts by passing status=draft.