Skip to main content

GraphQL API

The GraphQL API allows performing queries and mutations to interact with the content-types through Strapi's GraphQL plugin. Results can be filtered, sorted and paginated.

☑️ Prerequisites

To use the GraphQL API, install the GraphQL plugin:

yarn add @strapi/plugin-graphql

Once installed, the GraphQL playground is accessible at the /graphql URL and can be used to interactively build your queries and mutations and read documentation tailored to your content-types:

GraphQL playground use exampleGraphQL playground use example
✏️ No GraphQL API to upload media files

The GraphQL API does not support media upload. Use the REST API POST /upload endpoint for all file uploads and use the returned info to link to it in content types. You can still update or delete uploaded files with the updateUploadFile and deleteUploadFile mutations using media files id (see mutations on media files).

Queries

Queries in GraphQL are used to fetch data without modifying it.

When a content-type is added to your project, 2 automatically generated GraphQL queries are added to your schema, named after the content-type's singular and plural API IDs, as in the following example:

Content-type display nameSingular API IDPlural API ID
Restaurantrestaurantrestaurants
Singular API ID vs. Plural API ID:

Singular API ID and Plural API ID values are defined when creating a content-type in the Content-Type Builder, and can be found while editing a content-type in the admin panel (see User Guide). You can define custom API IDs while creating the content-type, but these can not modified afterwards.

Screenshot of the Content-Type Builder to retrieve singular and plural API IDsScreenshot of the Content-Type Builder to retrieve singular and plural API IDs

Fetch a single document

Documents can be fetched by their documentId.

Example query: Find a restaurant with its documentId
{
restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl") {
name
description
}
}

Fetch multiple documents

To fetch multiple documents you can use simple, flat queries or Relay-style queries:

To fetch multiple documents you can use flat queries like the following:

Example query: Find all restaurants
restaurants {
documentId
title
}

Fetch relations

You can ask to include relation data in your flat queries or in your Relay-style queries:

The following example fetches all documents from the "Restaurant" content-type, and for each of them, also returns some fields for the many-to-many relation with the "Category" content-type:

Example query: Find all restaurants and their associated categories
{
restaurants {
documentId
name
description
# categories is a many-to-many relation
categories {
documentId
name
}
}
}

Fetch media fields

Media fields content is fetched just like other attributes.

The following example fetches the url attribute value for each cover media field attached to each document from the "Restaurants" content-type:

{
restaurants {
images {
documentId
url
}
}
}

For multiple media fields, you can use flat queries or Relay-style queries:

The following example fetches some attributes from the images multiple media field found in the "Restaurant" content-type:

{
restaurants {
images_connection {
nodes {
documentId
url
}
}
}
}

Fetch components

Components content is fetched just like other attributes.

The following example fetches the label, start_date, and end_date attributes values for each closingPeriod component added to each document from the "Restaurants" content-type:

{
restaurants {
closingPeriod {
label
start_date
end_date
}
}
}

Fetch dynamic zone data

Dynamic zones are union types in GraphQL so you need to use fragments (i.e., with ...on) to query the fields, passing the component name (with the ComponentCategoryComponentname syntax) to __typename:

The following example fetches data for the label attribute of a "Closingperiod" component from the "Default" components category that can be added to the "dz" dynamic zone:

{
restaurants {
dz {
__typename
...on ComponentDefaultClosingperiod {
# define which attributes to return for the component
label
}
}
}
}

Fetch draft or published versions

If the Draft & Publish feature is enabled for the content-type, you can add a status parameter to queries to fetch draft or published versions of documents :

Example: Fetch draft versions of documents
query Query($status: PublicationStatus) {
restaurants(status: DRAFT) {
documentId
name
publishedAt # should return null
}
}
Example: Fetch published versions of documents
query Query($status: PublicationStatus) {
restaurants(status: PUBLISHED) {
documentId
name
publishedAt
}
}

Mutations

Mutations in GraphQL are used to modify data (e.g. create, update, and delete data).

When a content-type is added to your project, 3 automatically generated GraphQL mutations to create, update, and delete documents are added to your schema.

For instance, for a "Restaurant" content-type, the following mutations are generated:

Use caseSingular API ID
Create a new "Restaurant" documentcreateRestaurant
Update an existing "Restaurant" restaurantupdateRestaurant
Delete an existing "Restaurant" restaurantdeleteRestaurant

Create a new document

When creating new documents , the data argument will have an associated input type that is specific to your content-type.

For instance, if your Strapi project contains the "Restaurant" content-type, you will have the following:

MutationArgumentInput type
createRestaurantdataRestaurantInput!

The following example creates a new document for the "Restaurant" content-type and returns its name and documentId:

mutation CreateRestaurant($data: RestaurantInput!) {
createRestaurant(data: {
name: "Pizzeria Arrivederci"
}) {
name
documentId
}
}

When creating a new document, a documentId is automatically generated.

The implementation of the mutations also supports relational attributes. For example, you can create a new "Category" and attach many "Restaurants" (using their documentId) to it by writing your query like follows:

mutation CreateCategory {
createCategory(data: {
Name: "Italian Food"
restaurants: ["a1b2c3d4e5d6f7g8h9i0jkl", "bf97tfdumkcc8ptahkng4puo"]
}) {
documentId
Name
restaurants {
documentId
name
}
}
}
💡 Tip

If the Internationalization (i18n) feature is enabled for your content-type, you can create a document for a specific locale (see i18n documentation).

Update an existing document

When updating an existing document , pass the documentId and the data object containing new content. The data argument will have an associated input type that is specific to your content-type.

