diff --git a/docusaurus/docs/dev-docs/api/rest.md b/docusaurus/docs/dev-docs/api/rest.md index 29cd516a8f..0b98e88f46 100644 --- a/docusaurus/docs/dev-docs/api/rest.md +++ b/docusaurus/docs/dev-docs/api/rest.md @@ -1,5 +1,5 @@ --- -title: REST API +title: REST API reference description: Interact with your Content-Types using the REST API endpoints Strapi generates for you. displayed_sidebar: restApiSidebar diff --git a/docusaurus/docs/dev-docs/api/rest/guides/intro.md b/docusaurus/docs/dev-docs/api/rest/guides/intro.md new file mode 100644 index 0000000000..a74459e6a4 --- /dev/null +++ b/docusaurus/docs/dev-docs/api/rest/guides/intro.md @@ -0,0 +1,17 @@ +--- +title: REST API Guides +description: Deep dive into some specific REST API topics using guides that extensively explain some use cases or give step-by-step instructions. +pagination_prev: dev-docs/api/rest +pagination_next: dev-docs/api/rest/guides/understanding-populate +--- + +# REST API Guides + +The [REST API reference](/dev-docs/api/rest) documentation is meant to provide a quick reference for all the endpoints and parameters available. The following guides cover dedicated topics and provide detailed explanations (guides indicated with 🧠) or step-by-step instructions (guides indicated with 🛠️) for some use cases. + + + + + + + diff --git a/docusaurus/docs/dev-docs/api/rest/guides/populate-creator-fields.md b/docusaurus/docs/dev-docs/api/rest/guides/populate-creator-fields.md new file mode 100644 index 0000000000..764ac3cacf --- /dev/null +++ b/docusaurus/docs/dev-docs/api/rest/guides/populate-creator-fields.md @@ -0,0 +1,66 @@ +--- +title: How to populate creator fields +description: Learn how to populate creator fields such as createdBy and updatedBy by creating a custom controller that leverages the populate parameter. +--- + +# 🛠️ How to populate creator fields such as `createdBy` and `updatedBy` + +The creator fields `createdBy` and `updatedBy` are removed from the [REST API](/dev-docs/api/rest) response by default. These 2 fields can be returned in the REST API by activating the `populateCreatorFields` parameter at the content-type level. + +:::note + +The `populateCreatorFields` property is not available to the GraphQL API. + +Only the following fields will be populated: `id`, `firstname`, `lastname`, `username`, `preferedLanguage`, `createdAt`, and `updatedAt`. +::: + +To add `createdBy` and `updatedBy` to the API response: + +1. Open the content-type `schema.json` file. +2. Add `"populateCreatorFields": true` to the `options` object: + + ```json + "options": { + "draftAndPublish": true, + "populateCreatorFields": true + }, + ``` + +3. Save the `schema.json`. +4. Create a new route middleware either using the [generate CLI](/dev-docs/cli.md) or by manually creating a new file in `./src/api/[content-type-name]/middlewares/[your-middleware-name].js` +5. Add the following piece of code, you can modify this example to suit your needs: + + ```js title="./src/api/test/middlewares/defaultTestPopulate.js" + "use strict"; + + module.exports = (config, { strapi }) => { + return async (ctx, next) => { + if (!ctx.query.populate) { + ctx.query.populate = ["createdBy", "updatedBy"]; + } + + await next(); + }; + }; + ``` + +6. Modify your default route factory to enable this middleware on the specific routes you want this population to apply to and replacing the content-type/middleware name with yours: + + ```js title="./src/api/test/routes/test.js" + "use strict"; + + const { createCoreRouter } = require("@strapi/strapi").factories; + + module.exports = createCoreRouter("api::test.test", { + config: { + find: { + middlewares: ["api::test.default-test-populate"], + }, + findOne: { + middlewares: ["api::test.default-test-populate"], + }, + }, + }); + ``` + +REST API requests with no `populate` parameter will include the `createdBy` or `updatedBy` fields by default. diff --git a/docusaurus/docs/dev-docs/api/rest/guides/understanding-populate.md b/docusaurus/docs/dev-docs/api/rest/guides/understanding-populate.md new file mode 100644 index 0000000000..e9b4075d73 --- /dev/null +++ b/docusaurus/docs/dev-docs/api/rest/guides/understanding-populate.md @@ -0,0 +1,1591 @@ +--- +title: Understanding populate +description: Learn what populating means and how you can use the populate parameter in your REST API queries to add additional fields to your responses. +displayed_sidebar: restApiSidebar +toc_max_heading_level: 6 +--- + +import QsIntroFull from '/docs/snippets/qs-intro-full.md' +import QsForQueryTitle from '/docs/snippets/qs-for-query-title.md' +import QsForQueryBody from '/docs/snippets/qs-for-query-body.md' + +# 🧠 Understanding the `populate` parameter for the REST API + +When querying content-types with Strapi's [REST API](/dev-docs/api/rest), by default, reponses do not include any relations, media fields, components, or dynamic zones. + +Populating in the context of the Strapi REST API means including additional content with your response by returning more fields than the ones returned by default. You use the [`populate` parameter](#population) to achieve this. + +:::info +Throughout this guide, examples are built with real data queried from the server included with the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application. To test examples by yourself, setup FoodAdvisor, start the server in the `/api/` folder, and ensure that proper `find` permissions are given for the queried content-types before sending your queries. +::: + +The present guide will cover detailed explanations for the following use cases: + +- populate [all fields and relations, 1 level deep](#populate-all-relations-and-fields-1-level-deep), +- populate [some fields and relations, 1 level deep](#populate-1-level-deep-for-specific-relations), +- populate [some fields and relations, several levels deep](#populate-several-levels-deep-for-specific-relations), +- populate [components](#populate-components), +- populate [dynamic zones](#populate-dynamic-zones). + +:::strapi Advanced use case: Populating creator fields +In addition to the various ways of using the `populate` parameter in your queries, you can also build a custom controller as a workaround to populate creator fields (e.g., `createdBy` and `updatedBy`). This is explained in the dedicated [How to populate creator fields](/dev-docs/api/rest/guides/populate-creator-fields) guide. +::: + +## Populate all relations and fields, 1 level deep + +You can return all relations, media fields, components and dynamic zones with a single query. For relations, this will only work 1 level deep, to prevent performance issues and long response times. + +To populate everything 1 level deep, add the `populate=*` parameter to your query. + +The following diagram compares data returned by the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application with and without populating everything 1 level deep: + +![Diagram with populate use cases with FoodAdvisor data ](/img/assets/rest-api/populate-foodadvisor-diagram1.png) + +Let's compare and explain what happens with and without this query parameter: + +### Example: Without `populate` + +Without the populate parameter, a `GET` request to `/api/articles` only returns the default attributes and does not return any media fields, relations, components or dynamic zones. + +The following example is the full response for all 4 entries from the `articles` content-types. + +Notice how the response only includes the `title`, `slug`, `createdAt`, `updatedAt`, `publishedAt`, and `locale` fields, and the field content of the article as handled by the CKEditor plugin (`ckeditor_content`, truncated for brevity): + + + + +`GET /api/articles` + + + + + +```json +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": // truncated content + } + }, + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": // truncated content + } + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": // truncated content + } + }, + { + "id": 4, + "attributes": { + "title": "If you don't finish your plate in these countries, you might offend someone", + "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", + "createdAt": "2021-11-15T13:33:19.948Z", + "updatedAt": "2023-06-02T10:59:35.148Z", + "publishedAt": "2022-09-22T12:35:53.899Z", + "locale": "en", + "ckeditor_content": // truncated content + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +} +``` + + + + +### Example: With `populate=*` + +With the `populate=*` parameter, a `GET` request to `/api/articles` also returns all media fields, first-level relations, components and dynamic zones. + +The following example is the full response for the first of all 4 entries from the `articles` content-types (the data from articles with ids 2, 3, and 4 is truncated for brevity). + +Scroll down to see that the response size is much bigger than without populate. The response now includes additional fields (see highlighted lines) such as: +* the `image` media field (which stores all information about the article cover, including all its different formats), +* the first-level fields of the `blocks` dynamic zone and the `seo` component, +* the `category` relation and its fields, +* and even some information about the articles translated in other languages, as shown by the `localizations` object. + +:::tip +To populate deeply nested comments, see the [populate components](#populate-components) section. +::: + +
+ + + +`GET /api/articles?populate=*` + + + + + +```json {13-122} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": // truncated content + "image": { + "data": { + "id": 12, + "attributes": { + "name": "Basque dish", + "alternativeText": "Basque dish", + "caption": "Basque dish", + "width": 758, + "height": 506, + "formats": { + "thumbnail": { + "name": "thumbnail_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", + "hash": "thumbnail_basque_cuisine_17fa4567e0_f033424240", + "ext": ".jpeg", + "mime": "image/jpeg", + "width": 234, + "height": 156, + "size": 11.31, + "path": null, + "url": "/uploads/thumbnail_basque_cuisine_17fa4567e0_f033424240.jpeg" + }, + "medium": { + "name": "medium_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", + "hash": "medium_basque_cuisine_17fa4567e0_f033424240", + "ext": ".jpeg", + "mime": "image/jpeg", + "width": 750, + "height": 501, + "size": 82.09, + "path": null, + "url": "/uploads/medium_basque_cuisine_17fa4567e0_f033424240.jpeg" + }, + "small": { + "name": "small_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", + "hash": "small_basque_cuisine_17fa4567e0_f033424240", + "ext": ".jpeg", + "mime": "image/jpeg", + "width": 500, + "height": 334, + "size": 41.03, + "path": null, + "url": "/uploads/small_basque_cuisine_17fa4567e0_f033424240.jpeg" + } + }, + "hash": "basque_cuisine_17fa4567e0_f033424240", + "ext": ".jpeg", + "mime": "image/jpeg", + "size": 58.209999999999994, + "url": "/uploads/basque_cuisine_17fa4567e0_f033424240.jpeg", + "previewUrl": null, + "provider": "local", + "provider_metadata": null, + "createdAt": "2021-11-23T14:05:33.460Z", + "updatedAt": "2021-11-23T14:05:46.084Z" + } + } + }, + "blocks": [ + { + "id": 2, + "__component": "blocks.related-articles" + }, + { + "id": 2, + "__component": "blocks.cta-command-line", + "theme": "primary", + "title": "Want to give a try to a Strapi starter?", + "text": "❤️", + "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" + } + ], + "seo": { + "id": 1, + "metaTitle": "Articles - FoodAdvisor", + "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", + "keywords": "food", + "metaRobots": null, + "structuredData": null, + "metaViewport": null, + "canonicalURL": null + }, + "category": { + "data": { + "id": 4, + "attributes": { + "name": "European", + "slug": "european", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + }, + "localizations": { + "data": [ + { + "id": 10, + "attributes": { + "title": "Voici pourquoi il faut essayer la cuisine basque, selon un chef basque", + "slug": "voici-pourquoi-il-faut-essayer-la-cuisine-basque-selon-un-chef-basque", + "createdAt": "2021-11-18T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.606Z", + "publishedAt": "2022-09-22T13:00:00.069Z", + "locale": "fr-FR", + "ckeditor_content": // truncated content + } + } + ] + } + } + }, + { + "id": 2, + // truncated content + }, + { + "id": 3, + // truncated content + }, + { + "id": 4, + // truncated content + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +## Populate specific relations and fields + +You can also populate specific relations and fields, by explicitly defining what to populate. This requires that you know the name of fields and relations to populate. + +Relations and fields populated this way can be 1 or several levels deep. The following diagram compares data returned by the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application when you populate [1 level deep](#populate-1-level-deep-for-specific-relations) vs. [2 levels deep](#populate-several-levels-deep-for-specific-relations): + +![Diagram with populate use cases with FoodAdvisor data ](/img/assets/rest-api/populate-foodadvisor-diagram2.png) + + +Depending on your data structure, you might get similar data presented in different ways with different queries. For instance, the FoodAdvisor example application includes the article, category, and restaurant content-types that are all in relation to each other in different ways. This means that if you want to get data about the 3 content-types in a single GET request, you have 2 options: + +- query articles and populate categories, plus populate the nested relation between categories and restaurants ([2 levels deep population](#populate-several-levels-deep-for-specific-relations)) +- query categories and populate both articles and restaurants because categories have a 1st level relation with the 2 other content-types ([1 level deep](#populate-1-level-deep-for-specific-relations)) + +The 2 different strategies are illustrated in the following diagram: + +![Diagram with populate use cases with FoodAdvisor data ](/img/assets/rest-api/populate-foodadvisor-diagram3.png) + + + +
+Populate as an object vs. populate as an array: Using the interactive query builder + +The syntax for advanced query parameters can be quite complex to build manually. We recommend you use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool to generate the URL. + +Using this tool, you will write clean and readable requests in a familiar (JavaScript) format, which should help you understand the differences between different queries and different ways of populating. For instance, populating 2 levels deep implies using populate as an object, while populating several relations 1 level deep implies using populate as an array: + + + + +Populate as an object
(to populate 1 relation several levels deep): + +```json +{ + populate: { + category: { + populate: ['restaurants'], + }, + }, +} +``` + +
+ + +Populate as an array
(to populate many relations 1 level deep) + +```json +{ + populate: [ + 'articles', + 'restaurants' + ], +} + +``` + +
+
+ +
+ +### Populate 1 level deep for specific relations + +You can populate specific relations 1 level deep by using the populate parameter as an array. + +Since the REST API uses the [LHS bracket notation](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets) (i.e., with square brackets `[]`), the parameter syntaxes to populate 1 level deep would look like the following: + +| How many relations to populate | Syntax example | +|-------------------------------|--------------------| +| Only 1 relation | `populate[0]=a-relation-name` | +| Several relations | `populate[0]=relation-name&populate[1]=another-relation-name&populate[2]=yet-another-relation-name` | + +Let's compare and explain what happens with and without populating relations 1 level deep when sending queries to the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application: + +#### Example: Without `populate` + +Without the populate parameter, a `GET` request to `/api/articles` only returns the default attributes. + +The following example is the full response for all 4 entries from the `articles` content-type. + +Notice that the response does not include any media fields, relations, components or dynamic zones: + +
+ + + + +`GET /api/articles` + + + + + +```json +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 4, + "attributes": { + "title": "If you don't finish your plate in these countries, you might offend someone", + "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", + "createdAt": "2021-11-15T13:33:19.948Z", + "updatedAt": "2023-06-02T10:59:35.148Z", + "publishedAt": "2022-09-22T12:35:53.899Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +} +``` + + + + +#### Example: With `populate[0]=category` + +With `populate[0]=category` added to the request, we explicitly ask to include some information about `category`, which is a relation field that links the `articles` and the `categories` content-types. + +The following example is the full response for all 4 entries from the `articles` content-type. + +Notice that the response now includes additional data with the `category` field for each article (see highlighted lines): + + + + +`GET /api/articles?populate[0]=category` + + + + + +```json {13-23,36-46,59-69,82-92} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 4, + "attributes": { + "name": "European", + "slug": "european", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 13, + "attributes": { + "name": "Chinese", + "slug": "chinese", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 3, + "attributes": { + "name": "International", + "slug": "international", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 4, + "attributes": { + "title": "If you don't finish your plate in these countries, you might offend someone", + "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", + "createdAt": "2021-11-15T13:33:19.948Z", + "updatedAt": "2023-06-02T10:59:35.148Z", + "publishedAt": "2022-09-22T12:35:53.899Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 3, + "attributes": { + "name": "International", + "slug": "international", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +### Populate several levels deep for specific relations + +You can also populate specific relations several levels deep. For instance, when you populate a relation which itself populates another relation, you are populating 2 levels deep. Populating 2 levels deep is the example covered in this guide. + +:::caution +There is no limit on the number of levels that can be populated. However, the deeper the populates, the more the request will take time to be performed. +::: + +Since the REST API uses the [LHS bracket notation](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets), (i.e., with square brackets `[]`), for instance if you want to populate a relation nested inside another relation, the parameter syntax would look like the following: + +`populate[first-level-relation-to-populate][populate][0]=second-level-relation-to-populate` + +:::tip +The syntax for advanced query parameters can be quite complex to build manually. We recommend you use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool to generate the URL. For instance, the `/api/articles?populate[category][populate][0]=restaurants` URL used in the following examples has been generated by converting the following object using our tool: + +```json +{ + populate: { + category: { + populate: ['restaurants'], + }, + }, +} +``` + +::: + +The [FoodAdvisor](https://github.com/strapi/foodadvisor) example application includes various levels of relations between content-types. For instance: + +- an `article` content-type includes a relation with the `category` content-type, +- but a `category` can also be assigned to any `restaurant` content-type. + +With a single `GET` request to `/api/articles` and the appropriate populate parameters, you can return information about articles, restaurants, and categories simultaneously. + +Let's compare and explain the responses returned with `populate[0]=category` (1 level deep) and `populate[category][populate][0]=restaurants` (2 levels deep) when sending queries to FoodAdvisor: + +#### Example: With 1-level deep population + +When we only populate 1 level deep, asking for the categories associated to articles, we can get the following example response (highlighted lines show the `category` relations field): + + + + +`GET /api/articles?populate[0]=category` + + + + + +```json {13-23,36-46,59-69,82-92} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 4, + "attributes": { + "name": "European", + "slug": "european", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 13, + "attributes": { + "name": "Chinese", + "slug": "chinese", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 3, + "attributes": { + "name": "International", + "slug": "international", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + }, + { + "id": 4, + "attributes": { + "title": "If you don't finish your plate in these countries, you might offend someone", + "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", + "createdAt": "2021-11-15T13:33:19.948Z", + "updatedAt": "2023-06-02T10:59:35.148Z", + "publishedAt": "2022-09-22T12:35:53.899Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 3, + "attributes": { + "name": "International", + "slug": "international", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z" + } + } + } + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +#### Example: With 2-level deep population + +When we populate 2 levels deep, asking for the categories associated to articles, but also for restaurants associated to these categories, we can get the following example response. + +Notice that we now have the `restaurants` relation field included with the response inside the `category` relation (see highlighted lines): + + + + +`GET /api/articles?populate[category][populate][0]=restaurants` + + + + + +```json {13-56} +{{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "category": { + "data": { + "id": 4, + "attributes": { + "name": "European", + "slug": "european", + "createdAt": "2021-11-09T13:33:20.123Z", + "updatedAt": "2021-11-09T13:33:20.123Z", + "restaurants": { + "data": [ + { + "id": 1, + "attributes": { + "name": "Mint Lounge", + "slug": "mint-lounge", + "price": "p3", + "createdAt": "2021-11-09T14:07:47.125Z", + "updatedAt": "2021-11-23T16:41:30.504Z", + "publishedAt": "2021-11-23T16:41:30.501Z", + "locale": "en" + } + }, + { + "id": 9, + // truncated content + }, + { + "id": 10, + // truncated content + }, + { + "id": 12, + // truncated content + }, + { + "id": 21, + // truncated content + }, + { + "id": 26, + // truncated content + } + ] + } + } + } + } + } + }, + { + "id": 2, + // truncated content + }, + { + "id": 3, + // truncated content + }, + { + "id": 4, + // truncated content + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +### Populate components + +Components and dynamic zones are not included in responses by default and you need to explicitly populate each dynamic zones, components, and their nested components. + +Since the REST API uses the [LHS bracket notation](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets), (i.e., with square brackets `[]`), you need to pass all elements in a `populate` array. Nested fields can also be passed, and the parameter syntax could look like the following: + +`populate[0]=a-first-field&populate[1]=a-second-field&populate[2]=a-third-field&populate[3]=a-third-field.a-nested-field&populate[4]=a-third-field.a-nested-component.a-nested-field-within-the-component` + +:::tip +The syntax for advanced query parameters can be quite complex to build manually. We recommend you use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool to generate the URL. For instance, the `/api/articles?populate[0]=seo&populate[1]=seo.metaSocial&populate[2]=seo.metaSocial.image` URL used in the following examples has been generated by converting the following object using our tool: + +```json +{ + populate: [ + 'seoData', + 'seoData.sharedImage', + 'seoData.sharedImage.media', + ], +}, +``` + +::: + +The [FoodAdvisor](https://github.com/strapi/foodadvisor) example application includes various components and even components nested inside other components. For instance: + +- an `article` content-type includes a `seo` component (1), +- the `seo` component includes a nested, repeatable `metaSocial` component (2), +- and the `metaSocial` component itself has several fields, including an `image` media field (3). + +![FoodAdvisor's SEO component structure in the Content-Type Builder](/img/assets/rest-api/ctb-article-components-structure.png) + +By default, none of these fields or components are included in the response of a `GET` request to `/api/articles`. But with the appropriate populate parameters, you can return all of them in a single request. + +Let's compare and explain the responses returned with `populate[0]=seo` (1st level component) and `populate[0]=seo&populate[1]=seo.metaSocial` (2nd level component nested within the 1st level component): + +#### Example: Only 1st level component + +When we only populate the `seo` component, we go only 1 level deep, and we can get the following example response. Highlighted lines show the `seo` component. + +Notice there's no mention of the `metaSocial` component nested within the `seo` component: + + + + +`GET /api/articles?populate[0]=seo` + + + + + +```json {13-22} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "seo": { + "id": 1, + "metaTitle": "Articles - FoodAdvisor", + "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", + "keywords": "food", + "metaRobots": null, + "structuredData": null, + "metaViewport": null, + "canonicalURL": null + } + } + }, + { + "id": 2, + // truncated content + }, + { + "id": 3, + // truncated content + }, + { + "id": 4, + // truncated content + }, + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +#### Example: 1st level and 2nd level component + +When we populate 2 levels deep, asking both for the `seo` component and the `metaSocial` component nested inside `seo`, we can get the following example response. + +Notice that we now have the `metaSocial` component-related data included with the response (see highlighted lines): + + + + +`GET /api/articles?populate[0]=seo&populate[1]=seo.metaSocial` + + + + + +```json {13,22-29} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "seo": { + "id": 1, + "metaTitle": "Articles - FoodAdvisor", + "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", + "keywords": "food", + "metaRobots": null, + "structuredData": null, + "metaViewport": null, + "canonicalURL": null, + "metaSocial": [ + { + "id": 1, + "socialNetwork": "Facebook", + "title": "Browse our best articles about food and restaurants ", + "description": "Discover our articles about food, restaurants, bars and more!" + } + ] + } + } + }, + { + "id": 2, + // truncated content + }, + { + "id": 3, + // truncated content + }, + { + "id": 4, + // truncated content + }, + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +### Populate dynamic zones + +Dynamic zones are highly dynamic content structures by essence. +When populating dynamic zones, you can choose between the following 2 strategies: + +| Strategy name | Use case | +| ---------------------------------------------------- | ------------------------------------------------------------- | +| [Shared population](#shared-population-strategy) | Apply a unique behavior to all the dynamic zone's components. | +| [Detailed population](#detailed-population-strategy) | Explicitly define what to populate with the response. | + +#### Shared population strategy + +With the shared population strategy, you apply the same population to all the components of a dynamic zone. + +For instance, in the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application: + +- A `blocks` dynamic zone exists the `article` content-type (1). +- The dynamic zone includes 3 different components: `relatedArticles` (2), `faq` (3), and `CtaCommandLine` (4). All components have a different data structure containing various fields. + +![FoodAdvisor's 'blocks' dynamic zone structure in the Content-Type Builder](/img/assets/rest-api/ctb-blocks-dynamic-zone-structure.png) + +By default, none of these fields or components are included in the response of a `GET` request to `/api/articles`. But with the appropriate populate parameters, you can return all of them in a single request. And instead of explicitly defining all the field names to populate, you can choose to use the shared population strategy to populate all fields of all components by passing `[populate=*]`. + +:::tip +The syntax for advanced query parameters can be quite complex to build manually. We recommend you use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool to generate the URL. For instance, the `/api/articles?populate[blocks][populate]=*` URL used in the following example has been generated by converting the following object using our tool: + +```json +{ + populate: { + blocks: { // asking to populate the blocks dynamic zone + populate: '*' // populating all first-level fields in all components + } + }, +} +``` + +::: + +Let's compare and explain the responses returned with `populate[0]=blocks` (only populating the dynamic zone) and `populate[blocks][populate]=*` (populating the dynamic zone and applying a shared population strategy to all its components): + +##### Example: Populating only the dynamic zone + +When we only populate the `blocks` dynamic zone, we go only 1 level deep, and we can get the following example response. Highlighted lines show the `blocks` dynamic zone and the 2 components it includes: + + + + +`GET /api/articles?populate[0]=blocks` + + + + + +```json {13-26} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…" // truncated content + "blocks": [ + { + "id": 2, + "__component": "blocks.related-articles" + }, + { + "id": 2, + "__component": "blocks.cta-command-line", + "theme": "primary", + "title": "Want to give a try to a Strapi starter?", + "text": "❤️", + "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" + } + ] + } + }, + { + "id": 2, + // … + }, + { + "id": 3, + // … + }, + { + "id": 4, + // … + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +##### Example: Populating the dynamic zone and applying a shared strategy to its components + +When we populate the `blocks` dynamic zone and apply a shared population strategy to all its components with `[populate]=*`, we not only include components fields but also their 1st-level relations, as shown in the highlighted lines of the following example response: + + + + +`GET /api/articles?populate[blocks][populate]=*` + + + + + +```json {13-63} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "blocks": [ + { + "id": 2, + "__component": "blocks.related-articles", + "header": { + "id": 2, + "theme": "primary", + "label": "More, I want more!", + "title": "Similar articles" + }, + "articles": { + "data": [ + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 4, + "attributes": { + "title": "If you don't finish your plate in these countries, you might offend someone", + "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", + "createdAt": "2021-11-15T13:33:19.948Z", + "updatedAt": "2023-06-02T10:59:35.148Z", + "publishedAt": "2022-09-22T12:35:53.899Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + } + ] + } + }, + { + "id": 2, + "__component": "blocks.cta-command-line", + "theme": "primary", + "title": "Want to give a try to a Strapi starter?", + "text": "❤️", + "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" + } + ] + } + }, + { + "id": 2, + // … + }, + { + "id": 3, + // … + }, + { + "id": 4, + // … + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +#### Detailed population strategy + +With the detailed population strategy, you can define per-component populate queries using the `on` property. + +For instance, in the [FoodAdvisor](https://github.com/strapi/foodadvisor) example application: + +- A `blocks` dynamic zone exists the `article` content-type (1). +- The dynamic zone includes 3 different components: `relatedArticles` (2), `faq` (3), and `CtaCommandLine` (4). All components have a different data structure containing various fields. +- The `relatedArticles` component has an `articles` relation (5) with the article content-type. + +![FoodAdvisor's 'blocks' dynamic zone structure in the Content-Type Builder](/img/assets/rest-api/ctb-blocks-dynamic-zone-structure-2.png) + +By default, none of the deeply nested fields or relations are included in the response of a `GET` request to `/api/articles`. With the appropriate populate parameters and by applying a detailed population strategy, you can return precisely the data you need. + +:::tip +The syntax for advanced query parameters can be quite complex to build manually. We recommend you use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool to generate the URL. For instance, the `/api/articles?populate[blocks][on][blocks.related-articles][populate][articles][populate][0]=image&populate[blocks][on][blocks.cta-command-line][populate]=*` URL used in the following example has been generated by converting the following object using our tool: + +```json +{ + populate: { + blocks: { // asking to populate the blocks dynamic zone + on: { // using a detailed population strategy to explicitly define what you want + 'blocks.related-articles': { + populate: { + 'articles': { + populate: ['image'] + } + } + }, + 'blocks.cta-command-line': { + populate: '*' + } + }, + }, + }, +} +``` + +::: + +Let's compare and explain the responses returned with some examples of a shared population strategy and a detailed population strategy: + +##### Example: Shared population strategy + +When we populate the `blocks` dynamic zone and apply a shared population strategy to all its components with `[populate]=*`, we not only include components fields but also their 1st-level relations. + +Highlighted lines show that the response include the `articles` first-level relation with the `relatedArticles` component, and also data for all types of blocks, including the `faq` and `CtaCommandLine` blocks: + + + + + +`GET /api/articles?populate[blocks][populate]=*` + + + + + +```json {23-55,108-113} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "blocks": [ + { + "id": 2, + "__component": "blocks.related-articles", + "header": { + "id": 2, + "theme": "primary", + "label": "More, I want more!", + "title": "Similar articles" + }, + "articles": { + "data": [ + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 3, + // … + }, + { + "id": 4, + // … + } + ] + } + }, + { + "id": 2, + "__component": "blocks.cta-command-line", + "theme": "primary", + "title": "Want to give a try to a Strapi starter?", + "text": "❤️", + "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" + } + ] + } + }, + { + "id": 2, + // … + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "blocks": [ + { + "id": 1, + "__component": "blocks.related-articles", + "header": { + "id": 1, + "theme": "primary", + "label": "More, I want more!", + "title": "Similar articles" + }, + "articles": { + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + } + }, + { + "id": 2, + // … + }, + { + "id": 4, + // … + } + ] + } + }, + { + "id": 1, + "__component": "blocks.faq", + "title": "Frequently asked questions", + "theme": "muted" + }, + { + "id": 1, + "__component": "blocks.cta-command-line", + "theme": "secondary", + "title": "Want to give it a try with a brand new project?", + "text": "Up & running in seconds 🚀", + "commandLine": "npx create-strapi-app my-project --quickstart" + } + ] + } + }, + { + "id": 4, + // … + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + + + + +##### Example: Detailed population strategy + +When we populate the `blocks` dynamic zone and apply a detailed population strategy, we explicitly define which data to populate. + +In the following example response, highlighted lines show differences with the shared population strategy: + +- We deeply populate the `articles` relation of the `relatedArticles` component, and even the `image` media field of the related article. + +- But because we have only asked to populate everything for the `CtaCommandLine` component and have not defined anything for the `faq` component, no data from the `faq` component is returned. + + + + + +`GET /api/articles?populate[blocks][on][blocks.related-articles][populate][articles][populate][0]=image&populate[blocks][on][blocks.cta-command-line][populate]=*` + + + + + +```json {16-17,29-34} +{ + "data": [ + { + "id": 1, + "attributes": { + "title": "Here's why you have to try basque cuisine, according to a basque chef", + "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", + "createdAt": "2021-11-09T13:33:19.948Z", + "updatedAt": "2023-06-02T10:57:19.584Z", + "publishedAt": "2022-09-22T09:30:00.208Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "blocks": [ + { + "id": 2, + "__component": "blocks.related-articles", + "articles": { + "data": [ + { + "id": 2, + "attributes": { + "title": "What are chinese hamburgers and why aren't you eating them?", + "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", + "createdAt": "2021-11-11T13:33:19.948Z", + "updatedAt": "2023-06-01T14:32:50.984Z", + "publishedAt": "2022-09-22T12:36:48.312Z", + "locale": "en", + "ckeditor_content": "…", // truncated content + "image": { + "data": { + // … + } + } + } + } + }, + { + "id": 3, + // … + }, + { + "id": 4, + // … + } + ] + } + }, + { + "id": 2, + "__component": "blocks.cta-command-line", + "theme": "primary", + "title": "Want to give a try to a Strapi starter?", + "text": "❤️", + "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" + } + ] + } + }, + { + "id": 2, + // … + }, + { + "id": 3, + "attributes": { + "title": "7 Places worth visiting for the food alone", + "slug": "7-places-worth-visiting-for-the-food-alone", + "createdAt": "2021-11-12T13:33:19.948Z", + "updatedAt": "2023-06-02T11:30:00.075Z", + "publishedAt": "2023-06-02T11:30:00.075Z", + "locale": "en", + "ckeditor_content": "

There is no love sincerer than the love of food 

said George Bernard Shaw, and let us also add that there's arguably no better way to explore a culture than to eat voraciously while traveling. Yes, there are many five-star restaurants worth booking an entire trip for, but it's equally important to savor classic treats from across the world.

Paella in Valencia, Spain

\"Paella

It’s this classic rice-based dish that first pops to mind when thinking of Spanish gastronomy. For the best there is, head to the source: Valencia. And don’t forget to scrape the bottom of the pan for heavenly bites of crunchy rice, or socarrat: the most flavorful arroz ever to land on your palate.

 

 

 

Nasi Lemak in Malaysia

\"Nasi

Malaysia’s national dish, nasi lemak is a fragrant coconut-milk rice mixture, served with sambal sauce, fried crispy anchovies, toasted peanuts, and cucumber and cooked with screw pine (pandan) leaves. Available on almost every street corner, this much-loved classic hits all the notes.

Pintxos in San Sebastián, Spain

\"Pintxos

Among the most highly ranked cities for Michelin-starred restaurants, San Sebastián boasts pintxos (the equivalent of small tapas) with über-creative takes on classics and beyond. Spain’s haute cuisine shines in this culinary paradise on the Basque coast.

 

Pastel de Nata in Lisbon

\"Pastel

The most iconic Portuguese pastry, the pastel de nata is a sublime custard tart with hints of lemon, cinnamon, and vanilla. Buttery goodness in the middle, crunchy sweetness on top—what’s not to love?

 

 

 

Mole in Puebla, Mexico

\"Mole

Mole, a specialty in the Mexican city of Puebla, is a labor of love. The spicy-sweet combination of this rich, chocolate-colored sauce takes arduous preparation and packs ingredients such as ancho chiles, spices like anise and coriander, sesame seeds, almonds, peanuts, stale bread, brown sugar, raisins, chocolate, and ripe plantains. The culminating dish is fit for the gods.

Sichuan Hot Pot in China

\"Sichuan

This isn’t for the faint of heart. But if you’re an extreme spice lover, you’ll welcome the tears that come from the hot pot’s perfect nexus of pain and pleasure.

Tagine in Morocco

\"Tagine

This slow-cooked savory stew, typically made with sliced meat, poultry, or fish and lots of herbs and spices, is true Moroccan soul food. Cooked for hours in a clay cooking pot with a conical lid (known as a tagine), this irresistible dish is served with couscous or bread and can be found all over Morocco.

", + "blocks": [ + { + "id": 1, + "__component": "blocks.related-articles", + "articles": { + // … + } + }, + { + "id": 1, + "__component": "blocks.cta-command-line", + "theme": "secondary", + "title": "Want to give it a try with a brand new project?", + "text": "Up & running in seconds 🚀", + "commandLine": "npx create-strapi-app my-project --quickstart" + } + ] + } + }, + { + "id": 4, + // … + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 1, + "total": 4 + } + } +} +``` + +
+ +
diff --git a/docusaurus/docs/dev-docs/api/rest/populate-select.md b/docusaurus/docs/dev-docs/api/rest/populate-select.md index e05c542709..7dc52f8efa 100644 --- a/docusaurus/docs/dev-docs/api/rest/populate-select.md +++ b/docusaurus/docs/dev-docs/api/rest/populate-select.md @@ -93,646 +93,47 @@ await request(`/api/users?${query}`); - ## Population -Queries can accept a `populate` parameter to populate various field types: - -- [relations & media fields](#relations--media-fields) -- [components & dynamic zones](#components--dynamic-zones) -- [creator fields](#populating-createdby-and-updatedby) - -It is also possible to [combine population with multiple operators](#combining-population-with-other-operators) among various other operators to have much more control over the population. - -:::note - -- By default Strapi will not populate any type of fields. -- It's currently not possible to return just an array of IDs. This is something that is currently under discussion. - -::: +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. -### Relations & Media fields - -Queries can accept a `populate` parameter to explicitly define which fields to populate, with the following syntax option examples. +The `populate` parameter can be used alone or [in combination with with multiple operators](#combining-population-with-other-operators) to have much more control over the population. :::caution -If the Users & Permissions plugin is installed, the `find` permission must be enabled for the content-types that are being populated. If a role doesn't have access to a content-type it will not be populated. +The `find` permission must be enabled for the content-types that are being populated. If a role doesn't have access to a content-type it will not be populated (see [User Guide](/user-docs/users-roles-permissions/configuring-end-users-roles#editing-a-role) for additional information on how to enable `find` permissions for content-types). ::: - - - -#### Populate 1 level for all relations - -To populate one-level deep for all relations, use the `*` wildcard in combination with the `populate` parameter. - - - - -
- - - -`GET /api/articles?populate=*` - - - - - -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "title": "Test Article", - "slug": "test-article", - "body": "Test 1", - // ... - "headerImage": { - "data": { - "id": 1, - "attributes": { - "name": "17520.jpg", - "alternativeText": "17520.jpg", - "formats": { - // ... - } - // ... - } - } - }, - "author": { - // ... - }, - "categories": { - // ... - } - } - } - ], - "meta": { - // ... - } -} -``` - - - - -
- - - - -```js -const qs = require('qs'); -const query = qs.stringify( - { - populate: '*', - }, - { - encodeValuesOnly: true, // prettify URL - } -); - -await request(`/api/articles?${query}`); -``` - -
- -
-
- - - - -#### Populate 1 level - -To populate only specific relations one-level deep, use one of the following method: - -- Use the populate parameter as an array and put the relation name inside. -- Use the populate parameter as an object (using [LHS bracket notation](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets), i.e., with square brackets `[]`)) and put the relation name as a key with one of the following values: `true, false, t, f, 1, 0`. - - - - -
- - - -`GET /api/articles?populate[0]=categories` - - - - - -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "title": "Test Article", - // ... - "categories": { - "data": [ - { - "id": 1, - "attributes": { - "name": "Food" - // ... - } - } - ] - } - } - } - ], - "meta": { - // ... - } -} -``` - - - - -
- - - - -```js -// Array method -const qs = require('qs'); -const query = qs.stringify( - { - populate: ['categories'], - }, - { - encodeValuesOnly: true, // prettify URL - } -); -await request(`/api/articles?${query}`); -``` - -```js -// Object method -const qs = require('qs'); -const query = qs.stringify( - { - populate: { - categories: true - } - }, - { - encodeValuesOnly: true // prettify URL - } -); - -await request(`/api/articles?${query}`); -``` - -
- -
-
- - - - -#### Populate 2 levels - -To populate specific relations, one or several levels deep, use the [LHS bracket notation](https://christiangiacomi.com/posts/rest-design-principles/#lhs-brackets) (i.e., with square brackets `[]`) for fields names in combination with the `populate` parameter. - -
- :::note -There is no limit on the number of levels that can be populated. However, the more nested populates there are, the more the request will take time to be performed. +It's currently not possible to return just an array of ids with a request. ::: -
- - -
- - - -`GET /api/articles?populate[author][populate][0]=company` - - +:::strapi Populating guides - +The [REST API guides](/dev-docs/api/rest/guides/intro) section includes more detailed information about various possible use cases for the populate parameter: -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "title": "Test Article", - // ... - "author": { - "data": { - "id": 1, - "attributes": { - "name": "Kai Doe", - // ... - "company": { - "data": { - "id": 1, - "attributes": { - "name": "Strapi" - // ... - } - } - } - } - } - } - } - } - ], - "meta": { - // ... - } -} -``` - - - - -
- - - - -```js -const qs = require('qs'); -const query = qs.stringify( - { - populate: { - author: { - populate: ['company'], - }, - }, - }, - { - encodeValuesOnly: true, // prettify URL - } -); -await request(`/api/articles?${query}`); -``` - -
- -
-
+- The [Understanding populate](/dev-docs/api/rest/guides/understanding-populate) guide explains in details how populate works, with diagrams, comparisons, and real-world examples. +- The [How to populate creator fields](/dev-docs/api/rest/guides/populate-creator-fields) guide provides step-by-step instructions on how to add `createdBy` and `updatedBy` fields to your queries responses. -### Components & Dynamic Zones - -The `populate` parameter is used to explicitly define which Dynamic zones, components, and nested components to populate. - - - - -#### Example: Deeply populate a 2-level component & media +::: -To populate a 2-level component & its media, you need to explicitly ask for each element with the `populate` parameter, passing all elements in an array. +The following table sums up possible populate use cases and their associated parameter syntaxes, and links to sections of the Understanding populate guide which includes more detailed explanations: -
+| 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](/dev-docs/api/rest/guides/understanding-populate#populate-all-relations-and-fields-1-level-deep) | +| Populate one relation,
1 level deep | `populate[0]=a-relation-name`| [Populate 1 level deep for specific relations](/dev-docs/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](/dev-docs/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) | +| Populate some relations, several levels deep | `populate[first-level-relation-to-populate][populate][0]=second-level-relation-to-populate`| [Populate several levels deep for specific relations](/dev-docs/api/rest/guides/understanding-populate#populate-several-levels-deep-for-specific-relations) | +| Populate a component | `populate[0]=component-name`| [Populate components](/dev-docs/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](/dev-docs/api/rest/guides/understanding-populate#populate-components) | +| Populate a dynamic zone (only its first-level elements) | `populate[0]=dynamic-zone-name`| [Populate dynamic zones](/dev-docs/api/rest/guides/understanding-populate#populate-dynamic-zones) | +| Populate a dynamic zone and its nested elements and relations, using a unique, shared population strategy | `populate[dynamic-zone-name][populate]=*`| [Populate dynamic zones](/dev-docs/api/rest/guides/understanding-populate#shared-population-strategy) | +| Populate a dynamic zone and its nested elements and relations, using a precisely defined, detailed population strategy | `populate[dynamic-zone-name][on][dynamic-zone-name.component-name][populate][relation-name][populate][0]=field-name`| [Populate dynamic zones](/dev-docs/api/rest/guides/understanding-populate#detailed-population-strategy) | :::tip The easiest way to build complex queries with multiple-level population is to use our [interactive query builder](/dev-docs/api/rest/interactive-query-builder) tool. ::: -
- - - -
- - - -`GET /api/articles?populate[0]=seoData&populate[1]=seoData.sharedImage&populate[2]=seoData.sharedImage.media` - - - - - -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "title": "Test Article", - // ... - "seoData": { - "id": 1, - "metaTitle": "Test Article", - // ... - "sharedImage": { - "id": 1, - "alt": "starSky", - "media": { - "data": [ - { - "id": 1, - "attributes": { - "name": "17520.jpg", - "formats": { - // ... - }, - // ... - } - } - ] - } - } - } - } - } - ], - "meta": { - // ... -} -``` - - - - -
- - - - -```js -const qs = require('qs'); -const query = qs.stringify( - { - populate: [ - 'seoData', - 'seoData.sharedImage', - 'seoData.sharedImage.media', - ], - }, - { - encodeValuesOnly: true, // prettify URL - } -); - -await request(`/api/articles?${query}`); -``` - -
- -
-
- - - - -#### Example: Deeply populate a dynamic zone with 2 components - -Dynamic zones are highly dynamic content structures by essence. -When populating dynamic zones, you can choose between a shared population strategy or a detailed population strategy. - -In a shared population strategy, apply a unique behavior for all the dynamic zone's components. - - - - -
- - - -`GET /api/articles?populate[testDZ][populate]=*` - - - - - -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "testString": "test1", - // ... - "testDZ": [ - { - "id": 3, - "__component": "test.test-compo", - "testString": "test1", - "testNestedCompo": { - "id": 3, - "testNestedString": "testNested1" - }, - "otherField": "test" - }, - { - "id": 1, - "__component": "test.test-compo2", - "testInt": 1, - "otherField": "test" - } - ] - } - } - ], - "meta": { - // ... - } -} -``` - - - - -
- - - - -```js -const qs = require('qs'); -const query = qs.stringify( - { - populate: { - testDZ: { - populate: '*', - }, - }, - }, - { - encodeValuesOnly: true, // prettify URL - } -); - -await request(`/api/articles?${query}`); -``` - -
-
-
- - - - -With the detailed population strategy, define per-component populate queries using the `on` property. - - - - - - - - - -`GET /api/articles?populate[testDz][on][test.test-compo][fields][0]=testString&populate[testDz][on][test.test-compo][populate]=*&populate[testDz][on][test.test-compo2][fields][0]=testInt` - - - - - -```json -{ - "data": [ - { - "id": 1, - "attributes": { - "testString": "test1", - // ... - "testDZ": [ - { - "id": 3, - "__component": "test.test-compo", - "testString": "test1", - "testNestedCompo": { - "testNestedString": "testNested1" - } - }, - { - "id": 1, - "__component": "test.test-compo2", - "testInt": 1 - } - ] - } - } - ], - "meta": { - // ... - } -} -``` - - - - -
- - - - -```js -const qs = require('qs'); -const query = qs.stringify( - { - populate: { - testDz: { - on: { - 'test.test-compo': { - fields: ['testString'], - populate: '*', - }, - 'test.test-compo2': { - fields: ['testInt'], - }, - }, - }, - }, - }, - { - encodeValuesOnly: true, // prettify URL - } -); - -await request(`/api/articles?${query}`); -``` - -
- -
-
- -### Populating createdBy and updatedBy - -The creator fields `createdBy` and `updatedBy` are removed from the REST API response by default. The `createdBy` and `updatedBy` fields can be returned in the REST API by activating the `populateCreatorFields` parameter at the content-type level. - -To add `createdBy` and `updatedBy` to the API response: - -1. Open the content-type `schema.json` file. -2. Add `"populateCreatorFields": true` to the `options` object: - - ```json - "options": { - "draftAndPublish": true, - "populateCreatorFields": true - }, - ``` - -3. Save the `schema.json`. -4. Open the controller `[collection-name].js` file inside the corresponding API request. -5. Add the following piece of code, and make sure you replace the `[collection-name].js` with proper collection name: - - ```js - 'use strict'; - /** - * [collection-name] controller - */ - const { createCoreController } = require('@strapi/strapi').factories; - module.exports = createCoreController('api::[collection-name].[collection-name]', ({ strapi }) => ({ - async find(ctx) { - // Calling the default core action - const { data, meta } = await super.find(ctx); - const query = strapi.db.query('api::[collection-name].[collection-name]'); - await Promise.all( - data.map(async (item, index) => { - const foundItem = await query.findOne({ - where: { - id: item.id, - }, - populate: ['createdBy', 'updatedBy'], - }); - - data[index].attributes.createdBy = { - id: foundItem.createdBy.id, - firstname: foundItem.createdBy.firstname, - lastname: foundItem.createdBy.lastname, - }; - data[index].attributes.updatedBy = { - id: foundItem.updatedBy.id, - firstname: foundItem.updatedBy.firstname, - lastname: foundItem.updatedBy.lastname, - }; - }) - ); - return { data, meta }; - }, - })); - ``` - -REST API requests using the `populate` parameter that include the `createdBy` or `updatedBy` fields will now populate these fields. - -:::note - -The `populateCreatorFields` property is not available to the GraphQL API. -::: - ### Combining Population with other operators By utilizing the `populate` operator it is possible to combine other operators such as [field selection](/dev-docs/api/rest/populate-select#field-selection), [filters](/dev-docs/api/rest/filters-locale-publication), and [sort](/dev-docs/api/rest/sort-pagination) in the population queries. @@ -741,18 +142,10 @@ By utilizing the `populate` operator it is possible to combine other operators s The population and pagination operators cannot be combined. ::: - - - #### Populate with field selection `fields` and `populate` can be combined. - - - -
- @@ -817,21 +210,10 @@ await request(`/api/articles?${query}`); -
-
- - - - #### Populate with filtering `filters` and `populate` can be combined. - - - -
- @@ -901,6 +283,3 @@ await request(`/api/articles?${query}`); ``` - -
-
diff --git a/docusaurus/sidebars.js b/docusaurus/sidebars.js index 816b8bd946..4ec44e84f4 100644 --- a/docusaurus/sidebars.js +++ b/docusaurus/sidebars.js @@ -179,8 +179,6 @@ const sidebars = { id: "dev-docs/api/content-api", }, { - type: "category", - label: "REST API", items: [ { type: "doc", @@ -188,11 +186,25 @@ const sidebars = { label: "Introduction", }, "dev-docs/api/rest/parameters", - "dev-docs/api/rest/populate-select", + { + type: 'doc', + id: "dev-docs/api/rest/populate-select", + customProps: { + updated: true, + }, + }, "dev-docs/api/rest/filters-locale-publication", "dev-docs/api/rest/sort-pagination", "dev-docs/api/rest/relations", "dev-docs/api/rest/interactive-query-builder", + { + type: "doc", + id: "dev-docs/api/rest/guides/intro", + label: "Guides", + customProps: { + new: true, + } + } ], }, "dev-docs/api/graphql", @@ -847,7 +859,7 @@ const sidebars = { { type: "category", collapsed: false, - label: "REST API", + label: "REST API reference", items: [ { type: "category", @@ -909,24 +921,15 @@ const sidebars = { }, { type: "link", - label: "Populate Relations & Media Fields", - href: "/dev-docs/api/rest/populate-select#relations--media-fields", - }, - { - type: "link", - label: "Populate Components & Dynamic Zones", - href: "/dev-docs/api/rest/populate-select#components--dynamic-zones", - }, - { - type: "link", - label: "Populating createdBy and updatedBy", - href: "/dev-docs/api/rest/populate-select#populating-createdby-and-updatedby", + label: "Populate", + href: "/dev-docs/api/rest/populate-select#population", }, { type: "link", label: "Combining populate with other operators", href: "/dev-docs/api/rest/populate-select#combining-population-with-other-operators", }, + ], }, { @@ -1018,6 +1021,30 @@ const sidebars = { }, ], }, + { + type: "category", + label: "Rest API guides", + collapsed: false, + customProps: { + new: true, + }, + link: { + type: 'doc', + id: 'dev-docs/api/rest/guides/intro', + }, + items: [ + { + type: "doc", + label: "Understanding populate", + id: 'dev-docs/api/rest/guides/understanding-populate', + }, + { + type: "doc", + label: "How to populate creator fields", + id: 'dev-docs/api/rest/guides/populate-creator-fields', + }, + ], + } ], devDocsConfigSidebar: [ diff --git a/docusaurus/static/img/assets/rest-api/ctb-article-components-structure.png b/docusaurus/static/img/assets/rest-api/ctb-article-components-structure.png new file mode 100644 index 0000000000..50c8bf41bf Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/ctb-article-components-structure.png differ diff --git a/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure-2.png b/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure-2.png new file mode 100644 index 0000000000..6efc912ff7 Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure-2.png differ diff --git a/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure.png b/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure.png new file mode 100644 index 0000000000..b9b1a737c5 Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/ctb-blocks-dynamic-zone-structure.png differ diff --git a/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram.png b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram.png new file mode 100644 index 0000000000..c2e392caa7 Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram.png differ diff --git a/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram1.png b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram1.png new file mode 100644 index 0000000000..ef863936ed Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram1.png differ diff --git a/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram2.png b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram2.png new file mode 100644 index 0000000000..cebeccb34d Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram2.png differ diff --git a/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram3.png b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram3.png new file mode 100644 index 0000000000..05de02bd3c Binary files /dev/null and b/docusaurus/static/img/assets/rest-api/populate-foodadvisor-diagram3.png differ