# Account billing details Source: //cloud/account/account-billing # Account billing & invoices Through the *Profile* page, accessible by clicking on your profile picture on the top right hand corner of the interface then clicking on **Profile**, you can access the [ *Billing*](#account-billing) and [ *Invoices*](#account-invoices) tabs. ## Account billing The *Billing* tab displays and enables you to modify the billing details and payment method set for the account. The *Payment method* section of the *Billing* tab allows you to manage the credit cards that can be used for the Strapi Cloud projects. The *Billing details* section requires to be filled in, at least for the mandatory fields, as this information will be the default billing details for all Strapi Cloud projects related to your account. ### Adding a new credit card 1. In the *Payment method* section of the *Billing* tab, click on the **Add card** button. 2. Fill in the following fields: | Field name | Description | | --- | --- | | Card Number | Write the number of the credit card to add as payment method. | | Expires | Write the expiration date of the credit card. | | CVC | Write the 3-numbers code displayed at the back of the credit card. | 3. Click on the **Save** button. :::tip The first credit card to be added as payment method for the account will by default be the primary one. It is however possible to define another credit card as primary by clicking on the icon, then **Switch as primary**. ::: ### Deleting a credit card To remove a credit card from the list of payment methods for the account: 1. Click on the icon of the credit card you wish to delete. 2. Click **Remove card**. The card is immediately deleted. :::note You cannot delete the primary card as at least one credit card must be available as payment method, and the primary card is by default that one. If the credit card you wish to delete is currently the primary card, you must first define another credit card as primary, then delete it. ::: ## Account invoices The *Invoices* tab displays the complete list of invoices for all your Strapi Cloud projects. :::strapi Invoices are also available per project. In the *Settings > Invoices* tab of any project, you will find the invoices for that project only. Feel free to check the [dedicated documentation](/cloud/projects/settings#invoices). ::: # Profile settings Source: //cloud/account/account-settings # Profile settings The *Profile* page enables you to manage your account details and preferences. It is accessible by clicking on your profile picture, on the top right hand corner of the interface, and **Profile**. There are 3 tabs available in the *Profile* interface: [*General*](#general), *Billing* and Invoices (the last 2 are documented in the [Account billing details](/cloud/account/account-billing) section of this documentation). ## General The *General* tab enables you to edit the following details for your account profile: - Details: to see the name associated with your account. - Connected accounts: to manage Google, GitHub and GitLab accounts connected with your Strapi Cloud account (see [Managing connected accounts](#managing-connected-accounts)). - Delete account: to permanently delete your Strapi Cloud account (see [Deleting Strapi Cloud account](#deleting-strapi-cloud-account)). ### Managing connected accounts You can connect a Google, GitLab and GitHub account to your Strapi Cloud account. The _Connected accounts_ section lists accounts that are currently connected to your Strapi Cloud account. From there you can also connect a new Google, GitLab and GitHub account if one is not already connected. To connect a new Google, GitLab or GitHub account to your Strapi Cloud account, click on the **Connect account** button and follow the next steps on the corresponding website. You can also click on the three dots button of a connected account and click on the "Manage on" button to manage your GitHub, GitLab or Google account directly on the corresponding website. ### Deleting Strapi Cloud account You can delete your Strapi Cloud account, but it will be permanent and irreversible. All associated projects and their data will be deleted as well and the subscriptions for the projects will automatically be canceled. 1. In the *Delete account* section of the *General* tab, click on the **Delete account** button. 2. In the dialog, type `DELETE` in the textbox. 3. Confirm the deletion of your account by clicking on the **Delete** button. # Database Source: //cloud/advanced/database # Database Strapi Cloud provides a pre-configured PostgreSQL database by default. However, you can also configure it to utilize an external SQL database, if needed. :::prerequisites - A local Strapi project running on `v4.8.2+`. - Credentials for an external database. - If using an existing database, the schema must match the Strapi project schema. ::: :::caution While it's possible to use an external database with Strapi Cloud, you should do it while keeping in mind the following considerations: - Strapi Cloud already provides a managed database that is optimized for Strapi. - Using an external database may result in unexpected behavior and/or performance issues (e.g., network latency may impact performance). For performance reasons, it's recommended to host your external database close to the region where your Strapi Cloud project is hosted. You can find where your Strapi Cloud project is hosted in your Project Settings (see [Project Settings > General > Selected Region](/cloud/projects/settings#general)). - Strapi can't provide security or support with external databases used with Strapi Cloud. ::: ## Configuration The project `./config/database.js` or `./config/database.ts` file must match the configuration found in the [environment variables in database configurations](https://docs.strapi.io/cms/configurations/database#environment-variables-in-database-configurations) section. Before pushing changes, add environment variables to the Strapi Cloud project: 1. Log into Strapi Cloud and click on the corresponding project on the Projects page. 2. Click on the **Settings** tab and choose **Variables** in the left menu. 3. Add the following environment variables: | Variable | Value | Details | | ---------------------------------- | ---------------- |----------| | `DATABASE_CLIENT` | your_db | Should be one of `mysql`, `postgres`, or `sqlite`. | | `DATABASE_HOST` | your_db_host | The URL or IP address of your database host | | `DATABASE_PORT` | your_db_port | The port to access your database | | `DATABASE_NAME` | your_db_name | The name of your database | | `DATABASE_USERNAME` | your_db_username | The username to access your database | | `DATABASE_PASSWORD` | your_db_password | The password associated to this username | | `DATABASE_SSL_REJECT_UNAUTHORIZED` | false | Whether unauthorized connections should be rejected | | `DATABASE_SCHEMA` | public | - | 4. Click **Save**. :::caution To ensure a smooth deployment, it is recommended to not change the names of the environment variables. ::: ## Deployment To deploy the project and utilize the external database, push the changes from earlier. This will trigger a rebuild and new deployment of the Strapi Cloud project. Once the application finishes building, the project will use the external database. ## Reverting to the default database To revert back to the default database, remove the previously added environment variables related to the external database from the Strapi Cloud project dashboard, and save. For the changes to take effect, you must redeploy the Strapi Cloud project. # Email Provider Source: //cloud/advanced/email # Email Provider Strapi Cloud comes with a basic email provider out of the box. However, it can also be configured to utilize another email provider, if needed. :::caution Please be advised that Strapi is unable to provide support for third-party email providers. ::: :::prerequisites - A local Strapi project running on `v4.8.2+`. - Credentials for another email provider (see :::caution The file structure must match the above path exactly, or the configuration will not be applied to Strapi Cloud. ::: Each provider will have different configuration settings available. Review the respective entry for that provider in the :::tip Before pushing the above changes to GitHub, add environment variables to the Strapi Cloud project to prevent triggering a rebuild and new deployment of the project before the changes are complete. ::: ### Strapi Cloud Configuration 1. Log into Strapi Cloud and click on the corresponding project on the Projects page. 2. Click on the **Settings** tab and choose **Variables** in the left menu. 3. Add the required environment variables specific to the email provider. 4. Click **Save**. **Example:** ## Deployment To deploy the project and utilize another party email provider, push the changes from earlier. This will trigger a rebuild and new deployment of the Strapi Cloud project. Once the application finishes building, the project will use the new email provider. :::strapi Custom Provider If you want to create a custom email provider, please refer to the [Email providers](/cms/features/email#providers) documentation in the CMS Documentation. ::: # Upload Provider Source: //cloud/advanced/upload # Upload Provider Strapi Cloud comes with a local upload provider out of the box. However, it can also be configured to utilize a third-party upload provider, if needed. :::caution Please be advised that Strapi is unable to provide support for third-party upload providers. ::: :::prerequisites - A local Strapi project running on `v4.8.2+`. - Credentials for a third-party upload provider (see :::caution The file structure must match the above path exactly, or the configuration will not be applied to Strapi Cloud. ::: Each provider will have different configuration settings available. Review the respective entry for that provider in the ### Configure the Security Middleware Due to the default settings in the Strapi Security Middleware you will need to modify the `contentSecurityPolicy` settings to properly see thumbnail previews in the Media Library. To do this in your Strapi project: 1. Navigate to `./config/middleware.js` or `./config/middleware.ts` in your Strapi project. 2. Replace the default `strapi::security` string with the object provided by the upload provider. **Example:** :::tip Before pushing the above changes to GitHub, add environment variables to the Strapi Cloud project to prevent triggering a rebuild and new deployment of the project before the changes are complete. ::: ### Strapi Cloud Configuration 1. Log into Strapi Cloud and click on the corresponding project on the Projects page. 2. Click on the **Settings** tab and choose **Variables** in the left menu. 3. Add the required environment variables specific to the upload provider. 4. Click **Save**. **Example:** ## Deployment To deploy the project and utilize the third-party upload provider, push the changes from earlier. This will trigger a rebuild and new deployment of the Strapi Cloud project. Once the application finishes building, the project will use the new upload provider. :::strapi Custom Provider If you want to create a custom upload provider, please refer to the [Providers](/cms/features/media-library#providers) documentation in the CMS Documentation. ::: # Command Line Interface (CLI) Source: //cloud/cli/cloud-cli # Command Line Interface (CLI) Strapi Cloud comes with a Command Line Interface (CLI) which allows you to log in and out, and to deploy a local project without it having to be hosted on a remote git repository. The CLI works with both the `yarn` and `npm` package managers. :::note It is recommended to install Strapi locally only, which requires prefixing all of the following `strapi` commands with the package manager used for the project setup (e.g `npm run strapi help` or `yarn strapi help`) or a dedicated node package executor (e.g. `npx strapi help`). ::: ## strapi login **Alias:** `strapi cloud:login` Log in Strapi Cloud. ```bash strapi login ``` This command automatically opens a browser window to first ask you to confirm that the codes displayed in both the browser window and the terminal are the same. Then you will be able to log into Strapi Cloud via Google, GitHub or GitLab. Once the browser window confirms successful login, it can be safely closed. If the browser window doesn't automatically open, the terminal will display a clickable link as well as the code to enter manually. ## strapi deploy **Alias:** `strapi cloud:deploy` Deploy a new local project (< 100MB) in Strapi Cloud. ```bash strapi deploy ``` This command must be used after the `login` one. It deploys a local Strapi project on Strapi Cloud, without having to host it on a remote git repository beforehand. The terminal will inform you when the project is successfully deployed on Strapi Cloud. Deploying a Strapi project through the CLI creates a project on the Free plan. Once the project is first deployed on Strapi Cloud with the CLI, the `deploy` command can be reused to trigger a new deployment of the same project. :::note Once you deployed your project, if you visit the Strapi Cloud dashboard, you may see some limitations as well as impacts due to creating a Strapi Cloud project that is not in a remote repository and which was deployed with the CLI. - Some areas in the dashboard that are usually reserved to display information about the git provider will be blank. - Some buttons, such as the **Trigger deploy** button, will be greyed out and unclickable since, unless you have [connected a git repository to your Strapi Cloud project](/cloud/getting-started/deployment-cli#automatically-deploying-subsequent-changes). ::: ## strapi link **Alias:** `strapi cloud:link` Links project in the current folder to an existing project in Strapi Cloud. ```bash strapi link ``` This command connects your local project in the current directory with an existing project on your Strapi Cloud account. You will be prompted to select the project you wish to link from a list of available projects hosted on Strapi Cloud. ## strapi projects **Alias:** `strapi cloud:projects` Lists all Strapi Cloud projects associated with your account. ```bash strapi projects ``` This command retrieves and displays a list of all projects hosted on your Strapi Cloud account. ## strapi logout **Alias:** `strapi cloud:logout` Log out of Strapi Cloud. ```bash strapi logout ``` This command logs you out of Strapi Cloud. Once the `logout` command is run, a browser page will open and the terminal will display a confirmation message that you were successfully logged out. You will not be able to use the `deploy` command anymore. # Caching & Performance Source: //cloud/getting-started/caching # Caching & Performance For Strapi Cloud applications with large amounts of cacheable content, such as images, videos, and other static assets, enabling CDN (Content Delivery Network) caching via the # Strapi Cloud fundamentals Source: //cloud/getting-started/cloud-fundamentals # Strapi Cloud fundamentals Before going any further into this Strapi Cloud documentation, we recommend you to acknowledge the main concepts below. They will help you to understand how Strapi Cloud works, and ensure a smooth Strapi Cloud experience. - **Hosting Platform**
Strapi Cloud is a hosting platform that allows to deploy already existing Strapi projects created with Strapi CMS (Content Management System). Strapi Cloud is *not* the SaaS (Software as a Service) version of Strapi CMS. Feel free to refer to the [CMS documentation](https://docs.strapi.io/cms/intro) to learn more about Strapi CMS. - **Strapi Cloud Pricing Plans**
As a Strapi Cloud user you have the choice between 4 plans: Free, Essential, Pro and Scale. Depending on the plan, you have access to different functionalities, support and customization options (see [Pricing page](https://strapi.io/pricing-cloud) for more details). In this Strapi Cloud documentation, the , , and badges can be displayed below a section's title to indicate that the feature is only available starting from the corresponding paid plan. If no badge is shown, the feature is available on the Free plan. - **Types of Strapi Cloud users**
There can be 2 types of users on a Strapi Cloud project: owners and maintainers. The owner is the one who has created the project and has therefore access to all features and options for the project. Maintainers are users who have been invited to contribute to an already created project by its owner. Maintainers, as documented in the [Collaboration](/cloud/projects/collaboration) page, cannot view and access all features and options from the Strapi Cloud dashboard. - **Support**
The level of support provided by the Strapi Support team depends on the Strapi Cloud plan you subscribed for. The Free plan does not include access to support. The Essential and Pro plans include Basic support while the Scale plan includes Standard support. Please refer to the [dedicated support article](https://support.strapi.io/support/solutions/articles/67000680833-what-is-supported-by-the-strapi-team#Not-Supported) for all details regarding support levels. # with Cloud dashboard Source: //cloud/getting-started/deployment # Project deployment with the Cloud dashboard This is a step-by-step guide for deploying your project on Strapi Cloud for the first time, using the Cloud dashboard. :::prerequisites Before you can deploy your Strapi application on Strapi Cloud using the Cloud dashboard, you need to have the following prerequisites: * Strapi version `4.8.2` or higher * Project database must be compatible with PostgreSQL. Strapi does not support and does not recommend using any external databases, though it's possible to configure one (see [advanced database configuration](/cloud/advanced/database)). * Project(s) source code hosted on 5. Set up your Strapi Cloud project. 5.a. Fill in the following information: | Setting name | Instructions | |--------------|---------------------------------------------------------------------------------------------------------| | Display name | Write the name of your Strapi app, this is fetched from the repository name but can be edited. It is automatically converted to slug format (`my-strapi-app`). | | Git branch | Choose from the drop-down the default branch to use for this deployment. This uses the default branch of the repository. | | Deploy on push | Check the box to automatically deploy the latest changes from the selected branch. When disabled, you will need to manually deploy the latest changes. | | Region | Choose the geographic location of the servers where your Strapi application is hosted. Selected region can either be US (East), Europe (West), Asia (Southeast) or Oceania. | :::note The Git branch and "Deploy on push" settings can be modified afterwards through the project's setting, however the hosting region can only be chosen during the creation of the project (see [Project Settings](/cloud/projects/settings)). ::: 5.b. (optional) Click on **Show advanced settings** to fill in the following options: | Setting name | Instructions | |--------------|---------------------------------------------------------------------------------------------------------| | Base directory | Write the name of the directory where your Strapi app is located in the repository. This is useful if you have multiple Strapi apps in the same repository or if you have a monorepo. | | Environment variables | Click on **Add variable** to add environment variables used to configure your Strapi app (see [Environment variables](/cms/configurations/environment/) for more information). You can also add environment variables to your Strapi application by adding a `.env` file to the root of your Strapi app directory. The environment variables defined in the `.env` file will be used by Strapi Cloud. | | Node version | Choose a Node version from the drop-down. Default Node version will automatically be chosen to best match the version of your Strapi project. If you manually choose a version that doesn't match with your Strapi project, the build will fail but the explanation will be displayed in the build logs. | :::strapi Using Environment Variables You can use environment variable to connect your project to an external database rather than the default one used by Strapi Cloud (see [database configuration](/cms/configurations/database#environment-variables-in-database-configurations) for more details). If you would like to revert and use Strapi's default database again, you have to remove your `DATABASE_` environment variables (no automatic migration implied). You can also set up here a custom email provider. Sendgrid is set as the default one for the Strapi applications hosted on Strapi Cloud (see [providers configuration](/cms/features/email#providers) for more details). ::: ## Setting up billing details :::strapi No billing step for the Free plan If you chose the free plan, this billing step will be skipped as you will not be asked to share your credit card details at the creation of the project. To upgrade your project to a paid plan, you will need to fill in your billing information in the **Billing** section of your Profile. Skip to step 5 of the section below to finalise the creation of your project. ::: 1. Click on the **Continue to billing** button. You will directly be redirected to the second and final project deployment interface. There you can review all your new project setup information, enter payment & billing details and receive your invoice. 2. Review your project: make sure the plan and setup information are correct. If needed, click the **Edit** button to be redirected to the first interface of the project creation and fix any mistake. 3. In the Payment section, fill in at least all mandatory elements for *Payment method* and *Billing information*. 4. Check your invoice which informs you of what should be paid now and the following month. Optionally, you can enter a *Discount code* if you have one. 5. Click on the **Create project** button to finalize the deployment of your new Strapi Cloud project. An initial deployment will automatically be triggered and you will be redirected to the *Projects* page. :::caution Create your Admin user after the initial deployment is complete. Do not share your application URL with anyone until you have created your Admin user. ::: ## What to do next? Now that you have deployed your project via the Cloud dashboard, we encourage you to explore the following ideas to have an even more complete Strapi Cloud experience: - Invite other users to [collaborate on your project](/cloud/projects/collaboration). - Check out the [deployments management documentation](/cloud/projects/deploys) to learn how to trigger new deployments for your project. # with Cloud CLI Source: //cloud/getting-started/deployment-cli # Project deployment with the Command Line Interface (CLI) This is a step-by-step guide for deploying your project on Strapi Cloud for the first time, using the Command Line Interface. :::prerequisites Before you can deploy your Strapi application on Strapi Cloud using the Command Line Interface, you need to have the following prerequisites: - Have a Google, GitHub or GitLab account. - Have an already created Strapi project (see [Installing from CLI in the CMS Documentation](/cms/installation/cli)), stored locally. The project must be less than 100MB. - Have available storage in your hard drive where the temporary folder of your operating system is stored. ::: ## Logging in to Strapi Cloud 1. Open your terminal. 2. Navigate to the folder of your Strapi project, stored locally on your computer. 3. Enter the following command to log into Strapi Cloud: 4. In the browser window that opens automatically, confirm that the code displayed is the same as the one written in the terminal message. 5. Still in the browser window, choose whether to login via Google, GitHub or GitLab. The window should confirm the successful login soon after. ## Deploying your project 1. From your terminal, still from the folder of your Strapi project, enter the following command to deploy the project: 2. Follow the progression bar in the terminal until confirmation that the project was successfully deployed with Strapi Cloud. Deploying the project will create a new Strapi Cloud project on the Free plan. ### Automatically deploying subsequent changes By default, when creating and deploying a project with the Cloud CLI, you need to manually deploy again all subsequent changes by running the corresponding `deploy` command everytime you make a change. Another option is to enable automatic deployment through a git repository. To do so: 1. Host your code on a git repository, such as or . 2. Connect your Strapi Cloud project to the repository (see the _Connected repository_ setting in [Projects Settings > General](/cloud/projects/settings#general)). 3. Still in _Projects Settings > General_ tab, tick the box for the "Deploy the project on every commit pushed to this branch" setting. From now on, a new deployment to Strapi Cloud will be triggered any time a commit is pushed to the connected git repository. :::note Automatic deployment is compatible with all other deployment methods, so once a git repository is connected, you can trigger a new deployment to Strapi Cloud [from the Cloud dashboard](/cloud/projects/deploys), [from the CLI](/cloud/cli/cloud-cli#strapi-deploy), or by pushing new commits to your connected repository. ::: ## ⏩ What to do next? Now that you have deployed your project via the Command Line Interface, we encourage you to explore the following ideas to have an even more complete Strapi Cloud experience: - Visit the Cloud dashboard to follow [insightful metrics and information](/cloud/projects/overview) on your Strapi project. - Check out the full [Command Line Interface documentation](/cloud/cli/cloud-cli) to learn about the other commands available. # Project deployment Source: //cloud/getting-started/deployment-options # Project deployment with Strapi Cloud You have 2 options to deploy your project with Strapi Cloud: - either with the user interface (UI), meaning that you will perform all the actions directly on the Strapi Cloud dashboard, - or using the Cloud Comment Line Interface (CLI), meaning that you will only interact with a terminal. The guides below will guide you through all the steps for each of the deployment options. # Welcome to the Strapi Cloud Documentation! Source: //cloud/getting-started/intro # Welcome to the Strapi Cloud Documentation! The Strapi Cloud documentation contains all information related to the setup, deployment, update and customization of your Strapi Cloud account and applications. :::strapi What is Strapi Cloud? built on top of Strapi, the open-source headless CMS. ::: :::prerequisites The typical workflow, which is recommended by the Strapi team, is: 1. Create your Strapi application locally (v4.8.2 or later). 2. Optionally, extend the application with plugins or custom code. 3. Version the application's codebase through your git provider (GitHub or GitLab). 4. Deploy the application with Strapi Cloud. ::: The Strapi Cloud documentation is organised in topics in a order that should correspond to your journey with the product. The following cards, on which you can click, will redirect you to the main topics and steps. :::strapi Welcome to the Strapi community! Strapi Cloud is built on top of Strapi, an open-source, community-oriented project. The Strapi team has at heart to share their vision and build the future of Strapi with the Strapi community. This is why the is open: as all insights are very important and will help steer the project in the right direction. Any community member is most welcome to share ideas and opinions there. You can also join , the , and the and benefit from the years of experience, knowledge, and contributions by the Strapi community as a whole. ::: # Information on billing & usage Source: //cloud/getting-started/usage-billing # Information on billing & usage This page contains general information related to the usage and billing of your Strapi Cloud account and projects. Strapi Cloud offers 1 Free plan and 3 paid plans: Essential, Pro and Scale (see [Pricing page](https://strapi.io/pricing-cloud)). The table below summarizes Strapi Cloud usage-based pricing plans, for general features and usage: | Feature | Free | Essential | Pro | Scale | | -------------------------------- | ----- | --------- | --- | ----- | | **Database Entries** | 500 | Unlimited* | Unlimited* | Unlimited* | | **Asset Storage** | 10GB | 50GB | 250GB | 1,000GB | | **Asset Bandwidth (per month)** | 10GB | 50GB | 500GB | 1,000GB | | **API Requests (per month)** | 10,000 | 100,000 | 1,000,000 | 10,000,000 | | | | | | | | **Backups** | N/A | N/A | Weekly | Daily | | **Custom domains** | N/A | Included | Included | Included | | **Environments** | N/A | N/A | 0 included (up to 99 extra) | 1 included (up to 99 extra) | | **Emails (per month)** | 100 | Unlimited* | Unlimited* | Unlimited* | :::strapi Additional information on usage and features - General features & usage: - Database entries are the number of entries in your database. - Asset storage is the amount of storage used by your assets. - Asset bandwidth is the amount of bandwidth used by your assets. - API requests are the number of requests made to your APIs. This includes requests to the GraphQL and REST APIs, excluding requests for file and media assets counted towards CDN bandwidth and storage. - Cloud specific feature: - Backups refers to the automatic backups of Strapi Cloud projects (see [Backups documentation](/cloud/projects/settings#backups) for more information on the feature). - Custom domains refer to the ability to define a custom domain for your Strapi Cloud (see [Custom domains](/cloud/projects/settings#connecting-a-custom-domain)). - Environments refers to the number of environments included in the plan on top of the default production environment (see [Environments](/cloud/projects/settings#environments) documentation for more information on the feature). ::: :::info Scale-to-zero and cold start on the Free plan On the Free plan, projects automatically scale down to zero after a short period of inactivity. When the application is accessed again—either through the frontend or via an API request—it may take a few seconds (up to a minute) before a response is returned. Upgrading to a paid plan disables scaling to zero and cold starts, resulting in instant response times at all times. ::: ## Environments management Environments are isolated instances of your Strapi Cloud project. All projects have a default production environment, but other additional environments can be configured for projects on a Pro or Scale plan, from the *Environments* tab of a project's settings (see [Environments](/cloud/projects/settings#environments)). There is no limit to the number of additional environments that can be configured for a Strapi Cloud project. The usage limits of additional environments are the same as for the project's production environment (e.g. an additional environment on the Pro plan will be limited at 250GB for asset storage, and overages will be charged the same way as for the production environment). Note however that the asset bandwidth and API calls are project-based, not environment-based, so these usage limits do not change even with additional environments. ## Billing Billing is based on the usage of your Strapi Cloud account and projects. You will be billed monthly for the usage of your account and applications. You can view your usage and billing information in the section of your Strapi Cloud account. ### Overages :::caution Overages are not allowed on the Free plan. ::: If you exceed the limits of your plan for API Requests, Asset Bandwidth, or Asset Storage, you will be charged for the corresponding overages. For example, if you exceed the 500GB limit in asset bandwidth of the Pro plan, you will be charged for the excess bandwidth at the end of the current billing period or on project deletion. Overages are not prorated and are charged in full. Overages are charged according to the following rates: | Feature | Rate | | --- | --- | | **API Requests** | $1.50 / 25k requests | | **Asset Bandwidth** | $30.00 / 100GB | | **Asset Storage** | $0.60 / GB per month | ### Project suspension Projects may end up in a **Suspended** state for various reasons, including: not paying the invoice, exceeding the limits of your free plan, or violating the . If your project is suspended, you will no longer be able to access the application or trigger new deployments. You will also be unable to access the Strapi admin panel. You can view the status of your project in the section of your Strapi Cloud account and you will be notified by email. :::warning If you do not resolve the issue within 30 days, your suspended project will be deleted and all data will be permanently lost. To avoid this situation, you will be sent a first email when your project becomes suspended, then another email every 5 days until one week left, to remind you to solve the issue. The last week before the deletion of the project, you will be sent 3 more emails: 6 days, 3 days and 1 day before your project is finally deleted. ::: #### Project suspension for exceeding the Free plan limits When a project hosted with the Free plan exceeds either the API requests or the Asset Bandwidth limits, it will be suspended until the monthly allowance resets at the beginning of the following month. While the project is suspended: - Users cannot trigger new deployments - Access to the application is blocked - Users cannot make changes to the project’s settings To reactivate the project immediately, users can upgrade to a paid plan. #### Project suspension after subscription cancellation If you don't pay the invoice, the subscription of your project will automatically be canceled and the project will be suspended. You can reactivate the subscription through the billing modal (see [Edit subscription](/cloud/account/account-billing#account-billing)). 1. Log into the billing modal and go to the *Subscription details* of the subscription associated with the suspended project. You should see a warning message confirming that the subscription was canceled for the following reason: "Not Paid". 2. Go back to the homepage of the billing modal, listing subscriptions and billing options. 3. Go to *Payment methods* and add a new, working card to pay the invoice. As soon as the invoice is paid, your project will automatically be reactivated. #### Project suspension for other reasons If your project was suspended for reasons other than unpaid invoice leading to subscription cancellation, you may not have the possibility to reactivate your project yourself. You should receive an email with instructions on how to resolve the issue. If you do not receive the email notification, please contact [Strapi Support](mailto:support@strapi.io). # Collaboration Source: //cloud/projects/collaboration # Collaboration on projects Projects are created by a user via their Strapi Cloud account. Strapi Cloud users can share their projects to anyone else, so these new users can have access to the project dashboard and collaborate on that project, without the project owner to ever have to share their credentials. Users invited to collaborate on a project, called maintainers, do not have the same permissions as the project owner. Contrary to the project owner, maintainers: - Cannot share the project themselves to someone else - Cannot delete the project from the project settings - Cannot access the *Billing* section of project settings ## Sharing a project To invite a new maintainer to collaborate on a project: 1. From the *Projects* page, click on the project of your choice to be redirected to its dashboard. 2. Click on the **Share** button located in the dashboard's header. 3. In the *Share [project name]* dialog, type the email address of the person to invite in the textbox. A dropdown indicating "Invite [email address]" should appear. 4. Click on the dropdown: the email address should be displayed in a purple box right below the textbox. 5. (optional) Repeat steps 3 and 4 to invite more people. Email addresses can only entered one by one but invites can be sent to several email addresses at the same time. 6. Click on the **Send** button. New maintainers will be sent an email containing a link to click on to join the project. Once a project is shared, avatars representing the maintainers will be displayed in the project dashboard's header, next to the **Share** button, to see how many maintainers collaborate on that project and who they are. :::tip Avatars use GitHub, Google or GitLab profile pictures, but for pending users only initials will be displayed until the activation of the maintainer account. You can hover over an avatar to display the full name of the maintainer. ::: ## Managing maintainers From the *Share [project name]* dialog accessible by clicking on the **Share** button of a project dashboard, projects owners can view the full list of maintainers who have been invited to collaborate on the project. From there, it is possible to see the current status of each maintainer and to manage them. Maintainers whose full name is displayed are users who did activate their account following the invitation email. If however there are maintainers in the list whose email address is displayed, it means they haven't activated their accounts and can't access the project dashboard yet. In that case, a status should be indicated right next to the email address to explain the issue: - Pending: the invitation email has been sent but the maintainer hasn't acted on it yet. - Expired: the email has been sent over 72 hours ago and the invitation expired. For Expired statuses, it is possible to send another invitation email by clicking on the **Manage** button, then **Resend invite**. ### Revoking maintainers To revoke a maintainer's access to the project dashboard: 1. Click on the **Share** button in the project dashboard's header. 2. In the list of *People with access*, find the maintainer whose access to revoke and click on the **Manage** button. 3. Click on the **Revoke** button. 4. In the confirmation dialog, click again on the **Revoke** button. The revoked maintainer will completely stop having access to the project dashboard. :::note Maintainers whose access to the project has been revoked do not receive any email or notification. ::: # Deployments management Source: //cloud/projects/deploys # Deployments management The creation of a new Strapi Cloud project automatically trigger the deployment of that project. After that, deployments can be: - manually triggered whenever needed, [from the Cloud dashboard](#triggering-a-new-deployment) or [from the CLI](/cloud/cli/cloud-cli#strapi-deploy), - or automatically triggered everytime a new commit is pushed to the branch, if the Strapi Cloud project is connected to a git repository and the "deploy on push" option is enabled (see [Project settings](/cloud/projects/settings#modifying-git-repository--branch)). Ongoing deployments can also be [manually canceled](#cancelling-a-deployment) if needed. ## Triggering a new deployment To manually trigger a new deployment for your project, click on the **Trigger deployment** button always displayed in the right corner of a project dashboard's header. This action will add a new card in the *Deployments* tab, where you can monitor the status and view the deployment logs live (see [Deploy history and logs](/cloud/projects/deploys-history)). ## Cancelling a deployment If for any reason you want to cancel an ongoing and unfinished deployment: 1. Go to the *Deployment details* page of the latest triggered deployment (see [Accessing log details](/cloud/projects/deploys-history#accessing-deployment-details--logs)). 2. Click on the **Cancel deployment** button in the top right corner. The status of the deployment will automatically change to *Canceled*. :::tip You can also cancel a deployment from the *Deployments* tab which lists the deployments history. The card of ongoing deployment with the *Building* status will display a ![Cancel button](/img/assets/icons/clear.svg) button for cancelling the deployment. ::: # Deployment history & logs Source: //cloud/projects/deploys-history # Deployment history and logs {#deploy-history-and-logs} For each Strapi Cloud project, you can access the history of all deployments that occurred and their details including build and deployment logs. This information is available in the *Deployments* tab. ## Viewing the deployment history {#viewing-deploy-history} In the *Deployments* tab is displayed a chronological list of cards with the details of all historical deployments for your project. , with a direct link to your git provider, and commit message - Deployment status: - *Deploying* - *Done* - *Canceled* - *Build failed* - *Deployment failed* - Last deployment time (when the deployment was triggered and the duration) - Branch ## Accessing deployment details & logs From the *Deployments* tab, you can hover a deployment card to make the ![See logs button](/img/assets/icons/Eye.svg) **Show details** button appear. Clicking on this button will redirect you to the *Deployment details* page which contains the deployment's detailed logs. , with a direct link to your git provider, and commit message used for this deployment - *Status*, which can be *Building*, *Deploying*, *Done*, *Canceled*, *Build failed*, or *Deployment failed* - *Source*: the branch and commit message for this deployment - *Duration*: the amount of time the deployment took and when it occurred # Notifications Source: //cloud/projects/notifications # Notifications The Notification center can be opened by clicking the bell icon in the top navigation of the Cloud dashboard. It displays a list of the latest notifications for all your existing projects. Clicking on a notification card from the list will redirect you to the *Log details* page of the corresponding deployment (more information in [Deploy history & logs](/cloud/projects/deploys-history#accessing-deployment-details--logs)). The following notifications can be listed in the Notifications center: - *deployment completed*: when a deployment is successfully done. - *Build failed*: when a deployment fails during the build stage. - *deployment failed*: when a deployment fails during the deployment stage. - *deployment triggered*: when a deployment is triggered by a new push to the connected repository. This notification is however not sent when the deployment is triggered manually. :::note All notifications older than 30 days are automatically removed from the Notification center. ::: # Projects overview Source: //cloud/projects/overview # Projects overview The *Projects* page displays a list of all your Strapi Cloud projects. From here you can manage your projects and access the corresponding applications. Each project card displays the following information: * the project name * the last successful deployment’s date of the Production environment * the current status of the project: * *Disconnected*, if the project repository is not connected to Strapi Cloud * *Suspended*, if the project has been suspended (refer to [Project suspension](/cloud/getting-started/usage-billing#project-suspension) to reactivate the project) * *Incompatible version*, if the project is using a Strapi version that is not compatible with Strapi Cloud Each project card also displays a menu icon to access the following options: * **Visit App**: to be redirected to the application * **Go to Deployments**: to be redirected to the [*Deployment*](/cloud/projects/deploys) page * **Go to Settings**: to be redirected to the [*Settings*](/cloud/projects/settings) page ## Accessing a project's dashboard From the *Projects* page, click on any project card to access its dashboard. It displays the project and environment details and gives access to the deployment history and all available settings. From the dashboard's header of a chosen project, you can: - use the **Share** button to invite users to collaborate on the project (see [Collaboration](/cloud/projects/collaboration)) and see the icons of those who have already been invited , - use the **Settings** button to access the settings of the project and its existing environments , - choose which environment to visualise for the project or add a new environment , - trigger a new deployment (see [Deployments management](/cloud/projects/deploys)) and visit your application . Your project's dashboard also displays: - the *Deployments* and *Runtime logs* tabs, to see the deployments history (more details in [Deploy history and logs](/cloud/projects/deploys-history)) and the runtime logs of the project (see [dedicated documentation page](/cloud/projects/runtime-logs)) - the project and environment details in a box on the right of the interface , including: - the number of API calls, - the current usage for asset bandwidth and storage, - the name of the branch and a **Manage** button to be redirect to the branch settings (see [Modifying git repository & branch](/cloud/projects/settings#modifying-git-repository--branch)), - the name of the base directory, - the Strapi version number, - the Strapi app's url. # Runtime logs Source: //cloud/projects/runtime-logs # Runtime logs From a chosen project's dashboard, the *Runtime logs* tab displays the live logs of the project. :::note - The *Runtime logs* are only accessible once the project is successfully deployed. - Runtime logs are not live for projects on the Free plan and are reset each time the application is scaled to zero due to inactivity. ::: # Project settings Source: //cloud/projects/settings # Project settings From a chosen project's dashboard, the **Settings** button, located in the header, enables you to manage the configurations and settings for your Strapi Cloud project and its environments. The settings' menu on the left side of the interface is separated into 2 categories: the settings for the entire project and the settings specific to any configured environment for the project. ## Project-level settings There are 5 tabs available for the project's settings: - [*General*](#general), - [*Environments*](#environments), - [*Billing & Usage*](#billing--usage), - [Plans](#plans), - and [Invoices](#invoices). ### General The *General* tab for the project-level settings enables you to check and update the following options for the project: - *Basic information*, to see: - the name of your Strapi Cloud project — used to identify the project on the Cloud Dashboard, Strapi CLI, and deployment URLs — and change it (see [Renaming project](#renaming-project)). - the chosen hosting region for your Strapi Cloud project, meaning the geographical location of the servers where the project and its data and resources are stored. The hosting region is set at project creation (see [Project creation](/cloud/getting-started/deployment)) and cannot be modified afterwards. - the app's internal name for the project, which can be useful for debug & support purposes. - *Strapi CMS license key*: to enable and use some CMS features directly on your Cloud project (see [Pricing page](https://strapi.io/pricing-self-hosted) to purchase a license). - *Connected Git repository*: to change the repository and branch used for your project (see [Modifying git repository & branch](#modifying-git-repository--branch)). Also allows to enable/disable the "deploy on push" option. - *Delete project*: to permanently delete your Strapi Cloud project (see [Deleting Strapi Cloud project](#deleting-strapi-cloud-project)). #### Renaming project The project name is set at project creation (see [Project creation](/cloud/getting-started/deployment)) and can be modified afterwards via the project's settings. 1. In the *Basic information* section of the *General* tab, click on the edit button. 2. In the dialog, write the new project name of your choice in the *Project name* textbox. 3. Click on the **Rename** button to confirm the project name modification. #### Adding a CMS license key {#adding-cms-license-key} A CMS license key can be added and connected to a Strapi Cloud project to be able to use some features of Strapi CMS. The CMS features that will be accessible via the license key depend on the type of license that was purchased: please refer to the for more information and/or to purchase a license. :::note If you don't see the *Strapi CMS license key* section, it probably means that your subscription is a legacy one and does not support custom CMS licenses. It means that you already have one that is automatically included on your project. ::: 1. In the *Strapi CMS license key* section, click on the **Add license** button. 2. In the dialog, paste your license key in the field. 3. Click on **Save**. To remove the Strapi CMS license from your Strapi Cloud project, you can click on the **Unlink license** button. This will also remove access and usage to the CMS features included in the previously added license. #### Modifying git repository & branch The GitHub or GitLab repository, branch and base directory for a Strapi Cloud project are by default chosen at the creation of the project (see [Creating a project](/cloud/getting-started/deployment)). After the project's creation, via the project's settings, it is possible to update the project's repository or switch to another git provider. :::caution Updating the git repository could result in the loss of the project and its data, for instance if the wrong repository is selected or if the data schema between the old and new repository doesn't match. ::: 1. In the *Connected git repository* section of the *General* tab, click on the **Update repository** button. You will be redirected to another interface. 2. (optional) If you wish to not only update the repository but switch to another git provider, click on the **Switch Git provider** button at the top right corner of the interface. You will be redirected to the chosen git provider's authorization settings before getting back to the *Update repository* interface. 3. In the *Update repository* section, fill in the 2 available settings: | Setting name | Instructions | | --------------- | ------------------------------------------------------------------------ | | Account | Choose an account from the drop-down list. | | Repository | Choose a repository from the drop-down list. | 4. In the *Select Git branches* section, fill in the available settings for any of your environments. Note that the branch can be edited per environment via its own settings, see [General (environment)](#environments). | Setting name | Instructions | | --------------- | ------------------------------------------------------------------------ | | Branch | Choose a branch from the drop-down list. | | Base directory | Write the path of the base directory in the textbox. | | Auto-deploy | Tick the box to automatically trigger a new deployment whenever a new commit is pushed to the selected branch. Untick it to disable the option. | 5. Click on the **Update repository** button at the bottom of the *Update repository* interface. 6. In the *Update repository* dialog, confirm your changes by clicking on the **Confirm** button. #### Deleting Strapi Cloud project You can delete any Strapi Cloud project, but it will be permanent and irreversible. Associated domains, deployments and data will be deleted as well and the subscription for the project will automatically be canceled. 1. In the *Delete project* section of the *General* tab, click on the **Delete project** button. 2. In the dialog, select the reason why you are deleting your project. If selecting "Other" or "Missing feature", a textbox will appear to let you write additional information. 3. Confirm the deletion of your project by clicking on the **Delete project** button at the bottom of the dialog. ### Environments {#environments} The *Environments* tab allows to see all configured environments for the Strapi Cloud project, as well as to create new ones. Production is the default environment, which cannot be deleted. Other environments can be created (depending on the subscription plan for your project) to work more safely on isolated instances of your Strapi Cloud project (e.g. a staging environment where tests can be made before being available on production). :::tip Clicking on the **Manage** button for any environment will redirect you to the environment's own general settings, where it is possible to change the Node version, edit the git branches and delete or reset the environment. Please [refer to the dedicated documentation](#environments) for more information. ::: :::tip A new environment can also be added from the [project dashboard](/cloud/projects/overview#accessing-a-projects-dashboard). ::: To create a new environment: 1. Click on the **Add a new environment** button. 2. In the dialog that opens, you can see the price for the new environment and the date of the next invoice. 3. Fill in the available settings: | Setting name | Instructions | | ---------------- | ------------------------------------------------------------------------ | | Environment name | (mandatory) Write a name for your project's new environment. | | Git branch | (mandatory) Select the right branch for your new environment. | | Base directory | Write the name of the base directory of your new environment. | | Import variables | Tick the box to import variable names from an existing environment. Values will not be imported, and all variables will remain blank. | | Auto-deploy | Tick the box to automatically trigger a new deployment whenever a new commit is pushed to the selected branch. Untick it to disable the option. | 4. Click on the **Add environment** button to create your project's new environment. A new deployment will automatically be triggered. :::caution If the creation of a new environment fails but you are still charged, try creating the environment again. This time, the environment creation should be successful and you will not be charged a second time. This behaviour is an known issue that should be fixed in the upcoming weeks. ::: ### Billing & Usage The *Billing & Usage* displays your next estimated payment, all information on the current subscription plan and a detailed summary of the project's and its environments' usage. It also allows you to add new environments (please [refer to the documentation in the Environments section](#environments)) for your project. Through this tab, you also have the possibility to: - click the **Change** button to be redirected to the *Plans* tab, where you can change you subscription plan ([see related documentation](#plans)), - click the **Edit** button in order to set a new payment method (see [related documentation](/cloud/account/account-billing)). :::note You can attach a dedicated card to your project by choosing the payment method directly from this page. In that way, you can manage your subscriptions with different cards. ::: :::tip In the Usage section of the *Billing & Usage* tab, you can see the current monthly usage of your project compared to the maximum usage allowed by your project's subscription. Use the arrows in the top right corner to see the project's usage for any chosen month. Note also that if your usage indicates that another subscription plan would fit better for your project, a message will be displayed in the *Billing & Usage* tab to advise which plan you could switch to. ::: ### Plans The *Plans* tab displays an overview of the available Strapi Cloud plans and allows you to upgrade or downgrade from your current plan to another. :::info Strapi recently launched [new Cloud plans](https://strapi.io/pricing-cloud). For now, you can [downgrade](#downgrading-to-another-plan) or [upgrade](#upgrading-to-another-plan) to another plan directly from the Cloud dashboard, under the **Settings** > **Plans** section. If your project was created before the new plans were released, it may be on a *legacy* plan—deprecated but still supported. You can sidegrade to a new plan if desired (see [downgrade section](#downgrading-to-another-plan)). ::: #### Upgrading to another plan Strapi Cloud plan upgrades to another, higher plan are immediate and can be managed for each project via the project settings. :::note When using the Free plan, the buttons to upgrade to another plan are greyed out and unusable until you have filled in your billing information. Please refer to [Account billing details](/cloud/account/account-billing) for more information. ::: To upgrade your current plan to a higher one: 1. In the *Plans* tab of your project's settings, click on the **Upgrade** button of the plan you want to upgrade to. 2. In the window that opens, check the payment details that indicate how much you will have to pay immediately after confirming the upgrade, and the available options. a. (optional) Click the **Edit** button to select another payment method. b. (optional) Click **I have a discount code**, enter your discount code in the field, and click on the **Apply** button. 3. Click on the **Upgrade to [plan name]** button to confirm the upgrade of your Strapi project to another plan. #### Downgrading to another plan Strapi Cloud plan downgrades can be managed for each project via the project settings. Downgrades are however not immediately effective: the higher plan will still remain active until the end of the current month (e.g. if you downgrade from the Scale plan to the Pro plan on June 18th, your project will remain on the Scale plan until the end of the month: on July 1st, the Pro plan will be effective for the project). :::caution Make sure to check the usage of your Strapi Cloud project before downgrading: if your current usage exceeds the limits of the lower plan, you are taking the risk of getting charged for the overages. You may also lose access to some features: for example, downgrading to the Essential plan which doesn't include the Backups feature, would make you lose all your project's backups. Please refer to [Information on billing & usage](/cloud/getting-started/usage-billing) for more information. Note also that you cannot downgrade if you have additional environments (i.e. extra environments that have been purchased, not the default or included environments). For instance, if you wish to downgrade from the Pro plan to the Essential plan, you first need to delete all additional environments that have been configured (see [Resetting & Deleting environment](#resetting--deleting-environment)), for the **Downgrade** button to be displayed and available again. ::: To downgrade your current plan to a lower one: 1. In the *Plans* tab of your project's settings, click on the **Downgrade** button of the plan you want to downgrade to. 2. In the window that opens, check the information related to downgrading. 3. Click on the **Downgrade** button to confirm the downgrade of your Strapi project's plan. :::tip Downgrades are effective from the 1st of the following month. Before that date, you can click on the **Cancel downgrade** button to remain on the current plan. ::: ### Invoices The *Invoices* tab displays the full list of invoices for your Strapi Cloud project as well as their status. :::strapi Invoices are also available in your profile settings. In the *Profile > Invoices* tab, you will find the complete list of invoices for all your projects. Feel free to check the [dedicated documentation](/cloud/account/account-billing#account-invoices). ::: No invoice is issued for the Free plan. ## Environment-level settings In the project's environments' settings, you first need to select the environment whose settings you would like to configure, using the dropdown. Depending on the chosen environment, there are 3 to 4 tabs available: - [*Configuration*](#configuration), - [*Backups*](#backups), which are only available for the production environment, - [*Domains*](#domains), - and [*Variables*](#variables). ### Configuration The *Configuration* tab for the environment-level settings enables you to check and update the following options for the project: - *Basic information*, to see: - the name of your Strapi Cloud project's environment. The environment name is set when it is created and cannot be modified afterwards. - the Node version of the environment: to change the Node version of the project (see [Modifying Node version](#modifying-node-version)). - the app's internal name for the environment, which can be useful for debug & support purposes. - *Connected branch*: to change the branch of the GitHub repository used for your environment (see [Editing Git branch](#editing-git-branch)). Also allows to enable/disable the "deploy on push" option. - *Danger zone*: to reset or permanently delete your Strapi Cloud project's environment (see [Resetting & Deleting environment](#resetting--deleting-environment)). #### Modifying Node version The environment's Node version is based on the one chosen at the creation of the project (see [Creating a project](/cloud/getting-started/deployment)), through the advanced settings. It is possible to switch to another Node version afterwards, for any environment. 1. In the *Basic information* section of the *Configuration* tab, click on the *Node version*'s edit button. 2. Using the *Node version* drop-down in the dialog, click on the version of your choice. 3. Click on the **Save** button. 4. Trigger a new deployment in the environment for which you changed the Node version. If the deployment fails, it is because the Node version doesn't match the version of your Strapi project. You will have to switch to the other Node version and re-deploy your project again. #### Editing Git branch 2. In the *Edit branch* dialog, edit the available settings. Note that the branch can be edited for all environments at the same time via the project's settings, see [General](#general). | Setting name | Instructions | | --------------- | ------------------------------------------------------------------------ | | Selected branch | (mandatory) Choose a branch from the drop-down list. | | Base directory | Write the path of the base directory in the textbox. | | Deploy the project on every commit pushed to this branch | Tick the box to automatically trigger a new deployment whenever a new commit is pushed to the selected branch. Untick it to disable the option. | 3. Click on the **Save** button. #### Resetting & Deleting environment You can reset or delete any additional environment of your Strapi Cloud project, but it will be permanent and irreversible. The default, production environment, can however not be neither reset nor deleted. ##### Resetting an environment Resetting an environment deletes all environments data and resets the variables to their default. To do so: 1. In the *Danger zone* section of the *Configuration* tab, click on the **Reset environment** button. 2. In the dialog that opens, click on the **Continue** button to confirm the environment reset. 3. Fill in the available fields to reset the environment: | Setting name | Instructions | | --------------- | ------------------------------------------------------------------------ | | Environment name | (mandatory) Write a name for your project's new environment. | | Git branch | (mandatory) Choose a branch from the drop-down list. | | Base directory | Write the path of the base directory in the textbox. | | Import variables | Tick the box to import variable names from an existing environment. Values will not be imported, and all variables will remain blank. | | Auto-deploy | Deploy the project on every commit pushed to this branch | Tick the box to automatically trigger a new deployment whenever a new commit is pushed to the selected branch. Untick it to disable the option. | 4. Click on the **Reset** button. ##### Deleting an environment 1. In the *Danger zone* section of the *Configuration* tab, click on the **Delete environment** button. 2. Write in the textbox your *Environment name*. 3. Click on the **Delete environment** button to confirm the deletion. ### Backups {#backups} The *Backups* tab informs you of the status and date of the latest backup of your Strapi Cloud projects. The databases associated with all existing Strapi Cloud projects are indeed automatically backed up (weekly for Pro plans and daily for Scale plans). Backups are retained for a 28-day period. Additionally, you can create a single manual backup. :::note Notes - The backup feature is not available for Strapi Cloud projects using the Free or Essential plans. You will need to upgrade to either the Pro or Scale plan to have your project automatically backed up and to have access to manual backups. - The manual backup option should become available shortly after project's first succesful deployment. - The backup feature is only available for the default, production environment. Other additional environment's settings will not show the *Backups* tab. ::: :::tip For projects created before the release of the Backup feature in October 2023, the first backup will automatically be triggered with the next deployment of the project. ::: #### Creating a manual backup To create a manual backup, in the *Backups* section, click on the **Create backup** button. The manual backup should start immediately, and restoration or creation of other backups will be disabled until backup is complete. :::caution When creating a new manual backup, any existing manual backup will be deleted. You can only have one manual backup at a time. ::: #### Restoring a backup If you need to restore a backup of your project: 1. In the *Backups* section, click on the **Restore backup** button. 2. In the dialog, choose one of the available backups (automatic or manual) of your project in the *Choose backup* drop-down. 3. Click on the **Restore** button of the dialog. Once the restoration is finished, your project will be back to the state it was at the time of the chosen backup. You will be able to see the restoration timestamp and the backup restored in the *Backups* tab. 4. The timestamp of the last completed restoration will be displayed to help you track when the project was last restored. ### Domains The *Domains* tab enables you to manage domains and connect new ones. All existing domains for your Strapi Cloud project are listed in the *Domains* tab. For each domain, you can: - see its current status: - Active: the domain is currently confirmed and active - Pending: the domain transfer is being processed, waiting for DNS changes to propagate - Failed: the domain change request did not complete as an error occured - click the edit button to access the settings of the domain - click the delete button to delete the domain #### Connecting a custom domain Default domain names are made of 2 randomly generated words followed by a hash. They can be replaced by any custom domain of your choice. 1. Click the **Connect new domain** button. 2. In the window that opens, fill in the following fields: | Setting name | Instructions | | ------------------------- | ------------------------------------------------------------------------- | | Domain name | Type the new domain name (e.g. *custom-domain-name.com*) | | Hostname | Type the hostname (i.e. address end-users enter in web browser, or call through APIs). | | Target | Type the target (i.e. actual address where users are redirected when entering hostname). | | Set as default domain | Tick the box to make the new domain the default one. | 3. Click on the **Save** button. :::caution Custom domains and assets When using custom domains, these domains do not apply to the URLs of uploaded assets. Uploaded assets keep the Strapi Cloud project-based URL. This means that, if your custom domain is hosted at `https://my-custom-domain.com` and your Strapi Cloud project name is `my-strapi-cloud-instance`, API calls will still return URLs such as `https://my-strapi-cloud-instance.media.strapiapp.com/example.png`. ::: :::note Custom domains are not available on the Free plan. Downgrading to the Free plan will result in the application domain's being restored to the default one. ::: ### Variables Environment variables (more information in the [CMS Documentation](/cms/configurations/environment)) are used to configure the environment of your Strapi application, such as the database connection. In the *Variables* tab are listed both the default and custom environment variables for your Strapi Cloud project. Each variable is composed of a *Name* and a *Value*. #### Managing environment variables Hovering on an environment variable, either default or custom, displays the following available options: - **Show value** to replace the `*` characters with the actual value of a variable. - **Copy to clipboard** to copy the value of a variable. - **Actions** to access the Edit and Delete buttons. - When editing a default variable, the *Name* cannot be modified and the *Value* can only be automatically generated using the Generate value button. Don't forget to **Save** at the end. - When editing a custom variable, both the *Name* and *Value* can be modified by writing something new or by using the Generate value button. Don't forget to **Save** at the end. - When deleting a variable, you will be asked to click on a **Confirm** button to confirm the deletion. :::tip Use the search bar to find more quickly an environment variable in the list! ::: #### Creating custom environment variables Custom environment variables can be created for the Strapi Cloud project. Make sure to redeploy your project after creating or editing an environment variable. 1. In the *Custom environment variables* section, click on the **Add variable** button. 2. Write the *Name* and *Value* of the new environment variable in the same-named fields. Alternatively, you can click on the icon to generate automatically the name and value. 3. (optional) Click on **Add another** to directly create one or more other custom environment variables. 4. Click on the **Save** button to confirm the creation of the custom environment variables. # Admin panel customization Source: //cms/admin-panel-customization # Admin panel customization The **front-end part of Strapi** is called the admin panel. The admin panel presents a graphical user interface to help you structure and manage the content that will be accessible through the Content API. To get an overview of the admin panel, please refer to the [Getting Started > Admin panel](/cms/features/admin-panel) page. From a developer point of view, Strapi's admin panel is a React-based single-page application that encapsulates all the features and installed plugins of a Strapi application. Admin panel customization is done by tweaking the code of the `src/admin/app` file or other files included in the `src/admin` folder (see [project structure](/cms/project-structure)). By doing so, you can: - Customize some parts of the admin panel to better reflect your brand identity (logos, favicon) or your language, - Replace some other parts of the admin panel, such as the Rich text editor and the bundler, - Extend the theme or the admin panel to add new features or customize the existing user interface. ## General considerations :::prerequisites Before updating code to customize the admin panel: - Rename the default `app.example.tsx|js` file into `app.ts|js`. - Create a new `extensions` folder in `/src/admin/`. - If you want to see your changes applied live while developing, ensure the admin panel server is running (it's usually done with the `yarn develop` or `npm run develop` command if you have not changed the default [host, port, and path](/cms/configurations/admin-panel#admin-panel-server) of the admin panel). ::: Most basic admin panel customizations will be done in the `/src/admin/app` file, which includes a `config` object. Any file used by the `config` object (e.g., a custom logo) should be placed in a `/src/admin/extensions/` folder and imported inside `/src/admin/app.js`. This will replace the folder's content located at `./build`. Visit ## Basic example The following is an example of a basic customization of the admin panel: :::strapi Detailed examples in the codebase * You can see the full translation keys, for instance to change the welcome message, [on GitHub](https://github.com/strapi/strapi/blob/develop/packages/core/admin/admin/src/translations). * Light and dark colors are also found [on GitHub](https://github.com/strapi/design-system/tree/main/packages/design-system/src/themes). ::: # Admin panel bundlers Source: //cms/admin-panel-customization/bundlers Strapi's [admin panel](/cms/admin-panel-customization) is a React-based single-page application that encapsulates all the features and installed plugins of a Strapi application. 2 different bundlers can be used with your Strapi 5 application, [Vite](#vite) (the default one) and [webpack](#webpack). Both bundlers can be configured to suit your needs. :::info For simplification, the following documentation mentions the `strapi develop` command, but in practice you will probably use its alias by running either `yarn develop` or `npm run develop` depending on your package manager of choice. ::: ## Vite In Strapi 5, ## Webpack In Strapi 5, the default bundler is Vite. To use # Admin panel extension Source: //cms/admin-panel-customization/extension # Admin panel extension Strapi's [admin panel](/cms/admin-panel-customization) is a React-based single-page application that encapsulates all the features and installed plugins of a Strapi application. If the [customization options](/cms/admin-panel-customization#available-customizations) provided by Strapi are not enough for your use case, you will need to extend Strapi's admin panel. Extending Strapi's admin panel means leveraging its React foundation to adapt and enhance the interface and features according to the specific needs of your project, which might imply creating new components or adding new types of fields. There are 2 use cases where you might want to extend the admin panel: - As a Strapi plugin developer, you want to develop a Strapi plugin that extends the admin panel **everytime it's installed in any Strapi application**. 👉 This can be done by taking advantage of the [Admin Panel API for plugins](/cms/plugins-development/admin-panel-api). - As a Strapi developer, you want to develop a unique solution for a Strapi user who only needs to extend a specific instance of a Strapi application. 👉 This can be done by directly updating the `/src/admin/app` file, which can import any file located in `/src/admin/extensions`. :::strapi Additional resources * If you're searching for ways of replacing the default Rich text editor, please refer to the [corresponding page](/cms/admin-panel-customization/wysiwyg-editor). * The also provide extensive additional information on developing for Strapi's admin panel. ::: # Favicon Source: //cms/admin-panel-customization/favicon # Favicon Strapi's [admin panel](/cms/admin-panel-customization) displays its branding on various places, including the [logo](/cms/admin-panel-customization/logos) and the favicon. Replacing these images allows you to match the interface and application to your identity. To replace the favicon: 1. Create a `/src/admin/extensions/` folder if the folder does not already exist. 2. Upload your favicon into `/src/admin/extensions/`. 3. Replace the existing **favicon.png|ico** file at the Strapi application root with a custom `favicon.png|ico` file. 4. Update `/src/admin/app.[tsx|js]` with the following: ```js title="./src/admin/app.js" import favicon from "./extensions/favicon.png"; export default { config: { // replace favicon with a custom icon head: { favicon: favicon, }, }, }; ``` 5. Rebuild, launch and revisit your Strapi app by running `yarn build && yarn develop` in the terminal. :::tip This same process may be used to replace the login logo (i.e. `AuthLogo`) and menu logo (i.e. `MenuLogo`) (see [logos customization documentation](/cms/admin-panel-customization/logos)). ::: :::caution Make sure that the cached favicon is cleared. It can be cached in your web browser and also with your domain management tool like Cloudflare's CDN. ::: # Homepage customization Source: //cms/admin-panel-customization/homepage # Homepage customization :::note The API requires Strapi 5.13+ The `app.widgets.register` API only works with Strapi 5.13 and above. Trying to call the API with older versions of Strapi will crash the admin panel. Plugin developers who want to register widgets should either: - set `^5.13.0` as their `@strapi/strapi` peerDependency in their plugin `package.json`. This peer dependency powers the Marketplace's compatibility check. - or check if the API exists before calling it: ```js if ('widgets' in app) { // proceed with the registration } ``` The peerDependency approach is recommended if the whole purpose of the plugin is to register widgets. The second approach makes more sense if a plugin wants to add a widget but most of its functionality is elsewhere. ::: #### Widget API reference The `app.widgets.register()` method can take either a single widget configuration object or an array of configuration objects. Each widget configuration object can accept the following properties: | Property | Type | Description | Required | |-------------|------------------------|-------------------------------------------------------|----------| | `icon` | `React.ComponentType` | Icon component to display beside the widget title | Yes | | `title` | `MessageDescriptor` | Title for the widget with translation support | Yes | | `component` | `() => Promise :::tip For simplicity, the example below uses data fetching directly inside a useEffect hook. While this works for demonstration purposes, it may not reflect best practices in production. For more robust solutions, consider alternative approaches recommended in the [React documentation](https://react.dev/learn/build-a-react-app-from-scratch#data-fetching). If you're looking to integrate a data fetching library, we recommend using [TanStackQuery](https://tanstack.com/query/v3/). ::: **Data management**: ![Rendering and Data management](/img/assets/homepage-customization/rendering-data-management.png) The green box above represents the area where the user’s React component (from `widget.component` in the [API](#widget-api-reference)) is rendered. You can render whatever you like inside of this box. Everything outside that box is, however, rendered by Strapi. This ensures overall design consistency within the admin panel. The `icon`, `title`, and `link` (optional) properties provided in the API are used to display the widget. #### Widget helper components reference Strapi provides several helper components to maintain a consistent user experience across widgets: | Component | Description | Usage | |------------------|-----------------------------------------------------|--------------------------------------| | `Widget.Loading` | Displays a loading spinner and message | When data is being fetched | | `Widget.Error` | Displays an error state | When an error occurs | | `Widget.NoData` | Displays when no data is available | When the widget has no data to show | | `Widget.NoPermissions` | Displays when user lacks required permissions | When the user cannot access the widget | These components help maintain a consistent look and feel across different widgets. You could render these components without children to get the default wording: ` ))} ); }; ``` The following file defines a custom controller that counts all content-types: ```js title="src/plugins/content-metrics/server/src/controllers/metrics.js" 'use strict'; module.exports = ({ strapi }) => ({ async getContentCounts(ctx) { try { // Get all content types const contentTypes = Object.keys(strapi.contentTypes) .filter(uid => uid.startsWith('api::')) .reduce((acc, uid) => { const contentType = strapi.contentTypes[uid]; acc[contentType.info.displayName || uid] = 0; return acc; }, {}); // Count entities for each content type for (const [name, _] of Object.entries(contentTypes)) { const uid = Object.keys(strapi.contentTypes) .find(key => strapi.contentTypes[key].info.displayName === name || key === name ); if (uid) { // Using the count() method from the Document Service API const count = await strapi.documents(uid).count(); contentTypes[name] = count; } } ctx.body = contentTypes; } catch (err) { ctx.throw(500, err); } } }); ``` The following file ensures that the metrics controller is reachable at a custom `/count` route: ```js title="src/plugins/content-metrics/server/src/routes/index.js" 'content-api': { type: 'content-api', routes: [ { method: 'GET', path: '/count', handler: 'metrics.getContentCounts', config: { policies: [], }, }, ], }, }; ``` ))} ); }; ``` The following file defines a custom controller that counts all content-types: ```js title="src/plugins/content-metrics/server/src/controllers/metrics.js" 'use strict'; module.exports = ({ strapi }) => ({ async getContentCounts(ctx) { try { // Get all content types const contentTypes = Object.keys(strapi.contentTypes) .filter(uid => uid.startsWith('api::')) .reduce((acc, uid) => { const contentType = strapi.contentTypes[uid]; acc[contentType.info.displayName || uid] = 0; return acc; }, {}); // Count entities for each content type using Document Service for (const [name, _] of Object.entries(contentTypes)) { const uid = Object.keys(strapi.contentTypes) .find(key => strapi.contentTypes[key].info.displayName === name || key === name ); if (uid) { // Using the count() method from Document Service instead of strapi.db.query const count = await strapi.documents(uid).count(); contentTypes[name] = count; } } ctx.body = contentTypes; } catch (err) { ctx.throw(500, err); } } }); ``` The following file ensures that the metrics controller is reachable at a custom `/count` route: ```js title="src/plugins/content-metrics/server/src/routes/index.js" 'content-api': { type: 'content-api', routes: [ { method: 'GET', path: '/count', handler: 'metrics.getContentCounts', config: { policies: [], }, }, ], }, }; ``` # Locales & translations Source: //cms/admin-panel-customization/locales-translations # Locales & translations Strapi's [admin panel](/cms/admin-panel-customization) always ships with English translations, but can display additional languages. You can also override any text that appears in the interface. The present page shows how to define your own locales and extend Strapi or plugin translations from the project codebase. ## Defining locales To update the list of available locales in the admin panel, use the `config.locales` array: :::note Notes - The `en` locale cannot be removed from the build as it is both the fallback (i.e. if a translation is not found in a locale, the `en` will be used) and the default locale (i.e. used when a user opens the administration panel for the first time). - The full list of available locales is accessible on A plugin's key/value pairs are declared independently in the plugin's files at `/admin/src/translations/[language-name].json`. These key/value pairs can similarly be extended in the `config.translations` key by prefixing the key with the plugin's name (i.e. `[plugin name].[key]: 'value'`) as in the following example: If more translations files should be added, place them in the `/src/admin/extensions/translations` folder. # Logos Source: //cms/admin-panel-customization/logos # Logos Strapi's [admin panel](/cms/admin-panel-customization) displays its branding on both the login screen and in the main navigation. Replacing these images allows you to match the interface to your identity. The present page shows how to override the two logo files via the admin panel configuration. If you prefer uploading them directly in the UI, see [Customizing the logo](/cms/features/admin-panel#customizing-the-logo). The Strapi admin panel displays a logo in 2 different locations, represented by 2 different keys in the admin panel configuration: | Location in the UI | Configuration key to update | | ---------------------- | --------------------------- | | On the login page | `config.auth.logo` | | In the main navigation | `config.menu.logo` | :::note Logos uploaded via the admin panel supersede any logo set through the configuration files. ::: ### Logos location in the admin panel The logo handled by `config.auth.logo` logo is only shown on the login screen: ![Location of the auth logo](/img/assets/development/config-auth-logo.png) The logo handled by `config.menu.logo` logo is located in the main navigation at the top left corner of the admin panel: ![Location of Menu logo](/img/assets/development/config-menu-logo.png) ### Updating logos To update the logos, put image files in the `/src/admin/extensions` folder, import these files in `src/admin/app` and update the corresponding keys as in the following example: :::note There is no size limit for image files set through the configuration files. ::: # Theme extension Source: //cms/admin-panel-customization/theme-extension # Theme extension Strapi's [admin panel](/cms/admin-panel-customization) can be displayed either in light or dark mode (see [profile setup](/cms/getting-started/setting-up-admin-panel#setting-up-your-administrator-profile)), and both can be extended through custom theme settings. To extend the theme, use either: - the `config.theme.light` key for the Light mode - the `config.theme.dark` key for the Dark mode :::strapi Strapi Design System The default defines various theme-related keys (shadows, colors…) that can be updated through the `config.theme.light` and `config.theme.dark` keys in `./admin/src/app.js`. The is fully customizable and has a dedicated documentation. ::: # Customizing the rich text editor Source: //cms/admin-panel-customization/wysiwyg-editor # Change the default rich text editor Strapi's [admin panel](/cms/admin-panel-customization) comes with a built-in rich text editor. To change the default editor, several options are at your disposal: - You can install a third-party plugin, such as one for CKEditor, by visiting # Strapi Client Source: //cms/api/client # Strapi Client The Strapi Client library simplifies interactions with your Strapi back end, providing a way to fetch, create, update, and delete content. This guide walks you through setting up the Strapi Client, configuring authentication, and using its key features effectively. ## Getting Started :::prerequisites - A Strapi project has been created and is running. If you haven't set one up yet, follow the [Quick Start Guide](/cms/quick-start) to create one. - You know the URL of the Content API of your Strapi instance (e.g., `http://localhost:1337/api`). ::: ### Installation To use the Strapi Client in your project, install it as a dependency using your preferred package manager: ### Basic configuration To start interacting with your Strapi back end, initialize the Strapi Client and set the base API URL: ```js const client = strapi({ baseURL: 'http://localhost:1337/api' }); ``` If you're using the Strapi Client in a browser environment, you can include it using a ` ``` ### Authentication The Strapi Client supports different authentication strategies to access protected resources in your Strapi back end. If your Strapi instance uses API tokens, configure the Strapi Client as follows: ```js const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token-here', }); ``` This allows your requests to include the necessary authentication credentials automatically. ## API Reference The Strapi Client provides the following key properties and methods for interacting with your Strapi back end: | Parameter | Description | | ----------| -------------------------------------------------------------------------------------------- | | `baseURL` | The base API URL of your Strapi back end. | | `fetch()` | A utility method for making generic API requests similar to the native fetch API. | | `collection()` | Manages collection-type resources (e.g., blog posts, products). | | `single()` | Manages single-type resources (e.g., homepage settings, global configurations). | ### General purpose fetch The Strapi Client provides access to the underlying JavaScript `fetch` function to make direct API requests. The request is always relative to the base URL provided during client initialization: ```js const result = await client.fetch('articles', { method: 'GET' }); ``` ### Working with collection types Collection types in Strapi are entities with multiple entries (e.g., a blog with many posts). The Strapi Client provides a `collection()` method to interact with these resources, with the following methods available: | Parameter | Description | | ----------| -------------------------------------------------------------------------------------------- | | `find(queryParams?)` | Fetch multiple documents with optional filtering, sorting, or pagination. | | `findOne(documentID, queryParams?)` | Retrieve a single document by its unique ID. | | `create(data, queryParams?)` | Create a new document in the collection. | | `update(documentID, data, queryParams?)` | Update an existing document. | | `delete(documentID, queryParams?)` | Update an existing document. | **Usage examples:** ```js const articles = client.collection('articles'); // Fetch all english articles sorted by title const allArticles = await articles.find({ locale: 'en', sort: 'title', }); // Fetch a single article const singleArticle = await articles.findOne('article-document-id'); // Create a new article const newArticle = await articles.create({ title: 'New Article', content: '...' }); // Update an existing article const updatedArticle = await articles.update('article-document-id', { title: 'Updated Title' }); // Delete an article await articles.delete('article-id'); ``` ### Working with single types Single types in Strapi represent unique content entries that exist only once (e.g., the homepage settings or site-wide configurations). The Strapi Client provides a `single()` method to interact with these resources, with the following methods available: | Parameter | Description | | ----------| -------------------------------------------------------------------------------------------- | | `find(queryParams?)` | Fetch the document. | | `update(documentID, data, queryParams?)` | Update the document. | | `delete(queryParams?) ` | Remove the document. | **Usage examples:** ```js const homepage = client.single('homepage'); // Fetch the default homepage content const defaultHomepage = await homepage.find(); // Fetch the Spanish version of the homepage const spanishHomepage = await homepage.find({ locale: 'es' }); // Update the homepage draft content const updatedHomepage = await homepage.update( { title: 'Updated Homepage Title' }, { status: 'draft' } ); // Delete the homepage content await homepage.delete(); ``` ### Working with files The Strapi Client provides access to the [Media Library](/cms/features/media-library) via the `files` property. This allows you to retrieve and manage file metadata without directly interacting with the REST API. The following methods are available for working with files: | Method | Description | |--------|-------------| | `find(params?)` | Retrieves a list of file metadata based on optional query parameters | | `findOne(fileId)` | Retrieves the metadata for a single file by its ID | | `update(fileId, fileInfo)` | Updates metadata for an existing file | | `delete(fileId)` | Deletes a file by its ID | **Usage examples:** ```js // Initialize the client const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token', }); // Find all file metadata const allFiles = await client.files.find(); console.log(allFiles); // Find file metadata with filtering and sorting const imageFiles = await client.files.find({ filters: { mime: { $contains: 'image' }, // Only get image files name: { $contains: 'avatar' }, // Only get files with 'avatar' in the name }, sort: ['name:asc'], // Sort by name in ascending order }); // Find file metadata by ID const file = await client.files.findOne(1); console.log(file.name); // The file name console.log(file.url); // The file URL console.log(file.mime); // The file MIME type // Update file metadata const updatedFile = await client.files.update(1, { name: 'New file name', alternativeText: 'Descriptive alt text for accessibility', caption: 'A caption for the file', }); // Delete a file by ID const deletedFile = await client.files.delete(1); console.log('File deleted successfully'); console.log('Deleted file ID:', deletedFile.id); console.log('Deleted file name:', deletedFile.name); ``` :::strapi Additional information More details about the Strapi Strapi Client might be found in the . ::: # Content API Source: //cms/api/content-api # Strapi APIs to access your content Once you've created and configured a Strapi project, created a content structure with the [Content-Type Builder](/cms/features/content-type-builder) and started adding data through the [Content Manager](/cms/features/content-manager), you likely would like to access your content. From a front-end application, your content can be accessed through Strapi's Content API, which is exposed: - by default through the [REST API](/cms/api/rest) - and also through the [GraphQL API](/cms/api/graphql) if you installed the Strapi built-in [GraphQL plugin](/cms/plugins/graphql). You can also use the [Strapi Client](/cms/api/client) library to interact with the REST API. REST and GraphQL APIs represent the top-level layers of the Content API exposed to external applications. Strapi also provides 2 lower-level APIs: - The [Document Service API](/cms/api/document-service) is the recommended API to interact with your application's database within the [backend server](/cms/customization) or through [plugins](/cms/plugins-development/developing-plugins). The Document Service is the layer that handles **documents** # Documents Source: //cms/api/document
A **document** in Strapi 5 is an API-only concept. A document represents all the different variations of content for a given entry of a content-type. A single type contains a unique document, and a collection type can contain several documents. When you use the admin panel, the concept of a document is never mentioned and not necessary for the end user. Users create and edit **entries** in the [Content Manager](/cms/features/content-manager). For instance, as a user, you either list the entries for a given locale, or edit the draft version of a specific entry in a given locale. However, at the API level, the value of the fields of an entry can actually have: - different content for the English and the French locale, - and even different content for the draft and published version in each locale. The bucket that includes the content of all the draft and published versions for all the locales is a document. Manipulating documents with the [Document Service API](/cms/api/document-service) will help you create, retrieve, update, and delete documents or a specific subset of the data they contain. The following diagrams represent all the possible variations of content depending on which features, such as [Internationalization (i18n)](/cms/features/internationalization) and [Draft & Publish](/cms/features/draft-and-publish), are enabled for a content-type: - If the Internationalization (i18n) feature is enabled on the content-type, a document can have multiple **document locales**. - If the Draft & Publish feature is enabled on the content-type, a document can have a **published** and a **draft** version. :::strapi APIs to query documents data To interact with documents or the data they represent: - From the back-end server (for instance, from controllers, services, and the back-end part of plugins), use the [Document Service API](/cms/api/document-service). - From the front-end part of your application, query your data using the [REST API](/cms/api/rest) or the [GraphQL API](/cms/api/graphql). For additional information about the APIs, please refer to the [Content API introduction](/cms/api/content-api). ::: :::info Default version in returned results An important difference between the back-end and front-end APIs is about the default version returned when no parameter is passed: - The Document Service API returns the draft version by default, - while REST and GraphQL APIs return the published version by default. :::
# Document Service API Source: //cms/api/document-service # Document Service API The Document Service API is built on top of the **Query Engine API** and used to perform CRUD ([create](#create), [retrieve](#findone), [update](#update), and [delete](#delete)) operations on **documents** ## `findFirst()` Find the first document matching the parameters. Syntax: `findFirst(parameters: Params) => Document` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | [`locale`](/cms/api/document-service/locale#find-first) | Locale of the documents to find. | Default locale | String or `undefined` | | [`status`](/cms/api/document-service/status#find-first) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: | `'draft'` | `'published'` or `'draft'` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#findfirst) | [Select fields](/cms/api/document-service/fields#findfirst) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Examples
#### Generic example 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): #### Find the first document matching parameters Pass some parameters to `findFirst()` to return the first document matching them. If no `locale` or `status` parameters are passed, results return the draft version for the default locale: ## `findMany()` Find documents matching the parameters. Syntax: `findMany(parameters: Params) => Document[]` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | [`locale`](/cms/api/document-service/locale#find-many) | Locale of the documents to find. | Default locale | String or `undefined` | | [`status`](/cms/api/document-service/status#find-many) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: | `'draft'` | `'published'` or `'draft'` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#findmany) | [Select fields](/cms/api/document-service/fields#findmany) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | | [`pagination`](/cms/api/document-service/sort-pagination#pagination) | [Paginate](/cms/api/document-service/sort-pagination#pagination) results | | [`sort`](/cms/api/document-service/sort-pagination#sort) | [Sort](/cms/api/document-service/sort-pagination#sort) results | | | ### Examples
#### Generic example When no parameter is passed, `findMany()` returns the draft version in the default locale for each document: #### Find documents matching parameters 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()` Creates a drafted document and returns it. Pass fields for the content to create in a `data` object. Syntax: `create(parameters: Params) => Document` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | [`locale`](/cms/api/document-service/locale#create) | Locale of the documents to create. | Default locale | String or `undefined` | | [`fields`](/cms/api/document-service/fields#create) | [Select fields](/cms/api/document-service/fields#create) to return | All fields
(except those not populated by default) | Object | | [`status`](/cms/api/document-service/status#create) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Can be set to `'published'` to automatically publish the draft version of a document while creating it | -| `'published'` | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Example If no `locale` parameter is passed, `create()` creates the draft version of the document for the default locale: :::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()` Updates document versions and returns them. Syntax: `update(parameters: Params) => Promise ## `delete()` Deletes one document, or a specific locale of it. Syntax: `delete(parameters: Params): Promise<{ documentId: ID, entries: Number }>` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | `documentId`| Document id | | `ID`| | [`locale`](/cms/api/document-service/locale#delete) | Locale version of the document to delete. | `null`
(deletes only the default locale) | String, `'*'`, or `null` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#delete) | [Select fields](/cms/api/document-service/fields#delete) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Example If no `locale` parameter is passed, `delete()` only deletes the default locale version of a document. This deletes both the draft and published versions: ## `publish()` Publishes one or multiple locales of a document. This method is only available if [Draft & Publish](/cms/features/draft-and-publish) is enabled on the content-type. Syntax: `publish(parameters: Params): Promise<{ documentId: ID, entries: Number }>` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | `documentId`| Document id | | `ID`| | [`locale`](/cms/api/document-service/locale#publish) | Locale of the documents to publish. | Only the default locale | String, `'*'`, or `null` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#publish) | [Select fields](/cms/api/document-service/fields#publish) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Example If no `locale` parameter is passed, `publish()` only publishes the default locale version of the document: ## `unpublish()` Unpublishes one or all locale versions of a document, and returns how many locale versions were unpublished. This method is only available if [Draft & Publish](/cms/features/draft-and-publish) is enabled on the content-type. Syntax: `unpublish(parameters: Params): Promise<{ documentId: ID, entries: Number }>` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | `documentId`| Document id | | `ID`| | [`locale`](/cms/api/document-service/locale#unpublish) | Locale of the documents to unpublish. | Only the default locale | String, `'*'`, or `null` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#unpublish) | [Select fields](/cms/api/document-service/fields#unpublish) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Example If no `locale` parameter is passed, `unpublish()` only unpublishes the default locale version of the document: ## `discardDraft()` Discards draft data and overrides it with the published version. This method is only available if [Draft & Publish](/cms/features/draft-and-publish) is enabled on the content-type. Syntax: `discardDraft(parameters: Params): Promise<{ documentId: ID, entries: Number }>` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | `documentId`| Document id | | `ID`| | [`locale`](/cms/api/document-service/locale#discard-draft) | Locale of the documents to discard. | Only the default locale. | String, `'*'`, or `null` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | | [`fields`](/cms/api/document-service/fields#discarddraft) | [Select fields](/cms/api/document-service/fields#discarddraft) to return | All fields
(except those not populate by default) | Object | | [`populate`](/cms/api/document-service/populate) | [Populate](/cms/api/document-service/populate) results with additional fields. | `null` | Object | ### Example If no `locale` parameter is passed, `discardDraft()` discards draft data and overrides it with the published version only for the default locale: ## `count()` Count the number of documents that match the provided parameters. Syntax: `count(parameters: Params) => number` ### Parameters | Parameter | Description | Default | Type | |-----------|-------------|---------|------| | [`locale`](/cms/api/document-service/locale#count) | Locale of the documents to count | Default locale | String or `null` | | [`status`](/cms/api/document-service/status#count) | _If [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type_:
Publication status, can be: | `'draft'` | `'published'` or `'draft'` | | [`filters`](/cms/api/document-service/filters) | [Filters](/cms/api/document-service/filters) to use | `null` | Object | :::note Since published documents necessarily also have a draft counterpart, a published document is still counted as having a draft version. This means that counting with the `status: 'draft'` parameter still returns the total number of documents matching other parameters, even if some documents have already been published and are not displayed as "draft" or "modified" in the Content Manager anymore. There currently is no way to prevent already published documents from being counted. ::: ### Examples
#### Generic example If no parameter is passed, the `count()` method the total number of documents for the default locale: #### Count published documents To count only published documents, pass `status: 'published'` along with other parameters to the `count()` method. If no `locale` parameter is passed, documents are counted for the default locale. #### Count documents with filters Any [filters](/cms/api/document-service/filters) can be passed to the `count()` method. If no `locale` and no `status` parameter is passed, draft documents (which is the total of available documents for the locale since even published documents are counted as having a draft version) are counted only for the default locale: ```js /** * 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" }}})` ``` # Using fields with the Document Service API Source: //cms/api/document-service/fields # Document Service API: Selecting fields By default the [Document Service API](/cms/api/document-service) returns all the fields of a document but does not populate any fields. This page describes how to use the `fields` parameter to return only specific fields with the query results. :::tip You can also use the `populate` parameter to populate relations, media fields, components, or dynamic zones (see the [`populate` parameter](/cms/api/document-service/populate) documentation). ::: ## Select fields with `findFirst()` queries {#findfirst} To select fields to return while [finding the first document](/cms/api/document-service#findfirst) matching the parameters with the Document Service API: ## Select fields with `findMany()` queries {#findmany} To select fields to return while [finding documents](/cms/api/document-service#findmany) with the Document Service API: ## Select fields with `create()` queries {#create} To select fields to return while [creating documents](/cms/api/document-service#create) with the Document Service API: ## Select fields with `update()` queries {#update} To select fields to return while [updating documents](/cms/api/document-service#update) with the Document Service API: ## Select fields with `delete()` queries {#delete} To select fields to return while [deleting documents](/cms/api/document-service#delete) with the Document Service API: ## Select fields with `publish()` queries {#publish} To select fields to return while [publishing documents](/cms/api/document-service#publish) with the Document Service API: ## Select fields with `unpublish()` queries {#unpublish} To select fields to return while [unpublishing documents](/cms/api/document-service#unpublish) with the Document Service API: ## Select fields with `discardDraft()` queries {#discarddraft} To select fields to return while [discarding draft versions of documents](/cms/api/document-service#discarddraft) with the Document Service API: # Using filters with the Document Service API Source: //cms/api/document-service/filters # Document Service API: Filters The [Document Service API](/cms/api/document-service) offers the ability to filter results. The following operators are available: | Operator | Description | | -------------------------------- | ---------------------------------------- | | [`$eq`](#eq) | Equal | | [`$eqi`](#eqi) | Equal (case-insensitive) | | [`$ne`](#ne) | Not equal | | [`$nei`](#nei) | Not equal (case-insensitive) | | [`$lt`](#lt) | Less than | | [`$lte`](#lte) | Less than or equal to | | [`$gt`](#gt) | Greater than | | [`$gte`](#gte) | Greater than or equal to | | [`$in`](#in) | Included in an array | | [`$notIn`](#notin) | Not included in an array | | [`$contains`](#contains) | Contains | | [`$notContains`](#notcontains) | Does not contain | | [`$containsi`](#containsi) | Contains (case-insensitive) | | [`$notContainsi`](#notcontainsi) | Does not contain (case-insensitive) | | [`$null`](#null) | Is null | | [`$notNull`](#notnull) | Is not null | | [`$between`](#between) | Is between | | [`$startsWith`](#startswith) | Starts with | | [`$startsWithi`](#startswithi) | Starts with (case-insensitive) | | [`$endsWith`](#endswith) | Ends with | | [`$endsWithi`](#endswithi) | Ends with (case-insensitive) | | [`$or`](#or) | Joins the filters in an "or" expression | | [`$and`](#and) | Joins the filters in an "and" expression | | [`$not`](#not) | Joins the filters in an "not" expression | ## Attribute operators
### `$not` Negates the nested condition(s). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $not: { $contains: 'Hello World', }, }, }, }); ``` ### `$eq` Attribute equals input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $eq: 'Hello World', }, }, }); ``` `$eq` can be omitted: ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: 'Hello World', }, }); ``` ### `$eqi` Attribute equals input value (case-insensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $eqi: 'HELLO World', }, }, }); ``` ### `$ne` Attribute does not equal input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $ne: 'ABCD', }, }, }); ``` ### `$nei` Attribute does not equal input value (case-insensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $nei: 'abcd', }, }, }); ``` ### `$in` Attribute is contained in the input list. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $in: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` `$in` can be omitted when passing an array of values: ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: ['Hello', 'Hola', 'Bonjour'], }, }); ``` ### `$notIn` Attribute is not contained in the input list. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notIn: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` ### `$lt` Attribute is less than the input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $lt: 10, }, }, }); ``` ### `$lte` Attribute is less than or equal to the input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $lte: 10, }, }, }); ``` ### `$gt` Attribute is greater than the input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $gt: 5, }, }, }); ``` ### `$gte` Attribute is greater than or equal to the input value. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $gte: 5, }, }, }); ``` ### `$between` Attribute is between the 2 input values, boundaries included (e.g., `$between[1, 3]` will also return `1` and `3`). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $between: [1, 20], }, }, }); ``` ### `$contains` Attribute contains the input value (case-sensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $contains: 'Hello', }, }, }); ``` ### `$notContains` Attribute does not contain the input value (case-sensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notContains: 'Hello', }, }, }); ``` ### `$containsi` Attribute contains the input value. `$containsi` is not case-sensitive, while [$contains](#contains) is. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $containsi: 'hello', }, }, }); ``` ### `$notContainsi` Attribute does not contain the input value. `$notContainsi` is not case-sensitive, while [$notContains](#notcontains) is. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notContainsi: 'hello', }, }, }); ``` ### `$startsWith` Attribute starts with input value (case-sensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $startsWith: 'ABCD', }, }, }); ``` ### `$startsWithi` Attribute starts with input value (case-insensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $startsWithi: 'ABCD', // will return the same as filtering with 'abcd' }, }, }); ``` ### `$endsWith` Attribute ends with input value (case-sensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $endsWith: 'ABCD', }, }, }); ``` ### `$endsWithi` Attribute ends with input value (case-insensitive). **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $endsWith: 'ABCD', // will return the same as filtering with 'abcd' }, }, }, }); ``` ### `$null` Attribute is `null`. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $null: true, }, }, }); ``` ### `$notNull` Attribute is not `null`. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notNull: true, }, }, }); ``` ## Logical operators ### `$and` All nested conditions must be `true`. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { $and: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` `$and` will be used implicitly when passing an object with nested conditions: ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { title: 'Hello World', createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, }); ``` ### `$or` One or many nested conditions must be `true`. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { $or: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` ### `$not` Negates the nested conditions. **Example** ```js const entries = await strapi.documents('api::article.article').findMany({ filters: { $not: { title: 'Hello World', }, }, }); ``` :::note `$not` can be used as: - a logical operator (e.g. in `filters: { $not: { // conditions… }}`) - [an attribute operator](#not) (e.g. in `filters: { attribute-name: $not: { … } }`). ::: :::tip `$and`, `$or` and `$not` operators are nestable inside of another `$and`, `$or` or `$not` operator. ::: # Using the locale parameter with the Document Service API Source: //cms/api/document-service/locale # Document Service API: Using the `locale` parameter By default the [Document Service API](/cms/api/document-service) returns the default locale version of documents (which is 'en', i.e. the English version, unless another default locale has been set for the application, see [Internationalization (i18n) feature](/cms/features/internationalization)). This page describes how to use the `locale` parameter to get or manipulate data only for specific locales. ## Get a locale version with `findOne()` {#find-one} If a `locale` is passed, the [`findOne()` method](/cms/api/document-service#findone) of the Document Service API returns the version of the document for this locale: If no `status` parameter is passed, the `draft` version is returned by default. ## Get a locale version with `findFirst()` {#find-first} To return a specific locale while [finding the first document](/cms/api/document-service#findfirst) matching the parameters with the Document Service API: If no `status` parameter is passed, the `draft` version is returned by default. ## Get locale versions with `findMany()` {#find-many} When a `locale` is passed to the [`findMany()` method](/cms/api/document-service#findmany) of the Document Service API, the response will return all documents that have this locale available. If no `status` parameter is passed, the `draft` versions are returned by default.
Explanation: Given the following 4 documents that have various locales: - Document A: - en - `fr` - it - Document B: - en - it - Document C: - `fr` - Document D: - `fr` - it `findMany({ locale: 'fr' })` would only return the draft version of the documents that have a `‘fr’` locale version, that is documents A, C, and D.
## `create()` a document for a locale {#create} To create a document for specific locale, pass the `locale` as a parameter to the [`create` method](/cms/api/document-service#create) of the Document Service API: ## `update()` a locale version {#update} To update only a specific locale version of a document, pass the `locale` parameter to the [`update()` method](/cms/api/document-service#update) of the Document Service API: ## `delete()` locale versions {#delete} Use the `locale` parameter with the [`delete()` method](/cms/api/document-service#delete) of the Document Service API to delete only some locales. Unless a specific `status` parameter is passed, this deletes both the draft and published versions. ### Delete a locale version To delete a specific locale version of a document: ### Delete all locale versions The `*` wildcard is supported by the `locale` parameter and can be used to delete all locale versions of a document: ## `publish()` locale versions {#publish} To publish only specific locale versions of a document with the [`publish()` method](/cms/api/document-service#publish) of the Document Service API, pass `locale` as a parameter: ### Publish a locale version To publish a specific locale version of a document: ### Publish all locale versions The `*` wildcard is supported by the `locale` parameter to publish all locale versions of a document: ## `unpublish()` locale versions {#unpublish} To publish only specific locale versions of a document with the [`unpublish()` method](/cms/api/document-service#unpublish) of the Document Service API, pass `locale` as a parameter: ### Unpublish a locale version To unpublish a specific locale version of a document, pass the `locale` as a parameter to `unpublish()`: ### Unpublish all locale versions The `*` wildcard is supported by the `locale` parameter, to unpublish all locale versions of a document: ## `discardDraft()` for locale versions {#discard-draft} To discard draft data only for some locales versions of a document with the [`discardDraft()` method](/cms/api/document-service#discarddraft) of the Document Service API, pass `locale` as a parameter: ### Discard draft for a locale version To discard draft data for a specific locale version of a document and override it with data from the published version for this locale, pass the `locale` as a parameter to `discardDraft()`: ### Discard drafts for all locale versions The `*` wildcard is supported by the `locale` parameter, to discard draft data for all locale versions of a document and replace them with the data from the published versions: ## `count()` documents for a locale {#count} To count documents for a specific locale, pass the `locale` along with other parameters to the [`count()` method](/cms/api/document-service#count) of the Document Service API. If no `status` parameter is passed, draft documents are counted (which is the total of available documents for the locale since even published documents are counted as having a draft version): ```js // Count number of published documents in French strapi.documents('api::restaurant.restaurant').count({ locale: 'fr' }); ``` # Extending the Document Service behavior Source: //cms/api/document-service/middlewares # Document Service API: Middlewares The [Document Service API](/cms/api/document-service) offers the ability to extend its behavior thanks to middlewares. Document Service middlewares allow you to perform actions before and/or after a method runs.
Simplified Strapi backend diagram with controllers highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with the Document Service highlighted. The backend customization introduction page includes a complete, interactive diagram.
## Registering a middleware Syntax: `strapi.documents.use(middleware)` ### Parameters A middleware is a function that receives a context and a next function. Syntax: `(context, next) => ReturnType` | Parameter | Description | Type | |-----------|---------------------------------------|------------| | `context` | Middleware context | `Context` | | `next` | Call the next middleware in the stack | `function` | #### `context` | Parameter | Description | Type | |---------------|--------------------------------------------------------------------------------------|---------------| | `action` | The method that is running ([see available methods](/cms/api/document-service)) | `string` | | `params` | The method params ([see available methods](/cms/api/document-service)) | `Object` | | `uid` | Content type unique identifier | `string` | | `contentType` | Content type | `ContentType` |
Examples: The following examples show what `context` might include depending on the method called:
#### `next` `next` is a function without parameters that calls the next middleware in the stack and return its response. **Example** ```js strapi.documents.use((context, next) => { return next(); }); ``` ### Where to register Generaly speaking you should register your middlewares during the Strapi registration phase. #### Users The middleware must be registered in the general `register()` lifecycle method: ```js title="/src/index.js|ts" module.exports = { register({ strapi }) { strapi.documents.use((context, next) => { // your logic return next(); }); }, // bootstrap({ strapi }) {}, // destroy({ strapi }) {}, }; ``` #### Plugin developers The middleware must be registered in the plugin's `register()` lifecycle method: ```js title="/(plugin-root-folder)/strapi-server.js|ts" module.exports = { register({ strapi }) { strapi.documents.use((context, next) => { // your logic return next(); }); }, // bootstrap({ strapi }) {}, // destroy({ strapi }) {}, }; ``` ## Implementing a middleware When implementing a middleware, always return the response from `next()`. Failing to do this will break the Strapi application. ### Examples ```js const applyTo = ['api::article.article']; strapi.documents.use((context, next) => { // Only run for certain content types if (!applyTo.includes(context.uid)) { return next(); } // Only run for certain actions if (['create', 'update'].includes(context.action)) { context.params.data.fullName = `${context.params.data.firstName} ${context.params.data.lastName}`; } const result = await next(); // do something with the result before returning it return result }); ```
:::strapi Lifecycle hooks The Document Service API triggers various database lifecycle hooks based on which method is called. For a complete reference, see [Document Service API: Lifecycle hooks](/cms/migration/v4-to-v5/breaking-changes/lifecycle-hooks-document-service#table). ::: # Using Populate with the Document Service API Source: //cms/api/document-service/populate # Document Service API: Populating fields By default the [Document Service API](/cms/api/document-service) does not populate any relations, media fields, components, or dynamic zones. This page describes how to use the `populate` parameter to populate specific fields. :::tip You can also use the `select` parameter to return only specific fields with the query results (see the [`select` parameter](/cms/api/document-service/fields) documentation). ::: :::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. ::: ## Relations and media fields Queries can accept a `populate` parameter to explicitly define which fields to populate, with the following syntax option examples. ### Populate 1 level for all relations To populate one-level deep for all relations, use the `*` wildcard in combination with the `populate` parameter: ### Populate 1 level for specific relations To populate specific relations one-level deep, pass the relation names in a `populate` array: ### Populate several levels deep for specific relations To populate specific relations several levels deep, use the object format with `populate`: ## Components & Dynamic Zones Components are populated the same way as relations: Dynamic zones are highly dynamic content structures by essence. To populate a dynamic zone, you must define per-component populate queries using the `on` property. ## Populating with `create()` To populate while creating documents: ## Populating with `update()` To populate while updating documents: ## Populating with `publish()` To populate while publishing documents (same behavior with `unpublish()` and `discardDraft()`): # Using Sort & Pagination with the Document Service API Source: //cms/api/document-service/sort-pagination # Document Service API: Sorting and paginating results The [Document Service API](/cms/api/document-service) offers the ability to sort and paginate query results. ## Sort To sort results returned by the Document Service API, include the `sort` parameter with queries. ### Sort on a single field To sort results based on a single field: ### Sort on multiple fields To sort on multiple fields, pass them all in an array: ## Pagination To paginate results, pass the `limit` and `start` parameters: # Using Draft & Publish with the Document Service API Source: //cms/api/document-service/status # Document Service API: Usage with Draft & Publish By default the [Document Service API](/cms/api/document-service) returns the draft version of a document when the [Draft & Publish](/cms/features/draft-and-publish) feature is enabled. This page describes how to use the `status` parameter to: - return the published version of a document, - count documents depending on their status, - and directly publish a document while creating it or updating it. :::note Passing `{ status: 'draft' }` to a Document Service API query returns the same results as not passing any `status` parameter. ::: ## Get the published version with `findOne()` {#find-one} `findOne()` queries return the draft version of a document by default. To return the published version while [finding a specific document](/cms/api/document-service#findone) with the Document Service API, pass `status: 'published'`: ## Get the published version with `findFirst()` {#find-first} `findFirst()` queries return the draft version of a document by default. To return the published version while [finding the first document](/cms/api/document-service#findfirst) with the Document Service API, pass `status: 'published'`: ## Get the published version with `findMany()` {#find-many} `findMany()` queries return the draft version of documents by default. To return the published version while [finding documents](/cms/api/document-service#findmany) with the Document Service API, pass `status: 'published'`: ## `count()` only draft or published versions {#count} To take into account only draft or published versions of documents while [counting documents](/cms/api/document-service#count) with the Document Service API, pass the corresponding `status` parameter: ```js // Count draft documents (also actually includes published documents) const draftsCount = await strapi.documents("api::restaurant.restaurant").count({ status: 'draft' }); ``` ```js // Count only published documents const publishedCount = await strapi.documents("api::restaurant.restaurant").count({ status: 'published' }); ``` :::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. ::: ## Create a draft and publish it {#create} To automatically publish a document while creating it, add `status: 'published'` to parameters passed to `create()`: ## Update a draft and publish it {#update} To automatically publish a document while updating it, add `status: 'published'` to parameters passed to `update()`: # GraphQL API Source: //cms/api/graphql # GraphQL API The GraphQL API allows performing queries and mutations to interact with the [content-types](/cms/backend-customization/models#content-types) through Strapi's [GraphQL plugin](/cms/plugins/graphql). Results can be [filtered](#filters), [sorted](#sorting) and [paginated](#pagination). :::prerequisites To use the GraphQL API, install the [GraphQL](/cms/plugins/graphql) plugin: ::: 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: #### Fetch relations You can ask to include relation data in your flat queries or in your ::: ### 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: ```graphql { restaurants { images { documentId url } } } ``` For multiple media fields, you can use flat queries or ### 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: ```graphql { restaurants { closingPeriod { label start_date end_date } } } ``` ### Fetch dynamic zone data Dynamic zones are union types in GraphQL so you need to use ### Fetch a document in a specific locale {#locale-fetch} To fetch a documents ### Create a new localized document {#locale-create} The `locale` field can be passed to create a localized document for a specific locale (for more information about mutations with GraphQL, see [the GraphQL API documentation](/cms/api/graphql#create-a-new-document)). ```graphql title="Example: Create a new restaurant for the French locale" mutation CreateRestaurant($data: RestaurantInput!, $locale: I18NLocaleCode) { createRestaurant( data: { name: "Brasserie Bonjour", description: "Description in French goes here" }, locale: "fr" ) { documentId name description locale } ``` ### Update a document for a specific locale {#locale-update} A `locale` argument can be passed in the mutation to update a document for a given locale (for more information about mutations with GraphQL, see [the GraphQL API documentation](/cms/api/graphql#update-an-existing-document)). ```graphql title="Example: Update the description field of restaurant for the French locale" mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!, $locale: I18NLocaleCode) { updateRestaurant( documentId: "a1b2c3d4e5d6f7g8h9i0jkl" data: { description: "New description in French" }, locale: "fr" ) { documentId name description locale } ``` ### Delete a locale for a document {#locale-delete} Pass the `locale` argument in the mutation to delete a specific localization for a document : ```graphql mutation DeleteRestaurant($documentId: ID!, $locale: I18NLocaleCode) { deleteRestaurant(documentId: "xzmzdo4k0z73t9i68a7yx2kk", locale: "fr") { documentId } } ``` # REST API reference Source: //cms/api/rest # REST API reference The REST API allows accessing the [content-types](/cms/backend-customization/models) through API endpoints. Strapi automatically creates [API endpoints](#endpoints) when a content-type is created. [API parameters](/cms/api/rest/parameters) can be used when querying API endpoints to refine the results. This section of the documentation is for the REST API reference. We also have [guides](/cms/api/rest/guides/intro) available for specific use cases. :::prerequisites All content types are private by default and need to be either made public or queries need to be authenticated with the proper permissions. See the [Quick Start Guide](/cms/quick-start#step-4-set-roles--permissions), the user guide for the [Users & Permissions feature](/cms/features/users-permissions#roles), and [API tokens configuration documentation](/cms/features/api-tokens) for more details. ::: :::note By default, the REST API responses only include top-level fields and does not populate any relations, media fields, components, or dynamic zones. Use the [`populate` parameter](/cms/api/rest/populate-select) to populate specific fields. Ensure that the find permission is given to the field(s) for the relation(s) you populate. ::: :::strapi Strapi Client The [Strapi Client](/cms/api/client) library simplifies interactions with your Strapi back end, providing a way to fetch, create, update, and delete content. ::: ## Endpoints For each Content-Type, the following endpoints are automatically generated:
Plural API ID vs. Singular API ID: In the following tables: - `:singularApiId` refers to the value of the "API ID (Singular)" field of the content-type, - and `:pluralApiId` refers to the value of the "API ID (Plural)" field of the content-type. These 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](/cms/features/content-type-builder#creating-content-types)). For instance, by default, for an "Article" content-type: - `:singularApiId` will be `article` - `:pluralApiId` will be `articles`
Real-world examples of endpoints: The following endpoint examples are taken from the
:::strapi Upload API The Upload package (which powers the [Media Library feature](/cms/features/media-library)) has a specific API accessible through its [`/api/upload` endpoints](/cms/api/rest/upload). ::: :::note [Components](/cms/backend-customization/models#components-json) don't have API endpoints. ::: ## Requests :::strapi Strapi 5 vs. Strapi v4 Strapi 5's Content API includes 2 major differences with Strapi v4: - The response format has been flattened, which means attributes are no longer nested in a `data.attributes` object and are directly accessible at the first level of the `data` object (e.g., a content-type's "title" attribute is accessed with `data.title`). - Strapi 5 now uses **documents** ### Get a document {#get} Returns a document by `documentId`. :::strapi Strapi 5 vs. Strapi v4 In Strapi 5, a specific document is reached by its `documentId`. ::: ### Create a document {#create} Creates a document and returns its value. If the [Internationalization (i18n) plugin](/cms/features/internationalization) is installed, it's possible to use POST requests to the REST API to [create localized documents](/cms/api/rest/locale#rest-delete). :::note While creating a document, you can define its relations and their order (see [Managing relations through the REST API](/cms/api/rest/relations.md) for more details). ::: ### Update a document {#update} Partially updates a document by `id` and returns its value. Send a `null` value to clear fields. :::note NOTES * Even with the [Internationalization (i18n) plugin](/cms/features/internationalization) installed, it's currently not possible to [update the locale of a document](/cms/api/rest/locale#rest-update). * While updating a document, you can define its relations and their order (see [Managing relations through the REST API](/cms/api/rest/relations) for more details). ::: ### Delete a document {#delete} Deletes a document. `DELETE` requests only send a 204 HTTP status code on success and do not return any data in the response body. # Filters Source: //cms/api/rest/filters # REST API: Filters The [REST API](/cms/api/rest) offers the ability to filter results found with its ["Get entries"](/cms/api/rest#get-all) method.
Using optional Strapi features can provide some more filters: - If the [Internationalization (i18n) plugin](/cms/features/internationalization) is enabled on a content-type, it's possible to filter by locale. - If the [Draft & Publish](/cms/features/draft-and-publish) is enabled, it's possible to filter based on a `published` (default) or `draft` status. :::tip
JavaScript query (built with the qs library): ## Example: Find multiple restaurants with ids 3, 6,8 You can use the `$in` filter operator with an array of values to find multiple exact values.
JavaScript query (built with the qs library): ## Complex filtering Complex filtering is combining multiple filters using advanced methods such as combining `$and` & `$or`. This allows for more flexibility to request exactly the data needed.
JavaScript query (built with the qs library): ## Deep filtering Deep filtering is filtering on a relation's fields. :::note - Relations, media fields, components, and dynamic zones are not populated by default. Use the `populate` parameter to populate these content structures (see [`populate` documentation](/cms/api/rest/populate-select#population)) - You can filter what you populate, you can also filter nested relations, but you can't use filters for polymorphic content structures (such as media fields and dynamic zones). ::: :::caution Querying your API with deep filters may cause performance issues. If one of your deep filtering queries is too slow, we recommend building a custom route with an optimized version of the query. :::
JavaScript query (built with the qs library): # REST API Guides Source: //cms/api/rest/guides/intro # REST API Guides The [REST API reference](/cms/api/rest) documentation is meant to provide a quick reference for all the endpoints and parameters available. ## Guides The following guides, officially maintained by the Strapi Documentation team, cover dedicated topics and provide detailed explanations (guides indicated with 🧠) or step-by-step instructions (guides indicated with 🛠️) for some use cases: ## Additional resources :::strapi Want to help other users? Some of the additional resources listed in this section have been created for Strapi v4 and might not fully work with Strapi 5. If you want to update one of the following articles for Strapi 5, feel free to for the Write for the Community program. ::: Additional tutorials and guides can be found in the following blog posts: # Interactive Query Builder Source: //cms/api/rest/interactive-query-builder # Build your query URL with Strapi's interactive tool A wide range of parameters can be used and combined to query your content with the [REST API](/cms/api/rest), which can result in long and complex query URLs. Strapi's codebase uses to parse and stringify nested JavaScript objects. It's recommended to use `qs` directly to generate complex query URLs instead of creating them manually. You can use the following interactive query builder tool to generate query URLs automatically: 1. Replace the values in the _Endpoint_ and _Endpoint Query Parameters_ fields with content that fits your needs. 2. Click the **Copy to clipboard** button to copy the automatically generated _Query String URL_ which is updated as you type. :::info Parameters usage Please refer to the [REST API parameters table](/cms/api/rest/parameters) and read the corresponding parameters documentation pages to better understand parameters usage. :::


:::note The default endpoint path is prefixed with `/api/` and should be kept as-is unless you configured a different API prefix using [the `rest.prefix` API configuration option](/cms/configurations/api).
For instance, to query the `books` collection type using the default API prefix, type `/api/books` in the _Endpoint_ field. ::: :::caution Disclaimer The `qs` library and the interactive query builder provided on this page: - might not detect all syntax errors, - are not aware of the parameters and values available in a Strapi project, - and do not provide autocomplete features. Currently, these tools are only provided to transform the JavaScript object in an inline query string URL. Using the generated query URL does not guarantee that proper results will get returned with your API. ::: # Locale Source: //cms/api/rest/locale # REST API: `locale` The [Internationalization (i18n) feature](/cms/features/internationalization) adds new abilities to the [REST API](/cms/api/rest). :::prerequisites To work with API content for a locale, please ensure the locale has been already [added to Strapi in the admin panel](/cms/features/internationalization#settings). ::: The `locale` [API parameter](/cms/api/rest/parameters) can be used to work with documents only for a specified locale. `locale` takes a locale code as a value (see ### `GET` Get all documents in a specific locale {#rest-get-all} ### `GET` Get a document in a specific locale {#rest-get} To get a specific document in a given locale, add the `locale` parameter to the query: | Use case | Syntax format and link for more information | | -------------------- | ---------------------------------------------------------------------------------------------- | | In a collection type | [`GET /api/content-type-plural-name/document-id?locale=locale-code`](#get-one-collection-type) | | In a single type | [`GET /api/content-type-singular-name?locale=locale-code`](#get-one-single-type) | #### Collection types {#get-one-collection-type} To get a specific document in a collection type in a given locale, add the `locale` parameter to the query, after the `documentId`: #### Single types {#get-one-single-type} To get a specific single type document in a given locale, add the `locale` parameter to the query, after the single type name: ### `POST` Create a new localized document for a collection type {#rest-create} To create a localized document from scratch, send a POST request to the Content API. Depending on whether you want to create it for the default locale or for another locale, you might need to pass the `locale` parameter in the request's body | Use case | Syntax format and link for more information | | ----------------------------- | --------------------------------------------------------------------------------------- | | Create for the default locale | [`POST /api/content-type-plural-name`](#rest-create-default-locale) | | Create for a specific locale | [`POST /api/content-type-plural-name`](#rest-create-specific-locale)
+ pass locale in request body | #### For the default locale {#rest-create-default-locale} If no locale has been passed in the request body, the document is created using the default locale for the application: #### For a specific locale {#rest-create-specific-locale} To create a localized entry for a locale different from the default one, add the `locale` attribute to the body of the POST request: ### `PUT` Create a new, or update an existing, locale version for an existing document {#rest-update} With `PUT` requests sent to an existing document, you can: - create another locale version of the document, - or update an existing locale version of the document. Send the `PUT` request to the appropriate URL, adding the `locale=your-locale-code` parameter to the query URL and passing attributes in a `data` object in the request's body: | Use case | Syntax format and link for more information | | -------------------- | --------------------------------------------------------------------------------------- | | In a collection type | [`PUT /api/content-type-plural-name/document-id?locale=locale-code`](#rest-put-collection-type) | | In a single type | [`PUT /api/content-type-singular-name?locale=locale-code`](#rest-put-single-type) | :::caution When creating a localization for existing localized entries, the body of the request can only accept localized fields. ::: :::tip The Content-Type should have the [`createLocalization` permission](/cms/features/rbac#collection-and-single-types) enabled, otherwise the request will return a `403: Forbidden` status. ::: :::note It is not possible to change the locale of an existing localized entry. When updating a localized entry, if you set a `locale` attribute in the request body it will be ignored. ::: #### In a collection type {#rest-put-collection-type} To create a new locale for an existing document in a collection type, add the `locale` parameter to the query, after the `documentId`, and pass data to the request's body: #### In a single type {#rest-put-single-type} To create a new locale for an existing single type document, add the `locale` parameter to the query, after the single type name, and pass data to the request's body:
### `DELETE` Delete a locale version of a document {#rest-delete} To delete a locale version of a document, send a `DELETE` request with the appropriate `locale` parameter. `DELETE` requests only send a 204 HTTP status code on success and do not return any data in the response body. #### In a collection type {#rest-delete-collection-type} To delete only a specific locale version of a document in a collection type, add the `locale` parameter to the query after the `documentId`: #### In a single type {#rest-delete-single-type} To delete only a specific locale version of a single type document, add the `locale` parameter to the query after the single type name: # Parameters Source: //cms/api/rest/parameters # REST API parameters API parameters can be used with the [REST API](/cms/api/rest) to filter, sort, and paginate results and to select fields and relations to populate. Additionally, specific parameters related to optional Strapi features can be used, like the publication state and locale of a content-type. The following API parameters are available: | Operator | Type | Description | | ------------------ | ------------- | ----------------------------------------------------- | | `filters` | Object | [Filter the response](/cms/api/rest/filters) | | `locale` | String | [Select a locale](/cms/api/rest/locale) | | `status` | String | [Select the Draft & Publish status](/cms/api/rest/status) | | `populate` | String or Object | [Populate relations, components, or dynamic zones](/cms/api/rest/populate-select#population) | | `fields` | Array | [Select only specific fields to display](/cms/api/rest/populate-select#field-selection) | | `sort` | String or Array | [Sort the response](/cms/api/rest/sort-pagination.md#sorting) | | `pagination` | Object | [Page through entries](/cms/api/rest/sort-pagination.md#pagination) | Query parameters use the (i.e. they are encoded using square brackets `[]`). :::tip A wide range of REST API parameters can be used and combined to query your content, which can result in long and complex query URLs.
👉 You can use Strapi's [interactive query builder](/cms/api/rest/interactive-query-builder) tool to build query URLs more conveniently. 🤗 ::: # Populate and Select Source: //cms/api/rest/populate-select # REST API: Population & Field Selection The [REST API](/cms/api/rest) by default does not populate any relations, media fields, components, or dynamic zones. Use the [`populate` parameter](#population) to populate specific fields and the [`select` parameter](#field-selection) to return only specific fields with the query results. :::tip ## Population The REST API by default does not populate any type of fields, so it will not populate relations, media fields, components, or dynamic zones unless you pass a `populate` parameter to populate various field types. 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 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](/cms/features/users-permissions#editing-a-role) for additional information on how to enable `find` permissions for content-types). ::: :::note It's currently not possible to return just an array of ids with a request. ::: :::strapi Populating guides The [REST API guides](/cms/api/rest/guides/intro) section includes more detailed information about various possible use cases for the populate parameter: - The [Understanding populate](/cms/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](/cms/api/rest/guides/populate-creator-fields) guide provides step-by-step instructions on how to add `createdBy` and `updatedBy` fields to your queries responses. ::: 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](/cms/api/rest/guides/understanding-populate#populate-all-relations-and-fields-1-level-deep) | | Populate one relation,
1 level deep | `populate=a-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) | | Populate several relations,
1 level deep | `populate[0]=relation-name&populate[1]=another-relation-name&populate[2]=yet-another-relation-name`| [Populate 1 level deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-1-level-deep-for-specific-relations) | | Populate some relations, several levels deep | `populate[root-relation-name][populate][0]=nested-relation-name`| [Populate several levels deep for specific relations](/cms/api/rest/guides/understanding-populate#populate-several-levels-deep-for-specific-relations) | | Populate a component | `populate[0]=component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) | | Populate a component and one of its nested components | `populate[0]=component-name&populate[1]=component-name.nested-component-name`| [Populate components](/cms/api/rest/guides/understanding-populate#populate-components) | | Populate a dynamic zone (only its first-level elements) | `populate[0]=dynamic-zone-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) | | Populate a dynamic zone and its nested elements and relations, using a precisely defined, detailed population strategy | `populate[dynamic-zone-name][on][component-category.component-name][populate][relation-name][populate][0]=field-name`| [Populate dynamic zones](/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) | :::tip The easiest way to build complex queries with multiple-level population is to use our [interactive query builder](/cms/api/rest/interactive-query-builder) tool. ::: ### Combining Population with other operators By utilizing the `populate` operator it is possible to combine other operators such as [field selection](/cms/api/rest/populate-select#field-selection), [filters](/cms/api/rest/filters), and [sort](/cms/api/rest/sort-pagination) in the population queries. :::caution The population and pagination operators cannot be combined. ::: #### Populate with field selection `fields` and `populate` can be combined.
#### Populate with filtering `filters` and `populate` can be combined.
# Relations Source: //cms/api/rest/relations # Managing relations with API requests Defining relations between content-types (that are designated as entities in the database layers) is connecting entities with each other. Relations between content-types can be managed through the [admin panel](/cms/features/content-manager#relational-fields) or through [REST API](/cms/api/rest) or [Document Service API](/cms/api/document-service) requests. Relations can be connected, disconnected or set through the Content API by passing parameters in the body of the request: | Parameter name | Description | Type of update | |-------------------------|-------------|----------------| | [`connect`](#connect) | Connects new entities.

Can be used in combination with `disconnect`.

Can be used with [positional arguments](#relations-reordering) to define an order for relations. | Partial | | [`disconnect`](#disconnect) | Disconnects entities.

Can be used in combination with `connect`. | Partial | | [`set`](#set) | Set entities to a specific set. Using `set` will overwrite all existing connections to other entities.

Cannot be used in combination with `connect` or `disconnect`. | Full | :::note When [Internationalization (i18n)](/cms/features/internationalization) is enabled on the content-type, you can also pass a locale to set relations for a specific locale, as in this Document Service API example: ```js await strapi.documents('api::restaurant.restaurant').update({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr', data: { category: { connect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv'] } } }) ``` If no locale is passed, the default locale will be assumed. ::: ## `connect` Using `connect` in the body of a request performs a partial update, connecting the specified relations. `connect` accepts either a shorthand or a longhand syntax: | Syntax type | Syntax example | | ------------|----------------| | shorthand | `connect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv']` | | longhand | ```connect: [{ documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' }]``` | You can also use the longhand syntax to [reorder relations](#relations-reordering). `connect` can be used in combination with [`disconnect`](#disconnect). :::caution `connect` can not be used for media attributes ::: ### Relations reordering Omitting the `position` argument (as in `documentId: 'srkvrr77k96o44d9v6ef1vu9'`) defaults to `position: { end: true }`. All other relations are positioned relative to another existing `id` (using `after` or `before`) or relative to the list of relations (using `start` or `end`). Operations are treated sequentially in the order defined in the `connect` array, so the resulting database record will be the following: ```js categories: [ { id: 'nyk7047azdgbtjqhl7btuxw' }, { id: 'j9k8l7m6n5o4p3q2r1s0tuv' }, { id: '6u86wkc6x3parjd4emikhmx6' }, { id: '3r1wkvyjwv0b9b36s7hzpxl7' }, { id: 'a1b2c3d4e5f6g7h8i9j0klm' }, { id: 'rkyqa499i84197l29sbmwzl' }, { id: 'srkvrr77k96o44d9v6ef1vu9' } ] ``` ### Edge cases: Draft & Publish or i18n disabled When some built-in features of Strapi 5 are disabled for a content-type, such as [Draft & Publish](/cms/features/draft-and-publish) and [Internationalization (i18)](/cms/features/internationalization), the `connect` parameter might be used differently: **Relation from a `Category` with i18n _off_ to an `Article` with i18n _on_:** In this situation you can select which locale you are connecting to: ```js data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', locale: 'en' }, // Connect to the same document id but with a different locale 👇 { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', locale: 'fr' }, ] } } ``` **Relation from a `Category` with Draft & Publish _off_ to an `Article` with Draft & Publish _on_:** ```js data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', status: 'draft' }, // Connect to the same document id but with different publication states 👇 { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', status: 'published' }, ] } } ``` ## `disconnect` Using `disconnect` in the body of a request performs a partial update, disconnecting the specified relations. `disconnect` accepts either a shorthand or a longhand syntax: | Syntax type | Syntax example | | ------------|----------------| | shorthand | `disconnect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv']` | longhand | ```disconnect: [{ documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' }]``` | `disconnect` can be used in combination with [`connect`](#connect).
## `set` Using `set` performs a full update, replacing all existing relations with the ones specified, in the order specified. `set` accepts a shorthand or a longhand syntax: | Syntax type | Syntax example | | ----------- | ------------------------------- | | shorthand | `set: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv']` | | longhand | ```set: [{ documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' }]``` | As `set` replaces all existing relations, it should not be used in combination with other parameters. To perform a partial update, use [`connect`](#connect) and [`disconnect`](#disconnect). :::note Omitting set Omitting any parameter is equivalent to using `set`.
For instance, the following 3 syntaxes are all equivalent: - `data: { categories: set: [{ documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' }] }}` - `data: { categories: set: ['z0y2x4w6v8u1t3s5r7q9onm2', 'j9k8l7m6n5o4p3q2r1s0tuv'] }}` - `data: { categories: ['z0y2x4w6v8u1t3s5r7q9onm2', 'j9k8l7m6n5o4p3q2r1s0tuv'] }` ::: # Sort and Pagination Source: //cms/api/rest/sort-pagination # REST API: Sort & Pagination Entries that are returned by queries to the [REST API](/cms/api/rest) can be sorted and paginated. :::tip
JavaScript query (built with the qs library): ### Example: Sort using 2 fields and set the order Using the `sort` parameter and defining `:asc` or `:desc` on sorted fields, you can get results sorted in a particular order.
JavaScript query (built with the qs library): ## Pagination Queries can accept `pagination` parameters. Results can be paginated: - either by [page](#pagination-by-page) (i.e., specifying a page number and the number of entries per page) - or by [offset](#pagination-by-offset) (i.e., specifying how many entries to skip and to return) :::note Pagination methods can not be mixed. Always use either `page` with `pageSize` **or** `start` with `limit`. ::: ### Pagination by page To paginate results by page, use the following parameters: | Parameter | Type | Description | Default | | ----------------------- | ------- | ------------------------------------------------------------------------- | ------- | | `pagination[page]` | Integer | Page number | 1 | | `pagination[pageSize]` | Integer | Page size | 25 | | `pagination[withCount]` | Boolean | Adds the total numbers of entries and the number of pages to the response | True |
JavaScript query (built with the qs library): ### Pagination by offset To paginate results by offset, use the following parameters: | Parameter | Type | Description | Default | | ----------------------- | ------- | -------------------------------------------------------------- | ------- | | `pagination[start]` | Integer | Start value (i.e. first entry to return) | 0 | | `pagination[limit]` | Integer | Number of entries to return | 25 | | `pagination[withCount]` | Boolean | Toggles displaying the total number of entries to the response | `true` | :::tip The default and maximum values for `pagination[limit]` can be [configured in the `./config/api.js`](/cms/configurations/api) file with the `api.rest.defaultLimit` and `api.rest.maxLimit` keys. :::
JavaScript query (built with the qs library): # Status Source: //cms/api/rest/status # REST API: `status` The [REST API](/cms/api/rest) offers the ability to filter results based on their status, draft or published. :::prerequisites The [Draft & Publish](/cms/features/draft-and-publish) feature should be enabled. ::: Queries can accept a `status` parameter to fetch documents based on their status: - `published`: returns only the published version of documents (default) - `draft`: returns only the draft version of documents :::tip In the response data, the `publishedAt` field is `null` for drafts. ::: :::note Since published versions are returned by default, passing no status parameter is equivalent to passing `status=published`. :::

JavaScript query (built with the qs library): # Upload files Source: //cms/api/rest/upload # REST API: Upload files The [Media Library feature](/cms/features/media-library) is powered in the back-end server of Strapi by the `upload` package. To upload files to Strapi, you can either use the Media Library directly from the admin panel, or use the [REST API](/cms/api/rest), with the following available endpoints : | Method | Path | Description | | :----- | :---------------------- | :------------------ | | GET | `/api/upload/files` | Get a list of files | | GET | `/api/upload/files/:id` | Get a specific file | | POST | `/api/upload` | Upload files | | POST | `/api/upload?id=x` | Update fileInfo | | DELETE | `/api/upload/files/:id` | Delete a file | :::note Notes - [Folders](/cms/features/media-library#organizing-assets-with-folders) are an admin panel-only feature and are not part of the Content API (REST or GraphQL). Files uploaded through REST are located in the automatically created "API Uploads" folder. - The GraphQL API does not support uploading media files. To upload files, use the REST API or directly add files from the [Media Library](/cms/features/media-library) in the admin panel. Some GraphQL mutations to update or delete uploaded media files are still possible (see [GraphQL API documentation](/cms/api/graphql#mutations-on-media-files) for details). ::: ## Upload files Upload one or more files to your application. `files` is the only accepted parameter, and describes the file(s) to upload. The value(s) can be a Buffer or Stream: :::caution You have to send FormData in your request body. ::: ## Upload entry files Upload one or more files that will be linked to a specific entry. The following parameters are accepted: | Parameter | Description | | --------- | ----------- | |`files` | The file(s) to upload. The value(s) can be a Buffer or Stream. | |`path` (optional) | The folder where the file(s) will be uploaded to (only supported on strapi-provider-upload-aws-s3). | | `refId` | The ID of the entry which the file(s) will be linked to. | | `ref` | The unique ID (uid) of the model which the file(s) will be linked to (see more below). | | `source` (optional) | The name of the plugin where the model is located. | | `field` | The field of the entry which the file(s) will be precisely linked to. | For example, given the `Restaurant` model attributes: ```json title="/src/api/restaurant/content-types/restaurant/schema.json" { // ... "attributes": { "name": { "type": "string" }, "cover": { "type": "media", "multiple": false, } } // ... } ``` The following is an example of a corresponding front-end code: ```html
``` :::caution You have to send FormData in your request body. ::: ## Update fileInfo Update a file in your application. `fileInfo` is the only accepted parameter, and describes the fileInfo to update: ```js const fileId = 50; const newFileData = { alternativeText: 'My new alternative text for this image!', }; const form = new FormData(); form.append('fileInfo', JSON.stringify(newFileData)); const response = await fetch(`http://localhost:1337/api/upload?id=${fileId}`, { method: 'post', body: form, }); ``` ## Models definition Adding a file attribute to a [model](/cms/backend-customization/models) (or the model of another plugin) is like adding a new association. The following example lets you upload and attach one file to the `avatar` attribute: ```json title="/src/api/restaurant/content-types/restaurant/schema.json" { // ... { "attributes": { "pseudo": { "type": "string", "required": true }, "email": { "type": "email", "required": true, "unique": true }, "avatar": { "type": "media", "multiple": false, } } } // ... } ``` The following example lets you upload and attach multiple pictures to the `restaurant` content-type: ```json title="/src/api/restaurant/content-types/restaurant/schema.json" { // ... { "attributes": { "name": { "type": "string", "required": true }, "covers": { "type": "media", "multiple": true, } } } // ... } ``` # Back-end customization Source: //cms/backend-customization
:::strapi Disambiguation: Strapi back end As a headless CMS, the Strapi software as a whole can be considered as the "back end" of your website or application. But the Strapi software itself includes 2 different parts: - The **back-end** part of Strapi is an HTTP server that Strapi runs. Like any HTTP server, the Strapi back end receives requests and send responses. Your content is stored in a database, and the Strapi back end interacts with the database to create, retrieve, update, and delete content. - The **front-end** part of Strapi is called the admin panel. The admin panel presents a graphical user interface to help you structure and manage the content. Throughout this developer documentation, 'back end' refers _exclusively_ to the back-end part of Strapi. The [Getting Started > Admin panel page](/cms/features/admin-panel) gives an admin panel overview and the [admin panel customization section](/cms/admin-panel-customization) details the various customization options available for the admin panel. ::: The Strapi back end runs an HTTP server based on , a back-end JavaScript framework. Like any HTTP server, the Strapi back end receives requests and send responses. You can send requests to the Strapi back end to create, retrieve, update, or delete data through the [REST](/cms/api/rest) or [GraphQL](/cms/api/graphql) APIs. A request can travel through the Strapi back end as follows: 1. The Strapi server receives a [request](/cms/backend-customization/requests-responses). 2. The request hits [global middlewares](/cms/backend-customization/middlewares) that are run in a sequential order. 3. The request hits a [route](/cms/backend-customization/routes).
By default, Strapi generates route files for all the content-types that you create (see [REST API documentation](/cms/api/rest)), and more routes can be added and configured. 4. [Route policies](/cms/backend-customization/policies) act as a read-only validation step that can block access to a route. [Route middlewares](/cms/backend-customization/routes#middlewares) can control the request flow and mutate the request itself before moving forward. 5. [Controllers](/cms/backend-customization/controllers) execute code once a route has been reached. [Services](/cms/backend-customization/services) are optional, additional code that can be used to build custom logic reusable by controllers. 6. The code executed by the controllers and services interacts with the [models](/cms/backend-customization/models) that are a representation of the content content structure stored in the database.
Interacting with the data represented by the models is handled by the [Document Service](/cms/api/document-service) and [Query Engine](/cms/api/query-engine). 7. You can implement [Document Service middlewares](/cms/api/document-service/middlewares) to control the data before it's sent to the Query Engine. The Query Engine can also use lifecycle hooks though we recommend you use Document Service middlewares unless you absolutely need to directly interact with the database. 7. The server returns a [response](/cms/backend-customization/requests-responses). The response can travel back through route middlewares and global middlewares before being sent. Both global and route middlewares include an asynchronous callback function, `await next()`. Depending on what is returned by the middleware, the request will either go through a shorter or longer path through the back end: * If a middleware returns nothing, the request will continue travelling through the various core elements of the back end (i.e., controllers, services, and the other layers that interact with the database). * If a middleware returns before calling `await next()`, a response will be immediately sent, skipping the rest of the core elements. Then it will go back down the same chain it came up. :::info Please note that all customizations described in the pages of this section are only for the REST API. [GraphQL customizations](/cms/plugins/graphql#customization) are described in the GraphQL plugin documentation. ::: ## Interactive diagram The following diagram represents how requests travel through the Strapi back end. You can click on any shape to jump to the relevant page in the documentation.
# Controllers Source: //cms/backend-customization/controllers # Controllers Controllers are JavaScript files that contain a set of methods, called actions, reached by the client according to the requested [route](/cms/backend-customization/routes). Whenever a client requests the route, the action performs the business logic code and sends back the [response](/cms/backend-customization/requests-responses). Controllers represent the C in the model-view-controller (MVC) pattern. In most cases, the controllers will contain the bulk of a project's business logic. But as a controller's logic becomes more and more complicated, it's a good practice to use [services](/cms/backend-customization/services) to organize the code into re-usable parts.
Simplified Strapi backend diagram with controllers highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with controllers highlighted. The backend customization introduction page includes a complete, interactive diagram.
## Implementation Controllers can be [generated or added manually](#adding-a-new-controller). Strapi provides a `createCoreController` factory function that automatically generates core controllers and allows building custom ones or [extend or replace the generated controllers](#extending-core-controllers). ### Adding a new controller A new controller can be implemented: - with the [interactive CLI command `strapi generate`](/cms/cli) - or manually by creating a JavaScript file: - in `./src/api/[api-name]/controllers/` for API controllers (this location matters as controllers are auto-loaded by Strapi from there) - or in a folder like `./src/plugins/[plugin-name]/server/controllers/` for plugin controllers, though they can be created elsewhere as long as the plugin interface is properly exported in the `strapi-server.js` file (see [Server API for Plugins documentation](/cms/plugins-development/server-api)) Each controller action can be an `async` or `sync` function. Every action receives a context object (`ctx`) as a parameter. `ctx` contains the [request context](/cms/backend-customization/requests-responses#ctxrequest) and the [response context](/cms/backend-customization/requests-responses#ctxresponse).
:::note When a new [content-type](/cms/backend-customization/models#content-types) is created, Strapi builds a generic controller with placeholder code, ready to be customized. ::: :::tip To see a possible advanced usage for custom controllers, read the [services and controllers](/cms/backend-customization/examples/services-and-controllers) page of the backend customization examples cookbook. ::: ### Sanitization and Validation in controllers {#sanitization-and-validation-in-controllers} :::warning It's strongly recommended you sanitize (v4.8.0+) and/or validate (v4.13.0+) your incoming request query utilizing the new `sanitizeQuery` and `validateQuery` functions to prevent the leaking of private data. ::: Sanitization means that the object is “cleaned” and returned. Validation means an assertion is made that the data is already clean and throws an error if something is found that shouldn't be there. In Strapi 5, both query parameters and input data (i.e., create and update body data) are validated. Any create and update data requests with the following invalid input will throw a `400 Bad Request` error: - relations the user do not have permission to create - unrecognized values that are not present on a schema - non-writable fields and internal timestamps like `createdAt` and `createdBy` fields - setting or updating an `id` field (except for connecting relations) #### Sanitization when utilizing controller factories Within the Strapi factories the following functions are exposed that can be used for sanitization and validation: | Function Name | Parameters | Description | |------------------|----------------------------|--------------------------------------------------------------------------------------| | `sanitizeQuery` | `ctx` | Sanitizes the request query | | `sanitizeOutput` | `entity`/`entities`, `ctx` | Sanitizes the output data where entity/entities should be an object or array of data | | `sanitizeInput` | `data`, `ctx` | Sanitizes the input data | | `validateQuery` | `ctx` | Validates the request query (throws an error on invalid params) | | `validateInput` | `data`, `ctx` | (EXPERIMENTAL) Validates the input data (throws an error on invalid data) | These functions automatically inherit the sanitization settings from the model and sanitize the data accordingly based on the content-type schema and any of the content API authentication strategies, such as the Users & Permissions plugin or API tokens. :::warning Because these methods use the model associated with the current controller, if you query data that is from another model (i.e., doing a find for "menus" within a "restaurant" controller method), you must instead use the `@strapi/utils` tools, such as `sanitize.contentAPI.query` described in [Sanitizing Custom Controllers](#sanitize-validate-custom-controllers), or else the result of your query will be sanitized against the wrong model. ::: #### Sanitization and validation when building custom controllers {#sanitize-validate-custom-controllers} Within custom controllers, there are 5 primary functions exposed via the `@strapi/utils` package that can be used for sanitization and validation: | Function Name | Parameters | Description | |------------------------------|--------------------|---------------------------------------------------------| | `sanitize.contentAPI.input` | `data`, `schema`, `auth` | Sanitizes the request input including non-writable fields, removing restricted relations, and other nested "visitors" added by plugins | | `sanitize.contentAPI.output` | `data`, `schema`, `auth` | Sanitizes the response output including restricted relations, private fields, passwords, and other nested "visitors" added by plugins | | `sanitize.contentAPI.query` | `ctx.query`, `schema`, `auth` | Sanitizes the request query including filters, sort, fields, and populate | | `validate.contentAPI.query` | `ctx.query`, `schema`, `auth` | Validates the request query including filters, sort, fields (currently not populate) | | `validate.contentAPI.input` | `data`, `schema`, `auth` | (EXPERIMENTAL) Validates the request input including non-writable fields, removing restricted relations, and other nested "visitors" added by plugins | :::note Depending on the complexity of your custom controllers, you may need additional sanitization that Strapi cannot currently account for, especially when combining the data from multiple sources. ::: ### Extending core controllers {#extending-core-controllers} Default controllers and actions are created for each content-type. These default controllers are used to return responses to API requests (e.g. when `GET /api/articles/3` is accessed, the `findOne` action of the default controller for the "Article" content-type is called). Default controllers can be customized to implement your own logic. The following code examples should help you get started. :::tip An action from a core controller can be replaced entirely by [creating a custom action](#adding-a-new-controller) and naming the action the same as the original action (e.g. `find`, `findOne`, `create`, `update`, or `delete`). ::: :::tip When extending a core controller, you do not need to re-implement any sanitization as it will already be handled by the core controller you are extending. Where possible it's strongly recommended to extend the core controller instead of creating a custom controller. :::
Collection type examples :::tip The [backend customization examples cookbook](/cms/backend-customization/examples) shows how you can overwrite a default controller action, for instance for the [`create` action](/cms/backend-customization/examples/services-and-controllers#custom-controller). :::
Single type examples
## Usage Controllers are declared and attached to a route. Controllers are automatically called when the route is called, so controllers usually do not need to be called explicitly. However, [services](/cms/backend-customization/services) can call controllers, and in this case the following syntax should be used: ```js // access an API controller strapi.controller('api::api-name.controller-name'); // access a plugin controller strapi.controller('plugin::plugin-name.controller-name'); ``` :::tip To list all the available controllers, run `yarn strapi controllers:list`. ::: # Middlewares Source: //cms/backend-customization/middlewares # Middlewares customization Globally scoped custom middlewares should be added to the [middlewares configuration file](/cms/configurations/middlewares#loading-order) or Strapi won't load them. API level and plugin middlewares can be added into the specific router that they are relevant to like the following: ```js title="./src/api/[api-name]/routes/[collection-name].js or ./src/plugins/[plugin-name]/server/routes/index.js" module.exports = { routes: [ { method: "GET", path: "/[collection-name]", handler: "[controller].find", config: { middlewares: ["[middleware-name]"], // See the usage section below for middleware naming conventions }, }, ], }; ```
Example of a custom timer middleware
The GraphQL plugin also allows [implementing custom middlewares](/cms/plugins/graphql#middlewares), with a different syntax. ## Usage Middlewares are called different ways depending on their scope: - use `global::middleware-name` for application-level middlewares - use `api::api-name.middleware-name` for API-level middlewares - use `plugin::plugin-name.middleware-name` for plugin middlewares :::tip To list all the registered middlewares, run `yarn strapi middlewares:list`. ::: ### Restricting content access with an "is-owner policy" It is often required that the author of an entry is the only user allowed to edit or delete the entry. In previous versions of Strapi, this was known as an "is-owner policy". With Strapi v4, the recommended way to achieve this behavior is to use a middleware. Proper implementation largely depends on your project's needs and custom code, but the most basic implementation could be achieved with the following procedure: 1. From your project's folder, create a middleware with the Strapi CLI generator, by running the `yarn strapi generate` (or `npm run strapi generate`) command in the terminal. 2. Select `middleware` from the list, using keyboard arrows, and press Enter. 3. Give the middleware a name, for instance `isOwner`. 4. Choose `Add middleware to an existing API` from the list. 5. Select which API you want the middleware to apply. 6. Replace the code in the `/src/api/[your-api-name]/middlewares/isOwner.js` file with the following, replacing `api::restaurant.restaurant` in line 22 with the identifier corresponding to the API you choose at step 5 (e.g., `api::blog-post.blog-post` if your API name is `blog-post`): ```js showLineNumbers title="src/api/blog-post/middlewares/isOwner.js" "use strict"; /** * `isOwner` middleware */ module.exports = (config, { strapi }) => { // Add your own logic here. return async (ctx, next) => { const user = ctx.state.user; const entryId = ctx.params.id ? ctx.params.id : undefined; let entry = {}; /** * Gets all information about a given entry, * populating every relations to ensure * the response includes author-related information */ if (entryId) { entry = await strapi.documents('api::restaurant.restaurant').findOne( entryId, { populate: "*" } ); } /** * Compares user id and entry author id * to decide whether the request can be fulfilled * by going forward in the Strapi backend server */ if (user.id !== entry.author.id) { return ctx.unauthorized("This action is unauthorized."); } else { return next(); } }; }; ``` 7. Ensure the middleware is configured to apply on some routes. In the `config` object found in the `src/api/[your-api–name]/routes/[your-content-type-name].js` file, define the methods (`create`, `read`, `update`, `delete`) for which you would like the middleware to apply, and declare the `isOwner` middleware for these routes.

For instance, if you wish to allow GET (i.e., `read` method) and POST (i.e., `create` method) requests to any user for the `restaurant` content-type in the `restaurant` API, but would like to restrict PUT (i.e., `update` method) and DELETE requests only to the user who created the entry, you could use the following code in the `src/api/restaurant/routes/restaurant.js` file: ```js title="src/api/restaurant/routes/restaurant.js" /** * restaurant router */ const { createCoreRouter } = require("@strapi/strapi").factories; module.exports = createCoreRouter("api::restaurant.restaurant", { config: { update: { middlewares: ["api::restaurant.is-owner"], }, delete: { middlewares: ["api::restaurant.is-owner"], }, }, }); ``` :::info You can find more information about route middlewares in the [routes documentation](/cms/backend-customization/routes). ::: # Models Source: //cms/backend-customization/models # Models As Strapi is a headless Content Management System (CMS), creating a content structure for the content is one of the most important aspects of using the software. Models define a representation of the content structure. There are 2 different types of models in Strapi: - content-types, which can be collection types or single types, depending on how many entries they manage, - and components that are content structures re-usable in multiple content-types. If you are just starting out, it is convenient to generate some models with the [Content-type Builder](/cms/features/content-type-builder) directly in the admin panel. The user interface takes over a lot of validation tasks and showcases all the options available to create the content's content structure. The generated model mappings can then be reviewed at the code level using this documentation. ## Model creation Content-types and components models are created and stored differently. ### Content-types Content-types in Strapi can be created: - with the [Content-type Builder in the admin panel](/cms/features/content-type-builder), - or with [Strapi's interactive CLI `strapi generate`](/cms/cli#strapi-generate) command. The content-types use the following files: - `schema.json` for the model's [schema](#model-schema) definition. (generated automatically, when creating content-type with either method) - `lifecycles.js` for [lifecycle hooks](#lifecycle-hooks). This file must be created manually. These models files are stored in `./src/api/[api-name]/content-types/[content-type-name]/`, and any JavaScript or JSON file found in these folders will be loaded as a content-type's model (see [project structure](/cms/project-structure)). :::note In [TypeScript](/cms/typescript.md)-enabled projects, schema typings can be generated using the `ts:generate-types` command. ::: ### Components {#components-creation} Component models can't be created with CLI tools. Use the [Content-type Builder](/cms/features/content-type-builder) or create them manually. Components models are stored in the `./src/components` folder. Every component has to be inside a subfolder, named after the category the component belongs to (see [project structure](/cms/project-structure)). ## Model schema The `schema.json` file of a model consists of: - [settings](#model-settings), such as the kind of content-type the model represents or the table name in which the data should be stored, - [information](#model-information), mostly used to display the model in the admin panel and access it through the REST and GraphQL APIs, - [attributes](#model-attributes), which describe the content structure of the model, - and [options](#model-options) used to defined specific behaviors on the model. ### Model settings General settings for the model can be configured with the following parameters: | Parameter | Type | Description | | -------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------- | | `collectionName` | String | Database table name in which the data should be stored | | `kind`

_Optional,
only for content-types_ | String | Defines if the content-type is:
  • a collection type (`collectionType`)
  • or a single type (`singleType`)
| ```json // ./src/api/[api-name]/content-types/restaurant/schema.json { "kind": "collectionType", "collectionName": "Restaurants_v1", } ``` ### Model information The `info` key in the model's schema describes information used to display the model in the admin panel and access it through the Content API. It includes the following parameters: | Parameter | Type | Description | | -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | `displayName` | String | Default name to use in the admin panel | | `singularName` | String | Singular form of the content-type name.
Used to generate the API routes and databases/tables collection.

Should be kebab-case. | | `pluralName` | String | Plural form of the content-type name.
Used to generate the API routes and databases/tables collection.

Should be kebab-case. | | `description` | String | Description of the model | ```json title="./src/api/[api-name]/content-types/restaurant/schema.json" "info": { "displayName": "Restaurant", "singularName": "restaurant", "pluralName": "restaurants", "description": "" }, ``` ### Model attributes The content structure of a model consists of a list of attributes. Each attribute has a `type` parameter, which describes its nature and defines the attribute as a simple piece of data or a more complex structure used by Strapi. Many types of attributes are available: - scalar types (e.g. strings, dates, numbers, booleans, etc.), - Strapi-specific types, such as: - `media` for files uploaded through the [Media library](/cms/features/content-type-builder#media) - `relation` to describe a [relation](#relations) between content-types - `customField` to describe [custom fields](#custom-fields) and their specific keys - `component` to define a [component](#components-json) (i.e. a content structure usable in multiple content-types) - `dynamiczone` to define a [dynamic zone](#dynamic-zones) (i.e. a flexible space based on a list of components) - and the `locale` and `localizations` types, only used by the [Internationalization (i18n) plugin](/cms/features/internationalization) The `type` parameter of an attribute should be one of the following values: | Type categories | Available types | |------|-------| | String types |
  • `string`
  • `text`
  • `richtext`
  • `enumeration`
  • `email`
  • `password`
  • [`uid`](#uid-type)
| | Date types |
  • `date`
  • `time`
  • `datetime`
  • `timestamp`
| | Number types |
  • `integer`
  • `biginteger`
  • `float`
  • `decimal`
| | Other generic types |
  • `boolean`
  • `json`
| | Special types unique to Strapi |
  • `media`
  • [`relation`](#relations)
  • [`customField`](#custom-fields)
  • [`component`](#components-json)
  • [`dynamiczone`](#dynamic-zones)
| | Internationalization (i18n)-related types

_Can only be used if the [i18n](/cms/features/internationalization) is enabled on the content-type_|
  • `locale`
  • `localizations`
| #### Validations Basic validations can be applied to attributes using the following parameters: | Parameter | Type | Description | Default | | -------------- | ------- | --------------------------------------------------------------------------------------------------------- | ------- | | `required` | Boolean | If `true`, adds a required validator for this property | `false` | | `max` | Integer | Checks if the value is greater than or equal to the given maximum | - | | `min` | Integer | Checks if the value is less than or equal to the given minimum | - | | `minLength` | Integer | Minimum number of characters for a field input value | - | | `maxLength` | Integer | Maximum number of characters for a field input value | - | | `private` | Boolean | If `true`, the attribute will be removed from the server response.

💡 This is useful to hide sensitive data. | `false` | | `configurable` | Boolean | If `false`, the attribute isn't configurable from the Content-type Builder plugin. | `true` | ```json title="./src/api/[api-name]/content-types/restaurant/schema.json" { // ... "attributes": { "title": { "type": "string", "minLength": 3, "maxLength": 99, "unique": true }, "description": { "default": "My description", "type": "text", "required": true }, "slug": { "type": "uid", "targetField": "title" } // ... } } ``` #### Database validations and settings :::caution 🚧 This API is considered experimental. These settings should be reserved to an advanced usage, as they might break some features. There are no plans to make these settings stable. ::: Database validations and settings are custom options passed directly onto the `tableBuilder` Knex.js function during schema migrations. Database validations allow for an advanced degree of control for setting custom column settings. The following options are set in a `column: {}` object per attribute: | Parameter | Type | Description | Default | | ------------- | ------- | --------------------------------------------------------------------------------------------- | ------- | | `name` | string | Changes the name of the column in the database | - | | `defaultTo` | string | Sets the database `defaultTo`, typically used with `notNullable` | - | | `notNullable` | boolean | Sets the database `notNullable`, ensures that columns cannot be null | `false` | | `unsigned` | boolean | Only applies to number columns, removes the ability to go negative but doubles maximum length | `false` | | `unique` | boolean | Enforces database level unique, caution when using with draft & publish feature | `false` | | `type` | string | Changes the database type, if `type` has arguments, you should pass them in `args` | - | | `args` | array | Arguments passed into the Knex.js function that changes things like `type` | `[]` | ```json title="./src/api/[api-name]/content-types/restaurant/schema.json" { // ... "attributes": { "title": { "type": "string", "minLength": 3, "maxLength": 99, "unique": true, "column": { "unique": true // enforce database unique also } }, "description": { "default": "My description", "type": "text", "required": true, "column": { "defaultTo": "My description", // set database level default "notNullable": true // enforce required at database level, even for drafts } }, "rating": { "type": "decimal", "default": 0, "column": { "defaultTo": 0, "type": "decimal", // using the native decimal type but allowing for custom precision "args": [ 6,1 // using custom precision and scale ] } } // ... } } ``` #### `uid` type The `uid` type is used to automatically prefill the field value in the admin panel with a unique identifier (UID) (e.g. slugs for articles) based on 2 optional parameters: - `targetField` (string): If used, the value of the field defined as a target is used to auto-generate the UID. - `options` (string): If used, the UID is generated based on a set of options passed to #### Custom fields [Custom fields](/cms/features/custom-fields) extend Strapi’s capabilities by adding new types of fields to content-types. Custom fields are explicitly defined in the [attributes](#model-attributes) of a model with `type: customField`. Custom fields' attributes also show the following specificities: - a `customField` attribute whose value acts as a unique identifier to indicate which registered custom field should be used. Its value follows: - either the `plugin::plugin-name.field-name` format if a plugin created the custom field - or the `global::field-name` format for a custom field specific to the current Strapi application - and additional parameters depending on what has been defined when registering the custom field (see [custom fields documentation](/cms/features/custom-fields)). ```json title="./src/api/[apiName]/[content-type-name]/content-types/schema.json" { // … "attributes": { "attributeName": { // attributeName would be replaced by the actual attribute name "type": "customField", "customField": "plugin::color-picker.color", "options": { "format": "hex" } } } // … } ``` #### Components {#components-json} Component fields create a relation between a content-type and a component structure. Components are explicitly defined in the [attributes](#model-attributes) of a model with `type: 'component'` and accept the following additional parameters: | Parameter | Type | Description | | ------------ | ------- | ---------------------------------------------------------------------------------------- | | `repeatable` | Boolean | Could be `true` or `false` depending on whether the component is repeatable or not | | `component` | String | Define the corresponding component, following this format:
`.` | ```json title="./src/api/[apiName]/restaurant/content-types/schema.json" { "attributes": { "openinghours": { "type": "component", "repeatable": true, "component": "restaurant.openinghours" } } } ``` #### Dynamic zones Dynamic zones create a flexible space in which to compose content, based on a mixed list of [components](#components-json). Dynamic zones are explicitly defined in the [attributes](#model-attributes) of a model with `type: 'dynamiczone'`. They also accept a `components` array, where each component should be named following this format: `.`. ```json title="./src/api/[api-name]/content-types/article/schema.json" { "attributes": { "body": { "type": "dynamiczone", "components": ["article.slider", "article.content"] } } } ``` ### Model options The `options` key is used to define specific behaviors and accepts the following parameter: | Parameter | Type | Description | |---------------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `privateAttributes` | Array of strings | Allows treating a set of attributes as private, even if they're not actually defined as attributes in the model. It could be used to remove them from API responses timestamps.

The `privateAttributes` defined in the model are merged with the `privateAttributes` defined in the global Strapi configuration. | | `draftAndPublish` | Boolean | Enables the draft and publish feature.

Default value: `true` (`false` if the content-type is created from the interactive CLI). | | `populateCreatorFields` | Boolean | Populates `createdBy` and `updatedBy` fields in responses returned by the REST API (see [guide](/cms/api/rest/guides/populate-creator-fields) for more details).

Default value: `false`. | ```json title="./src/api/[api-name]/content-types/restaurant/schema.json" { "options": { "privateAttributes": ["id", "createdAt"], "draftAndPublish": true } } ``` ## Lifecycle hooks Lifecycle hooks are functions that get triggered when Strapi queries are called. They are triggered automatically when managing content through the administration panel or when developing custom code using `queries`· Lifecycle hooks can be customized declaratively or programmatically. :::caution Lifecycles hooks are not triggered when using directly the Using the database layer API, it's also possible to register a subscriber and listen to events programmatically: ```js title="./src/index.js" module.exports = { async bootstrap({ strapi }) { // registering a subscriber strapi.db.lifecycles.subscribe({ models: [], // optional; beforeCreate(event) { const { data, where, select, populate } = event.params; event.state = 'doStuffAfterWards'; }, afterCreate(event) { if (event.state === 'doStuffAfterWards') { } const { result, params } = event; // do something to the result }, }); // generic subscribe for generic handling strapi.db.lifecycles.subscribe((event) => { if (event.action === 'beforeCreate') { // do something } }); } } ``` # Policies Source: //cms/backend-customization/policies # Policies Policies are functions that execute specific logic on each request before it reaches the [controller](/cms/backend-customization/controllers). They are mostly used for securing business logic. Each [route](/cms/backend-customization/routes) of a Strapi project can be associated to an array of policies. For example, a policy named `is-admin` could check that the request is sent by an admin user, and restrict access to critical routes. Policies can be global or scoped. [Global policies](#global-policies) can be associated to any route in the project. Scoped policies only apply to a specific [API](#api-policies) or [plugin](#plugin-policies).
Simplified Strapi backend diagram with routes and policies highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with policies and routes highlighted. The backend customization introduction page includes a complete, interactive diagram.
## Implementation A new policy can be implemented: - with the [interactive CLI command `strapi generate`](/cms/cli#strapi-generate) - or manually by creating a JavaScript file in the appropriate folder (see [project structure](/cms/project-structure)): - `./src/policies/` for global policies - `./src/api/[api-name]/policies/` for API policies - `./src/plugins/[plugin-name]/policies/` for plugin policies
Global policy implementation example: `policyContext` is a wrapper around the [controller](/cms/backend-customization/controllers) context. It adds some logic that can be useful to implement a policy for both REST and GraphQL.
Policies can be configured using a `config` object: ## Usage To apply policies to a route, add them to its configuration object (see [routes documentation](/cms/backend-customization/routes#policies)). Policies are called different ways depending on their scope: - use `global::policy-name` for [global policies](#global-policies) - use `api::api-name.policy-name` for [API policies](#api-policies) - use `plugin::plugin-name.policy-name` for [plugin policies](#plugin-policies) :::tip To list all the available policies, run `yarn strapi policies:list`. ::: ### Global policies Global policies can be associated to any route in a project. ### Plugin policies Plugins can add and expose policies to an application. For example, the [Users & Permissions feature](/cms/features/users-permissions) comes with policies to ensure that the user is authenticated or has the rights to perform an action: ### API policies API policies are associated to the routes defined in the API where they have been declared. To use a policy in another API, reference it with the following syntax: `api::[apiName].[policyName]`: # Requests and Responses Source: //cms/backend-customization/requests-responses # Requests and Responses The Strapi back end server is based on . When you send requests through the [REST API](/cms/api/rest), a context object (`ctx`) is passed to every element of the Strapi back end (e.g., [policies](/cms/backend-customization/policies), [controllers](/cms/backend-customization/controllers), [services](/cms/backend-customization/services)). `ctx` includes 3 main objects: - [`ctx.request`](#ctxrequest) for information about the request sent by the client making an API request, - [`ctx.state`](#ctxstate) for information about the state of the request within the Strapi back end, - and [`ctx.response`](#ctxresponse) for information about the response that the server will return. :::tip The request's context can also be accessed from anywhere in the code with the [`strapi.requestContext` function](#accessing-the-request-context-anywhere). ::: :::info In addition to the concepts and parameters described in the following documentation, you might find additional information in the , and . :::
Simplified Strapi backend diagram with requests and responses highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with requests and responses highlighted. The backend customization introduction page includes a complete, interactive diagram.
## `ctx.request` The `ctx.request` object contains the following parameters: | Parameter | Description | Type | | --------------------- | -------------------------------------------------------------------------------------------- | -------- | | `ctx.request.body` | Parsed version of the body. | `Object` | | `ctx.request.files` | Files sent with the request. | `Array` | | `ctx.request.headers` | Headers sent with the request. | `Object` | | `ctx.request.host` | Host part of the URL, including the port. | `String` | | `ctx.request.hostname`| Host part of the URL, excluding the port. | `String` | | `ctx.request.href` | Complete URL of the requested resource, including the protocol, domain, port (if specified), path, and query parameters. | `String` | | `ctx.request.ip` | IP of the person sending the request.| `String` | | `ctx.request.ips` | When `X-Forwarded-For` is present and `app.proxy` is enabled, an array of IPs is returned, ordered from upstream to downstream.

For example if the value were "client, proxy1, proxy2", you would receive the `["client", "proxy1", "proxy2"]` array. | `Array` | | `ctx.request.method` | Request method (e.g., `GET`, `POST`). | `String` | | `ctx.request.origin` | URL part before the first `/`. | `String` | | `ctx.request.params` | Parameters sent in the URL.

For example, if the internal URL is `/restaurants/:id`, whatever you replace `:id` in the real request becomes accessible through `ctx.request.params.id`. | `Object` | | `ctx.request.path` | Path of the requested resource, excluding the query parameters. | `String` | | `ctx.request.protocol`| Protocol being used (e.g., `https` or `http`). | `String` | | `ctx.request.query` | Strapi-specific [query parameters](#ctxrequestquery). | `Object` | | `ctx.request.subdomains`| Subdomains included in the URL.

For example, if the domain is `tobi.ferrets.example.com`, the value is the following array: `["ferrets", "tobi"]`. | `Array` | | `ctx.request.url` | Path and query parameters of the requested resource, excluding the protocol, domain, and port. | `String` |
Differences between protocol, origin, url, href, path, host, and hostname : Given an API request sent to the `https://example.com:1337/api/restaurants?id=123` URL, here is what different parameters of the `ctx.request` object return: | Parameter | Returned value | | ---------- | ------------------------------------------------- | | `ctx.request.href` | `https://example.com:1337/api/restaurants?id=123` | | `ctx.request.protocol` | `https` | | `ctx.request.host` | `localhost:1337` | | `ctx.request.hostname` | `localhost` | | `ctx.request.origin` | `https://example.com:1337` | | `ctx.request.url` | `/api/restaurants?id=123` | | `ctx.request.path` | `/api/restaurants` |
### `ctx.request.query` `ctx.request` provides a `query` object that gives access to Strapi query parameters. The following table lists available parameters with a short description and a link to the relevant REST API documentation section (see [REST API parameters](/cms/api/rest/parameters) for more information): | Parameter | Description | Type | | -------------------------------------| --------------------------------------------------------------------------------------------------------------------------- | -------------------- | | `ctx.request.query`
`ctx.query` | The whole query object. | `Object` | | `ctx.request.query.sort` | Parameters to [sort the response](/cms/api/rest/sort-pagination.md#sorting) | `String` or `Array` | | `ctx.request.query.filters` | Parameters to [filter the response](/cms/api/rest/filters) | `Object` | | `ctx.request.query.populate` | Parameters to [populate relations, components, or dynamic zones](/cms/api/rest/populate-select#population) | `String` or `Object` | | `ctx.request.query.fields` | Parameters to [select only specific fields to return with the response](/cms/api/rest/populate-select#field-selection) | `Array` | | `ctx.request.query.pagination` | Parameter to [page through entries](/cms/api/rest/sort-pagination.md#pagination) | `Object` | | `ctx.request.query.publicationState` | Parameter to [select the Draft & Publish state](/cms/api/rest/status) | `String` | | `ctx.request.query.locale` | Parameter to [select one or multiple locales](/cms/api/rest/locale) | `String` or `Array` | ## `ctx.state` The `ctx.state` object gives access to the state of the request within the Strapi back end, including specific values about the [user](#ctxstateuser), [authentication](#ctxstateauth), [route](#ctxstateroute): | Parameter | Description | Type | | ---------------------------|---------------------------------------------------------------------------- | -------- | | `ctx.state.isAuthenticated`| Returns whether the current user is authenticated in any way. | `Boolean` | ### `ctx.state.user` The `ctx.state.user` object gives access to information about the user performing the request and includes the following parameters: | Parameter | Description | Type | | ----------| -------------------------------------------------------------------------------------------- | -------- | | `ctx.state.user`| User's information. Only one relation is populated. | `Object` | | `ctx.state.user.role`| The user's role | `Object` | ### `ctx.state.auth` The `ctx.state.auth` object gives access to information related to the authentication and includes the following parameters: | Parameter | Description | Type | | ------------------------------| -------------------------------------------------------------------------------------------- | -------- | | `ctx.state.auth.strategy` | Information about the currently used authentication strategy ([Users & Permissions plugin](/cms/features/users-permissions) or [API tokens](/cms/features/api-tokens)) | `Object` | | `ctx.state.auth.strategy.name`| Name of the currently used strategy | `String` | | `ctx.state.auth.credentials` | The user's credentials | `String` | ### `ctx.state.route` The `ctx.state.route` object gives access to information related to the current route and includes the following parameters: | Parameter | Description | Type | | ----------| -------------------------------------------------------------------------------------------- | -------- | | `ctx.state.route.method`| Method used to access the current route. | `String` | | `ctx.state.route.path`| Path of the current route. | `String` | | `ctx.state.route.config`| Configuration information about the current route. | `Object` | | `ctx.state.route.handler`| Handler (controller) of the current route. | `Object` | | `ctx.state.route.info`| Additional information about the current route, such as the apiName and the API request type. | `Object` | | `ctx.state.route.info.apiName`| Name of the used API. | `String` | | `ctx.state.route.info.type`| Type of the used API. | `String` | ## `ctx.response` The `ctx.response` object gives access to information related to the response that the server will return and includes the following parameters: | Parameter | Description | Type | | ----------| -------------------------------------------------------------------------------------------- | -------- | | `ctx.response.body`| Body of the response. | `Any` | | `ctx.response.status` | Status code of the response. | `Integer` | | `ctx.response.message`| Status message of the response.

By default, `response.message` is associated with `response.status`. | `String` | | `ctx.response.header`
`ctx.response.headers`| Header(s) sent with the response. | `Object` | | `ctx.response.length`| header value as a number when present, or deduces it from `ctx.body` when possible; otherwise, returns `undefined`. | `Integer` | | `ctx.response.redirect`
`ctx.response.redirect(url, [alt])` | Performs a `302` redirect to the URL. The string "back" is special-cased to provide Referrer support; when Referrer is not present, alt or "/" is used.

Example: `ctx.response.redirect('back', '/index.html');` | `Function` | | `ctx.response.attachment`

`ctx.response.attachment([filename], [options])` | Sets header to "attachment" to signal the client to prompt for download. Optionally specify the filename of the download and some . | `Function` | | `ctx.response.type`| header, void of parameters such as "charset". | `String` | | `ctx.response.lastModified`| header as a Date, if it exists. | `DateTime` | | `ctx.response.etag`| Sets the of a response including the wrapped "s.
There is no corresponding `response.etag` getter. | `String` | ## Accessing the request context anywhere Strapi exposes a way to access the current request context from anywhere in the code (e.g. lifecycle functions). You can access the request as follows: ```js const ctx = strapi.requestContext.get(); ``` You should only use this inside of functions that will be called in the context of an HTTP request. ```js // correct const service = { myFunction() { const ctx = strapi.requestContext.get(); console.log(ctx.state.user); }, }; // incorrect const ctx = strapi.requestContext.get(); const service = { myFunction() { console.log(ctx.state.user); }, }; ``` **Example:** ```js title="./api/test/content-types/article/lifecycles.js" module.exports = { beforeUpdate() { const ctx = strapi.requestContext.get(); console.log('User info in service: ', ctx.state.user); }, }; ``` :::note Strapi uses a Node.js feature called to make the context available anywhere. ::: # Routes Source: //cms/backend-customization/routes # Routes Requests sent to Strapi on any URL are handled by routes. By default, Strapi generates routes for all the content-types (see [REST API documentation](/cms/api/rest)). Routes can be [added](#implementation) and configured: - with [policies](#policies), which are a way to block access to a route, - and with [middlewares](#middlewares), which are a way to control and change the request flow and the request itself. Once a route exists, reaching it executes some code handled by a controller (see [controllers documentation](/cms/backend-customization/controllers)). To view all existing routes and their hierarchal order, you can run `yarn strapi routes:list` (see [CLI reference](/cms/cli)).
Simplified Strapi backend diagram with routes highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with routes highlighted. The backend customization introduction page includes a complete, interactive diagram.
## Implementation Implementing a new route consists in defining it in a router file within the `./src/api/[apiName]/routes` folder (see [project structure](/cms/project-structure)). There are 2 different router file structures, depending on the use case: - configuring [core routers](#configuring-core-routers) - or creating [custom routers](#creating-custom-routers). ### Configuring core routers Core routers (i.e. `find`, `findOne`, `create`, `update`, and `delete`) correspond to [default routes](/cms/api/rest#endpoints) automatically created by Strapi when a new [content-type](/cms/backend-customization/models#model-creation) is created. Strapi provides a `createCoreRouter` factory function that automatically generates the core routers and allows: - passing in configuration options to each router - and disabling some core routers to [create custom ones](#creating-custom-routers). A core router file is a JavaScript file exporting the result of a call to `createCoreRouter` with the following parameters: | Parameter | Description | Type | | ----------| -------------------------------------------------------------------------------------------- | -------- | | `prefix` | Allows passing in a custom prefix to add to all routers for this model (e.g. `/test`) | `String` | | `only` | Core routes that will only be loaded

Anything not in this array is ignored. | `Array` | --> | `except` | Core routes that should not be loaded

This is functionally the opposite of the `only` parameter. | `Array` | | `config` | Configuration to handle [policies](#policies), [middlewares](#middlewares) and [public availability](#public-routes) for the route | `Object` |

Generic implementation example: This only allows a `GET` request on the `/restaurants` path from the core `find` [controller](/cms/backend-customization/controllers) without authentication. ### Creating custom routers Creating custom routers consists in creating a file that exports an array of objects, each object being a route with the following parameters: | Parameter | Description | Type | | -------------------------- | -------------------------------------------------------------------------------- | -------- | | `method` | Method associated to the route (i.e. `GET`, `POST`, `PUT`, `DELETE` or `PATCH`) | `String` | | `path` | Path to reach, starting with a forward-leading slash (e.g. `/articles`)| `String` | | `handler` | Function to execute when the route is reached.
Should follow this syntax: `.` | `String` | | `config`

_Optional_ | Configuration to handle [policies](#policies), [middlewares](#middlewares) and [public availability](#public-routes) for the route

| `Object` |
Dynamic routes can be created using parameters and regular expressions. These parameters will be exposed in the `ctx.params` object. For more details, please refer to the
## Configuration Both [core routers](#configuring-core-routers) and [custom routers](#creating-custom-routers) have the same configuration options. The routes configuration is defined in a `config` object that can be used to handle [policies](#policies) and [middlewares](#middlewares) or to [make the route public](#public-routes). ### Policies [Policies](/cms/backend-customization/policies) can be added to a route configuration: - by pointing to a policy registered in `./src/policies`, with or without passing a custom configuration - or by declaring the policy implementation directly, as a function that takes `policyContext` to extend ### Middlewares [Middlewares](/cms/backend-customization/middlewares) can be added to a route configuration: - by pointing to a middleware registered in `./src/middlewares`, with or without passing a custom configuration - or by declaring the middleware implementation directly, as a function that takes ### Public routes By default, routes are protected by Strapi's authentication system, which is based on [API tokens](/cms/features/api-tokens) or on the use of the [Users & Permissions plugin](/cms/features/users-permissions). In some scenarios, it can be useful to have a route publicly available and control the access outside of the normal Strapi authentication system. This can be achieved by setting the `auth` configuration parameter of a route to `false`: # Services Source: //cms/backend-customization/services # Services Services are a set of reusable functions. They are particularly useful to respect the "don’t repeat yourself" (DRY) programming concept and to simplify [controllers](/cms/backend-customization/controllers.md) logic.
Simplified Strapi backend diagram with services highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with services highlighted. The backend customization introduction page includes a complete, interactive diagram.
## Implementation Services can be [generated or added manually](#adding-a-new-service). Strapi provides a `createCoreService` factory function that automatically generates core services and allows building custom ones or [extend or replace the generated services](#extending-core-services). ### Adding a new service A new service can be implemented: - with the [interactive CLI command `strapi generate`](/cms/cli#strapi-generate) - or manually by creating a JavaScript file in the appropriate folder (see [project structure](/cms/project-structure.md)): - `./src/api/[api-name]/services/` for API services - or `./src/plugins/[plugin-name]/services/` for [plugin services](/cms/plugins-development/server-api#services). To manually create a service, export a factory function that returns the service implementation (i.e. an object with methods). This factory function receives the `strapi` instance: :::strapi Document Service API To get started creating your own services, see Strapi's built-in functions in the [Document Service API](/cms/api/document-service) documentation. :::
Example of a custom email service (using Nodemailer) The goal of a service is to store reusable functions. A `sendNewsletter` service could be useful to send emails from different functions in our codebase that have a specific purpose: The service is now available through the `strapi.service('api::restaurant.restaurant').sendNewsletter(...args)` global variable. It can be used in another part of the codebase, like in the following controller:
:::note When a new [content-type](/cms/backend-customization/models.md#content-types) is created, Strapi builds a generic service with placeholder code, ready to be customized. ::: ### Extending core services Core services are created for each content-type and could be used by [controllers](/cms/backend-customization/controllers.md) to execute reusable logic through a Strapi project. Core services can be customized to implement your own logic. The following code examples should help you get started. :::tip A core service can be replaced entirely by [creating a custom service](#adding-a-new-service) and naming it the same as the core service (e.g. `find`, `findOne`, `create`, `update`, or `delete`). :::
Collection type examples
Single type examples
## Usage Once a service is created, it's accessible from [controllers](/cms/backend-customization/controllers.md) or from other services: ```js // access an API service strapi.service('api::apiName.serviceName').FunctionName(); // access a plugin service strapi.service('plugin::pluginName.serviceName').FunctionName(); ``` In the syntax examples above, `serviceName` is the name of the service file for API services or the name used to export the service file to `services/index.js` for plugin services. :::tip To list all the available services, run `yarn strapi services:list`. ::: # Webhooks Source: //cms/backend-customization/webhooks # Webhooks Webhook is a construct used by an application to notify other applications that an event occurred. More precisely, webhook is a user-defined HTTP callback. Using a webhook is a good way to tell third-party providers to start some processing (CI, build, deployment ...). The way a webhook works is by delivering information to a receiving application through HTTP requests (typically POST requests). ## User content-type webhooks To prevent from unintentionally sending any user's information to other applications, Webhooks will not work for the User content-type. If you need to notify other applications about changes in the Users collection, you can do so by creating [Lifecycle hooks](/cms/backend-customization/models#lifecycle-hooks) using the `./src/index.js` example. ## Available configurations You can set webhook configurations inside the file `./config/server`. - `webhooks` - `defaultHeaders`: You can set default headers to use for your webhook requests. This option is overwritten by the headers set in the webhook itself. **Example configuration** ## Securing your webhooks Most of the time, webhooks make requests to public URLs, therefore it is possible that someone may find that URL and send it wrong information. To prevent this from happening you can send a header with an authentication token. Using the Admin panel you would have to do it for every webhook. Another way is to define `defaultHeaders` to add to every webhook request. You can configure these global headers by updating the file at `./config/server`: If you are developing the webhook handler yourself you can now verify the token by reading the headers. ## Available events By default Strapi webhooks can be triggered by the following events: | Name | Description | | ----------------- | ----------------------------------------------------- | | [`entry.create`](#entrycreate) | Triggered when a Content Type entry is created. | | [`entry.update`](#entryupdate) | Triggered when a Content Type entry is updated. | | [`entry.delete`](#entrydelete) | Triggered when a Content Type entry is deleted. | | [`entry.publish`](#entrypublish) | Triggered when a Content Type entry is published.\* | | [`entry.unpublish`](#entryunpublish) | Triggered when a Content Type entry is unpublished.\* | | [`media.create`](#mediacreate) | Triggered when a media is created. | | [`media.update`](#mediaupdate) | Triggered when a media is updated. | | [`media.delete`](#mediadelete) | Triggered when a media is deleted. | | [`review-workflows.updateEntryStage`](#review-workflowsupdateentrystage) | Triggered when content is moved between review stages (see [review workflows](/cms/features/review-workflows#configuration)).
This event is only available with the edition of Strapi. | | [`releases.publish`](#releases-publish) | Triggered when a Release is published (see [Releases](/cms/features/releases)).
This event is only available with the or plan of Strapi CMS. | \*only when `draftAndPublish` is enabled on this Content Type. ## Payloads :::tip NOTE Private fields and s are not sent in the payload. ::: ### Headers When a payload is delivered to your webhook's URL, it will contain specific headers: | Header | Description | | ---------------- | ------------------------------------------ | | `X-Strapi-Event` | Name of the event type that was triggered. | ### `entry.create` This event is triggered when a new entry is created. **Example payload** ```json { "event": "entry.create", "createdAt": "2020-01-10T08:47:36.649Z", "model": "address", "entry": { "id": 1, "geolocation": {}, "city": "Paris", "postal_code": null, "category": null, "full_name": "Paris", "createdAt": "2020-01-10T08:47:36.264Z", "updatedAt": "2020-01-10T08:47:36.264Z", "cover": null, "images": [] } } ``` ### `entry.update` This event is triggered when an entry is updated. **Example payload** ```json { "event": "entry.update", "createdAt": "2020-01-10T08:58:26.563Z", "model": "address", "entry": { "id": 1, "geolocation": {}, "city": "Paris", "postal_code": null, "category": null, "full_name": "Paris", "createdAt": "2020-01-10T08:47:36.264Z", "updatedAt": "2020-01-10T08:58:26.210Z", "cover": null, "images": [] } } ``` ### `entry.delete` This event is triggered when an entry is deleted. **Example payload** ```json { "event": "entry.delete", "createdAt": "2020-01-10T08:59:35.796Z", "model": "address", "entry": { "id": 1, "geolocation": {}, "city": "Paris", "postal_code": null, "category": null, "full_name": "Paris", "createdAt": "2020-01-10T08:47:36.264Z", "updatedAt": "2020-01-10T08:58:26.210Z", "cover": null, "images": [] } } ``` ### `entry.publish` This event is triggered when an entry is published. **Example payload** ```json { "event": "entry.publish", "createdAt": "2020-01-10T08:59:35.796Z", "model": "address", "entry": { "id": 1, "geolocation": {}, "city": "Paris", "postal_code": null, "category": null, "full_name": "Paris", "createdAt": "2020-01-10T08:47:36.264Z", "updatedAt": "2020-01-10T08:58:26.210Z", "publishedAt": "2020-08-29T14:20:12.134Z", "cover": null, "images": [] } } ``` ### `entry.unpublish` This event is triggered when an entry is unpublished. **Example payload** ```json { "event": "entry.unpublish", "createdAt": "2020-01-10T08:59:35.796Z", "model": "address", "entry": { "id": 1, "geolocation": {}, "city": "Paris", "postal_code": null, "category": null, "full_name": "Paris", "createdAt": "2020-01-10T08:47:36.264Z", "updatedAt": "2020-01-10T08:58:26.210Z", "publishedAt": null, "cover": null, "images": [] } } ``` ### `media.create` This event is triggered when you upload a file on entry creation or through the media interface. **Example payload** ```json { "event": "media.create", "createdAt": "2020-01-10T10:58:41.115Z", "media": { "id": 1, "name": "image.png", "hash": "353fc98a19e44da9acf61d71b11895f9", "sha256": "huGUaFJhmcZRHLcxeQNKblh53vtSUXYaB16WSOe0Bdc", "ext": ".png", "mime": "image/png", "size": 228.19, "url": "/uploads/353fc98a19e44da9acf61d71b11895f9.png", "provider": "local", "provider_metadata": null, "createdAt": "2020-01-10T10:58:41.095Z", "updatedAt": "2020-01-10T10:58:41.095Z", "related": [] } } ``` ### `media.update` This event is triggered when you replace a media or update the metadata of a media through the media interface. **Example payload** ```json { "event": "media.update", "createdAt": "2020-01-10T10:58:41.115Z", "media": { "id": 1, "name": "image.png", "hash": "353fc98a19e44da9acf61d71b11895f9", "sha256": "huGUaFJhmcZRHLcxeQNKblh53vtSUXYaB16WSOe0Bdc", "ext": ".png", "mime": "image/png", "size": 228.19, "url": "/uploads/353fc98a19e44da9acf61d71b11895f9.png", "provider": "local", "provider_metadata": null, "createdAt": "2020-01-10T10:58:41.095Z", "updatedAt": "2020-01-10T10:58:41.095Z", "related": [] } } ``` ### `media.delete` This event is triggered only when you delete a media through the media interface. **Example payload** ```json { "event": "media.delete", "createdAt": "2020-01-10T11:02:46.232Z", "media": { "id": 11, "name": "photo.png", "hash": "43761478513a4c47a5fd4a03178cfccb", "sha256": "HrpDOKLFoSocilA6B0_icA9XXTSPR9heekt2SsHTZZE", "ext": ".png", "mime": "image/png", "size": 4947.76, "url": "/uploads/43761478513a4c47a5fd4a03178cfccb.png", "provider": "local", "provider_metadata": null, "createdAt": "2020-01-07T19:34:32.168Z", "updatedAt": "2020-01-07T19:34:32.168Z", "related": [] } } ``` ### `review-workflows.updateEntryStage` This event is only available with the plan of Strapi.
The event is triggered when content is moved to a new review stage (see [Review Workflows](/cms/features/review-workflows#configuration)). **Example payload** ```json { "event": "review-workflows.updateEntryStage", "createdAt": "2023-06-26T15:46:35.664Z", "model": "model", "uid": "uid", "entity": { "id": 2 }, "workflow": { "id": 1, "stages": { "from": { "id": 1, "name": "Stage 1" }, "to": { "id": 2, "name": "Stage 2" } } } } ``` ### `releases.publish` {#releases-publish} The event is triggered when a [release](/cms/features/releases) is published. **Example payload** ```json { "event": "releases.publish", "createdAt": "2024-02-21T16:45:36.877Z", "isPublished": true, "release": { "id": 2, "name": "Fall Winter highlights", "releasedAt": "2024-02-21T16:45:36.873Z", "scheduledAt": null, "timezone": null, "createdAt": "2024-02-21T15:16:22.555Z", "updatedAt": "2024-02-21T16:45:36.875Z", "actions": { "count": 1 } } } ``` ## Best practices for webhook handling - Validate incoming requests by checking headers and payload signatures. - Implement retries for failed webhook requests to handle transient errors. - Log webhook events for debugging and monitoring. - Use secure, HTTPS endpoints for receiving webhooks. - Set up rate limiting to avoid being overwhelmed by multiple webhook requests. # Command Line Interface Source: //cms/cli # Command Line Interface (CLI) Strapi comes with a full featured Command Line Interface (CLI) which lets you scaffold and manage your project in seconds. The CLI works with both the `yarn` and `npm` package managers. :::caution Interactive commands such as `strapi admin:create-user` don't display prompts with `npm`. A fix for the `npm` package manager is anticipated by March 2023. In the meantime, consider using the `yarn` package manager. ::: :::note It is recommended to install Strapi locally only, which requires prefixing all of the following `strapi` commands with the package manager used for the project setup (e.g `npm run strapi help` or `yarn strapi help`) or a dedicated node package executor (e.g. `npx strapi help`). To pass options with `npm` use the syntax: `npm run strapi -- --
### `compression` The `compression` middleware is based on
### `cors` This security middleware is about cross-origin resource sharing (CORS) and is based on
Example: Custom configuration for the cors middleware within a function as parameter `origin` can take a Function as parameter following this signature ```ts title="./config/middlewares.ts" // ... { name: 'strapi::cors', config: { origin: (ctx): string | string[] => { const origin = ctx.request.header.origin; if (origin === 'http://localhost:3000') { return origin; // The returns will be part of the Access-Control-Allow-Origin header } return ''; // Fail cors check } }, }, // ... ] ```
### `errors` The errors middleware handles [errors](/cms/error-handling.md) thrown by the code. Based on the type of error it sets the appropriate HTTP status to the response. By default, any error not supposed to be exposed to the end user will result in a 500 HTTP response. The middleware doesn't have any configuration options. ### `favicon` The `favicon` middleware serves the favicon and is based on
#### `ip` The `ip` middleware is an IP filter middleware based on
### `logger` The `logger` middleware is used to log requests. To define a custom configuration for the `logger` middleware, create a dedicated configuration file (`./config/logger.js`). It should export an object that must be a complete or partial
### `poweredBy` The `poweredBy` middleware adds a `X-Powered-By` parameter to the response header. It accepts the following options: | Option | Description | Type | Default value | |-------------|------------------------------------|----------|------------------------| | `poweredBy` | Value of the `X-Powered-By` header | `String` | `'Strapi '` |
details Example: Custom configuration for the poweredBy middleware
### `query` The `query` middleware is a query parser based on
### `response-time` The `response-time` middleware enables the `X-Response-Time` (in milliseconds) for the response header. The middleware doesn't have any configuration options. ### `public` The `public` middleware is a static file serving middleware, based on
### `security` The security middleware is based on
### `session` The `session` middleware allows the use of cookie-based sessions, based on
# Plugins configuration Source: //cms/configurations/plugins # Plugins configuration Plugin configurations are stored in `/config/plugins.js|ts` (see [project structure](/cms/project-structure)). Each plugin can be configured with the following available parameters: | Parameter | Description | Type | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `enabled` | Enable (`true`) or disable (`false`) an installed plugin | Boolean | | `config`

_Optional_ | Used to override default plugin configuration ([defined in strapi-server.js](/cms/plugins-development/server-api#configuration)) | Object | | `resolve`
_Optional, only required for local plugins_ | Path to the plugin's folder | String | :::note Some features of Strapi are provided by plugins and the following plugins can also have specific configuration options: the [GraphQL](/cms/plugins/graphql#code-based-configuration) plugin and the [Upload](/cms/features/media-library#available-options) package which powers the Media Library. ::: **Basic example custom configuration for plugins:** :::tip If no specific configuration is required, a plugin can also be declared with the shorthand syntax `'plugin-name': true`. ::: # Server configuration Source: //cms/configurations/server # Server configuration The `/config/server.js` file is used to define the server configuration for a Strapi application. :::caution Changes to the `server.js` file require rebuilding the admin panel. After saving the modified file run either `yarn build` or `npm run build` in the terminal to implement the changes. ::: ## Available options The `./config/server.js` file can include the following parameters: | Parameter | Description | Type | Default | | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------- | | `host`

❗️ _Mandatory_ | Host name | string | `localhost` | | `port`

❗️ _Mandatory_ | Port on which the server should be running. | integer | `1337` | | `app.keys`

❗️ _Mandatory_ | Declare session keys (based on # TypeScript configuration Source: //cms/configurations/typescript # TypeScript configuration [TypeScript](/cms/typescript)-enabled Strapi projects have a specific project structure and handle TypeScript project configuration through [`tsconfig.json` files](#project-structure-and-typescript-specific-configuration-files). Strapi also has dedicated TypeScript features that are configured [in the `config/typescript.js|ts` file](#strapi-specific-configuration-for-typescript). ## Project structure and TypeScript-specific configuration files TypeScript-enabled Strapi applications have a specific [project structure](/cms/project-structure) with the following dedicated folders and configuration files: | TypeScript-specific directories and files | Location | Purpose | | ----------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `./dist` directory | application root | Adds the location for compiling the project JavaScript source code. | | `build` directory | `./dist` | Contains the compiled administration panel JavaScript source code. The directory is created on the first `yarn build` or `npm run build` command | | `tsconfig.json` file | application root | Manages TypeScript compilation for the server. | | `tsconfig.json` file | `./src/admin/` | Manages TypeScript compilation for the admin panel. | ## Strapi-specific configuration for TypeScript :::caution 🚧 This feature is considered experimental. These settings are considered experimental and might have issues or break some features. ::: Types generated by Strapi are based on the user project structure. Once the type definitions are emitted into their dedicated files, Strapi reads the type definitions to adapt the autocompletion results accordingly. To avoid having to [manually generate types](/cms/typescript/development#generate-typings-for-content-types-schemas) every time the server restarts, an optional `config/typescript.js|ts` configuration file can be added, which currently accepts only one parameter: | Parameter | Description | Type | Default | | -------------- | -------------------------------------------------------------- | --------- | ------- | | `autogenerate` | Enable or disable automatic types generation on server restart | `Boolean` | `false` | **Example:** # Customization Source: //cms/customization # Customization Strapi includes 2 main components: - The back-end part of Strapi is a **server** that receives requests and handles them to return responses that can surface the data you built and saved through the Content-Type Builder and Content Manager. The backend server is described in more details in the [Backend Customization introduction](/cms/backend-customization). Most of the parts of the backend server can be customized. - The front-end, user-facing part of Strapi is called the **admin panel**. The admin panel is the graphical user interface (GUI) that you use to build a content structure, create and manage content, and perform various other actions that can be managed by built-in or 3rd-party plugins. Some parts of the admin panel can be customized. From a bigger picture, this is how Strapi integrates in a typical, generic setup: Strapi includes 2 parts, a back-end server and an admin panel, and interact with a database (which stores data) and an external, front-end application that displays your data. Both parts of Strapi can be customized to some extent. :::info Customizing the database or the external, front-end application are outside of the scope of the present documentation section. - You can learn more about databases usage with Strapi by reading the installation documentation, which lists [supported databases](/cms/installation/cli#preparing-the-installation), and the configuration documentation, which describes how to [configure a database](/cms/configurations/database) with your project. - You can learn more about how external front-end applications can interact with Strapi by reading the Strapi's . ::: # Database migrations Source: //cms/database-migrations # Database migrations Database migrations exist to run one-time queries against the database, typically to modify the tables structure or the data when upgrading the Strapi application. These migrations are run automatically when the application starts and are executed before the automated schema migrations that Strapi also performs on boot. :::callout 🚧 Experimental feature Database migrations are experimental. This feature is still a work in progress and will continue to be updated and improved. In the meantime, feel free to ask for help on the Additionally, if you want to continue using existing JavaScript migrations alongside TypeScript migrations, you can set `allowJs: true` in your `tsconfig.json` file's compiler options, as mentioned in the [database configuration documentation](/cms/configurations/database#settings-configuration-object). # Database transactions Source: //cms/database-transactions # Database transactions :::caution This is an experimental feature and is subject to change in future versions. ::: Strapi 5 provide an API to wrap a set of operations in a transaction that ensures the integrity of data. Transactions are a set of operations that are executed together as a single unit. If any of the operations fail, the entire transaction fails and the data is rolled back to its previous state. If all operations succeed, the transaction is committed and the data is permanently saved to the database. ## Usage Transactions are handled by passing a handler function into `strapi.db.transaction`: ```js await strapi.db.transaction(async ({ trx, rollback, commit, onCommit, onRollback }) => { // It will implicitly use the transaction await strapi.entityService.create(); await strapi.entityService.create(); }); ``` After the transaction handler is executed, the transaction is committed if all operations succeed. If any of the operations throws, the transaction is rolled back and the data is restored to its previous state. :::note Every `strapi.entityService` or `strapi.db.query` operation performed in a transaction block will implicitly use the transaction. ::: ### Transaction handler properties The handler function receives an object with the following properties: | Property | Description | | ------------ | ------------------------------------------------------------------------------------------- | | `trx` | The transaction object. It can be used to perform knex queries within the transaction. | | `commit` | Function to commit the transaction. | | `rollback` | Function to rollback the transaction. | | `onCommit` | Function to register a callback that will be executed after the transaction is committed. | | `onRollback` | Function to register a callback that will be executed after the transaction is rolled back. | ### Nested transactions Transactions can be nested. When a transaction is nested, the inner transaction is committed or rolled back when the outer transaction is committed or rolled back. ```js await strapi.db.transaction(async () => { // It will implicitly use the transaction await strapi.entityService.create(); // Nested transactions will implicitly use the outer transaction await strapi.db.transaction(async ({}) => { await strapi.entityService.create(); }); }); ``` ### onCommit and onRollback The `onCommit` and `onRollback` hooks can be used to execute code after the transaction is committed or rolled back. ```js await strapi.db.transaction(async ({ onCommit, onRollback }) => { // It will implicitly use the transaction await strapi.entityService.create(); await strapi.entityService.create(); onCommit(() => { // This will be executed after the transaction is committed }); onRollback(() => { // This will be executed after the transaction is rolled back }); }); ``` ### Using knex queries Transactions can also be used with knex queries, but in those cases `.transacting(trx)` must be explicitly called. ```js await strapi.db.transaction(async ({ trx, rollback, commit }) => { await knex('users').where('id', 1).update({ name: 'foo' }).transacting(trx); }); ``` ## When to use transactions Transactions should be used in cases where multiple operations should be executed together and their execution is dependent on each other. For example, when creating a new user, the user should be created in the database and a welcome email should be sent to the user. If the email fails to send, the user should not be created in the database. ## When not to use transactions Transactions should not be used for operations that are not dependent on each other since it can result in performance penalties. ## Potential problems of transactions Performing multiple operations within a transaction can lead to locking, which can block the execution of transactions from other processes until the original transaction is complete. Furthermore, transactions can stall if they are not committed or rolled back appropriately. For example, if a transaction is opened but there is a path in your code that does not close it, the transaction will be left open indefinitely and could cause instability until your server is restarted and the connection is forced to close. These issues can be difficult to debug, so use transactions with care in the cases they are necessary. # Deployment Source: //cms/deployment # Deployment Strapi provides many deployment options for your project or application. Your Strapi applications can be deployed on traditional hosting servers or your preferred hosting provider. The following documentation covers the basics of how to prepare Strapi for deployment on with several common hosting options. :::strapi Strapi Cloud You can use [Strapi Cloud](/cloud/intro) to quickly deploy and host your project. ::: :::tip If you already created a content structure with the Content-Type Builder and added some data through the Content Manager to your local (development) Strapi instance, you can leverage the [data management system](/cms/features/data-management) to transfer data from a Strapi instance to another one. Another possible workflow is to first create the content structure locally, push your project to a git-based repository, deploy the changes to production, and only then add content to the production instance. ::: ## General guidelines ### Hardware and software requirements To provide the best possible environment for Strapi the following requirements apply to development (local) and staging and production workflows. Run the server with the `production` settings: :::caution We highly recommend using to manage your process. ::: If you need a server.js file to be able to run `node server.js` instead of `npm run start` then create a `./server.js` file as follows: ```js title="path: ./server.js" const strapi = require('@strapi/strapi'); strapi.createStrapi(/* {...} */).start(); ``` :::caution If you are developing a `TypeScript`-based project you must provide the `distDir` option to start the server. For more information, consult the [TypeScript documentation](/cms/typescript/development#use-the-createstrapi-factory). ::: ### Advanced configurations If you want to host the administration on another server than the API, [please take a look at this dedicated section](/cms/configurations/admin-panel#deploy-on-different-servers). ## Additional resources :::prerequisites * Your Strapi project is [created](/cms/installation) and its code is hosted on GitHub. * You have read the [general deployment guidelines](/cms/deployment#general-guidelines). ::: The of the Strapi website include information on how to integrate Strapi with many resources, including how to deploy Strapi on the following 3rd-party platforms:
In addition, community-maintained guides for additional providers are available in the . This includes the following guides:
The following external guide(s), not officially maintained by Strapi, might also help deploy Strapi on various environments: :::strapi Multi-tenancy If you're looking for multi-tenancy options, the Strapi Blog has a . ::: # Error handling Source: //cms/error-handling # Error handling Strapi is natively handling errors with a standard format. There are 2 use cases for error handling: - As a developer querying content through the [REST](/cms/api/rest) or [GraphQL](/cms/api/graphql) APIs, you might [receive errors](#receiving-errors) in response to the requests. - As a developer customizing the backend of your Strapi application, you could use controllers and services to [throw errors](#throwing-errors). ## Receiving errors Errors are included in the response object with the `error` key and include information such as the HTTP status code, the name of the error, and additional information. ### REST errors Errors thrown by the REST API are included in the [response](/cms/api/rest#requests) that has the following format: ```json { "data": null, "error": { "status": "", // HTTP status "name": "", // Strapi error name ('ApplicationError' or 'ValidationError') "message": "", // A human readable error message "details": { // error info specific to the error type } } } ``` ### GraphQL errors Errors thrown by the GraphQL API are included in the response that has the following format: ```json { "errors": [ { "message": "", // A human reable error message "extensions": { "error": { "name": "", // Strapi error name ('ApplicationError' or 'ValidationError'), "message": "", // A human reable error message (same one as above); "details": {}, // Error info specific to the error type }, "code": "" // GraphQL error code (ex: BAD_USER_INPUT) } } ], "data": { "graphQLQueryName": null } } ``` ## Throwing errors
### Controllers and middlewares The recommended way to throw errors when developing any custom logic with Strapi is to have the [controller](/cms/backend-customization/controllers) or [middleware](/cms/backend-customization/middlewares) respond with the correct status and body. This can be done by calling an error function on the context (i.e. `ctx`). Available error functions are listed in the ### Services and models lifecycles Once you are working at a deeper layer than the controllers or middlewares there are dedicated error classes that can be used to throw errors. These classes are extensions of #### Example: Throwing an error in a model lifecycle** This example shows building a [custom model lifecycle](/cms/backend-customization/models#lifecycle-hooks) and being able to throw an error that stops the request and will return proper error messages to the admin panel. Generally you should only throw an error in `beforeX` lifecycles, not `afterX` lifecycles. ### Policies [Policies](/cms/backend-customization/policies) are a special type of middleware that are executed before a controller. They are used to check if the user is allowed to perform the action or not. If the user is not allowed to perform the action and a `return false` is used then a generic error will be thrown. As an alternative, you can throw a custom error message using a nested class extensions from the Strapi `ForbiddenError` class, `ApplicationError` class (see [Default error classes](#default-error-classes) for both classes), and finally the ### Default error classes The default error classes are available from the `@strapi/utils` package and can be imported and used in your code. Any of the default error classes can be extended to create a custom error class. The custom error class can then be used in your code to throw errors. ### Deleting content You can delete content by deleting any entry of a collection type, or the default entry of a single type. 1. In the edit view of the entry, click on at the top right of the interface, and click the **Delete document** button.
If Internationalization is enabled for the content-type, you can also choose to delete only the currently selected locale by clicking on the **Delete locale** button. 2. In the window that pops up, click on the **Confirm** button to confirm the deletion. :::tip You can delete entries from the list view of a collection type, by clicking on on the right side of the entry's record in the table, then choosing the **Delete document** button.
If [Internationalization](/cms/features/internationalization) is enabled for the content-type, **Delete document** deletes all locales while **Delete locale** only deletes the currently listed locale. # Content-type Builder Source: //cms/features/content-type-builder # Content-type Builder From the ## Overview 3. Click the **Finish** button in the dialog. 4. Click the **Save** button in the Content-Type Builder navigation. #### Fields From the table that lists the fields of your content-type, you can: - Click on the #### Rich Text (Blocks) {#rich-text-blocks} The Rich Text (Blocks) field displays an editor with live rendering and various options to manage rich text. This field can be used for long written content, even including images and code. :::strapi React renderer If using the Blocks editor, we recommend that you also use the #### Date {#date} The Date field can display a date (year, month, day), time (hour, minute, second) or datetime (year, month, day, hour, minute, and second) picker. #### Password The Password field displays a password field that is encrypted. #### Media {#media} The Media field allows to choose one or more media files (e.g. image, video) from those uploaded in the Media Library of the application. #### Relation {#relation} The Relation field allows to establish a relation with another content-type, that must be a collection type. There are 6 different types of relations: - One way: Content-type A *has one* Content-type B - One-to-one: Content-type A *has and belong to one* Content-type B - One-to-many: Content-type A *belongs to many* Content-type B - Many-to-one: Content-type B *has many* Content-type A - Many-to-many: Content-type A *has and belongs to many* Content-type B - Many way: Content-type A *has many* Content-type B #### Boolean {#boolean} The Boolean field displays a toggle button to manage boolean values (e.g. Yes or No, 1 or 0, True or False). #### JSON {#json} The JSON field allows to configure data in a JSON format, to store JSON objects or arrays. #### Email {#email} The Email field displays an email address field with format validation to ensure the email address is valid. #### Password {#password} The Password field displays a password field that is encrypted. #### Enumeration {#enum} The Enumeration field allows to configure a list of values displayed in a drop-down list. :::caution Enumeration values should always have an alphabetical character preceding any number as it could otherwise cause the server to crash without notice when the GraphQL plugin is installed. ::: #### UID {#uid} The UID field displays a field that sets a unique identifier, optionally based on an existing other field from the same content-type. :::tip The UID field can be used to create a slug based on the Attached field. ::: #### Rich Text (Markdown) {#rich-text-markdown} The Rich Text (Markdown) field displays an editor with basic formatting options to manage rich text written in Markdown. This field can be used for long written content. #### Components {#components} Components are a combination of several fields. Components allow to create reusable sets of fields, that can be quickly added to content-types, dynamic zones but also nested into other components. When configuring a component through the Content-type Builder, it is possible to either: - create a new component by clicking on *Create a new component* (see [Creating a new component](#new-component)), - or use an existing one by clicking on *Use an existing component*. #### Dynamic zones {#dynamiczones} Dynamic zones are a combination of components that can be added to content-types. They allow a flexible content structure as once in the Content Manager, administrators have the choice of composing and rearranging the components of the dynamic zone how they want. After configuring the settings of the dynamic zone, its components must be configured as well. It is possible to either choose an existing component or create a new one. :::caution When using dynamic zones, different components cannot have the same field name with different types (or with enumeration fields, different values). ::: #### Custom fields [Custom fields](/cms/features/custom-fields) are a way to extend Strapi’s capabilities by adding new types of fields to content-types or components. Once installed (see [Marketplace](/cms/plugins/installing-plugins-via-marketplace) documentation), custom fields are listed in the _Custom_ tab when selecting a field for a content-type. Each custom field type can have basic and advanced settings. The lists available custom fields, and hosts dedicated documentation for each custom field, including specific settings. ### Deleting content-types Content types and components can be deleted through the Content-type Builder. Deleting a content-type automatically deletes all entries from the Content Manager that were based on that content-type. The same goes for the deletion of a component, which is automatically deleted from every content-type or entry where it was used. 1. In the Content-type Builder sub navigation, click on the name of the content-type or component to delete. 2. In the edition interface of the chosen content-type or component, click on the **Edit** button on the right side of the content-type's or component's name. 3. In the edition window, click on the **Delete** button. 4. In the confirmation window, confirm the deletion. 5. Click on the **Save** button in the Content-type Builder sub navigation. :::caution Deleting a content-type only deletes what was created and available from the Content-type Builder, and by extent from the admin panel of your Strapi application. All the data that was created based on that content-type is however kept in the database. For more information, please refer to the related . ::: # Custom Fields Source: //cms/features/custom-fields # Custom Fields Custom fields extend Strapi’s capabilities by adding new types of fields to content-types and components. Once created or added to Strapi via plugins, custom fields can be used in the Content-Type Builder and Content Manager just like built-in fields. ## Configuration Ready-made custom fields can be found on the [Marketplace](https://market.strapi.io/plugins?categories=Custom+fields). Once installed these, no other configuration is required, and you can start using them (see [usage](#usage)). You can also develop your own custom field. ### Developing your own custom field Though the recommended way to add a custom field is through creating a plugin, app-specific custom fields can also be registered within the global `register` [function](/cms/configurations/functions) found in `src/index` and `src/admin/app` files. :::note Current limitations * Custom fields can only be shared and distributed on the Marketplace using plugins. * Custom fields cannot add new data types to Strapi and must use existing, built-in Strapi data types described in the [models' attributes](/cms/backend-customization/models#model-attributes) documentation. * You also cannot modify an existing data type. * Special data types unique to Strapi, such as relation, media, component, or dynamic zone data types, cannot be used in custom fields. ::: :::prerequisites The custom field could also be declared directly within the `strapi-server.js` file if you didn't have the plugin code scaffolded by the CLI generator: #### Registering a custom field in the admin panel :::prerequisites ##### Components `app.customFields.register()` must pass a `components` object with an `Input` React component to use in the Content Manager's edit view. **Example: Registering an Input component:** In the following example, the `color-picker` plugin was created using the CLI generator (see [plugins development](/cms/plugins-development/developing-plugins.md)):
Props passed to the custom field Input component: | Prop | Description | Type | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | `attribute` | The attribute object with custom field's underlying Strapi type and options | `{ type: String, customField: String }` | | `description` | The field description set in [configure the view](/cms/features/content-manager#edit-view-settings) | :::tip For a more detailed view of the props provided to the customFields and how they can be used check out the :::tip The Strapi codebase gives an example of how settings objects can be described: check the file for the `base` settings and the file for the `advanced` settings. The base form lists the settings items inline but the advanced form gets the items from an file. ::: ## Usage
### In the admin panel Custom fields can be added to Strapi either by installing them from the [Marketplace](/cms/plugins/installing-plugins-via-marketplace) or by creating your own. Once added to Strapi, custom fields can be added to any content type. Custom fields are listed in the _Custom_ tab when selecting a field for a content-type. Each custom field type can have basic and advanced settings. The lists available custom fields, and hosts dedicated documentation for each custom field, including specific settings. ### In the code Once created and used, custom fields are defined like any other attribute in the model's schema. Custom fields are explicitly defined in the [attributes](/cms/backend-customization/models#model-attributes) of a model with `type: customField`. As compared to how other types of models are defined, custom fields' attributes also show the following specificities: - Custom field have a `customField` attribute. Its value acts as a unique identifier to indicate which registered custom field should be used, and follows one of these 2 formats: | Format | Origin | |----------------------|------------------| | `plugin::plugin-name.field-name` | The custom field was created through a plugin | | `global::field-name` | The custom field is specific to the current Strapi application and was created directly within the `register` [function](/cms/configurations/functions) | - Custom fields can have additional parameters depending on what has been defined when registering the custom field (see [server registration](#registering-a-custom-field-on-the-server) and [admin panel registration](#registering-a-custom-field-in-the-admin-panel)). **Example: A simple `color` custom field model definition:** ```json title="/src/api/[apiName]/[content-type-name]/content-types/schema.json" { // … "attributes": { "color": { // name of the custom field defined in the Content-Type Builder "type": "customField", "customField": "plugin::color-picker.color", "options": { "format": "hex" } } } // … } ``` # Data Management Source: //cms/features/data-management # Data Management The Data Management feature can be used to import, export, or transfer data. Data Management is CLI-based only, but is partly configured in the admin panel. ## Configuration Some configuration options for the Data Management feature are available in the admin panel, and some are handled via your Strapi project's code. ### Admin panel settings :::prerequisites A `transfer.token.salt` should be defined in the `config/admin` configuration file (see [code-based configuration](#code-based-configuration)). ::: **Path to configure the feature:** ## Usage The Data Management system is CLI-based only, meaning any import, export, or transfer command must be executed from the terminal. Exhaustive documentation for each command is accessible from the following pages: # Draft & Publish Source: //cms/features/draft-and-publish # Draft & Publish The Draft & Publish feature allows to manage drafts for your content. On the back-end server of Strapi, the Document Service API can also be used to interact with localized content: # Email Source: //cms/features/email # Email The Email feature enables Strapi applications to send emails from a server or an external provider. ## Configuration Most configuration options for the Email feature are handled via your Strapi project's code. The Email feature is not configurable in the admin panel, however users can test email delivery if it has been setup by an administrator. ### Admin panel settings **Path to configure the feature:** ##### Configuring providers Newly installed providers are enabled and configured in [the `/config/plugins` file](/cms/configurations/plugins). If this file does not exist you must create it. Each provider will have different configuration settings available. Review the respective entry for that provider in the [Marketplace](/cms/plugins/installing-plugins-via-marketplace) or :::note * When using a different provider per environment, specify the correct configuration in `/config/env/${yourEnvironment}/plugins.js|ts` (See [Environments](/cms/configurations/environment)). * Only one email provider will be active at a time. If the email provider setting isn't picked up by Strapi, verify the `plugins.js|ts` file is in the correct folder. * When testing the new email provider with those two email templates created during strapi setup, the _shipper email_ on the template defaults to `no-reply@strapi.io` and needs to be updated according to your email provider, otherwise it will fail the test (See [Configure templates locally](/cms/features/users-permissions#templating-emails)). ::: ###### Configuration per environment When configuring your provider you might want to change the configuration based on the `NODE_ENV` environment variable or use environment specific credentials. You can set a specific configuration in the `/config/env/{env}/plugins.js|ts` configuration file and it will be used to overwrite the default configuration. ##### Creating providers To implement your own custom provider you must In the send function you will have access to: * `providerOptions` that contains configurations written in `plugins.js|ts` * `settings` that contains configurations written in `plugins.js|ts` * `options` that contains options you send when you call the send function from the email plugin service You can review the # Internationalization Source: //cms/features/internationalization # Internationalization (i18n) The Internationalization feature allows to manage content in different languages, called "locales". On the back-end server of Strapi, the Document Service API can also be used to interact with localized content: # Media Library Source: //cms/features/media-library # Media Library The :::info Code-based configuration instructions on the present page detail options for the default upload provider. If using another provider, please refer to the available configuration parameters in that provider's documentation. ::: #### Available options When using the default upload provider, the following specific configuration options can be declared in an `upload.config` object within [the `config/plugins` file](/cms/configurations/plugins). All parameters are optional: | Parameter | Description | Type | Default | | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------- | ------- | | `providerOptions.localServer` | Options that will be passed to #### Local server By default Strapi accepts `localServer` configurations for locally uploaded files. These will be passed as the options for #### Max file size The Strapi middleware in charge of parsing requests needs to be configured to support file sizes larger than the default of 200MB. This must be done in addition to provider options passed to the Upload package for `sizeLimit`. :::caution You may also need to adjust any upstream proxies, load balancers, or firewalls to allow for larger file sizes. For instance, In addition to the middleware configuration, you can pass the `sizeLimit`, which is an integer in bytes, in the [/config/plugins file](/cms/configurations/plugins): #### Upload request timeout By default, the value of `strapi.server.httpServer.requestTimeout` is set to 330 seconds. This includes uploads. To make it possible for users with slow internet connection to upload large files, it might be required to increase this timeout limit. The recommended way to do it is by setting the `http.serverOptions.requestTimeout` parameter in [the `config/servers` file](/cms/configurations/server). An alternate method is to set the `requestTimeout` value in [the `bootstrap` function](/cms/configurations/functions#bootstrap) that runs before Strapi gets started. This is useful in cases where it needs to change programmatically—for example, to temporarily disable and re-enable it: #### Responsive Images When the [`Responsive friendly upload` admin panel setting](#admin-panel-configuration) is enabled, the plugin will generate the following responsive image sizes: | Name | Largest dimension | | :------ | :--------- | | large | 1000px | | medium | 750px | | small | 500px | These sizes can be overridden in `/config/plugins`: :::caution Breakpoint changes will only apply to new images, existing images will not be resized or have new sizes generated. ::: ## Usage **Path to use the feature:** ### Use public assets in your code {#public-assets} Public assets are static files (e.g., images, video, CSS files, etc.) that you want to make accessible to the outside world. Because an API may need to serve static assets, every new Strapi project includes by default a folder named `/public`. Any file located in this directory is accessible if the request's path doesn't match any other defined route and if it matches a public file name (e.g. an image named `company-logo.png` in `./public/` is accessible through `/company-logo.png` URL). :::tip `index.html` files are served if the request corresponds to a folder name (`/pictures` url will try to serve `public/pictures/index.html` file). ::: :::caution The dotfiles are not exposed. It means that every file name that starts with `.`, such as `.htaccess` or `.gitignore`, are not served. ::: # Preview Source: //cms/features/preview # Preview With the Preview feature, you can preview your front end application directly from Strapi's admin panel. This is helpful to see how updates to your content in the Edit View of the Content Manager will affect the final result.
Caching in Next.js: In Next.js, [cache persistence](https://nextjs.org/docs/app/building-your-application/caching) may require additional steps. You might need to invalidate the cache by making an API call from the client side to the server, where the revalidation logic will be handled. Please refer to Next.js documentation for details, for instance with the [revalidatePath() method](https://nextjs.org/docs/app/building-your-application/caching#revalidatepath).
#### [Front end] Next steps Once the preview system is set up, you need to adapt your data fetching logic to handle draft content appropriately. This involves the following steps: 1. Create or adapt your data fetching utility to check if draft mode is enabled 2. Update your API calls to include the draft status parameter when appropriate The following, taken from the
Caching in Next.js: In Next.js, [cache persistence](https://nextjs.org/docs/app/building-your-application/caching) may require additional steps. You might need to invalidate the cache by making an API call from the client side to the server, where the revalidation logic will be handled. Please refer to Next.js documentation for details, for instance with the [revalidatePath() method](https://nextjs.org/docs/app/building-your-application/caching#revalidatepath).
#### [Front end] Next steps Once the preview system is set up, you need to adapt your data fetching logic to handle draft content appropriately. This involves the following steps: 1. Create or adapt your data fetching utility to check if draft mode is enabled 2. Update your API calls to include the draft status parameter when appropriate The following, taken from the Strapi demo application, is an example of how to implement draft-aware data fetching in your Next.js front-end application: ```typescript {8-18} contentType: string, params: Record = {} ): Promise { // Check if Next.js draft mode is enabled const { isEnabled: isDraftMode } = draftMode(); try { const queryParams = { ...params }; // Add status=draft parameter when draft mode is enabled if (isDraftMode) { queryParams.status = "draft"; } const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`; const response = await fetch(url); if (!response.ok) { throw new Error( `Failed to fetch data from Strapi (url=${url}, status=${response.status})` ); } return await response.json(); } catch (error) { console.error("Error fetching content:", error); throw error; } } ``` This utility method can then be used in your page components to fetch either draft or published content based on the preview state: ```typescript // In your page component: const pageData = await fetchContentType('api::page.page', { // Your other query parameters }); ``` ## Usage **Path to use the feature:** Content Manager, edit view of your content type :::strapi Preview vs. Live Preview Based on your CMS plan, your experience with Preview will be different: - With the Free plan, Preview will be full screen only. - With the and plans, you get access to Live Preview. With Live Preview, you can see the Preview alongside the Edit view of the Content Manager, allowing you to edit your content and previewing it simultaneously. ::: Once the Preview feature is properly set up, an **Open preview** button is visible on the right side of the [Content Manager's edit view](/cms/features/content-manager#overview). Clicking it will display the preview of your content as it will appear in your front-end application, but directly within Strapi's the admin panel. Once the Preview is open, you can: - click the close button in the upper left corner to go back to the Edit View of the Content Manager, - switch between previewing the draft and the published version (if [Draft & Publish](/cms/features/draft-and-publish) is enabled for the content-type), - and click the link icon in the upper right corner to copy the preview link. Depending on the preview tab you are currently viewing, this will either copy the link to the preview of the draft or the published version. Additionally, with Live Preview, you can: - with and plans, expand the side panel by clicking on the button to make the Preview full screen, - and, with the plan, use buttons at the top right of the editor to define the assignee and stage for the [Review Workflows feature](/cms/features/review-workflows) if enabled. :::note In the Edit view of the Content Manager, the Open preview button will be disabled if you have unsaved changes. Save your latest changes and you should be able to preview content again. ::: # Role-Based Access Control (RBAC) Source: //cms/features/rbac # Role-Based Access Control (RBAC) The Role-Based Access Control (RBAC) feature allows the management of the administrators, who are the users of the admin panel. More specifically, RBAC manages the administrators' accounts and roles. 4. Click on the **Save** button on the top right corner. #### Setting custom conditions for permissions For each permission of each category, a ## Usage **Path to use the feature:** *Settings > Administration panel > Users* The *Users* interface displays a table listing all the administrators of your Strapi application. More specifically, for each administrator listed in the table, their main account information are displayed, including name, email and attributed role. The status of their account is also indicated: active or inactive, depending on whether the administrator has already logged in to activate the account or not. From this interface, it is possible to: - make a textual search to find specific administrators, - set filters to find specific administrators, - create a new administrator account (see [Creating a new account](#creating-a-new-account)) , - delete an administrator account (see [Deleting an account](#deleting-an-account)), - or access information regarding an administrator account, and edit it (see [Editing an account](#editing-an-account)). :::tip Sorting can be enabled for most fields displayed in the table. Click on a field name, in the header of the table, to sort on that field. ::: ### Creating a new account 1. Click on the **Invite new user** button. 2. In the *Invite new user* window, fill in the Details information about the new administrator: | User information | Instructions | | ---------------- | ---------------------------------------------------------------------------- | | First name | (mandatory) Write the administrator's first name in the textbox. | | Last name | (mandatory) Write the administrator's last name in the textbox. | | Email | (mandatory) Write the administrator's complete email address in the textbox. | 3. Fill in the Login settings about the new administrator: | Setting | Instructions | | ---------------- | --------------------------------------------------------------------------------------------------------------- | | User's roles | (mandatory) Choose from the drop-down list the role to attribute to the new administrator. | | Connect with SSO | (optional) Click **TRUE** or **FALSE** to connect the new administrator account with SSO. | 4. Click on the **Invite user** button in the bottom right corner of the *Add new user* window. 5. A URL appears at the top of the window: it is the URL to send the new administrator for them to log in for the first time to your Strapi application. Click the copy button to copy the URL. 6. Click on the **Finish** button in the bottom right corner to finish the new administrator account creation. The new administrator should now be listed in the table. :::note The administrator invitation URL is accessible from the administrator's account until it has been activated. ::: ### Deleting an account It is possible to delete one or several administrator accounts at the same time. 1. Click on the delete button on the right side of the account's record, or select one or more accounts by ticking the boxes on the left side of the accounts' records then click on the **Delete** button above the table. 2. In the deletion window, click on the **Confirm** button to confirm the deletion. ### Editing an account 1. Click on the name of the administrator whose account you want to edit. 2. In the *Details* area, edit your chosen account details: | User information | Instructions | | --------------------- | ----------------------- | | First name | Write the administrator's first name in the textbox. | | Last name | Write the administrator's last name in the textbox. | | Email | Write the administrator's complete email address in the textbox. | | Username | Write the administrator's username in the textbox. | | Password | Write the new administrator account's password in the textbox. | | Confirm password | Write the new password in the textbox for confirmation. | | Active | Click on **TRUE** to activate the administrator's account. | 3. (optional) In the *Roles* area, edit the role of the administrator: - Click on the drop-down list to choose a new role, and/or add it to the already attributed one. - Click on the delete button to delete an already attributed role. 4. Click on the **Save** button in the top right corner. # Releases Source: //cms/features/releases # Releases ## Configuration To be able to include your content in releases, and to schedule and publish those releases, you must first create them. You can also modify the releases' default timezone to use when scheduling a publication, as well as deleting releases that are obsolete or irrelevant. ### Choosing default timezone **Path to configure the feature:** Settings 1. Click on the _Default timezone_ dropdown list and choose the default timezone to use. 2. Click **Save**. ### Creating a release **Path to configure the feature:** Releases 1. Click the **New Release** button in the upper right corner. 2. Give the release a name. 3. (_optional_) If you want to schedule the release publication instead of publishing the release manually, check the **Schedule release** checkbox and define the date, time, and timezone for publication. 4. Click the **Continue** button. :::tip Your releases can be renamed afterwards, by editing the release using the then **Edit** buttons. ::: ### Deleting a release **Path:** Releases Deleting a release will only delete the release itself, but not the content-type entries included in the release. 1. Click on the button in the top right corner of the admin panel. 2. Select **Delete**. 3. In the confirmation dialog, click **Confirm**. ## Usage **Path to use the feature:** Releases and Content Manager :::caution Since publishing an entry with a release means turning a draft entry into a published entry, Releases will not work if [Draft & Publish](/cms/features/draft-and-publish) is disabled for the content-type. ::: ### Including content in a release :::prerequisites - Before entries can be added to a release, you must create a release from the Releases page. - Adding content to a release requires the appropriate permissions for the Content-Releases plugin (see [configuring administrator roles](/cms/features/users-permissions)). ::: #### One entry at a time **Path:** Edit view of the Content Manager 1. Click on in the _Entry_ area on the right side of the interface. 2. In the list, click on the **Add to release** button. 2. Select which release to add this entry to. 3. Click on the **Publish** or **Unpublish** button depending on whether you want the entry to be published or unpublished when the release itself is published, then click **Continue**. The *Releases* box on the right should show which release(s) the entry is included in. :::info If [Releases scheduling](/cms/features/releases#scheduling-a-release) is enabled and the entry is added to a scheduled release, the release date and time will also be displayed. ::: #### Multiple entries at a time **Path:** List view of the Content Manager 1. Select which entries you want to add by ticking the box on the left side of the entries' record. 2. Click on the **Add to release** button located above the header of the table. 3. In the modal, select which release to add these entries to. 4. Click on the **Publish** or **Unpublish** button to decide whether these entries will be published or unpublished when the release is published, then click **Continue**. ### Removing content from a release {#removing-an-entry-from-a-release} **Path:** Edit view of the Content Manager 1. In the *Releases* box in the right sidebar, click on below the name of the release. 2. Click the **Remove from release** button. ### Scheduling a release **Path:** Releases Releases can be [published manually](#publishing-a-release) or scheduled to be automatically published at a given date and time, with the timezone of your choice. You can schedule a release: - when [creating the release](#creating-a-release), - or once the release is already created, by editing it. To schedule an existing release, while on a release page: 1. Click on the button in the top right corner of the admin panel. 2. Select **Edit**. 3. In the modal, check the **Schedule release** checkbox. 4. Select a date, time, and timezone for the release to be published. 5. Click **Save**. ### Publishing a release **Path:** Releases Publishing a release means that all the actions (publish or unpublish) defined for each entry included in the release will be performed simultaneously. To publish a release, click the **Publish** button in the top right corner of the admin panel. The _Status_ column displays the status of each entry: - Already published: the entry is already published and publishing the release will not affect this entry - Already unpublished: the entry is already unpublished, and publishing the release will not affect this entry. - Ready to publish: the entry is ready to be published with the release - Ready to unpublish: the entry is ready to be unpublished with the release - Not ready to publish: the entry cannot be published because some fields are incorrectly filled, or it hasn't reached the required stage for publishing. In this case, the release will be indicated as *Blocked* until all issues have been fixed. If some of your entries have a status, click the and the **Edit the entry** button to fix the issues until all entries have the status. Note that you will have to click on the **Refresh** button to update the release page as you fix the various entries issues. :::caution Once a release is published, the release itself cannot be updated. You can not re-release that specific release with the same group of entries with some modifications; you must create another release. ::: # Review Workflows Source: //cms/features/review-workflows # Review Workflows ## Configuration **Path to configure the feature:** Settings > Global settings > Review Workflows For the review workflows to be usable in the [Content Manager](/cms/features/content-manager), the default one should be configured or a new one should be created. The default workflow is configured to have 4 stages: To do, In progress, Ready to review, and Reviewed. All 4 stages can be edited, reordered or deleted as needed, and it is also possible to add new stages. ### Creating a new workflow 1. Click on the **Create new workflow** button or on the edit button of a workflow. 2. In the workflow edit interface, configure the new workflow: | Setting name | Instructions | | -------------- | ------------------------------------------------------------------------ | | Workflow name | Write a unique name of workflow. | | Associated to | (optional) Assign this workflow to one or more existing content-types. | | Stages | Add review stages (see [Adding a new stage](#adding-a-new-stage)). | 3. Click on the **Save** button. The new workflow will be displayed in the list view and for every content-type assigned. :::note The maximum number of . ::: ### Editing a workflow #### Adding a new stage 1. Click on the **Add new stage** button. 2. Write the *Stage name*. 3. Select a *Color*. 4. Select *Roles* that can change the stage, if the entity is currently in that review stage. 5. Click on the **Save** button. By default new stages are appended, but they can be reordered anytime using the button. :::tip To set up roles for each stage, you can either click "Apply to all stages" to apply the current roles to all other stages of the workflow or use "Duplicate stage" of the stage context menu. ::: #### Duplicating a stage 1. Click **Duplicate Stage** in the context menu of the stage. 2. Change the name of the duplicated stage. 2. Click on the **Save** button. #### Deleting a stage To delete a stage, click in the context menu of the stage, then **Delete**. If you delete a stage that has pending reviews, the reviews will be moved to first stage in the workflow. Every workflow needs to contain at least one stage and therefore it is not possible to delete the last stage. ### Deleting a workflow To delete a workflow click on the delete button of a workflow in the list view. :::note It is not possible to delete the last workflow. ::: ## Usage **Path to use the feature:** Content Manager ### Changing review stage {#change-review-stage} As content is created and revised among your team, you can change the review stage of the content to any stage defined in the review workflow. 1. Access the edit view of your content-type. 2. In the *Review Workflows* box on the right side of the interface, click on the _Review stage_ drop-down list. 3. Choose the new review stage of your entry. It is automatically saved. ### Defining assignee {#change-assignee} Entries of a review workflow content type can be assigned to any admin user in Strapi for review. 1. Access the edit view of your content-type. 2. In the *Review Workflows* box on the right side of the interface, click on the _Assignee_ drop-down list. 3. Choose the new assignee of your entry. It is automatically saved. # Single Sign-On (SSO) Source: //cms/features/sso # Single Sign-On (SSO) ## Usage To access the admin panel using a specific provider instead of logging in with a regular Strapi administrator account: 1. Go to the URL of your Strapi application's admin panel. 2. Click on a chosen provider, which logo should be displayed at the bottom of the login form. If you cannot see your provider, click the button to access the full list of all available providers. 3. You will be redirected to your provider's own login page where you will be able to authenticate. # Users & Permissions Source: //cms/features/users-permissions # Users & Permissions The Users & Permissions feature allows the management of the end-users of a Strapi project. It provides a full authentication process based on JSON Web Tokens (JWT) to protect your API, and an access-control list (ACL) strategy that enables you to manage permissions between groups of users. ## Admin panel configuration The Users & Permissions feature is configured from both the admin panel settings, and via the code base. ### Roles The Users & Permissions feature allows to create and manage roles for end users, to configure what they can have access to. #### Creating a new role **Path:** | View | Injection zone name & Location | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | List view | `actions`: sits between Filters and the cogs icon | Edit view | `right-links`: sits between "Configure the view" and "Edit" buttons | #### Creating a custom injection zone To create a custom injection zone, declare it as a `` React component with an `area` prop that takes a string with the following naming convention: `plugin-name.viewName.injectionZoneName`. #### Injecting components A plugin has 2 different ways of injecting a component: * to inject a component from a plugin into another plugin's injection zones, use the `injectComponent()` function * to specifically inject a component into one of the Content Manager's [predefined injection zones](#using-predefined-injection-zones), use the `getPlugin('content-manager').injectComponent()` function instead Both the `injectComponent()` and `getPlugin('content-manager').injectComponent()` methods accept the following arguments: | Argument | Type | Description | | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | first argument | String | The view where the component is injected | second argument | String | The zone where the component is injected | third argument | Object | An object with the following keys:
  • `name` (string): the name of the component
  • `Component` (function or class): the React component to be injected
|
Example: Inject a component in the informations box of the Edit View of the Content Manager: ```jsx title="my-plugin/admin/src/index.js" bootstrap(app) { app.getPlugin('content-manager').injectComponent()('editView', 'informations', { name: 'my-plugin-my-compo', Component: () => 'my-compo', }); } } ```
Example: Creating a new injection zone and injecting it from a plugin to another one: ```jsx title="my-plugin/admin/src/injectionZones.js" // Use the injection zone in a view const HomePage = () => { return (

This is the homepage

); }; ``` ```jsx title="my-plugin/admin/src/index.js" // Declare this injection zone in the register lifecycle of the plugin register() { app.registerPlugin({ // ... injectionZones: { homePage: { right: [] } } }); }, } ``` ```jsx title="my-other-plugin/admin/src/index.js" // Inject the component from a plugin in another plugin register() { // ... }, bootstrap(app) { app.getPlugin('my-plugin').injectComponent('homePage', 'right', { name: 'my-other-plugin-component', Component: () => 'This component is injected', }); } }; ```
#### Accessing data with the `useCMEditViewDataManager` React hook Once an injection zone is defined, the component to be injected in the Content Manager can have access to all the data of the Edit View through the `useCMEditViewDataManager` React hook.
Example of a basic component using the 'useCMEditViewDataManager' hook ```js const MyCompo = () => { const { createActionAllowedFields: [], // Array of fields that the user is allowed to edit formErrors: {}, // Object errors readActionAllowedFields: [], // Array of field that the user is allowed to edit slug: 'api::address.address', // Slug of the content-type updateActionAllowedFields: [], allLayoutData: { components: {}, // components layout contentType: {}, // content-type layout }, initialData: {}, isCreatingEntry: true, isSingleType: true, status: 'resolved', layout: {}, // Current content-type layout hasDraftAndPublish: true, modifiedData: {}, onPublish: () => {}, onUnpublish: () => {}, addComponentToDynamicZone: () => {}, addNonRepeatableComponentToField: () => {}, addRelation: () => {}, addRepeatableComponentToField: () => {}, moveComponentDown: () => {}, moveComponentField: () => {}, moveComponentUp: () => {}, moveRelation: () => {}, onChange: () => {}, onRemoveRelation: () => {}, removeComponentFromDynamicZone: () => {}, removeComponentFromField: () => {}, removeRepeatableField: () => {}, } = useCMEditViewDataManager() return null } ```
### Reducers API Reducers are reducers that can be used to share state between components. Reducers can be useful when: * Large amounts of application state are needed in many places in the application. * The application state is updated frequently. * The logic to update that state may be complex. Reducers can be added to a plugin interface with the `addReducers()` function during the [`register`](#register) lifecycle. A reducer is declared as an object with this syntax: **Example:** ```js title="my-plugin/admin/src/index.js" const reducers = { // Reducer Syntax [`${pluginId}_exampleReducer`]: exampleReducer } register(app) { app.addReducers(reducers) }, bootstrap() {}, }; ``` ### Hooks API The Hooks API allows a plugin to create and register hooks, i.e. places in the application where plugins can add personalized behavior. Hooks should be registered during the [bootstrap](#bootstrap) lifecycle of a plugin. Hooks can then be run in series, in waterfall or in parallel: * `runHookSeries` returns an array corresponding to the result of each function executed, ordered * `runHookParallel` returns an array corresponding to the result of the promise resolved by the function executed, ordered * `runHookWaterfall` returns a single value corresponding to all the transformations applied by the different functions starting with the initial value `args`.
Example: Create a hook in a plugin and use it in another plugin ```jsx title="my-plugin/admin/src/index.js" // Create a hook in a plugin register(app) { app.createHook('My-PLUGIN/MY_HOOK'); } } ``` ```jsx title="my-other-plugin/admin/src/index.js" // Use the hook in another plugin bootstrap(app) { app.registerHook('My-PLUGIN/MY_HOOK', (...args) => { console.log(args) // important: return the mutated data return args }); app.registerPlugin({...}) } } ```
#### Predefined hooks Strapi includes a predefined `Admin/CM/pages/ListView/inject-column-in-table` hook that can be used to add or mutate a column of the List View of the [Content Manager](/cms/intro): ```jsx runHookWaterfall(INJECT_COLUMN_IN_TABLE, { displayedHeaders: ListFieldLayout[], layout: ListFieldLayout, }); ``` ```tsx interface ListFieldLayout { /** * The attribute data from the content-type's schema for the field */ attribute: Attribute.Any | { type: 'custom' }; /** * Typically used by plugins to render a custom cell */ cellFormatter?: ( data: Document, header: Omit, { collectionType, model }: { collectionType: string; model: string } ) => React.ReactNode; label: string | MessageDescriptor; /** * the name of the attribute we use to display the actual name e.g. relations * are just ids, so we use the mainField to display something meaninginful by * looking at the target's schema */ mainField?: string; name: string; searchable?: boolean; sortable?: boolean; } interface ListLayout { layout: ListFieldLayout[]; components?: never; metadatas: { [K in keyof Contracts.ContentTypes.Metadatas]: Contracts.ContentTypes.Metadatas[K]['list']; }; options: LayoutOptions; settings: LayoutSettings; } type LayoutOptions = Schema['options'] & Schema['pluginOptions'] & object; interface LayoutSettings extends Contracts.ContentTypes.Settings { displayName?: string; icon?: never; } ``` Strapi also includes a `Admin/CM/pages/EditView/mutate-edit-view-layout` hook that can be used to mutate the Edit View of the [Content Manager](/cms/intro): ```tsx interface EditLayout { layout: Array>; components: { [uid: string]: { layout: Array; settings: Contracts.Components.ComponentConfiguration['settings'] & { displayName?: string; icon?: string; }; }; }; metadatas: { [K in keyof Contracts.ContentTypes.Metadatas]: Contracts.ContentTypes.Metadatas[K]['edit']; }; options: LayoutOptions; settings: LayoutSettings; } interface EditFieldSharedProps extends Omit { hint?: string; mainField?: string; size: number; unique?: boolean; visible?: boolean; } /** * Map over all the types in Attribute Types and use that to create a union of new types where the attribute type * is under the property attribute and the type is under the property type. */ type EditFieldLayout = { [K in Attribute.Kind]: EditFieldSharedProps & { attribute: Extract; type: K; }; }[Attribute.Kind]; type LayoutOptions = Schema['options'] & Schema['pluginOptions'] & object; interface LayoutSettings extends Contracts.ContentTypes.Settings { displayName?: string; icon?: never; } ``` :::note `EditViewLayout` and `ListViewLayout` are parts of the `useDocumentLayout` hook (see ). ::: # Content Manager APIs Source: //cms/plugins-development/content-manager-apis # Content Manager APIs Content-Manager APIs are part of the [Admin Panel API](/cms/plugins-development/admin-panel-api). They are a way to add content or options from plugins to the [Content-Manager](/cms/features/content-manager), so you can extend the Content-Manager by adding functionality from your own plugin, just like you can do it with [Injection Zones](/cms/plugins-development/admin-panel-api#injection-zones-api). Strapi 5 provides 4 Content-Manager APIs, all accessible through `app.getPlugin('content-manager').apis`: - [`addEditViewSidePanel`](#addeditviewsidepanel), - [`addDocumentAction`](#adddocumentaction), - [`addDocumentHeaderAction`](#adddocumentheaderaction), - and [`addBulkAction`](#addbulkaction). ## General information All the Content Manager APIs share the same API shape and must use components. ### API shape All Content Manager APIs works in the same way: to use them, call them on your plugin’s [bootstrap](/cms/plugins-development/admin-panel-api#bootstrap) function, in 2 possible ways: - Passing an array with what you want to add. For example, the following code would add the ReleasesPanel at the end of the current EditViewSidePanels: ```jsx app.getPlugin('content-manager').apis.addEditViewSidePanel([ReleasesPanel])` ``` - Passing a function that receives the current elements and return the new ones. This is useful if, for example, you want to add something in a specific position in the list, like in the following code: ```jsx app.getPlugin('content-manager').apis.addEditViewSidePanel( (panels) => [SuperImportantPanel, ...panels] ) ``` ### Components You need to pass components to the API in order to add things to the Content Manager. These components are basically functions that receive some properties and return and object with some shape (depending on the function). Each component’s return object is different based on the function you’re using, but they receive similar properties, depending on whether you use a ListView or EditView API. These properties include important information about the document(s) you are viewing or editing. #### ListViewContext ```jsx interface ListViewContext { /** * Will be either 'single-types' | 'collection-types' */ collectionType: string; /** * The current selected documents in the table */ documents: Document[]; /** * The current content-type's model. */ model: string; } ``` #### EditViewContext ```jsx interface EditViewContext { /** * This will only be null if the content-type * does not have draft & publish enabled. */ activeTab: 'draft' | 'published' | null; /** * Will be either 'single-types' | 'collection-types' */ collectionType: string; /** * Will be undefined if someone is creating an entry. */ document?: Document; /** * Will be undefined if someone is creating an entry. */ documentId?: string; /** * Will be undefined if someone is creating an entry. */ meta?: DocumentMetadata; /** * The current content-type's model. */ model: string; } ``` :::tip More information about types and APIs can be found in . ::: **Example:** Adding a panel to the sidebar can be done this way: ```jsx title="my-plugin/components/my-panel.ts" const Panel: PanelComponent = ({ activeTab, collectionType, document, documentId, meta, model }: PanelComponentProps) => { return { title: 'My Panel', content:

I'm on {activeTab}

} } ``` ## Available APIs
### `addEditViewSidePanel` Use this to add new panels to the Edit view sidebar, just like in the following example where something is added to the Releases panel: ![addEditViewSidePanel](/img/assets/content-manager-apis/add-edit-view-side-panel.png) ```jsx addEditViewSidePanel(panels: DescriptionReducer | PanelComponent[]) ``` #### PanelDescription The interface of the API only receives the following 2 properties: ```jsx { title: string; content: React.ReactNode; } ``` ### `addDocumentAction` Use this API to add more actions to the Edit view or the List View of the Content Manager. There are 3 positions available: - `header` of the Edit view: ![Header of the Edit view](/img/assets/content-manager-apis/add-document-action-header.png) - `panel` of the Edit view: ![Panel of the Edit View](/img/assets/content-manager-apis/add-document-action-panel.png) - `table-row` of the List view: ![Table-row in the List View](/img/assets/content-manager-apis/add-document-action-tablerow.png) ```jsx addDocumentAction(actions: DescriptionReducer | DocumentActionComponent[]) ``` #### DocumentActionDescription The interface and properties of the API look like the following: ```jsx interface DocumentActionDescription { label: string; onClick?: (event: React.SyntheticEvent) => Promise | boolean | void; icon?: React.ReactNode; /** * @default false */ disabled?: boolean; /** * @default 'panel' * @description Where the action should be rendered. */ position?: DocumentActionPosition | DocumentActionPosition[]; dialog?: DialogOptions | NotificationOptions | ModalOptions; /** * @default 'secondary' */ variant?: ButtonProps['variant']; } type DocumentActionPosition = 'panel' | 'header' | 'table-row'; interface DialogOptions { type: 'dialog'; title: string; content?: React.ReactNode; variant?: ButtonProps['variant']; onConfirm?: () => void | Promise; onCancel?: () => void | Promise; } interface NotificationOptions { type: 'notification'; title: string; link?: { label: string; url: string; target?: string; }; content?: string; onClose?: () => void; status?: NotificationConfig['type']; timeout?: number; } interface ModalOptions { type: 'modal'; title: string; content: React.ComponentType<{ onClose: () => void; }> | React.ReactNode; footer?: React.ComponentType<{ onClose: () => void; }> | React.ReactNode; onClose?: () => void; } ``` ### `addDocumentHeaderAction` Use this API to add more actions to the header of the Edit view of the Content Manager: ![addEditViewSidePanel](/img/assets/content-manager-apis/add-document-header-action.png) ```jsx addDocumentHeaderAction(actions: DescriptionReducer | HeaderActionComponent[]) ``` #### HeaderActionDescription The interface and properties of the API look like the following: ```jsx interface HeaderActionDescription { disabled?: boolean; label: string; icon?: React.ReactNode; type?: 'icon' | 'default'; onClick?: (event: React.SyntheticEvent) => Promise | boolean | void; dialog?: DialogOptions; options?: Array<{ disabled?: boolean; label: string; startIcon?: React.ReactNode; textValue?: string; value: string; }>; onSelect?: (value: string) => void; value?: string; } interface DialogOptions { type: 'dialog'; title: string; content?: React.ReactNode; footer?: React.ReactNode; } ``` ### `addBulkAction` Use this API to add buttons that show up when entries are selected on the List View of the Content Manager, just like the "Add to Release" button for instance: ![addEditViewSidePanel](/img/assets/content-manager-apis/add-bulk-action.png) ```jsx addBulkAction(actions: DescriptionReducer | BulkActionComponent[]) ``` #### BulkActionDescription The interface and properties of the API look like the following: ```jsx interface BulkActionDescription { dialog?: DialogOptions | NotificationOptions | ModalOptions; disabled?: boolean; icon?: React.ReactNode; label: string; onClick?: (event: React.SyntheticEvent) => void; /** * @default 'default' */ type?: 'icon' | 'default'; /** * @default 'secondary' */ variant?: ButtonProps['variant']; } ``` # Plugin creation & setup Source: //cms/plugins-development/create-a-plugin # Plugin creation There are many ways to create a Strapi 5 plugin, but the fastest and recommended way is to use the Plugin SDK. The Plugin SDK is a set of commands orientated around developing plugins to use them as local plugins or to publish them on NPM and/or submit them to the Marketplace. With the Plugin SDK, you do not need to set up a Strapi project before creating a plugin. The present guide covers creating a plugin from scratch, linking it to an existing Strapi project, and publishing the plugin. If you already have an existing plugin, you can instead retrofit the plugin setup to utilise the Plugin SDK commands (please refer to the [Plugin SDK reference](/cms/plugins-development/plugin-sdk) for a full list of available commands). :::note This guide assumes you want to develop a plugin external to your Strapi project. However, the steps largely remain the same if you want to develop a plugin within your existing project. If you are not [using a monorepo](#monorepo) the steps are exactly the same. ::: :::prerequisites The path `my-strapi-plugin` can be replaced with whatever you want to call your plugin, including the path to where it should be created (e.g., `code/strapi-plugins/my-new-strapi-plugin`). You will be ran through a series of prompts to help you setup your plugin. If you selected yes to all options the final structure will be similar to the default [plugin structure](/cms/plugins-development/plugin-structure). ### Linking the plugin to your project In order to test your plugin during its development, the recommended approach is to link it to a Strapi project. Linking your plugin to a project is done with the `watch:link` command. The command will output explanations on how to link your plugin to a Strapi project. In a new terminal window, run the following commands: :::note In the above examples we use the name of the plugin (`my-strapi-plugin`) when linking it to the project. This is the name of the package, not the name of the folder. ::: Because this plugin is installed via `node_modules` you won't need to explicity add it to your `plugins` [configuration file](/cms/configurations/plugins), so running the [`develop command`](/cms/cli#strapi-develop) to start your Strapi project will automatically pick up your plugin. Now that your plugin is linked to a project, run `yarn develop` or `npm run develop` to start the Strapi application. You are now ready to develop your plugin how you see fit! If you are making server changes, you will need to restart your server for them to take effect. ### Building the plugin for publishing When you are ready to publish your plugin, you will need to build it. To do this, run the following command: The above commands will not only build the plugin, but also verify that the output is valid and ready to be published. You can then publish your plugin to NPM as you would any other package. ## Working with the Plugin SDK in a monorepo environment {#monorepo} If you are working with a monorepo environment to develop your plugin, you don't need to use the `watch:link` command because the monorepo workspace setup will handle the symlink. You can use the `watch` command instead. However, if you are writing admin code, you might add an `alias` that targets the source code of your plugin to make it easier to work with within the context of the admin panel: ```ts config.resolve.alias = { ...config.resolve.alias, 'my-strapi-plugin': path.resolve( __dirname, // We've assumed the plugin is local. '../plugins/my-strapi-plugin/admin/src' ), }; return config; }; ``` :::caution Because the server looks at the `server/src/index.ts|js` file to import your plugin code, you must use the `watch` command otherwise the code will not be transpiled and the server will not be able to find your plugin. ::: ### Configuration with a local plugin Since the Plugin SDK is primarily designed for developing plugins, not locally, the configuration needs to be adjusted manually for local plugins. When developing your plugin locally (using `@strapi/sdk-plugin`), your plugins configuration file looks like in the following example: ```js title="/config/plugins.js|ts" myplugin: { enabled: true, resolve: `./src/plugins/local-plugin`, }, ``` However, this setup can sometimes lead to errors such as the following: ```js Error: 'X must be used within StrapiApp'; ``` This error often occurs when your plugin attempts to import core Strapi functionality, for example using: ```js ``` To resolve the issue, remove `@strapi/strapi` as a dev dependency from your plugin. This ensures that your plugin uses the same instance of Strapi’s core modules as the main application, preventing conflicts and the associated errors. ## Setting a local plugin in a monorepo environment without the Plugin SDK In a monorepo, you can configure your local plugin without using the Plugin SDK by adding 2 entry point files at the root of your plugin: - server entry point: `strapi-server.js|ts` - admin entry point: `strapi-admin.js|ts` ### Server entry point The server entry point file initializes your plugin’s server-side functionalities. The expected structure for `strapi-server.js` (or its TypeScript variant) is: ```js module.exports = () => { return { register, config, controllers, contentTypes, routes, }; }; ``` Here, you export a function that returns your plugin's core components such as controllers, routes, and configuration. For more details, please refer to the [Server API reference](/cms/plugins-development/server-api). ### Admin entry point The admin entry point file sets up your plugin within the Strapi admin panel. The expected structure for `strapi-admin.js` (or its TypeScript variant) is: ```js register(app) {}, bootstrap() {}, registerTrads({ locales }) {}, }; ``` This object includes methods to register your plugin with the admin application, perform bootstrapping actions, and handle translations. For more details, please refer to the [Admin Panel API reference](/cms/plugins-development/admin-panel-api). :::tip For a complete example of how to structure your local plugin in a monorepo environment, please check out our . ::: # Developing plugins Source: //cms/plugins-development/developing-plugins # Developing Strapi plugins Strapi allows the development of plugins that work exactly like the built-in plugins or 3rd-party plugins available from the :::strapi Custom fields plugins Plugins can also be used to add [custom fields](/cms/features/custom-fields) to Strapi. ::: ## Guides
:::strapi Additional resources The can also include additional information useful while developing a Strapi plugin. ::: # How to create components for Strapi plugins Source: //cms/plugins-development/guides/create-components-for-plugins # How to create components for Strapi plugins When [developing a Strapi plugin](/cms/plugins-development/developing-plugins), you might want to create reusable components for your plugin. Components in Strapi are reusable data structures that can be used across different content-types. To create components for your Strapi plugin, you'll need to follow a similar approach to creating content-types, but with some specific differences. ## Creating components You can create components for your plugins in 2 different ways: using the Content-Type Builder (recommended way) or manually. ### Using the Content-Type Builder The recommended way to create components for your plugin is through the Content-Type Builder in the admin panel. The [Content-Type Builder documentation](/cms/features/content-type-builder#new-component) provides more details on this process. ### Creating components manually If you prefer to create components manually, you'll need to: 1. Create a component schema in your plugin's structure. 2. Make sure the component is properly registered. Components for plugins should be placed in the appropriate directory within your plugin structure. You would typically create them within the server part of your plugin (see [plugin structure documentation](/cms/plugins-development/plugin-structure)). For more detailed information about components in Strapi, you can refer to the [Model attributes documentation](/cms/backend-customization/models#components-json). ## Reviewing the component structure Components in Strapi follow the following format in their definition: ```javascript title="/my-plugin/server/components/category/component-name.json" { "attributes": { "myComponent": { "type": "component", "repeatable": true, "component": "category.componentName" } } } ``` ## Making components visible in the admin panel To ensure your plugin's components are visible in the admin panel, you need to set the appropriate `pluginOptions` in your component schema: ```javascript {9-16} { "kind": "collectionType", "collectionName": "my_plugin_components", "info": { "singularName": "my-plugin-component", "pluralName": "my-plugin-components", "displayName": "My Plugin Component" }, "pluginOptions": { "content-manager": { "visible": true }, "content-type-builder": { "visible": true } }, "attributes": { "name": { "type": "string" } } } ``` This configuration ensures your components will be visible and editable in both the Content-Type Builder and Content Manager. # How to pass data from server to admin panel with a Strapi plugin Source: //cms/plugins-development/guides/pass-data-from-server-to-admin # How to pass data from server to admin panel with a Strapi plugin Strapi is **headless** . The admin panel is completely separate from the server. When [developing a Strapi plugin](/cms/plugins-development/developing-plugins) you might want to pass data from the `/server` to the `/admin` folder. Within the `/server` folder you have access to the Strapi object and can do database queries whereas in the `/admin` folder you can't. Passing data from the `/server` to the `/admin` folder can be done using the admin panel's Axios instance: To pass data from the `/server` to `/admin` folder you would first [create a custom admin route](#create-a-custom-admin-route) and then [get the data returned in the admin panel](#get-the-data-in-the-admin-panel). ## Create a custom admin route Admin routes are like the routes that you would have for any controller, except that the `type: 'admin'` declaration hides them from the general API router, and allows you to access them from the admin panel. The following code will declare a custom admin route for the `my-plugin` plugin: ```js title="/my-plugin/server/routes/index.js" module.exports = { 'pass-data': { type: 'admin', routes: [ { method: 'GET', path: '/pass-data', handler: 'myPluginContentType.index', config: { policies: [], auth: false, }, }, ] } // ... }; ``` This route will call the `index` method of the `myPluginContentType` controller when you send a GET request to the `/my-plugin/pass-data` URL endpoint. Let's create a basic custom controller that simply returns a simple text: ```js title="/my-plugin/server/controllers/my-plugin-content-type.js" 'use strict'; module.exports = { async index(ctx) { ctx.body = 'You are in the my-plugin-content-type controller!'; } } ``` This means that when sending a GET request to the `/my-plugin/pass-data` URL endpoint, you should get the `You are in the my-plugin-content-type controller!` text returned with the response. ## Get the data in the admin panel Any request sent from an admin panel component to the endpoint for which we defined the custom route `/my-plugin/pass-data` should now return the text message returned by the custom controller. So for instance, if you create an `/admin/src/api/foobar.js` file and copy and paste the following code example: ```js title="/my-plugin/admin/src/api/foobar.js" const foobarRequests = { getFoobar: async () => { const data = await axios.get(`/my-plugin/pass-data`); return data; }, }; ``` You will be able to use `foobarRequests.getFoobar()` in the code of an admin panel component and have it return the `You are in the my-plugin-content-type controller!` text with the data. For instance, within a React component, you could use `useEffect` to get the data after the component initializes: ```js title="/my-plugin/admin/src/components/MyComponent/index.js" const [foobar, setFoobar] = useState([]); // … useEffect(() => { foobarRequests.getFoobar().then(res => { setSchemas(res.data); }); }, [setFoobar]); // … ``` This would set the `You are in the my-plugin-content-type controller!` text within the `foobar` data of the component's state. # How to store and access data from a Strapi plugin Source: //cms/plugins-development/guides/store-and-access-data # How to store and access data from a Strapi plugin The generator CLI is interactive and asks a few questions about the content-type and the attributes it will contain. Answer the first questions, then for the `Where do you want to add this model?` question, choose the `Add model to existing plugin` option and type the name of the related plugin when asked.
Generating a content-type plugin with the CLI
The strapi generate content-type CLI generator is used to create a basic content-type for a plugin.

The CLI will generate some code required to use your plugin, which includes the following: - the [content-type schema](/cms/backend-customization/models#model-schema) - and a basic [controller](/cms/backend-customization/controllers), [service](/cms/backend-customization/services), and [route](/cms/backend-customization/routes) for the content-type :::tip You may want to create the whole structure of your content-types either entirely with the CLI generator or by directly creating and editing `schema.json` files. We recommend you first create a simple content-type with the CLI generator and then leverage the [Content-Type Builder](/cms/features/content-type-builder) in the admin panel to edit your content-type. If your content-type is not visible in the admin panel, you might need to set the `content-manager.visible` and `content-type-builder.visible` parameters to `true` in the `pluginOptions` object of the content-type schema:
Making a plugin content-type visible in the admin panel: The following highlighted lines in an example `schema.json` file show how to make a plugin content-type visible to the Content-Type Builder and Content-Manager: ```json title="/server/content-types/my-plugin-content-type/schema.json" {13-20} showLineNumbers { "kind": "collectionType", "collectionName": "my_plugin_content_types", "info": { "singularName": "my-plugin-content-type", "pluralName": "my-plugin-content-types", "displayName": "My Plugin Content-Type" }, "options": { "draftAndPublish": false, "comment": "" }, "pluginOptions": { "content-manager": { "visible": true }, "content-type-builder": { "visible": true } }, "attributes": { "name": { "type": "string" } } } ```
::: ### Ensure plugin content-types are imported The CLI generator might not have imported all the related content-type files for your plugin, so you might have to make the following adjustments after the `strapi generate content-type` CLI command has finished running: 1. In the `/server/index.js` file, import the content-types: ```js {7,22} showLineNumbers title="/server/index.js" 'use strict'; const register = require('./register'); const bootstrap = require('./bootstrap'); const destroy = require('./destroy'); const config = require('./config'); const contentTypes = require('./content-types'); const controllers = require('./controllers'); const routes = require('./routes'); const middlewares = require('./middlewares'); const policies = require('./policies'); const services = require('./services'); module.exports = { register, bootstrap, destroy, config, controllers, routes, services, contentTypes, policies, middlewares, }; ``` 2. In the `/server/content-types/index.js` file, import the content-type folder: ```js title="/server/content-types/index.js" 'use strict'; module.exports = { // In the line below, replace my-plugin-content-type // with the actual name and folder path of your content type "my-plugin-content-type": require('./my-plugin-content-type'), }; ``` 3. Ensure that the `/server/content-types/[your-content-type-name]` folder contains not only the `schema.json` file generated by the CLI, but also an `index.js` file that exports the content-type with the following code: ```js title="/server/content-types/my-plugin-content-type/index.js 'use strict'; const schema = require('./schema'); module.exports = { schema, }; ``` ## Interact with data from the plugin Once you have created a content-type for your plugin, you can create, read, update, and delete data. :::note A plugin can only interact with data from the `/server` folder. If you need to update data from the admin panel, please refer to the [passing data guide](/cms/plugins-development/guides/pass-data-from-server-to-admin). ::: To create, read, update, and delete data, you can use either the [Entity Service API](/cms/api/entity-service) or the [Query Engine API](/cms/api/query-engine). While it's recommended to use the Entity Service API, especially if you need access to components or dynamic zones, the Query Engine API is useful if you need unrestricted access to the underlying database. Use the `plugin::your-plugin-slug.the-plugin-content-type-name` syntax for content-type identifiers in Entity Service and Query Engine API queries. **Example:** Here is how to find all the entries for the `my-plugin-content-type` collection type created for a plugin called `my-plugin`: ```js // Using the Document Service API let data = await strapi.documents('plugin::my-plugin.my-plugin-content-type').findMany(); // Using the Query Engine API let data = await strapi.db.query('plugin::my-plugin.my-plugin-content-type').findMany(); ```` :::tip You can access the database via the `strapi` object which can be found in `middlewares`, `policies`, `controllers`, `services`, as well as from the `register`, `boostrap`, `destroy` lifecycle functions. ::: # Plugin SDK Source: //cms/plugins-development/plugin-sdk # Plugin SDK reference The Plugin SDK is set of commands provided by the package orientated around developing plugins to use them as local plugins or to publish them on NPM and/or submit them to the Marketplace. The present documentation lists the available Plugin SDK commands. The [associated guide](/cms/plugins-development/create-a-plugin) illustrates how to use these commands to create a plugin from scratch, link it to an existing project, and publish it. ## npx @strapi/sdk-plugin init Create a new plugin at a given path. ```bash npx @strapi/sdk-plugin init ``` | Arguments | Type | Description | Default | | --------- | :----: | ------------------ | ------------------------- | | `path` | string | Path to the plugin | `./src/plugins/my-plugin` | | Option | Type | Description | Default | | ------------- | :--: | --------------------------------------- | ------- | | `-d, --debug` | - | Enable debugging mode with verbose logs | false | | `--silent` | - | Do not log anything | false | ## strapi-plugin build Bundle the Strapi plugin for publishing. ```bash strapi-plugin build ``` | Option | Type | Description | Default | | -------------- | :----: | ----------------------------------------------------------------------------------------------------------------- | ------- | | `--force` | string | Automatically answer "yes" to all prompts, including potentially destructive requests, and run non-interactively. | - | | `-d, --debug` | - | Enable debugging mode with verbose logs | false | | `--silent` | - | Do not log anything | false | | `--minify` | - | Minify the output | true | | `--sourcemaps` | - | Produce sourcemaps | false | ## strapi-plugin watch:link Recompiles the plugin automatically on changes and runs `yalc push --publish`. For testing purposes, it is very convenient to link your plugin to an existing application to experiment with it in real condition. This command is made to help you streamline this process. ```bash strapi-plugin watch:link ``` | Option | Type | Description | Default | | ------------- | :--: | --------------------------------------- | ------- | | `-d, --debug` | - | Enable debugging mode with verbose logs | false | | `--silent` | - | Do not log anything | false | ## strapi-plugin watch Watch the plugin source code for any change and rebuild it everytime. Useful when implementing your plugin and testing it in an application. ```bash strapi-plugin watch ``` | Option | Type | Description | Default | | ------------- | :--: | --------------------------------------- | ------- | | `-d, --debug` | - | Enable debugging mode with verbose logs | false | | `--silent` | - | Do not log anything | false | ## strapi-plugin verify Verify the output of the plugin before publishing it. ```bash strapi-plugin verify ``` | Option | Type | Description | Default | | ------------- | :--: | --------------------------------------- | ------- | | `-d, --debug` | - | Enable debugging mode with verbose logs | false | | `--silent` | - | Do not log anything | false | # Plugin structure Source: //cms/plugins-development/plugin-structure # Plugin structure When [creating a plugin with Plugin SDK](/cms/plugins-development/create-a-plugin), Strapi generates the following boilerplate structure for you in the `/src/plugins/my-plugin` folder: A Strapi plugin is divided into 2 parts, each living in a different folder and offering a different API: | Plugin part | Description | Folder | API | |-------------|-------------|--------------|-----| | Admin panel | Includes what will be visible in the [admin panel](/cms/intro) (components, navigation, settings, etc.) | `admin/` |[Admin Panel API](/cms/plugins-development/admin-panel-api)| | Backend server | Includes what relates to the [backend server](/cms/backend-customization) (content-types, controllers, middlewares, etc.) |`server/` |[Server API](/cms/plugins-development/server-api)|
:::note Notes about the usefulness of the different parts for your specific use case - **Server-only plugin**: You can create a plugin that will just use the server part to enhance the API of your application. For instance, this plugin could have its own visible or invisible content-types, controller actions, and routes that are useful for a specific use case. In such a scenario, you don't need your plugin to have an interface in the admin panel. - **Admin panel plugin vs. application-specific customization**: You can create a plugin to inject some components into the admin panel. However, you can also achieve this by creating a `/src/admin/index.js` file and invoking the `bootstrap` lifecycle function to inject your components. In this case, deciding whether to create a plugin depends on whether you plan to reuse and distribute the code or if it's only useful for a unique Strapi application. :::
:::strapi What to read next? The next steps of your Strapi plugin development journey will require you to use any of the Strapi plugins APIs. 2 different types of resources help you understand how to use the plugin APIs: - The reference documentation for the [Admin Panel API](/cms/plugins-development/admin-panel-api) and [Server API](/cms/plugins-development/server-api) give an overview of what is possible to do with a Strapi plugin. - [Guides](/cms/plugins-development/developing-plugins#guides) cover some specific, use-case based examples. ::: # Plugins extension Source: //cms/plugins-development/plugins-extension # Plugins extension Strapi comes with plugins that can be installed from the [Marketplace](/cms/plugins/installing-plugins-via-marketplace#installing-marketplace-plugins-and-providers) or as npm packages. You can also create your own plugins (see [plugins development](/cms/plugins-development/developing-plugins)) or extend the existing ones. :::warning * Any plugin update could break this plugin's extensions. * New versions of Strapi will be released with migration guides when required, but these guides never cover plugin extensions. Consider forking a plugin if extensive customizations are required. * Currently, the admin panel part of a plugin can only be extended using , but please consider that doing so might break your plugin in future versions of Strapi. ::: Plugin extensions code is located in the `./src/extensions` folder (see [project structure](/cms/project-structure)). Some plugins automatically create files there, ready to be modified.
Example of extensions folder structure ```bash /extensions /some-plugin-to-extend strapi-server.js|ts /content-types /some-content-type-to-extend model.json /another-content-type-to-extend model.json /another-plugin-to-extend strapi-server.js|ts ```
Plugins can be extended in 2 ways: - [extending the plugin's content-types](#extending-a-plugins-content-types) - [extending the plugin's interface](#extending-a-plugins-interface) (e.g. to add controllers, services, policies, middlewares and more) ## Extending a plugin's content-types A plugin's Content-Types can be extended in 2 ways: using the programmatic interface within `strapi-server.js|ts` and by overriding the content-types schemas. The final schema of the content-types depends on the following loading order: 1. the content-types of the original plugin, 2. the content-types overridden by the declarations in the [schema](/cms/backend-customization/models#model-schema) defined in `./src/extensions/plugin-name/content-types/content-type-name/schema.json` 3. the content-types declarations in the [`content-types` key exported from `strapi-server.js|ts`](/cms/plugins-development/server-api#content-types) 4. the content-types declarations in the [`register()` function](/cms/configurations/functions#register) of the Strapi application To overwrite a plugin's [content-types](/cms/backend-customization/models): 1. _(optional)_ Create the `./src/extensions` folder at the root of the app, if the folder does not already exist. 2. Create a subfolder with the same name as the plugin to be extended. 3. Create a `content-types` subfolder. 4. Inside the `content-types` subfolder, create another subfolder with the same [singularName](/cms/backend-customization/models#model-information) as the content-type to overwrite. 5. Inside this `content-types/name-of-content-type` subfolder, define the new schema for the content-type in a `schema.json` file (see [schema](/cms/backend-customization/models#model-schema) documentation). 6. _(optional)_ Repeat steps 4 and 5 for each content-type to overwrite. ## Extending a plugin's interface When a Strapi application is initializing, plugins, extensions and global lifecycle functions events happen in the following order: 1. Plugins are loaded and their interfaces are exposed. 2. Files in `./src/extensions` are loaded. 3. The `register()` and `bootstrap()` functions in `./src/index.js|ts` are called. A plugin's interface can be extended at step 2 (i.e. within `./src/extensions`) or step 3 (i.e. inside `./src/index.js|ts`). :::note If your Strapi project is TypeScript-based, please ensure that the `index` file has a TypeScript extension (i.e., `src/index.ts`) otherwise it will not be compiled. ::: ### Within the extensions folder To extend a plugin's server interface using the `./src/extensions` folder: 1. _(optional)_ Create the `./src/extensions` folder at the root of the app, if the folder does not already exist. 2. Create a subfolder with the same name as the plugin to be extended. 3. Create a `strapi-server.js|ts` file to extend a plugin's back end using the [Server API](/cms/plugins-development/server-api). 4. Within this file, define and export a function. The function receives the `plugin` interface as an argument so it can be extended.
Example of backend extension ```js title="./src/extensions/some-plugin-to-extend/strapi-server.js|ts" module.exports = (plugin) => { plugin.controllers.controllerA.find = (ctx) => {}; plugin.policies[newPolicy] = (ctx) => {}; plugin.routes['content-api'].routes.push({ method: 'GET', path: '/route-path', handler: 'controller.action', }); return plugin; }; ```
### Within the register and bootstrap functions To extend a plugin's interface within `./src/index.js|ts`, use the `bootstrap()` and `register()` [functions](/cms/configurations/functions) of the whole project, and access the interface programmatically with [getters](/cms/plugins-development/server-api#usage).
Example of extending a plugin's content-type within ./src/index.js|ts ```js title="./src/index.js|ts" module.exports = { register({ strapi }) { const contentTypeName = strapi.contentType('plugin::my-plugin.content-type-name') contentTypeName.attributes = { // Spread previous defined attributes ...contentTypeName.attributes, // Add new, or override attributes 'toto': { type: 'string', } } }, bootstrap({ strapi }) {}, }; ```
# Server API for plugins Source: //cms/plugins-development/server-api # Server API for plugins A Strapi plugin can interact with both the back end and the [front end](/cms/plugins-development/admin-panel-api) of a Strapi application. The Server API is about the back-end part, i.e. how the plugin interacts with the server part of a Strapi application. :::prerequisites You have [created a Strapi plugin](/cms/plugins-development/create-a-plugin). ::: The Server API includes: - an [entry file](#entry-file) which export the required interface, - [lifecycle functions](#lifecycle-functions), - a [configuration](#configuration) API, - and the ability to [customize all elements of the back-end server](#backend-customization). Once you have declared and exported the plugin interface, you will be able to [use the plugin interface](#usage). :::note The whole code for the server part of your plugin could live in the `/server/src/index.ts|js` file. However, it's recommended to split the code into different folders, just like the [structure](/cms/plugins-development/plugin-structure) created by the Plugin SDK. ::: ## Entry file The `/src/server/index.js` file at the root of the plugin folder exports the required interface, with the following parameters available: | Parameter type | Available parameters | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Lifecycle functions |
  • [register](#register)
  • [bootstrap](#bootstrap)
  • [destroy](#destroy)
| | Configuration |
  • [config](#configuration) object
| | Backend customizations |
  • [contentTypes](#content-types)
  • [routes](#routes)
  • [controllers](#controllers)
  • [services](#services)
  • [policies](#policies)
  • [middlewares](#middlewares)
| ## Lifecycle functions
### register() This function is called to load the plugin, before the application is [bootstrapped](#bootstrap), in order to register [permissions](/cms/features/users-permissions), the server part of [custom fields](/cms/features/custom-fields#registering-a-custom-field-on-the-server), or database migrations. **Type**: `Function` **Example:** ### bootstrap() The [bootstrap](/cms/configurations/functions#bootstrap) function is called right after the plugin has [registered](#register). **Type**: `Function` **Example:** ### destroy() The [destroy](/cms/configurations/functions#destroy) lifecycle function is called to cleanup the plugin (close connections, remove listeners, etc.) when the Strapi instance is destroyed. **Type**: `Function` **Example:** ## Configuration `config` stores the default plugin configuration. It loads and validates the configuration inputted from the user within the [`./config/plugins.js` configuration file](/cms/configurations/plugins). **Type**: `Object` | Parameter | Type | Description | | ----------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `default` | Object, or Function that returns an Object | Default plugin configuration, merged with the user configuration | | `validator` | Function |
  • Checks if the results of merging the default plugin configuration with the user configuration is valid
  • Throws errors when the resulting configuration is invalid
| **Example:** Once defined, the configuration can be accessed: - with `strapi.plugin('plugin-name').config('some-key')` for a specific configuration property, - or with `strapi.config.get('plugin.plugin-name')` for the whole configuration object. :::tip Run `yarn strapi console` or `npm run strapi console` to access the strapi object in a live console. ::: ## Backend customization All elements of the back-end server of Strapi can be customized through a plugin using the Server API. :::prerequisites To better understand this section, ensure you have read through the [back-end customization](/cms/backend-customization) documentation of a Strapi application. ::: ### Content-types An object with the [content-types](/cms/backend-customization/models) the plugin provides. **Type**: `Object` :::note Content-Types keys in the `contentTypes` object should re-use the `singularName` defined in the [`info`](/cms/backend-customization/models#model-information) key of the schema. ::: **Example:** ### Routes An array of [routes](/cms/backend-customization/routes) configuration. **Type**: `Object[]` **Examples:** ### Controllers An object with the [controllers](/cms/backend-customization/controllers) the plugin provides. **Type**: `Object` **Example:** ### Services An object with the [services](/cms/backend-customization/services) the plugin provides. Services should be functions taking `strapi` as a parameter. **Type**: `Object` **Example:** ### Policies An object with the [policies](/cms/backend-customization/policies) the plugin provides. **Type**: `Object` **Example:** ### Middlewares An object with the [middlewares](/cms/configurations/middlewares) the plugin provides. **Type**: `Object` **Example:** ## Usage Once a plugin is exported and loaded into Strapi, its features are accessible in the code through getters. The Strapi instance (`strapi`) exposes both top-level getters and global getters: - top-level getters imply chaining functions
(e.g., `strapi.plugin('the-plugin-name').controller('the-controller-name'`), - global getters are syntactic sugar that allows direct access using a feature's uid
(e.g., `strapi.controller('plugin::plugin-name.controller-name')`). ```js // Access an API or a plugin controller using a top-level getter strapi.api['api-name'].controller('controller-name') strapi.plugin('plugin-name').controller('controller-name') // Access an API or a plugin controller using a global getter strapi.controller('api::api-name.controller-name') strapi.controller('plugin::plugin-name.controller-name') ```
Top-level getter syntax examples ```js strapi.plugin('plugin-name').config strapi.plugin('plugin-name').routes strapi.plugin('plugin-name').controller('controller-name') strapi.plugin('plugin-name').service('service-name') strapi.plugin('plugin-name').contentType('content-type-name') strapi.plugin('plugin-name').policy('policy-name') strapi.plugin('plugin-name').middleware('middleware-name') ```
Global getter syntax examples ```js strapi.controller('plugin::plugin-name.controller-name'); strapi.service('plugin::plugin-name.service-name'); strapi.contentType('plugin::plugin-name.content-type-name'); strapi.policy('plugin::plugin-name.policy-name'); strapi.middleware('plugin::plugin-name.middleware-name'); ```
:::strapi Document Service API To interact with the content-types, use the [Document Service API](/cms/api/document-service). ::: # Documentation plugin Source: //cms/plugins/documentation # Documentation plugin The Documentation plugin automates your API documentation creation. It basically generates a swagger file. It follows the :::caution Unmaintained plugin The Documentation plugin is not actively maintained and may not work with Strapi 5. ::: Once the plugin is installed, starting Strapi generates the API documentation. ## Configuration Most configuration options for the Documentation plugin are handled via your Strapi project's code. A few settings are available in the admin panel. ### Admin panel settings The Documentation plugin affects multiple parts of the admin panel. The following table lists all the additional options and settings that are added to a Strapi application once the plugin has been installed: | Section impacted | Options and settings | |------------------|-------------------------------------------------------------| | Documentation |
    Addition of a new Documentation option in the main navigation which shows a panel with buttons to open and regenerate the documentation.
| | Settings |
  • Addition of a "Documentation plugin" setting section, which controls whether the documentation endpoint is private or not (see [restricting access](#restrict-access)).
    👉 Path reminder: *Settings > Documentation plugin*

  • Activation of role based access control for accessing, updating, deleting, and regenerating the documentation. Administrators can authorize different access levels to different types of users in the *Plugins* tab and the *Settings* tab (see [Users & Permissions documentation](/cms/features/users-permissions)).
    👉 Path reminder: *Settings > Administration Panel > Roles*
| #### Restricting access to your API documentation {#restrict-access} By default, your API documentation will be accessible by anyone. To restrict API documentation access, enable the **Restricted Access** option from the admin panel: 1. Navigate to *Settings* in the main navigation of the admin panel. 2. Choose **Documentation**. 3. Toggle **Restricted Access** to `ON`. 4. Define a password in the `password` input. 5. Save the settings. ### Code-based configuration To configure the Documentation plugin, create a `settings.json` file in the `src/extensions/documentation/config` folder. In this file, you can specify all your environment variables, licenses, external documentation links, and all the entries listed in the . The following is an example configuration: ```json title="src/extensions/documentation/config/settings.json" { "openapi": "3.0.0", "info": { "version": "1.0.0", "title": "DOCUMENTATION", "description": "", "termsOfService": "YOUR_TERMS_OF_SERVICE_URL", "contact": { "name": "TEAM", "email": "contact-email@something.io", "url": "mywebsite.io" }, "license": { "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" } }, "x-strapi-config": { "plugins": ["upload", "users-permissions"], "path": "/documentation" }, "servers": [ { "url": "http://localhost:1337/api", "description": "Development server" } ], "externalDocs": { "description": "Find out more", "url": "https://docs.strapi.io/developer-docs/latest/getting-started/introduction.html" }, "security": [ { "bearerAuth": [] } ] } ``` :::tip If you need to add a custom key, prefix it by `x-` (e.g., `x-strapi-something`). ::: #### Creating a new version of the documentation {#create-a-new-version-of-the-documentation} To create a new version, change the `info.version` key in the `settings.json` file: ```json title="src/extensions/documentation/config/settings.json" { "info": { "version": "2.0.0" } } ``` This will automatically create a new version. #### Defining which plugins need documentation generated {#define-which-plugins} If you want plugins to be included in documentation generation, they should be included in the `plugins` array in the `x-strapi-config` object. By default, the array is initialized with `["upload", "users-permissions"]`: ```json title="src/extensions/documentation/config/settings.json" { "x-strapi-config": { "plugins": ["upload", "users-permissions"] } } ``` To add more plugins, such as your custom plugins, add their name to the array. If you do not want plugins to be included in documentation generation, provide an empty array (i.e., `plugins: []`). #### Overriding the generated documentation The Documentation plugins comes with 3 methods to override the generated documentation: [`excludeFromGeneration`](#excluding-from-generation), [`registerOverride`](#register-override), and [`mutateDocumentation`](#mutate-documentation). ##### excludeFromGeneration() {#excluding-from-generation} To exclude certain APIs or plugins from being generated, use the `excludeFromGeneration` found on the documentation plugin’s `override` service in your application or plugin's [`register` lifecycle](/cms/plugins-development/admin-panel-api#register). :::note `excludeFromGeneration` gives more fine-grained control over what is generated. For example, pluginA might create several new APIs while pluginB may only want to generate documentation for some of those APIs. In that case, pluginB could still benefit from the generated documentation it does need by excluding only what it does not need. ::: ***** | Parameter | Type | Description | | --------- | -------------------------- | -------------------------------------------------------- | | `api` | String or Array of Strings | The name of the API/plugin, or list of names, to exclude | ```js title="Application or plugin register lifecycle" module.exports = { register({ strapi }) { strapi .plugin("documentation") .service("override") .excludeFromGeneration("restaurant"); // or several strapi .plugin("documentation") .service("override") .excludeFromGeneration(["address", "upload"]); } } ``` ##### registerOverride() {#register-override} If the Documentation plugin fails to generate what you expect, it is possible to replace what has been generated. The Documentation plugin exposes an API that allows you to replace what was generated for the following OpenAPI root level keys: `paths`, `tags`, `components` . To provide an override, use the `registerOverride` function found on the Documentation plugin’s `override` service in your application or plugin's [`register` lifecycle](/cms/plugins-development/admin-panel-api#register). | Parameter | Type | Description | | ----------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------- | | `override` | Object | OpenAPI object including any of the following keys paths, tags, components. Accepts JavaScript, JSON, or yaml | | `options` | Object | Accepts `pluginOrigin` and `excludeFromGeneration` | | `options.pluginOrigin` | String | The plugin that is registering the override | | `options.excludeFromGeneration` | String or Array of String | The name of the API/plugin, or list of names, to exclude | :::caution Plugin developers providing an override should always specify the `pluginOrigin` options key. Otherwise the override will run regardless of the user’s configuration. ::: The Documentation plugin will use the registered overrides to replace the value of common keys on the generated documentation with what the override provides. If no common keys are found, the plugin will add new keys to the generated documentation. If the override completely replaces what the documentation generates, you can specify that generation is no longer necessary by providing the names of the APIs or plugins to exclude in the options key array `excludeFromGeneration`. If the override should only be applied to a specific version, the override must include a value for `info.version`. Otherwise, the override will run on all documentation versions. ```js title="Application or plugin register lifecycle" module.exports = { register({ strapi }) { if (strapi.plugin('documentation')) { const override = { // Only run this override for version 1.0.0 info: { version: '1.0.0' }, paths: { '/answer-to-everything': { get: { responses: { 200: { description: "*" }} } } } } strapi .plugin('documentation') .service('override') .registerOverride(override, { // Specify the origin in case the user does not want this plugin documented pluginOrigin: 'upload', // The override provides everything don't generate anything excludeFromGeneration: ['upload'], }); } }, } ``` The overrides system is provided to try and simplify amending the generated documentation. It is the only way a plugin can add or modify the generated documentation. ##### mutateDocumentation() {#mutate-documentation} The Documentation plugin’s configuration also accepts a `mutateDocumentation` function on `info['x-strapi-config']`. This function receives a draft state of the generated documentation that be can be mutated. It should only be applied from an application and has the final say in the OpenAPI schema. | Parameter | Type | Description | | --------------------------- | ------ | ---------------------------------------------------------------------- | | `generatedDocumentationDraft` | Object | The generated documentation with applied overrides as a mutable object | ```js title="config/plugins.js" module.exports = { documentation: { config: { "x-strapi-config": { mutateDocumentation: (generatedDocumentationDraft) => { generatedDocumentationDraft.paths[ "/answer-to-everything" // must be an existing path ].get.responses["200"].description = "*"; }, }, }, }, }; ``` ## Usage The Documentation plugin visualizes your API using . To access the UI, select in the main navigation of the admin panel. Then click **Open documentation** to open the Swagger UI. Using the Swagger UI you can view all of the endpoints available on your API and trigger API calls. :::tip Once the plugin is installed, the plugin user interface can be accessed at the following URL: `:/documentation/` (e.g., ). ::: ### Regenerating documentation {#regenerate-documentation} There are 2 ways to update the documentation after making changes to your API: - restart your application to regenerate the version of the documentation specified in the Documentation plugin's configuration, - or go to the Documentation plugin page and click the **regenerate** button for the documentation version you want to regenerate. ### Authenticating requests Strapi is secured by default, which means that most of your endpoints require the user to be authorized. If the CRUD action has not been set to Public in the [Users & Permissions feature](/cms/features/users-permissions#roles) then you must provide your JSON web token (JWT). To do this, while viewing the API Documentation, click the **Authorize** button and paste your JWT in the _bearerAuth_ _value_ field. # GraphQL plugin Source: //cms/plugins/graphql # GraphQL plugin By default Strapi create [REST endpoints](/cms/api/rest#endpoints) for each of your content-types. The GraphQL plugin adds a GraphQL endpoint to fetch and mutate your content. With the GraphQL plugin installed, you can use the Apollo Server-based GraphQL Sandbox to interactively build your queries and mutations and read documentation tailored to your content types. Once installed, the GraphQL sandbox 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. Once the plugin is installed, the **GraphQL Sandbox** is accessible at the `/graphql` route (e.g., #### Dynamically enable Apollo Sandbox You can use a function to dynamically enable Apollo Sandbox depending on the environment: #### CORS exceptions for Landing Page If the landing page is enabled in production environments (which is not recommended), CORS headers for the Apollo Server landing page must be added manually. To add them globally, you can merge the following into your middleware configuration: ```javascript title="/config/middlewares" { name: "strapi::security", config: { contentSecurityPolicy: { useDefaults: true, directives: { "connect-src": ["'self'", "https:", "apollo-server-landing-page.cdn.apollographql.com"], "img-src": ["'self'", "data:", "blob:", "apollo-server-landing-page.cdn.apollographql.com"], "script-src": ["'self'", "'unsafe-inline'", "apollo-server-landing-page.cdn.apollographql.com"], "style-src": ["'self'", "'unsafe-inline'", "apollo-server-landing-page.cdn.apollographql.com"], "frame-src": ["sandbox.embed.apollographql.com"] } } } } ``` To add these exceptions only for the `/graphql` path (recommended), you can create a new middleware to handle it. For example: #### Shadow CRUD To simplify and automate the build of the GraphQL schema, we introduced the Shadow CRUD feature. It automatically generates the type definitions, queries, mutations and resolvers based on your models. **Example:** If you've generated an API called `Document` using [the interactive `strapi generate` CLI](/cms/cli#strapi-generate) or the administration panel, your model looks like this: ```json title="/src/api/[api-name]/content-types/document/schema.json" { "kind": "collectionType", "collectionName": "documents", "info": { "singularName": "document", "pluralName": "documents", "displayName": "document", "name": "document" }, "options": { "draftAndPublish": true }, "pluginOptions": {}, "attributes": { "name": { "type": "string" }, "description": { "type": "richtext" }, "locked": { "type": "boolean" } } } ```
Generated GraphQL type and queries ```graphql # Document's Type definition input DocumentFiltersInput { name: StringFilterInput description: StringFilterInput locked: BooleanFilterInput createdAt: DateTimeFilterInput updatedAt: DateTimeFilterInput publishedAt: DateTimeFilterInput and: [DocumentFiltersInput] or: [DocumentFiltersInput] not: DocumentFiltersInput } input DocumentInput { name: String description: String locked: Boolean createdAt: DateTime updatedAt: DateTime publishedAt: DateTime } type Document { name: String description: String locked: Boolean createdAt: DateTime updatedAt: DateTime publishedAt: DateTime } type DocumentEntity { id: ID attributes: Document } type DocumentEntityResponse { data: DocumentEntity } type DocumentEntityResponseCollection { data: [DocumentEntity!]! meta: ResponseCollectionMeta! } type DocumentRelationResponseCollection { data: [DocumentEntity!]! } # Queries to retrieve one or multiple restaurants. type Query { document(id: ID): DocumentEntityResponse documents( filters: DocumentFiltersInput pagination: PaginationArg = {} sort: [String] = [] publicationState: PublicationState = LIVE ):DocumentEntityResponseCollection } # Mutations to create, update or delete a restaurant. type Mutation { createDocument(data: DocumentInput!): DocumentEntityResponse updateDocument(id: ID!, data: DocumentInput!): DocumentEntityResponse deleteDocument(id: ID!): DocumentEntityResponse } ```
#### Customization Strapi provides a programmatic API to customize GraphQL, which allows: * disabling some operations for the [Shadow CRUD](#shadow-crud) * [using getters](#using-getters) to return information about allowed operations * registering and using an `extension` object to [extend the existing schema](#extending-the-schema) (e.g. extend types or define custom resolvers, policies and middlewares)
Example of GraphQL customizations
##### Disabling operations in the Shadow CRUD The `extension` service provided with the GraphQL plugin exposes functions that can be used to disable operations on Content-Types: | Content-type function | Description | Argument type | Possible argument values | | -------------------- | ---------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | | `disable()` | Fully disable the Content-Type | - | - | | `disableQueries()` | Only disable queries for the Content-Type | - | - | | `disableMutations()` | Only disable mutations for the Content-Type | - | - | | `disableAction()` | Disable a specific action for the Content-Type | String | One value from the list:
  • `create`
  • `find`
  • `findOne`
  • `update`
  • `delete`
| | `disableActions()` | Disable specific actions for the Content-Type | Array of Strings | Multiple values from the list:
  • `create`
  • `find`
  • `findOne`
  • `update`
  • `delete`
| Actions can also be disabled at the field level, with the following functions: | Field function | Description | | ------------------ | -------------------------------- | | `disable()` | Fully disable the field | | `disableOutput()` | Disable the output on a field | | `disableInput()` | Disable the input on a field | | `disableFilters()` | Disable filters input on a field | **Examples:** ```js // Disable the 'find' operation on the 'restaurant' content-type in the 'restaurant' API strapi .plugin('graphql') .service('extension') .shadowCRUD('api::restaurant.restaurant') .disableAction('find') // Disable the 'name' field on the 'document' content-type in the 'document' API strapi .plugin('graphql') .service('extension') .shadowCRUD('api::document.document') .field('name') .disable() ``` ##### Using getters The following getters can be used to retrieve information about operations allowed on content-types: | Content-type getter | Description | Argument type | Possible argument values | | -------------------------- | ----------------------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------- | | `isEnabled()` | Returns whether a content-type is enabled | - | - | | `isDisabled()` | Returns whether a content-type is disabled | - | - | | `areQueriesEnabled()` | Returns whether queries are enabled on a content-type | - | - | | `areQueriesDisabled()` | Returns whether queries are disabled on a content-type | - | - | | `areMutationsEnabled()` | Returns whether mutations are enabled on a content-type | - | - | | `areMutationsDisabled()` | Returns whether mutations are disabled on a content-type | - | - | | `isActionEnabled(action)` | Returns whether the passed `action` is enabled on a content-type | String | One value from the list:
  • `create`
  • `find`
  • `findOne`
  • `update`
  • `delete`
| | `isActionDisabled(action)` | Returns whether the passed `action` is disabled on a content-type | String | One value from the list:
  • `create`
  • `find`
  • `findOne`
  • `update`
  • `delete`
| The following getters can be used to retrieve information about operations allowed on fields: | Field getter | Description | | --------------------- | --------------------------------------------- | | `isEnabled()` | Returns whether a field is enabled | | `isDisabled()` | Returns whether a field is disabled | | `hasInputEnabled()` | Returns whether a field has input enabled | | `hasOutputEnabled()` | Returns whether a field has output enabled | | `hasFiltersEnabled()` | Returns whether a field has filtering enabled | ###### Extending the schema The schema generated by the Content API can be extended by registering an extension. This extension, defined either as an object or a function returning an object, will be used by the `use()` function exposed by the `extension` [service](/cms/backend-customization/services) provided with the GraphQL plugin. The object describing the extension accepts the following parameters: | Parameter | Type | Description | | ----------------- | ------ | -------------------------------------------------------------------------------------------- | | `types` | Array | Allows extending the schema types using
::: ###### Custom configuration for resolvers A resolver is a GraphQL query or mutation handler (i.e. a function, or a collection of functions, that generate(s) a response for a GraphQL query or mutation). Each field has a default resolver. When [extending the GraphQL schema](#extending-the-schema), the `resolversConfig` key can be used to define a custom configuration for a resolver, which can include: * [authorization configuration](#authorization-configuration) with the `auth` key * [policies with the `policies`](#policies) key * and [middlewares with the `middlewares`](#middlewares) key ###### Authorization configuration By default, the authorization of a GraphQL request is handled by the registered authorization strategy that can be either [API token](/cms/features/api-tokens) or through the [Users & Permissions plugin](#usage-with-the-users--permissions-plugin). The Users & Permissions plugin offers a more granular control.
Authorization with the Users & Permissions plugin With the Users & Permissions plugin, a GraphQL request is allowed if the appropriate permissions are given. For instance, if a 'Category' content-type exists and is queried through GraphQL with the `Query.categories` handler, the request is allowed if the appropriate `find` permission for the 'Categories' content-type is given. To query a single category, which is done with the `Query.category` handler, the request is allowed if the the `findOne` permission is given. Please refer to the user guide on how to [define permissions with the Users & Permissions plugin](/cms/features/rbac#editing-a-role).
To change how the authorization is configured, use the resolver configuration defined at `resolversConfig.[MyResolverName]`. The authorization can be configured: * either with `auth: false` to fully bypass the authorization system and allow all requests, * or with a `scope` attribute that accepts an array of strings to define the permissions required to authorize the request.
Examples of authorization configuration
###### Policies [Policies](/cms/backend-customization/policies) can be applied to a GraphQL resolver through the `resolversConfig.[MyResolverName].policies` key. The `policies` key is an array accepting a list of policies, each item in this list being either a reference to an already registered policy or an implementation that is passed directly (see [policies configuration documentation](/cms/backend-customization/routes#policies)). Policies directly implemented in `resolversConfig` are functions that take a `context` object and the `strapi` instance as arguments. The `context` object gives access to: * the `parent`, `args`, `context` and `info` arguments of the GraphQL resolver, * Koa's
###### Middlewares [Middlewares](/cms/backend-customization/middlewares) can be applied to a GraphQL resolver through the `resolversConfig.[MyResolverName].middlewares` key. The only difference between the GraphQL and REST implementations is that the `config` key becomes `options`. The `middlewares` key is an array accepting a list of middlewares, each item in this list being either a reference to an already registered middleware or an implementation that is passed directly (see [middlewares configuration documentation](/cms/backend-customization/routes#middlewares)). Middlewares directly implemented in `resolversConfig` can take the GraphQL resolver's ##### Security GraphQL is a query language allowing users to use a broader panel of inputs than traditional REST APIs. GraphQL APIs are inherently prone to security risks, such as credential leakage and denial of service attacks, that can be reduced by taking appropriate precautions. ### Disable introspection and Sandbox in production In production environments, disabling the GraphQL Sandbox and the introspection query is strongly recommended. If you haven't edited the [configuration file](#available-options), it is already disabled in production by default. ###### Limit max depth and complexity A malicious user could send a query with a very high depth, which could overload your server. Use the `depthLimit` [configuration parameter](/cms/plugins/graphql#code-based-configuration) to limit the maximum number of nested fields that can be queried in a single request. By default, `depthLimit` is set to 10 but can be set to a higher value during testing and development. :::tip To increase GraphQL security even further, 3rd-party tools can be used. See the guide about You should see a new user is created in the `Users` collection type in your Strapi admin panel. #### Authentication To perform authorized requests, you must first get a JWT: Then on each request, send along an `Authorization` header in the form of `{ "Authorization": "Bearer YOUR_JWT_GOES_HERE" }`. This can be set in the HTTP Headers section of your GraphQL Sandbox. #### Usage with API tokens {#api-tokens} To use API tokens for authentication, pass the token in the `Authorization` header using the format `Bearer your-api-token`. :::note Using API tokens in the the GraphQL Sandbox requires adding the authorization header with your token in the `HTTP HEADERS` tab: ```http { "Authorization" : "Bearer # Installing Plugins via the Marketplace Source: //cms/plugins/installing-plugins-via-marketplace # Using the Marketplace Strapi comes with built-in plugins such as [Documentation](/cms/plugins/documentation), [GraphQL](/cms/plugins/graphql), and [Sentry](/cms/plugins/sentry). The Marketplace is where users can find additional plugins to customize Strapi applications, and additional providers to extend plugins. The Marketplace is located in the admin panel, indicated by _Marketplace_. In the Marketplace, users can browse or search for plugins and providers, link to detailed descriptions for each, and submit new plugins and providers. :::note strapi In-app Marketplace vs. Market website The Marketplace in the admin panel displays all existing plugins, regardless of the version of Strapi they are for. All plugins can also be discoverable through the website. Keep in mind however that v4 and v5 plugins are not cross-compatible, but that providers are compatible both with v4 and v5 plugins. ::: The Plugins and Providers tabs display each plugin/provider on individual cards containing: - their name, sometimes followed by either of the following badges: - maintained by Strapi icon to indicate it is made by Strapi, - to indicate it was verified by Strapi. - the number of times the plugin/provider was starred on GitHub and downloaded - the description - a **More** button to be redirected to the Market website for additional information, including about the version of Strapi the plugin is for, and implementation instructions In the top right corner of the Marketplace, the **Submit plugin** button redirects to the Strapi Market where it is possible to submit your own plugin and provider. :::tip Tips - The search bar displays incremental search results based on the plugin/provider name and description. - Use the "Sort by" button or set filters to find plugins more easily. ::: ## Installing Marketplace plugins and providers To install a new plugin or provider via the Marketplace: 1. Go to the *Marketplace*. 2. Choose the **Plugins** tab to browse available plugins or the **Providers** tab to browse available providers. 3. Choose an available plugin/provider and click on the **More** button. 4. Once redirected to the Strapi Market website, follow the plugin/provider-specific implementation instructions. :::strapi Developing Strapi plugins Can't find a plugin that suits your use case? Feel free to [create your own](/cms/plugins-development/developing-plugins)! ::: # Sentry plugin Source: //cms/plugins/sentry # Sentry plugin This plugin enables you to track errors in your Strapi application using Sentry. By using the Sentry plugin you can: * Initialize a Sentry instance upon startup of a Strapi application * Send Strapi application errors as events to Sentry * Include additional metadata in Sentry events to assist in debugging * Expose a global Sentry service usable by the Strapi server ## Installation Install the Sentry plugin by adding the dependency to your Strapi application as follows: ## Configuration Create or edit your `/config/plugins` file to configure the Sentry plugin. The following properties are available: | Property | Type | Default Value | Description | | -------- | ---- | ------------- |------------ | | `dsn` | string | `null` | Your Sentry ### Disabling for non-production environments If the `dsn` property is set to a nil value (`null` or `undefined`) while `sentry.enabled` is true, the Sentry plugin will be available to use in the running Strapi instance, but the service will not actually send errors to Sentry. That allows you to write code that runs on every environment without additional checks, but only send errors to Sentry in production. When you start Strapi with a nil `dsn` config property, the plugin will print the following warning:
`info: @strapi/plugin-sentry is disabled because no Sentry DSN was provided` You can make use of that by using the [`env` utility](/cms/configurations/guides/access-cast-environment-variables) to set the `dsn` configuration property depending on the environment. ### Disabling the plugin completely Like every other Strapi plugin, you can also disable this plugin in the plugins configuration file. This will cause `strapi.plugins('sentry')` to return `undefined`: ## Usage After installing and configuring the plugin, you can access a Sentry service in your Strapi application as follows: ```js const sentryService = strapi.plugin('sentry').service('sentry'); ``` This service exposes the following methods: | Method | Description | Parameters | | ------ | ----------- | ---------- | | `sendError()` | Manually send errors to Sentry. |
  • error: The error to be sent.
  • configureScope: Optional. Enables you to customize the error event.
See the official for more details. | | `getInstance()` | Used for direct access to the Sentry instance. | - | The `sendError()` method can be used as follows: ```js try { // Your code here } catch (error) { // Either send a simple error strapi .plugin('sentry') .service('sentry') .sendError(error); // Or send an error with a customized Sentry scope strapi .plugin('sentry') .service('sentry') .sendError(error, (scope, sentryInstance) => { // Customize the scope here scope.setTag('my_custom_tag', 'Tag value'); }); throw error; } ``` The `getInstance()` method is accessible as follows: ```js const sentryInstance = strapi .plugin('sentry') .service('sentry') .getInstance(); ``` # Project structure Source: //cms/project-structure # Project structure The structure of a Strapi project depends on whether the project was created with [TypeScript](/cms/typescript) (which is the default if you used the `--quickstart` option while creating the project) or with vanilla JavaScript, and looks like the following: # Quick Start Guide - Strapi Developer Docs Source: //cms/quick-start # Quick Start Guide Strapi offers a lot of flexibility. Whether you want to go fast and quickly see the final result, or would rather dive deeper into the product, we got you covered. For this tutorial, we'll go for the DIY approach and build a project and content structure from scratch, then deploy your project to Strapi Cloud to add data from there. *Estimated completion time: 5-10 minutes* :::prerequisites 2. The terminal will invite you to create a Strapi Cloud account. Ensure `Login/Sign up` is selected in the terminal, or use arrow keys to select it, and press Enter. 3. In the new browser tab that opens, ensure the confirmation code is the same as in the terminal and click **Confirm**. 4. Still in the browser tab, click **Continue with GitHub**. If you are not already logged in into GitHub with your current browser session, you might be redirected to a GitHub login page. 5. Once logged in, the browser will display a "Congratulations, you're all set!" message and you can safely close the browser tab and get back to the terminal. 3. Answer questions in the terminal, giving your project a name (you can press Enter to keep the default name), choosing the recommended NodeJS version, and selecting the region closer to your current place: ![Strapi Cloud terminal questions and answers](/img/assets/quick-start-guide/qsg-strapi-cloud-terminal-questions.png) Within a few moments, your local project will be hosted on Strapi Cloud. 🚀 Once it's done, the terminal will provide you a clickable link that starts with `https://cloud.strapi.io/projects`. Click on the link, or copy and paste it in your browser address bar, to visit the page. You will see the Strapi Cloud project we've just created, `my-strapi-project`, visible in the Strapi Cloud dashboard. Click the **Visit app** button in the top right corner to access your deployed Strapi project. :::callout Congratulations! Now your project is hosted on Strapi Cloud and accessible online. You can learn more about Strapi Cloud by reading [its dedicated documentation](/cloud/intro) or proceed to part D to log in into your online Strapi project and add your first data from there. ::: :::tip Feel free to play with the Content-Type Builder even further and add more fields to your content-types or create new content-types. Anytime you make such changes, deploy them again on Strapi Cloud, by running the appropriate `deploy` command, and see your hosted project updated within a few minutes. Magical, isn't it? 🪄 ::: ## Part D: Add content to your Strapi Cloud project with the Content Manager Now that we have created a basic content structure with 2 collection types, "Restaurant" and "Category", and deployed your project to Strapi Cloud, let's use the Cloud to actually add content by creating new entries.
Step 1: Log in to the admin panel of your new Strapi Cloud project ### Step 1: Log in to the admin panel of your new Strapi Cloud project Now that your Strapi Cloud project is created, let's log in into the project: 1. From your , click the `my-strapi-project` project. 3. Click the **Visit app** button. 4. In the new page that opens, complete the form to create the first administrator user of this Strapi Cloud project. Logged in into our first Strapi Cloud project, we will now add data from there.
Additional information and tips about users and Strapi Cloud projects: :::note Note: Local users and Strapi Cloud users are different The databases for your Strapi Cloud project and your local project are different. This means that data is not automatically transferred from your local project to Strapi Cloud. This includes users that you previously created locally. That's why you are invited to create a new administrator account when logging in to your Strapi Cloud project for the first time. ::: :::tip Tip: Directly accessing the admin panel of your Strapi Cloud project Any project hosted on Strapi Cloud is accessible from its own URL, something like `https://my-strapi-project-name.strapiapp.com`. To access the admin panel of your online project, simply add `/admin` to the URL, for instance as in `https://my-strapi-project-name.strapiapp.com/admin`. URLs can be found in your Strapi Cloud dashboard and you can also directly access your Strapi Cloud projects from there by clicking on the name of your project then on the **Visit app** button. :::
Step 2: Create an entry for the "Restaurant" collection type ### Step 2: Create an entry for the "Restaurant" collection type 1. Go to _Content Manager > Collection types - Restaurant_ in the navigation. 2. Click on **Create new entry**. 3. Type the name of your favorite local restaurant in the _Name_ field. Let's say it's `Biscotte Restaurant`. 4. In the _Description_ field, write a few words about it. If you're lacking some inspiration, you can use `Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers.` 5. Click **Save**. The restaurant is now listed in the _Collection types - Restaurant_ view of the _Content Manager_.
Step 3: Add Categories #### Step 3: Add Categories Let's go to _Content Manager > Collection types - Category_ and create 2 categories: 1. Click on **Create new entry**. 2. Type `French Food` in the _Name_ field. 3. Click **Save**. 4. Go back to _Collection types - Category_, then click again on **Create new entry**. 5. Type `Brunch` in the _Name_ field, then click **Save**. The "French Food" and "Brunch" categories are now listed in the _Collection types - Category_ view of the _Content Manager_. Now, we will add a category to a restaurant: 1. Go to _Content Manager > Collection types - Restaurant_ in the navigation, and click on "Biscotte Restaurant". 2. In the **Categories** drop-down list at the bottom of the page, select "French Food". Scroll back to the top of the page and click **Save**.
Step 4: Set Roles & Permissions ### Step 4: Set Roles & Permissions We have just added a restaurant and 2 categories. We now have enough content to consume (pun intended). But first, we need to make sure that the content is publicly accessible through the API: 1. Click on _ Settings_ at the bottom of the main navigation. 2. Under _Users & Permissions Plugin_, choose _Roles_. 3. Click the **Public** role. 4. Scroll down under _Permissions_. 5. In the _Permissions_ tab, find _Restaurant_ and click on it. 6. Click the checkboxes next to **find** and **findOne**. 7. Repeat with _Category_: click the checkboxes next to **find** and **findOne**. 8. Finally, click **Save**.
Step 5: Publish the content ### Step 5: Publish the content By default, any content you create is saved as a draft. Let's publish our categories and restaurant. First, navigate to _Content Manager > Collection types - Category_. From there: 1. Click the "Brunch" entry. 2. On the next screen, click **Publish**. 3. In the _Confirmation_ window, click **Yes, publish**. Then, go back to the Categories list and repeat for the "French Food" category. Finally, to publish your favorite restaurant, go to _Content Manager > Collection types - Restaurant_, click the "Biscotte Restaurant" entry, and **Publish** it.
Step 6: Use the API ### Step 6: Use the API OK dear gourmet, we have just finished creating our content and making it accessible through the API. You can give yourself a pat on the back — but you have yet to see the final result of your hard work. There you are: the list of restaurants should be accessible by visting the `/api/restaurants` path of your Strapi Cloud project URL (e.g., `https://beautiful-first-strapi-project.strapiapp.com/api/restaurants`). Try it now! The result should be similar to the example response below 👇.
Click me to view an example of API response: ```json { "data": [ { "id": 3, "documentId": "wf7m1n3g8g22yr5k50hsryhk", "Name": "Biscotte Restaurant", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers." } ] } ], "createdAt": "2024-09-10T12:49:32.350Z", "updatedAt": "2024-09-10T13:14:18.275Z", "publishedAt": "2024-09-10T13:14:18.280Z", "locale": null } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ```
:::callout Congratulations! Now your content is created, published, and you have permissions to request it through the API. Keep on creating amazing content! ::: :::tip Tip: Transfer data between your local and Strapi Cloud projects The databases for your Strapi Cloud project and your local project are different. This means that data is not automatically synchronized between your Strapi Cloud and local projects. You can use the [data management system](/cms/features/data-management) to transfer data between projects. ::: ## What to do next? Now that you know the basics of creating and publishing content with Strapi, we encourage you to explore and dig deeper into some Strapi features: learn how to use Strapi's [REST](/cms/api/rest) API to query the content,
learn more about Strapi features by browsing the **Features** category,
learn more about Strapi Cloud projects by reading the [Cloud Documentation](/cloud/intro),
and [customize your Strapi back end](/cms/backend-customization) and [admin panel](/cms/admin-panel-customization) for advanced use cases.
# Templates Source: //cms/templates # Templates Templates in Strapi 5 are standalone, pre-made Strapi applications designed for specific use cases. Strapi 5 templates are folders that include all files and folders that you would find in a typical Strapi application (see [project structure](/cms/project-structure)). ## Using a template To create a new Strapi project based on a template, run the following command: In addition to the mandatory `--template` parameter, you can pass the optional `--template-path` and `--template-branch` options to more precisely define the template to use. The following table lists all the possible ways to define which template to use: | Syntax | Description | |--------|-------------| | `--template website` | Using one of the calling it by its (folder) name. | | `--template strapi/strapi` | Using the template's GitHub repository shorthand.
This will use the default repository branch. | | `--template strapi/strapi/some/sub/path` | Using the template's GitHub repository shorthand and specifying a subpath.
This will use the default repository branch. | | `--template strapi/strapi`
`--template-branch=xxx`
`--template-path=some/sub/path` | The most verbose way, explicitly defining a template branch and a subpath. | | `--template https://github.com/owner/some-template-repo` | Using a full repository URL.
This will use the default repository branch. | | `--template https://github.com/owner/some-template-repo --template-branch=xxx --template-path=sub/path` | Using a full repository URL, and specifying both the branch and the subpath for the template. | | `--template https://github.com/strapi/strapi/tree/branch/sub/path` | Using a repository, branch, and subpath directly.

⚠️ _Warning: This won't work with branch names that include a `/`. In such cases, it's best to explicitly define `--template-branch` and `--template-path`._ | ## Creating a template Creating a Strapi 5 template is as simple as creating a Strapi application. Create the application (see [CLI installation](/cms/installation/cli)) and the generated folder containing your Strapi 5 application can serve as a template. You can then pass it to the `--template` flag when creating a new Strapi 5 application to use it as a template. An example of what a template could look like is the . # Testing Source: //cms/testing # Unit testing :::strapi The Strapi blog has a tutorial on how to implement Once this is done add this to `package.json` file add `test` command to `scripts` section ```json "scripts": { "develop": "strapi develop", "start": "strapi start", "build": "strapi build", "strapi": "strapi", "test": "jest --forceExit --detectOpenHandles" }, ``` and add those lines at the bottom of file ```json "jest": { "testPathIgnorePatterns": [ "/node_modules/", ".tmp", ".cache" ], "testEnvironment": "node" } ``` Those will inform `Jest` not to look for test inside the folder where it shouldn't. ## Set up a testing environment Test framework must have a clean empty environment to perform valid test and also not to interfere with current database. Once `jest` is running it uses the `test` [environment](/cms/configurations/environment) (switching `NODE_ENV` to `test`) so we need to create a special environment setting for this purpose. Create a new config for test env `./config/env/test/database.js` and add the following value `"filename": ".tmp/test.db"` - the reason of that is that we want to have a separate sqlite database for tests, so our test will not touch real data. This file will be temporary, each time test is finished, we will remove that file that every time tests are run on the clean database. The whole file will look like this: ```js title="path: ./config/env/test/database.js" module.exports = ({ env }) => ({ connection: { client: 'sqlite', connection: { filename: env('DATABASE_FILENAME', '.tmp/test.db'), }, useNullAsDefault: true, debug: false }, }); ``` ## Create a Strapi instance In order to test anything we need to have a strapi instance that runs in the testing environment, basically we want to get instance of strapi app as object, similar like creating an instance for . These tasks require adding some files - let's create a folder `tests` where all the tests will be put and inside it, next to folder `helpers` where main Strapi helper will be in file strapi.js. ```js title="path: ./tests/helpers/strapi.js" const Strapi = require("@strapi/strapi"); const fs = require("fs"); let instance; async function setupStrapi() { if (!instance) { await Strapi().load(); instance = strapi; await instance.server.mount(); } return instance; } async function cleanupStrapi() { const dbSettings = strapi.config.get("database.connection"); //close server to release the db-file await strapi.server.httpServer.close(); // close the connection to the database before deletion await strapi.db.connection.destroy(); //delete test database after all tests have completed if (dbSettings && dbSettings.connection && dbSettings.connection.filename) { const tmpDbFile = dbSettings.connection.filename; if (fs.existsSync(tmpDbFile)) { fs.unlinkSync(tmpDbFile); } } } module.exports = { setupStrapi, cleanupStrapi }; ``` ## Test a Strapi instance We need a main entry file for our tests, one that will also test our helper file. ```js title="path: ./tests/app.test.js" const fs = require('fs'); const { setupStrapi, cleanupStrapi } = require("./helpers/strapi"); beforeAll(async () => { await setupStrapi(); }); afterAll(async () => { await cleanupStrapi(); }); it("strapi is defined", () => { expect(strapi).toBeDefined(); }); ``` Actually this is all we need for writing unit tests. Just run `yarn test` and see a result of your first test ```bash yarn run v1.13.0 $ jest PASS tests/app.test.js ✓ strapi is defined (2 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 4.187 s Ran all test suites. ✨ Done in 5.73s. ``` :::tip If you receive a timeout error for Jest, please add the following line right before the `beforeAll` method in the `app.test.js` file: `jest.setTimeout(15000)` and adjust the milliseconds value as you need. ::: ## Test a basic endpoint controller :::tip In the example we'll use and example `Hello world` `/hello` endpoint from [controllers](/cms/backend-customization/controllers) section. ::: Some might say that API tests are not unit but limited integration tests, regardless of nomenclature, let's continue with testing first endpoint. We'll test if our endpoint works properly and route `/hello` does return `Hello World` Let's create a separate test file where `supertest` will be used to check if endpoint works as expected. ```js title="path: ./tests/hello/index.js" const request = require('supertest'); it("should return hello world", async () => { await request(strapi.server.httpServer) .get("/api/hello") .expect(200) // Expect response http code 200 .then((data) => { expect(data.text).toBe("Hello World!"); // expect the response text }); }); ``` Then include this code to `./tests/app.test.js` at the bottom of that file ```js require('./hello'); ``` and run `yarn test` which should return ```bash ➜ my-project yarn test yarn run v1.13.0 $ jest --detectOpenHandles PASS tests/app.test.js (5.742 s) ✓ strapi is defined (4 ms) ✓ should return hello world (208 ms) [2020-05-22T14:37:38.018Z] debug GET /hello (58 ms) 200 Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 6.635 s, estimated 7 s Ran all test suites. ✨ Done in 9.09s. ``` :::tip If you receive an error `Jest has detected the following 1 open handles potentially keeping Jest from exiting` check `jest` version as `26.6.3` works without an issue. ::: ## Test an `auth` endpoint controller In this scenario we'll test authentication login endpoint with two tests 1. Test `/auth/local` that should login user and return `jwt` token 2. Test `/users/me` that should return users data based on `Authorization` header ```js title="path: ./tests/user/index.js" const request = require('supertest'); // user mock data const mockUserData = { username: "tester", email: "tester@strapi.com", provider: "local", password: "1234abc", confirmed: true, blocked: null, }; it("should login user and return jwt token", async () => { /** Creates a new user and save it to the database */ await strapi.plugins["users-permissions"].services.user.add({ ...mockUserData, }); await request(strapi.server.httpServer) // app server is an instance of Class: http.Server .post("/api/auth/local") .set("accept", "application/json") .set("Content-Type", "application/json") .send({ identifier: mockUserData.email, password: mockUserData.password, }) .expect("Content-Type", /json/) .expect(200) .then((data) => { expect(data.body.jwt).toBeDefined(); }); }); it('should return users data for authenticated user', async () => { /** Gets the default user role */ const defaultRole = await strapi.query('plugin::users-permissions.role').findOne({}, []); const role = defaultRole ? defaultRole.id : null; /** Creates a new user an push to database */ const user = await strapi.plugins['users-permissions'].services.user.add({ ...mockUserData, username: 'tester2', email: 'tester2@strapi.com', role, }); const jwt = strapi.plugins['users-permissions'].services.jwt.issue({ id: user.id, }); await request(strapi.server.httpServer) // app server is an instance of Class: http.Server .get('/api/users/me') .set('accept', 'application/json') .set('Content-Type', 'application/json') .set('Authorization', 'Bearer ' + jwt) .expect('Content-Type', /json/) .expect(200) .then(data => { expect(data.body).toBeDefined(); expect(data.body.id).toBe(user.id); expect(data.body.username).toBe(user.username); expect(data.body.email).toBe(user.email); }); }); ``` Then include this code to `./tests/app.test.js` at the bottom of that file ```js require('./user'); ``` All the tests above should return an console output like ```bash ➜ my-project git:(master) yarn test yarn run v1.13.0 $ jest --forceExit --detectOpenHandles [2020-05-27T08:30:30.811Z] debug GET /hello (10 ms) 200 [2020-05-27T08:30:31.864Z] debug POST /auth/local (891 ms) 200 PASS tests/app.test.js (6.811 s) ✓ strapi is defined (3 ms) ✓ should return hello world (54 ms) ✓ should login user and return jwt token (1049 ms) ✓ should return users data for authenticated user (163 ms) Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total Snapshots: 0 total Time: 6.874 s, estimated 9 s Ran all test suites. ✨ Done in 8.40s. ``` # TypeScript Source: //cms/typescript # TypeScript - Add TypeScript support to an existing Strapi project using the provided [conversion](/cms/typescript/adding-support-to-existing-project) steps.
:::strapi What to do next? - Understand the [structure](/cms/project-structure) of a TypeScript-based Strapi project - Learn about the [configuration options](/cms/configurations/typescript) options related to TypeScript - Deep dive into TypeScript-related development [options and features](/cms/typescript/development) - Read the [guides](/cms/typescript/guides) for specific use cases ::: # TypeScript development Source: //cms/typescript/development # TypeScript development with Strapi While developing a [TypeScript](/cms/typescript)-based application with Strapi, you can: - access [typings for the `Strapi`](#use-strapi-typescript-typings) class with autocompletion, - [generate typings](#generate-typings-for-content-types-schemas) for your project's content-types, - [start Strapi programmatically](#start-strapi-programmatically), - and follow some TypeScript-specific instructions for [plugins development](#develop-a-plugin-using-typescript). :::strapi Documents and entries More information and best practices on how to manipulate documents and entries with a TypeScript-based project can be found in the [dedicated guide](/cms/typescript/documents-and-entries). ::: ## Use `Strapi` TypeScript typings Strapi provides typings on the `Strapi` class to enhance the TypeScript development experience. These typings come with an autocomplete feature that automatically offers suggestions while developing. To experience TypeScript-based autocomplete while developing Strapi applications, you could try the following: 1. Open the `./src/index.ts` file from your code editor. 2. Declare the `strapi` argument as type `Strapi` within the global `register` method: ```typescript title="./src/index.ts" import { Strapi } from '@strapi/strapi'; export default { register({ strapi }: { strapi: Strapi }) { // ... }, }; ``` 3. Within the body of the `register` method, start typing `strapi.` and use keyboard arrows to browse the available properties. 4. Choose `runLifecyclesFunctions` from the list. 5. When the `strapi.runLifecyclesFunctions` method is added, a list of available lifecycle types (i.e. `register`, `bootstrap` and `destroy`) are returned by the code editor. Use keyboard arrows to choose one of the lifecycles and the code will autocomplete. ## Generate typings for content-types schemas To generate typings for your project schemas use the [`ts:generate-types` CLI command](/cms/cli#strapi-ts). The `ts:generate-types` command creates the folder `types`, at the project root, which stores the typings for your project. The optional `--debug` flag returns a detailed table of the generated schemas. To use `ts:generate-types`run the following code in a terminal at the project root: :::tip Tip: Automatically generate types Types can be automatically generated on server restart by adding `autogenerate: true` to [the `config/typescript.js|ts` configuration file](/cms/configurations/typescript#strapi-specific-configuration-for-typescript). ::: :::tip Tip: Using types in your front-end application To use Strapi types in your front-end application, you can until Strapi implements an official solution. ::: ### Fix build issues with the generated types The generated types can be excluded so that the Entity Service doesn't use them and falls back on looser types that don't check the actual properties available in the content types. To do that, edit the `tsconfig.json` of the Strapi project and add `types/generated/**` to the `exclude` array: ```json title="./tsconfig.json" // ... "exclude": [ "node_modules/", "build/", "dist/", ".cache/", ".tmp/", "src/admin/", "**/*.test.ts", "src/plugins/**", "types/generated/**" ] // ... ``` However, if you still want to use the generated types on your project, but don't want Strapi to use them, a workaround could be to copy those generated types and paste them outside of the `generated` directory (so that they aren't overwritten when the types are regenerated) and remove the `declare module '@strapi/types'` from the bottom of the file. :::warning Types should only be imported from `@strapi/strapi` to avoid breaking changes. The types in `@strapi/types` are for internal use only and may change without notice. ::: ## Start Strapi programmatically To start Strapi programmatically in a TypeScript project the Strapi instance requires the compiled code location. This section describes how to set and indicate the compiled code directory. ### Use the `strapi()` factory {#use-the-createstrapi-factory} Strapi can be run programmatically by using the `strapi()` factory. Since the code of TypeScript projects is compiled in a specific directory, the parameter `distDir` should be passed to the factory to indicate where the compiled code should be read: ```js title="./server.js" const strapi = require('@strapi/strapi'); const app = strapi.createStrapi({ distDir: './dist' }); app.start(); ``` ### Use the `strapi.compile()` function The `strapi.compile()` function should be mostly used for developing tools that need to start a Strapi instance and detect whether the project includes TypeScript code. `strapi.compile()` automatically detects the project language. If the project code contains any TypeScript code, `strapi.compile()` compiles the code and returns a context with specific values for the directories that Strapi requires: ```js const strapi = require('@strapi/strapi'); strapi.compile().then(appContext => strapi(appContext).start()); ``` ## Develop a plugin using TypeScript New plugins can be generated following the [plugins development documentation](/cms/plugins-development/developing-plugins), ensuring you select "TypeScript" when prompted by the CLI tool. There are 2 important distinctions for TypeScript applications: - After creating the plugin, run `yarn` or `npm install` in the plugin directory `src/admin/plugins/[my-plugin-name]` to install the dependencies for the plugin. - Run `yarn build` or `npm run build` in the plugin directory `src/admin/plugins/[my-plugin-name]` to build the admin panel including the plugin. :::note It is not necessary to repeat the `yarn` or `npm install` command after the initial installation. The `yarn build` or `npm run build` command is necessary to implement any plugin development that affects the admin panel. ::: # TypeScript Guides Source: //cms/typescript/guides # TypeScript guides The following guides will help you on specific aspects of a [Typescript-based](/cms/typescript) Strapi project. # Upgrade tool Source: //cms/upgrade-tool # Upgrade tool The upgrade tool assists Strapi users in upgrading their Strapi application dependencies and code to a specific version. Running the upgrade tool triggers the update of the application dependencies, their installation, and the execution of a series of **codemods**