diff --git a/docusaurus/docs/cms/api/document-service.md b/docusaurus/docs/cms/api/document-service.md
index e7bb5270a0..758e515657 100644
--- a/docusaurus/docs/cms/api/document-service.md
+++ b/docusaurus/docs/cms/api/document-service.md
@@ -86,6 +86,7 @@ Syntax: `findOne(parameters: Params) => Document`
| `documentId` | Document id | | `ID` |
| [`locale`](/cms/api/document-service/locale#find-one)| Locale of the document to find. | Default locale | String or `undefined` |
| [`status`](/cms/api/document-service/status#find-one) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be:
- `'published'` to find only published documents
- `'draft'` to find only draft documents
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`fields`](/cms/api/document-service/fields#findone) | [Select fields](/cms/api/document-service/fields#findone) to return | All fields
(except those not populated by default) | Object |
| [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object |
@@ -135,6 +136,7 @@ Syntax: `findFirst(parameters: Params) => Document`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#find-first) | Locale of the documents to find. | Default locale | String or `undefined` |
| [`status`](/cms/api/document-service/status#find-first) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find only draft documents
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
| [`fields`](/cms/api/document-service/fields#findfirst) | [Select fields](/cms/api/document-service/fields#findfirst) to return | All fields
(except those not populate by default) | Object |
| [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object |
@@ -225,6 +227,7 @@ Syntax: `findMany(parameters: Params) => Document[]`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#find-many) | Locale of the documents to find. | Default locale | String or `undefined` |
| [`status`](/cms/api/document-service/status#find-many) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find only draft documents
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
| [`fields`](/cms/api/document-service/fields#findmany) | [Select fields](/cms/api/document-service/fields#findmany) to return | All fields
(except those not populate by default) | Object |
| [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object |
@@ -732,12 +735,13 @@ Syntax: `count(parameters: Params) => number`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#count) | Locale of the documents to count | Default locale | String or `null` |
| [`status`](/cms/api/document-service/status#count) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find draft documents (will return all documents)
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
:::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 documents that have never been published, use the [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) parameter.
:::
#### Examples
@@ -746,7 +750,7 @@ This means that counting with the `status: 'draft'` parameter still returns the
##### Generic example
-If no parameter is passed, the `count()` method the total number of documents for the default locale:
+If no parameter is passed, the `count()` method returns the total number of documents for the default locale:
diff --git a/docusaurus/docs/cms/api/document-service/status.md b/docusaurus/docs/cms/api/document-service/status.md
index efb7042581..57652948f3 100644
--- a/docusaurus/docs/cms/api/document-service/status.md
+++ b/docusaurus/docs/cms/api/document-service/status.md
@@ -1,6 +1,6 @@
---
title: Using Draft & Publish with the Document Service API
-description: Use Strapi's Document Service API to return either the draft or the published version of a document
+description: Use Strapi's Document Service API to return draft or published versions of a document and filter by publication history with hasPublishedVersion.
displayed_sidebar: cmsSidebar
sidebar_label: Status
tags:
@@ -12,6 +12,7 @@ tags:
- findOne()
- findMany()
- findFirst()
+- hasPublishedVersion
- published version
- status
@@ -19,11 +20,12 @@ tags:
# Document Service API: Usage with Draft & Publish
-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:
+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` and `hasPublishedVersion` parameters to:
- return the published version of a document,
-- count documents depending on their status,
-- and directly publish a document while creating it or updating it.
+- filter documents by whether they have ever been published,
+- count documents depending on their status,
+- directly publish a document while creating it or updating it.
:::note
Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter.
@@ -152,7 +154,7 @@ const publishedCount = await strapi.documents("api::restaurant.restaurant").coun
:::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 documents that have never been published, use the [`hasPublishedVersion`](#has-published-version) parameter.
:::
## Create a draft and publish it {#create}
@@ -224,3 +226,133 @@ await strapi.documents('api::restaurant.restaurant').update({
+## Filter by publication history with `hasPublishedVersion` {#has-published-version}
+
+The `hasPublishedVersion` parameter filters documents by whether a published version exists. Use `hasPublishedVersion` with `status: 'draft'` to distinguish documents that have never been published from drafts of already-published documents.
+
+The parameter accepts a boolean (`true` or `false`) and works with `findOne()`, `findFirst()`, `findMany()`, and `count()`.
+
+### Find never-published documents
+
+Pass `hasPublishedVersion: false` with `status: 'draft'` to return only documents that have never been published:
+
+
+
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: false,
+});
+```
+
+
+
+
+
+```js
+[
+ {
+ documentId: "ln1gkzs6ojl9d707xn6v86mw",
+ name: "Restaurant B",
+ publishedAt: null, // never published
+ locale: "en",
+ // …
+ },
+ // … other never-published documents
+]
+```
+
+
+
+
+### Find drafts of published documents
+
+Pass `hasPublishedVersion: true` with `status: 'draft'` to return only drafts of documents that have been published at least once:
+
+
+
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: true,
+});
+```
+
+
+
+
+
+```js
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null, // draft version is returned, but a published version exists
+ locale: "en",
+ // …
+ },
+ // …
+]
+```
+
+
+
+
+:::note
+Because the draft version is returned, `publishedAt` is `null` even when a published version exists.
+:::
+
+### Count never-published documents
+
+Combine `hasPublishedVersion: false` with `count()` to count documents that have never been published:
+
+```js
+const neverPublishedCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'draft',
+ hasPublishedVersion: false,
+});
+```
+
+### Combine with other parameters
+
+`hasPublishedVersion` can be combined with [`locale`](/cms/api/document-service/locale), [`filters`](/cms/api/document-service/filters), and [`populate`](/cms/api/document-service/populate):
+
+
+
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: false,
+ locale: 'en',
+ filters: {
+ name: { $startsWith: 'Pizzeria' },
+ },
+});
+```
+
+
+
+
+
+```js
+[
+ {
+ documentId: "j9k8l7m6n5o4p3q2r1s0tuv",
+ name: "Pizzeria Napoli",
+ publishedAt: null,
+ locale: "en",
+ // …
+ },
+]
+```
+
+
+
+
+:::tip
+When `hasPublishedVersion` is used with [`populate`](/cms/api/document-service/populate), the filter also applies to populated relations. Each related document is filtered based on whether a published version exists for it.
+:::
+
diff --git a/docusaurus/docs/cms/api/graphql.md b/docusaurus/docs/cms/api/graphql.md
index 2b785e7364..53ba544473 100644
--- a/docusaurus/docs/cms/api/graphql.md
+++ b/docusaurus/docs/cms/api/graphql.md
@@ -425,6 +425,52 @@ query Query($status: PublicationStatus) {
}
```
+### Filter by publication history with `hasPublishedVersion` {#haspublishedversion}
+
+If the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled for the content-type, you can add a `hasPublishedVersion` parameter to queries to filter documents based on whether they have a published version.
+
+Use `hasPublishedVersion` with `status: DRAFT` to distinguish between documents that have never been published and drafts of already-published documents:
+
+```graphql title="Example: Fetch only never-published drafts"
+{
+ restaurants(status: DRAFT, hasPublishedVersion: false) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+```graphql title="Example: Fetch drafts of already-published documents"
+{
+ restaurants(status: DRAFT, hasPublishedVersion: true) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+`hasPublishedVersion` also works with Relay-style connection queries:
+
+```graphql title="Example: Count never-published drafts with Relay-style query"
+{
+ restaurants_connection(status: DRAFT, hasPublishedVersion: false) {
+ nodes {
+ documentId
+ name
+ }
+ pageInfo {
+ total
+ }
+ }
+}
+```
+
+:::tip
+When `hasPublishedVersion` is used with queries that include relations, the filter also applies to related documents. Each related document is filtered based on whether a published version exists for it.
+:::
+
## Aggregations
Aggregations can be used to compute metrics such as counts, sums, or grouped totals without fetching every document individually. Aggregations are exposed through connection queries: every collection type includes an `aggregate` field under its `_connection` query.
diff --git a/docusaurus/docs/cms/api/rest/parameters.md b/docusaurus/docs/cms/api/rest/parameters.md
index 84d57396b1..900af60392 100644
--- a/docusaurus/docs/cms/api/rest/parameters.md
+++ b/docusaurus/docs/cms/api/rest/parameters.md
@@ -25,6 +25,7 @@ The following API parameters are available:
| `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) |
+| `hasPublishedVersion` | Boolean | [Filter by whether the document has a published version](/cms/api/rest/status#haspublishedversion-) |
| `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) |
diff --git a/docusaurus/docs/cms/api/rest/status.md b/docusaurus/docs/cms/api/rest/status.md
index 2d38ff9a0f..556b947688 100644
--- a/docusaurus/docs/cms/api/rest/status.md
+++ b/docusaurus/docs/cms/api/rest/status.md
@@ -1,6 +1,6 @@
---
title: Status
-description: Use Strapi's REST API to work with draft or published versions of your documents.
+description: Use Strapi's REST API to work with draft or published versions of your documents and filter by publication history with hasPublishedVersion.
sidebarDepth: 3
sidebar_label: Status
displayed_sidebar: cmsSidebar
@@ -8,6 +8,7 @@ tags:
- API
- Content API
- find
+- hasPublishedVersion
- interactive query builder
- REST API
- qs library
@@ -18,14 +19,16 @@ import QsIntroFull from '/docs/snippets/qs-intro-full.md'
import QsForQueryBody from '/docs/snippets/qs-for-query-body.md'
import QsForQueryTitle from '/docs/snippets/qs-for-query-title.md'
-# REST API: `status`
+# REST API: `status` and `hasPublishedVersion`
-The [REST API](/cms/api/rest) offers the ability to filter results based on their status, draft or published.
+The [REST API](/cms/api/rest) offers the ability to filter results based on their status, draft or published, and by whether they have a published version.
:::prerequisites
The [Draft & Publish](/cms/features/draft-and-publish) feature should be enabled.
:::
+## `status`
+
Queries can accept a `status` parameter to fetch documents based on their status:
- `published`: returns only the published version of documents (default)
@@ -107,3 +110,132 @@ await request(`/api/articles?${query}`);
+
+## `hasPublishedVersion`
+
+Queries can accept a `hasPublishedVersion` parameter to filter documents by whether a published version exists. Use `hasPublishedVersion` with `status=draft` to distinguish documents that have never been published from drafts of already-published documents.
+
+- `hasPublishedVersion=false`: returns only documents that have never been published
+- `hasPublishedVersion=true`: returns only drafts of documents that have a published version
+
+
+
+
+
+
+`GET /api/articles?status=draft&hasPublishedVersion=false`
+
+
+
+
+JavaScript query (built with the qs library):
+
+
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ hasPublishedVersion: false,
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/articles?${query}`);
+```
+
+
+
+
+
+```json
+{
+ "data": [
+ {
+ "id": 8,
+ "documentId": "ln1gkzs6ojl9d707xn6v86mw",
+ "Name": "Restaurant B",
+ "createdAt": "2024-03-06T13:43:30.172Z",
+ "updatedAt": "2024-03-06T21:38:46.353Z",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+`GET /api/articles?status=draft&hasPublishedVersion=true`
+
+
+
+
+JavaScript query (built with the qs library):
+
+
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ hasPublishedVersion: true,
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/articles?${query}`);
+```
+
+
+
+
+
+```json
+{
+ "data": [
+ {
+ "id": 5,
+ "documentId": "znrlzntu9ei5onjvwfaalu2v",
+ "Name": "Biscotte Restaurant",
+ "createdAt": "2024-03-06T13:43:30.172Z",
+ "updatedAt": "2024-03-06T21:38:46.353Z",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
+
+
+:::note
+Because the draft version is returned, `publishedAt` is `null` even when a published version exists.
+:::
+
+:::caution
+Passing an invalid value for `hasPublishedVersion` (anything other than `true` or `false`) returns a `400 Bad Request` error.
+:::
diff --git a/docusaurus/static/llms-code.txt b/docusaurus/static/llms-code.txt
index 4c6f261c79..b87c875167 100644
--- a/docusaurus/static/llms-code.txt
+++ b/docusaurus/static/llms-code.txt
@@ -4834,6 +4834,120 @@ File path: N/A
```
+## Find never-published documents
+Description: Code example from "Find never-published documents"
+(Source: https://docs.strapi.io/cms/api/document-service/status#find-never-published-documents)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: false,
+});
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+[
+ {
+ documentId: "ln1gkzs6ojl9d707xn6v86mw",
+ name: "Restaurant B",
+ publishedAt: null, // never published
+ locale: "en",
+ // …
+ },
+ // … other never-published documents
+]
+```
+
+
+## Find drafts of published documents
+Description: Code example from "Find drafts of published documents"
+(Source: https://docs.strapi.io/cms/api/document-service/status#find-drafts-of-published-documents)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: true,
+});
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+[
+ {
+ documentId: "a1b2c3d4e5f6g7h8i9j0klm",
+ name: "Biscotte Restaurant",
+ publishedAt: null, // draft version is returned, but a published version exists
+ locale: "en",
+ // …
+ },
+ // …
+]
+```
+
+
+## Count never-published documents
+Description: Combine hasPublishedVersion: false with count() to count documents that have never been published:
+(Source: https://docs.strapi.io/cms/api/document-service/status#count-never-published-documents)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const neverPublishedCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'draft',
+ hasPublishedVersion: false,
+});
+```
+
+
+## Combine with other parameters
+Description: Code example from "Combine with other parameters"
+(Source: https://docs.strapi.io/cms/api/document-service/status#combine-with-other-parameters)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const documents = await strapi.documents("api::restaurant.restaurant").findMany({
+ status: 'draft',
+ hasPublishedVersion: false,
+ locale: 'en',
+ filters: {
+ name: { $startsWith: 'Pizzeria' },
+ },
+});
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+[
+ {
+ documentId: "j9k8l7m6n5o4p3q2r1s0tuv",
+ name: "Pizzeria Napoli",
+ publishedAt: null,
+ locale: "en",
+ // …
+ },
+]
+```
+
+
# Entity Service API
Source: https://docs.strapi.io/cms/api/entity-service
@@ -6042,6 +6156,55 @@ query Query($status: PublicationStatus) {
```
+## Filter by publication history with hasPublishedVersion
+Description: Use hasPublishedVersion with status: DRAFT to distinguish between documents that have never been published and drafts of already-published documents:
+(Source: https://docs.strapi.io/cms/api/graphql#haspublishedversion)
+
+Language: JSON
+File path: Example:
+
+```json
+{
+ restaurants(status: DRAFT, hasPublishedVersion: false) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+---
+Language: JSON
+File path: Example:
+
+```json
+{
+ restaurants(status: DRAFT, hasPublishedVersion: true) {
+ documentId
+ name
+ publishedAt
+ }
+}
+```
+
+Language: JSON
+File path: Example:
+
+```json
+{
+ restaurants_connection(status: DRAFT, hasPublishedVersion: false) {
+ nodes {
+ documentId
+ name
+ }
+ pageInfo {
+ total
+ }
+ }
+}
+```
+
+
## Aggregations
Description: Aggregations can be used to compute metrics such as counts, sums, or grouped totals without fetching every document individually.
(Source: https://docs.strapi.io/cms/api/graphql#aggregations)
@@ -10625,9 +10788,9 @@ File path: ./config/api.js
# Status
Source: https://docs.strapi.io/cms/api/rest/status
-## REST API: status
-Description: Code example from "REST API: status"
-(Source: https://docs.strapi.io/cms/api/rest/status#rest-api-status)
+## REST API: status and hasPublishedVersion
+Description: Code example from "REST API: status and hasPublishedVersion"
+(Source: https://docs.strapi.io/cms/api/rest/status#rest-api-status-and-haspublishedversion)
Language: JavaScript
File path: N/A
@@ -10685,6 +10848,98 @@ File path: N/A
```
+## hasPublishedVersion
+Description: Code example from "hasPublishedVersion"
+(Source: https://docs.strapi.io/cms/api/rest/status#haspublishedversion)
+
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ hasPublishedVersion: false,
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/articles?${query}`);
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "id": 8,
+ "documentId": "ln1gkzs6ojl9d707xn6v86mw",
+ "Name": "Restaurant B",
+ "createdAt": "2024-03-06T13:43:30.172Z",
+ "updatedAt": "2024-03-06T21:38:46.353Z",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+---
+Language: JavaScript
+File path: N/A
+
+```js
+const qs = require('qs');
+const query = qs.stringify({
+ status: 'draft',
+ hasPublishedVersion: true,
+}, {
+ encodeValuesOnly: true, // prettify URL
+});
+
+await request(`/api/articles?${query}`);
+```
+
+---
+Language: JSON
+File path: N/A
+
+```json
+{
+ "data": [
+ {
+ "id": 5,
+ "documentId": "znrlzntu9ei5onjvwfaalu2v",
+ "Name": "Biscotte Restaurant",
+ "createdAt": "2024-03-06T13:43:30.172Z",
+ "updatedAt": "2024-03-06T21:38:46.353Z",
+ "publishedAt": null,
+ "locale": "en"
+ }
+ ],
+ "meta": {
+ "pagination": {
+ "page": 1,
+ "pageSize": 25,
+ "pageCount": 1,
+ "total": 1
+ }
+ }
+}
+```
+
+
# Upload files
Source: https://docs.strapi.io/cms/api/rest/upload
diff --git a/docusaurus/static/llms-full.txt b/docusaurus/static/llms-full.txt
index 95e6a117e6..736ef04c0c 100644
--- a/docusaurus/static/llms-full.txt
+++ b/docusaurus/static/llms-full.txt
@@ -2180,6 +2180,7 @@ Syntax: `findFirst(parameters: Params) => Document`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#find-first) | Locale of the documents to find. | Default locale | String or `undefined` |
| [`status`](/cms/api/document-service/status#find-first) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find only draft documents
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
| [`fields`](/cms/api/document-service/fields#findfirst) | [Select fields](/cms/api/document-service/fields#findfirst) to return | All fields
(except those not populate by default) | Object |
| [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object |
@@ -2214,6 +2215,7 @@ Syntax: `findMany(parameters: Params) => Document[]`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#find-many) | Locale of the documents to find. | Default locale | String or `undefined` |
| [`status`](/cms/api/document-service/status#find-many) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find only draft documents
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
| [`fields`](/cms/api/document-service/fields#findmany) | [Select fields](/cms/api/document-service/fields#findmany) to return | All fields
(except those not populate by default) | Object |
| [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object |
@@ -2441,12 +2443,13 @@ Syntax: `count(parameters: Params) => number`
|-----------|-------------|---------|------|
| [`locale`](/cms/api/document-service/locale#count) | Locale of the documents to count | Default locale | String or `null` |
| [`status`](/cms/api/document-service/status#count) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: - `'published'` to find only published documents
- `'draft'` to find draft documents (will return all documents)
| `'draft'` | `'published'` or `'draft'` |
+| [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Filter by whether the document has a published version. Ignored when not set. | `undefined` | Boolean |
| [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object |
:::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 documents that have never been published, use the [`hasPublishedVersion`](/cms/api/document-service/status#has-published-version) parameter.
:::
#### Examples
@@ -2455,7 +2458,7 @@ This means that counting with the `status: 'draft'` parameter still returns the
##### Generic example
-If no parameter is passed, the `count()` method the total number of documents for the default locale:
+If no parameter is passed, the `count()` method returns the total number of documents for the default locale:
@@ -3425,11 +3428,12 @@ Source: https://docs.strapi.io/cms/api/document-service/status
# Document Service API: Usage with Draft & Publish
-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:
+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` and `hasPublishedVersion` parameters to:
- return the published version of a document,
-- count documents depending on their status,
-- and directly publish a document while creating it or updating it.
+- filter documents by whether they have ever been published,
+- count documents depending on their status,
+- directly publish a document while creating it or updating it.
:::note
Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter.
@@ -3480,7 +3484,7 @@ const publishedCount = await strapi.documents("api::restaurant.restaurant").coun
:::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 documents that have never been published, use the [`hasPublishedVersion`](#has-published-version) parameter.
:::
## Create a draft and publish it {#create}
@@ -3495,6 +3499,41 @@ To automatically publish a document while updating it, add `status: 'published'`
+## Filter by publication history with `hasPublishedVersion` {#has-published-version}
+
+
+
+### Find drafts of published documents
+
+Pass `hasPublishedVersion: true` with `status: 'draft'` to return only drafts of documents that have been published at least once:
+
+
+
+:::note
+Because the draft version is returned, `publishedAt` is `null` even when a published version exists.
+:::
+
+### Count never-published documents
+
+Combine `hasPublishedVersion: false` with `count()` to count documents that have never been published:
+
+```js
+const neverPublishedCount = await strapi.documents("api::restaurant.restaurant").count({
+ status: 'draft',
+ hasPublishedVersion: false,
+});
+```
+
+### Combine with other parameters
+
+`hasPublishedVersion` can be combined with [`locale`](/cms/api/document-service/locale), [`filters`](/cms/api/document-service/filters), and [`populate`](/cms/api/document-service/populate):
+
+
+
+:::tip
+When `hasPublishedVersion` is used with [`populate`](/cms/api/document-service/populate), the filter also applies to populated relations. Each related document is filtered based on whether a published version exists for it.
+:::
+
# GraphQL API
@@ -4125,6 +4164,7 @@ The following API parameters are available:
| `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) |
+| `hasPublishedVersion` | Boolean | [Filter by whether the document has a published version](/cms/api/rest/status#has-published-version) |
| `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) |
@@ -4462,9 +4502,9 @@ The default and maximum values for `pagination[limit]` can be [configured in the
# Status
Source: https://docs.strapi.io/cms/api/rest/status
-# REST API: `status`
+# REST API: `status` and `hasPublishedVersion`
-The [REST API](/cms/api/rest) offers the ability to filter results based on their status, draft or published.
+The [REST API](/cms/api/rest) offers the ability to filter results based on their status, draft or published, and by whether they have a published version.
:::prerequisites
The [Draft & Publish](/cms/features/draft-and-publish) feature should be enabled.
@@ -4490,6 +4530,28 @@ Since published versions are returned by default, passing no status parameter is
+## `hasPublishedVersion`
+
+
+JavaScript query (built with the qs library):
+
+
+
+
+
+
+JavaScript query (built with the qs library):
+
+
+
+:::note
+Because the draft version is returned, `publishedAt` is `null` even when a published version exists.
+:::
+
+:::caution
+Passing an invalid value for `hasPublishedVersion` (anything other than `true` or `false`) returns a `400 Bad Request` error.
+:::
+
# Upload files
diff --git a/docusaurus/static/llms.txt b/docusaurus/static/llms.txt
index 4a3d7506c7..773d78b36e 100644
--- a/docusaurus/static/llms.txt
+++ b/docusaurus/static/llms.txt
@@ -39,7 +39,7 @@
- [Extending the Document Service behavior](https://docs.strapi.io/cms/api/document-service/middlewares): This document provides information about the middlewares in the Document Service API.
- [Using Populate with the Document Service API](https://docs.strapi.io/cms/api/document-service/populate): Use Strapi's Document Service API to populate or select some fields.
- [Using Sort & Pagination with the Document Service API](https://docs.strapi.io/cms/api/document-service/sort-pagination): Use Strapi's Document Service API to sort and paginate query results
-- [Using Draft & Publish with the Document Service API](https://docs.strapi.io/cms/api/document-service/status): Use Strapi's Document Service API to return either the draft or the published version of a document
+- [Using Draft & Publish with the Document Service API](https://docs.strapi.io/cms/api/document-service/status): Use Strapi's Document Service API to return draft or published versions of a document and filter by publication history with hasPublishedVersion.
- [GraphQL API](https://docs.strapi.io/cms/api/graphql): import DeepFilteringBlogLink from '/docs/snippets/deep-filtering-blog.md'
- [OpenAPI specification](https://docs.strapi.io/cms/api/openapi): Learn how to generate OpenAPI specifications for your Strapi applications using the @strapi/openapi package
- [REST API reference](https://docs.strapi.io/cms/api/rest): Interact with your Content-Types using the REST API endpoints Strapi generates for you.
@@ -51,7 +51,7 @@
- [Populate and Select](https://docs.strapi.io/cms/api/rest/populate-select): Use Strapi's REST API to populate or select certain fields.
- [Relations](https://docs.strapi.io/cms/api/rest/relations): Use the REST API to manage the order of relations
- [Sort and Pagination](https://docs.strapi.io/cms/api/rest/sort-pagination): Use Strapi's REST API to sort or paginate your data.
-- [Status](https://docs.strapi.io/cms/api/rest/status): Use Strapi's REST API to work with draft or published versions of your documents.
+- [Status](https://docs.strapi.io/cms/api/rest/status): Use Strapi's REST API to work with draft or published versions of your documents and filter by publication history with hasPublishedVersion.
- [Upload files](https://docs.strapi.io/cms/api/rest/upload): Learn how to use the /api/upload endpoints to upload files to Strapi with the REST API.
- [Back-end customization](https://docs.strapi.io/cms/backend-customization): Strapi’s back end is a Koa-based server where requests pass through global middlewares, routes, controllers, services, and models before the Document Service returns responses.
- [Controllers](https://docs.strapi.io/cms/backend-customization/controllers): Controllers bundle actions that handle business logic for each route within Strapi’s MVC pattern. This documentation demonstrates generating controllers, extending core ones with createCoreController, and delegating heavy logic to services.