For instance, if your Strapi project contains the "Restaurant" content-type, you will have the following:

MutationArgumentInput type
updateRestaurantdataRestaurantInput!

For instance, the following example updates an existing document from the "Restaurants" content-type and give it a new name:

mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!) {
updateRestaurant(
documentId: "bf97tfdumkcc8ptahkng4puo",
data: { name: "Pizzeria Amore" }
) {
documentId
name
}
}
💡 Tip

If the Internationalization (i18n) feature is enabled for your content-type, you can create a document for a specific locale (see i18n documentation).

Update relations

You can update relational attributes by passing a documentId or an array of documentId (depending on the relation type).

For instance, the following example updates a document from the "Restaurant" content-type and adds a relation to a document from the "Category" content-type through the categories relation field:

mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!) {
updateRestaurant(
documentId: "slwsiopkelrpxpvpc27953je",
data: { categories: ["kbbvj00fjiqoaj85vmylwi17"] }
) {
documentId
name
categories {
documentId
Name
}
}
}

Delete a document

To delete a document , pass its documentId:

mutation DeleteRestaurant {
deleteRestaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl") {
documentId
}
}
💡 Tip

If the Internationalization (i18n) feature is enabled for your content-type, you can delete a specific localized version of a document (see i18n documentation).

Mutations on media files

Caution

Currently, mutations on media fields use Strapi v4 id, not Strapi 5 documentId, as unique identifiers for media files.

Media fields mutations use files id. However, GraphQL API queries in Strapi 5 do not return id anymore. Media files id can be found:

  • either in the Media Library from the admin panel,

    Media Library screenshot highlighting how to find a media file idMedia Library screenshot highlighting how to find a media file id
  • or by sending REST API GET requests that populate media files, because REST API requests currently return both id and documentId for media files.

Update an uploaded media file

When updating an uploaded media file, pass the media's id (not its documentId) and the info object containing new content. The info argument will has an associated input type that is specific to media files.

For instance, if your Strapi project contains the "Restaurant" content-type, you will have the following:

MutationArgumentInput type
updateUploadFileinfoFileInfoInput!

For instance, the following example updates the alternativeText attribute for a media file whose id is 3:

mutation Mutation($updateUploadFileId: ID!, $info: FileInfoInput) {
updateUploadFile(
id: 3,
info: {
alternativeText: "New alt text"
}
) {
documentId
url
alternativeText
}
}
💡 Tip

If upload mutations return a forbidden access error, ensure proper permissions are set for the Upload plugin (see User Guide).

Delete an uploaded media file

When deleting an uploaded media file, pass the media's id (not its documentId).

Example: Delete the media file with id 4
mutation DeleteUploadFile($deleteUploadFileId: ID!) {
deleteUploadFile(id: 4) {
documentId # return its documentId
}
}
💡 Tip

If upload mutations return a forbidden access error, ensure proper permissions are set for the Upload plugin (see User Guide).

Filters

Queries can accept a filters parameter with the following syntax:

filters: { field: { operator: value } }

Multiple filters can be combined together, and logical operators (and, or, not) can also be used and accept arrays of objects.

The following operators are available:

OperatorDescription
eqEqual
neNot equal
ltLess than
lteLess than or equal to
gtGreater than
gteGreater than or equal to
inIncluded in an array
notInNot included in an array
containsContains, case sensitive
notContainsDoes not contain, case sensitive
containsiContains, case insensitive
notContainsiDoes not contain, case insensitive
nullIs null
notNullIs not null
betweenIs between
startsWithStarts with
endsWithEnds with
andLogical and
orLogical or
notLogical not
Example with advanced filters: Fetch pizzerias with an averagePrice lower than 20
{
restaurants(
filters: {
averagePrice: { lt: 20 },
or: [
{ name: { eq: "Pizzeria" }}
{ name: { startsWith: "Pizzeria" }}
]}
) {
documentId
name
averagePrice
}
}

Sorting

Queries can accept a sort parameter with the following syntax:

  • to sort based on a single value: sort: "value"
  • to sort based on multiple values: sort: ["value1", "value2"]

The sorting order can be defined with :asc (ascending order, default, can be omitted) or :desc (for descending order).

Example: Fetch and sort on name by ascending order
{
restaurants(sort: "name") {
documentId
name
}
}
Example: Fetch and sort on average price by descending order
{
restaurants(sort: "averagePrice:desc") {
documentId
name
averagePrice
}
}
Example: Fetch and sort on title by ascending order, then on average price by descending order
{
restaurants(sort: ["name:asc", "averagePrice:desc"]) {
documentId
name
averagePrice
}
}

Pagination

Relay-style queries can accept a pagination parameter. Results can be paginated either by page or by offset.

✏️ Note

Pagination methods can not be mixed. Always use either page with pageSize or start with limit.

Pagination by page

ParameterDescriptionDefault
pagination.pagePage number1
pagination.pageSizePage size10
Example query: Pagination by page
{
restaurants_connection(pagination: { page: 1, pageSize: 10 }) {
nodes {
documentId
name
}
pageInfo {
page
pageSize
pageCount
total
}
}
}

Pagination by offset

ParameterDescriptionDefaultMaximum
pagination.startStart value0-
pagination.limitNumber of entities to return10-1
Example query: Pagination by offset
{
restaurants_connection(pagination: { start: 10, limit: 19 }) {
nodes {
documentId
name
}
pageInfo {
page
pageSize
pageCount
total
}
}
}
💡 Tip

The default and maximum values for pagination.limit can be configured in the ./config/plugins.js file with the graphql.config.defaultLimit and graphql.config.maxLimit keys.