# Document Service API

> Source: https://docs.strapi.io/cms/api/document-service

The Document Service API is the recommended backend API for interacting with content, providing `findOne`, `findMany`, `create`, `update`, and `delete` methods that work with stable `documentId` identifiers and support Draft & Publish operations.

The Document Service API is built on top of the **Query Engine API** <Annotation>2 different back-end APIs allow you to interact with your content: <ul><li>The [Query Engine API](/cms/api/query-engine) is the lower-level layer that offers unrestricted access to the database, but is not aware of complex Strapi content structures such as components and dynamic zones.</li><li>The Document Service API is built on top of the Query Engine and is the recommended way to interact with your content while you are customizing the back end server or developing plugins.</li></ul>More details can be found in the [Content API](/cms/api/content-api) and [backend customization](/cms/backend-customization) introductions. and is used to perform CRUD ([create](#create), [retrieve](#findone), [update](#update), and [delete](#delete)) operations on **documents** .

The Document Service API also supports [counting](#count) documents and, if [Draft & Publish](/cms/features/draft-and-publish) is enabled on the content-type, performing Strapi-specific operations such as [publishing](#publish), [unpublishing](#unpublish), and [discarding drafts](#discarddraft).

In Strapi 5, documents are uniquely identified by their `documentId` at the API level.

**`documentId` explained: Replacing `id` from Strapi v4**

In previous Strapi versions, the concept of `id` (used both in the Content API and as the database row identifier) was not always stable: a single entry could have multiple versions or localizations, and its numeric identifier `id` could change in cases such as duplication or import/export operations.

To address this limitation, Strapi 5 introduced `documentId`, a 24-character alphanumeric string, as a unique and persistent identifier for a content entry, independent of its physical records.

This new identifier is used internally in Strapi 5 to manage relationships, publishing, localization, and version history, as all possible variations of a content entry are now grouped under a single [document](/cms/api/document) concept.

As a result, starting with Strapi 5, many APIs and services rely on `documentId` instead of `id` to ensure consistency across operations. Some APIs may still return both `documentId` and `id` to ease the transition, but using `documentId` for content queries is strongly recommended, as `documentId` might be the only identifier used in future Strapi versions.

For more details on the transition from `id` to `documentId`, refer to the [breaking change page](/cms/migration/v4-to-v5/breaking-changes/use-document-id) and the [migration guide from Entity Service to Document Service API](/cms/migration/v4-to-v5/additional-resources/from-entity-service-to-document-service).

:::strapi Entity Service API is deprecated in Strapi 5
The Document Service API replaces the Entity Service API used in Strapi v4 ([see Strapi v4 documentation](https://docs-v4.strapi.io/dev-docs/api/entity-service)).

Additional information on how to migrate from the Entity Service API to the Document Service API can be found in the [migration reference](/cms/migration/v4-to-v5/additional-resources/from-entity-service-to-document-service).
:::

:::note
Relations can also be connected, disconnected, and set through the Document Service API, just like with the REST API (see the [REST API relations documentation](/cms/api/rest/relations) for examples).
:::

:::caution Document Service returns unsanitized data
The Document Service is a data-access layer: it interacts with the database and is not aware of user permissions or field visibility. Results may include private fields, passwords, and restricted relations.

The built-in REST and GraphQL APIs automatically sanitize responses before sending them to the client. But if you build custom controllers or plugin routes that call Document Service methods directly, you must sanitize the output yourself before returning it. Use `strapi.contentAPI.sanitize.output()` in your controller (see [Sanitization and validation when building custom controllers](/cms/backend-customization/controllers#sanitize-validate-custom-controllers) for details and code examples).
:::

## Configuration

The `documents.strictParams` option enables strict validation of parameters passed to Document Service methods such as `findMany` and `findOne`. Configure it in the [API configuration](/cms/configurations/api) file (`./config/api.js` or `./config/api.ts`). See the [API configuration](/cms/configurations/api) table for details on `documents.strictParams`.

## Document objects

Document methods return a document object or a list of document objects, which represent a version of a content entry grouped under a stable `documentId`. Returned objects typically include:

- `documentId`: Persistent identifier for the entry across locales and draft/published versions.
- `id`: Database identifier for the specific locale/version record.
- model fields: All fields defined in the content-type schema. Relations, components, and dynamic zones are not populated unless you opt in with `populate` (see [Populating fields](/cms/api/document-service/populate)) or limit fields with `fields` (see [Selecting fields](/cms/api/document-service/fields)).
- metadata: `publishedAt`, `createdAt`, `updatedAt`, and `createdBy`/`updatedBy` when available.

Optionally, document objects can also include a `status` and `locale` property if [Draft & Publish](/cms/features/draft-and-publish) and [Internationalization](/cms/features/internationalization) are enabled for the content-type.

## Method overview

Each section below documents the parameters and examples for a specific method:

| Method | Purpose |
| --- | --- |
| [`findOne()`](#findone) | Fetch a document by `documentId`, optionally scoping to a locale or status. |
| [`findFirst()`](#findfirst) | Return the first document that matches filters. |
| [`findMany()`](#findmany) | List documents with filters, sorting, and pagination. |
| [`create()`](#create) | Create a document, optionally targeting a locale. |
| [`update()`](#update) | Update a document by `documentId`. |
| [`delete()`](#delete) | Delete a document or a specific locale version. |
| [`deleteMany()`](#deletemany) | Delete multiple documents matching filters and relation parameters. |
| [`publish()`](#publish) | Publish the draft version of a document. |
| [`unpublish()`](#unpublish) | Move a published document back to draft. |
| [`discardDraft()`](#discarddraft) | Drop draft data and keep only the published version. |
| [`count()`](#count) | Count how many documents match the parameters. |

:::note Draft & Publish method availability
The [`publish()`](#publish), [`unpublish()`](#unpublish), and [`discardDraft()`](#discarddraft) methods are only available when the Draft & Publish feature is enabled on the content-type. Calling these methods on a content-type that does not have Draft & Publish enabled will throw an error. To enable Draft & Publish, see the [Draft & Publish documentation](/cms/features/draft-and-publish).
:::

### `findOne()`

Syntax: `findOne(parameters: Params) => Document`

#### GET strapi.documents().findOne() — findOne()

Find a document matching the passed documentId and parameters. If only a documentId is passed without any other parameters, findOne() returns the draft version of a document in the default locale. Returns the matching document if found, otherwise returns null.

**Parameters:**
- `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).
- `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`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').findOne({
  documentId: 'a1b2c3d4e5f6g7h8i9j0klmn'
})
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "name": "Biscotte Restaurant",
  "publishedAt": null,
  "locale": "en"
}
```

### `findFirst()`

Syntax:  `findFirst(parameters: Params) => Document`

#### GET strapi.documents().findFirst() — findFirst()

Find the first document matching the parameters. By default, findFirst() returns the draft version, in the default locale, of the first document for the passed unique identifier (collection type id or single type id).

**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).
- `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`.

**Generic example:**
```
await strapi.documents('api::restaurant.restaurant').findFirst()
```

**With filters:**
```
await strapi.documents('api::restaurant.restaurant').findFirst(
  {
    filters: {
      name: {
        $startsWith: "Pizzeria"
      }
    }
  }
)
```

**Response 200 Generic:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "name": "Restaurant Biscotte",
  "publishedAt": null,
  "locale": "en"
}
```

**Response 200 With filters:**
```json
{
  "documentId": "j9k8l7m6n5o4p3q2r1s0tuvw",
  "name": "Pizzeria Arrivederci",
  "publishedAt": null,
  "locale": "en"
}
```

If no `locale` or `status` parameters are passed, results return the draft version for the default locale.

### `findMany()`

Syntax: `findMany(parameters: Params) => Document[]`

#### GET strapi.documents().findMany() — findMany()

Find documents matching the parameters. When no parameter is passed, findMany() returns the draft version in the default locale for each document.

**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).
- `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`.
- `pagination` (Object): Paginate (/cms/api/document-service/sort-pagination#pagination) results.
- `sort` (Object): Sort (/cms/api/document-service/sort-pagination#sort) results.

**Generic example:**
```
await strapi.documents('api::restaurant.restaurant').findMany()
```

**With filters:**
```
await strapi.documents('api::restaurant.restaurant').findMany(
  {
    filters: {
      name: {
        $startsWith: 'Pizzeria'
      }
    }
  }
)
```

**Response 200 Generic:**
```json
[
  {
    "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
    "name": "Biscotte Restaurant",
    "publishedAt": null,
    "locale": "en"
  },
  {
    "documentId": "j9k8l7m6n5o4p3q2r1s0tuvw",
    "name": "Pizzeria Arrivederci",
    "publishedAt": null,
    "locale": "en"
  }
]
```

**Response 200 With filters:**
```json
[
  {
    "documentId": "j9k8l7m6n5o4p3q2r1s0tuvw",
    "name": "Pizzeria Arrivederci",
    "locale": "en",
    "publishedAt": null
  }
]
```

Available filters are detailed in the [filters](/cms/api/document-service/filters) page of the Document Service API reference.

If no `locale` or `status` parameters are passed, results return the draft version for the default locale.

### `create()`

Syntax: `create(parameters: Params) => Document`

#### GET strapi.documents().create() — create()

Create a new document. If no locale parameter is passed, create() creates the draft version of the document for the default locale.

**Parameters:**
- `locale` (String or undefined): Locale of the document to create. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#create).
- `fields` (Object): Select fields (/cms/api/document-service/fields#create) to return. Defaults to all fields (except those not populated by default).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: can be set to `published` to automatically publish the draft version of a document while creating it. See status docs (/cms/api/document-service/status#create).
- `populate` (Object): Populate (/cms/api/document-service/populate) results with additional fields. Default: `null`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').create({
  data: {
    name: 'Restaurant B'
  }
})
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "name": "Restaurant B",
  "publishedAt": null,
  "locale": "en"
}
```

:::tip
If the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled on the content-type, you can automatically publish a document while creating it (see [`status` documentation](/cms/api/document-service/status#create)).
:::

### `update()`

Syntax: `update(parameters: Params) => Promise<Document>`

#### GET strapi.documents().update() — update()

Update a document by documentId. If no locale parameter is passed, update() updates the document for the default locale.

**Parameters:**
- `documentId` (ID, required): Document id
- `locale` (String or null): Locale of the document to update. Defaults to the default locale. See locale docs (/cms/api/document-service/locale#update).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#update) to return. Defaults to all fields (except those not populated by default).
- `status` (): If Draft & Publish (/cms/features/draft-and-publish) is enabled: can be set to `published` to automatically publish the draft version of a document while updating it. See status docs (/cms/api/document-service/status#update).
- `populate` (Object): Populate (/cms/api/document-service/populate) results with additional fields. Default: `null`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').update({
    documentId: 'a1b2c3d4e5f6g7h8i9j0klmn',
    data: { name: "New restaurant name" }
})
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "name": "New restaurant name",
  "locale": "en",
  "publishedAt": null
}
```

:::tip
Published versions are read-only, so you can not technically update the published version of a document.
To update a document and publish the new version right away, you can:

- update its draft version with `update()`, then [publish it](#publish) with `publish()`,
- or directly add `status: 'published'` along with the other parameters passed to `update()` (see [`status` documentation](/cms/api/document-service/status#update)).
:::

:::caution
It's not recommended to update repeatable components with the Document Service API (see the related [breaking change entry](/cms/migration/v4-to-v5/breaking-changes/do-not-update-repeatable-components-with-document-service-api.md) for more details).
:::

### `delete()`

Syntax: `delete(parameters: Params): Promise<{ documentId: ID, entries: Number }>`

#### GET strapi.documents().delete() — delete()

Delete a document or a specific locale version. If no locale parameter is passed, delete() only deletes the default locale version of a document. This deletes both the draft and published versions.

**Parameters:**
- `documentId` (ID, required): Document id
- `locale` (String, ): Locale version of the document to delete. Default: `null` (deletes only the default locale). See locale docs (/cms/api/document-service/locale#delete).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#delete) 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`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').delete({
  documentId: 'a1b2c3d4e5f6g7h8i9j0klmn',
})
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "entries": [
    {
      "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
      "name": "Biscotte Restaurant",
      "publishedAt": "2024-03-14T18:30:48.870Z",
      "locale": "en"
    }
  ]
}
```

### `deleteMany()`

Syntax: `deleteMany(parameters: Params): Promise<{ documentId: ID, entries: Number }>`

#### GET strapi.documents().deleteMany() — deleteMany()

Delete multiple documents matching filters and relation parameters.

**Parameters:**
- `locale` (String, ): Locale version of documents to delete. Default: only the default locale. See locale docs (/cms/api/document-service/locale#delete).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#delete) 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`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').deleteMany({
  filters: {
    city: {
      name: {
        $eq: 'New York'
      }
    }
  }
});
```

**Response 200 OK:**
```json
{
  "documentId": "multiple_documents",
  "entries": 3
}
```

### `publish()`

Syntax: `publish(parameters: Params): Promise<{ documentId: ID, entries: Number }>`

#### GET strapi.documents().publish() — publish()

Publish the draft version of a document. This method is only available if Draft & Publish is enabled on the content-type. If no locale parameter is passed, publish() only publishes the default locale version of the document.

**Parameters:**
- `documentId` (ID, required): Document id
- `locale` (String, ): Locale of the documents to publish. Default: only the default locale. See locale docs (/cms/api/document-service/locale#publish).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#publish) 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`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').publish({
  documentId: 'a1b2c3d4e5f6g7h8i9j0klmn',
});
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "entries": [
    {
      "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
      "name": "Biscotte Restaurant",
      "publishedAt": "2024-03-14T18:30:48.870Z",
      "locale": "en"
    }
  ]
}
```

### `unpublish()`

Syntax: `unpublish(parameters: Params): Promise<{ documentId: ID, entries: Number }>`

#### GET strapi.documents().unpublish() — unpublish()

Move a published document back to draft. This method is only available if Draft & Publish is enabled on the content-type. If no locale parameter is passed, unpublish() only unpublishes the default locale version of the document.

**Parameters:**
- `documentId` (ID, required): Document id
- `locale` (String, ): Locale of the documents to unpublish. Default: only the default locale. See locale docs (/cms/api/document-service/locale#unpublish).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#unpublish) 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`.

**Request:**
```
await strapi.documents('api::restaurant.restaurant').unpublish({
  documentId: 'a1b2c3d4e5f6g7h8i9j0klmn'
});
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "entries": [
    {
      "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
      "name": "Biscotte Restaurant",
      "publishedAt": null,
      "locale": "en"
    }
  ]
}
```

### `discardDraft()`

Syntax: `discardDraft(parameters: Params): Promise<{ documentId: ID, entries: Number }>`

#### GET strapi.documents().discardDraft() — discardDraft()

Drop draft data and keep only the published version. This method is only available if Draft & Publish is enabled on the content-type. If no locale parameter is passed, discardDraft() discards draft data and overrides it with the published version only for the default locale.

**Parameters:**
- `documentId` (ID, required): Document id
- `locale` (String, ): Locale of the documents to discard. Default: only the default locale. See locale docs (/cms/api/document-service/locale#discard-draft).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.
- `fields` (Object): Select fields (/cms/api/document-service/fields#discarddraft) 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`.

**Request:**
```
strapi.documents('api::restaurant.restaurant').discardDraft({
  documentId: 'a1b2c3d4e5f6g7h8i9j0klmn',
});
```

**Response 200 OK:**
```json
{
  "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
  "entries": [
    {
      "documentId": "a1b2c3d4e5f6g7h8i9j0klmn",
      "name": "Biscotte Restaurant",
      "publishedAt": null,
      "locale": "en"
    }
  ]
}
```

### `count()`

Syntax: `count(parameters: Params) => number`

#### GET strapi.documents().count() — count()

Count how many documents match the parameters. If no parameter is passed, the count() method returns the total number of documents for the default locale.

**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).
- `filters` (Object): Filters (/cms/api/document-service/filters) to use. Default: `null`.

**Generic example:**
```
await strapi.documents('api::restaurant.restaurant').count()
```

**Count published:**
```
strapi.documents('api::restaurant.restaurant').count({ status: 'published' })
```

**With filters:**
```
/**
 * Count number of draft documents (default if status is omitted)
 * in English (default locale)
 * whose name starts with 'Pizzeria'
 */
strapi.documents('api::restaurant.restaurant').count({ filters: { name: { $startsWith: "Pizzeria" }}})
```

:::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.
:::
