# 云邮件提供商 Source: https://docs.strapi.io/cloud/advanced/email ## 配置提供者 Description: 🌐 In your Strapi project, create a /config/env/production/plugins.js or /config/env/production/plugins.ts file with the following content: (Source: https://docs.strapi.io/cloud/advanced/email#configure-the-provider) Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // … some unrelated plugins configuration options // highlight-start email: { config: { // … provider-specific upload configuration options go here } // highlight-end // … some other unrelated plugins configuration options } }); ``` --- Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // … some unrelated plugins configuration options // highlight-start email: { config: { // … provider-specific upload configuration options go here } // highlight-end // … some other unrelated plugins configuration options } }); ``` Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // ... email: { config: { provider: 'sendgrid', providerOptions: { apiKey: env('SENDGRID_API_KEY'), }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` --- Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // ... email: { config: { provider: 'amazon-ses', providerOptions: { key: env('AWS_SES_KEY'), secret: env('AWS_SES_SECRET'), amazon: 'https://email.us-east-1.amazonaws.com', }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` --- Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // ... email: { config: { provider: 'mailgun', providerOptions: { key: env('MAILGUN_API_KEY'), // Required domain: env('MAILGUN_DOMAIN'), // Required url: env('MAILGUN_URL', 'https://api.mailgun.net'), //Optional. If domain region is Europe use 'https://api.eu.mailgun.net' }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // ... email: { config: { provider: 'sendgrid', providerOptions: { apiKey: env('SENDGRID_API_KEY'), }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` --- Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // ... email: { config: { provider: 'amazon-ses', providerOptions: { key: env('AWS_SES_KEY'), secret: env('AWS_SES_SECRET'), amazon: 'https://email.us-east-1.amazonaws.com', }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` --- Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // ... email: { config: { provider: 'mailgun', providerOptions: { key: env('MAILGUN_API_KEY'), // Required domain: env('MAILGUN_DOMAIN'), // Required url: env('MAILGUN_URL', 'https://api.mailgun.net'), //Optional. If domain region is Europe use 'https://api.eu.mailgun.net' }, settings: { defaultFrom: 'myemail@protonmail.com', defaultReplyTo: 'myemail@protonmail.com', }, }, }, // ... }); ``` # Strapi Cloud 的中间件配置 Source: https://docs.strapi.io/cloud/advanced/middlewares ## Strapi Cloud 的中间件配置 Description: 🌐 To apply custom middleware configuration on Strapi Cloud, place your changes in: (Source: https://docs.strapi.io/cloud/advanced/middlewares#middleware-configuration-for-strapi-cloud) Language: JavaScript File path: N/A ``` config/env/production/middlewares.js ``` --- Language: JavaScript File path: N/A ``` config/env/production/middlewares.ts ``` ## 自定义内容安全策略 (CSP) Description: 🌐 Create or update config/env/production/middlewares: (Source: https://docs.strapi.io/cloud/advanced/middlewares#custom-content-security-policy-csp) Language: JavaScript File path: config/env/production/middlewares.js ```js module.exports = [ 'strapi::errors', { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'your-custom-domain.com', // replace with your provider domain ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'your-custom-domain.com', // replace with your provider domain ], upgradeInsecureRequests: null, }, }, }, }, 'strapi::cors', 'strapi::poweredBy', 'strapi::logger', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` --- Language: TypeScript File path: config/env/production/middlewares.ts ```ts export default [ 'strapi::errors', { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'your-custom-domain.com', // replace with your provider domain ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'your-custom-domain.com', // replace with your provider domain ], upgradeInsecureRequests: null, }, }, }, }, 'strapi::cors', 'strapi::poweredBy', 'strapi::logger', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` ## 自定义 CORS 头 Description: 🌐 If your frontend sends custom request headers (e.g. (Source: https://docs.strapi.io/cloud/advanced/middlewares#custom-cors-headers) Language: JavaScript File path: config/env/production/middlewares.js ```js module.exports = ({ env }) => [ 'strapi::errors', 'strapi::security', { name: 'strapi::cors', config: { enabled: true, origin: [env('CLIENT_URL')], headers: [ 'Content-Type', 'Authorization', 'Origin', 'Accept', 'X-Requested-With', 'your-custom-header', // add any custom headers your frontend sends ], }, }, 'strapi::poweredBy', 'strapi::logger', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` --- Language: TypeScript File path: config/env/production/middlewares.ts ```ts export default ({ env }) => [ 'strapi::errors', 'strapi::security', { name: 'strapi::cors', config: { enabled: true, origin: [env('CLIENT_URL')], headers: [ 'Content-Type', 'Authorization', 'Origin', 'Accept', 'X-Requested-With', 'your-custom-header', // add any custom headers your frontend sends ], }, }, 'strapi::poweredBy', 'strapi::logger', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` # 为 Strapi Cloud 上传提供商配置 Source: https://docs.strapi.io/cloud/advanced/upload ## 配置提供程序 Description: 🌐 To configure a third-party upload provider in your Strapi project, create or edit the plugins configuration file for your production environment /config/env/production/plugins.js|ts by adding upload configuration options as follows: (Source: https://docs.strapi.io/cloud/advanced/upload#configure-the-provider) Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // … some unrelated plugins configuration options // highlight-start upload: { config: { // … provider-specific upload configuration options go here } // highlight-end // … some other unrelated plugins configuration options } }); ``` --- Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // … some unrelated plugins configuration options // highlight-start upload: { config: { // … provider-specific upload configuration options go here } // highlight-end // … some other unrelated plugins configuration options } }); ``` Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // ... upload: { config: { provider: 'cloudinary', providerOptions: { cloud_name: env('CLOUDINARY_NAME'), api_key: env('CLOUDINARY_KEY'), api_secret: env('CLOUDINARY_SECRET'), }, actionOptions: { upload: {}, uploadStream: {}, delete: {}, }, }, }, // ... }); ``` --- Language: JavaScript File path: /config/env/production/plugins.js ```js module.exports = ({ env }) => ({ // ... upload: { config: { provider: 'aws-s3', providerOptions: { baseUrl: env('CDN_URL'), rootPath: env('CDN_ROOT_PATH'), s3Options: { credentials: { accessKeyId: env('AWS_ACCESS_KEY_ID'), secretAccessKey: env('AWS_ACCESS_SECRET'), }, region: env('AWS_REGION'), params: { ACL: env('AWS_ACL', 'public-read'), signedUrlExpires: env('AWS_SIGNED_URL_EXPIRES', 15 * 60), Bucket: env('AWS_BUCKET'), }, }, }, actionOptions: { upload: {}, uploadStream: {}, delete: {}, }, }, }, // ... }); ``` Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // ... upload: { config: { provider: 'cloudinary', providerOptions: { cloud_name: env('CLOUDINARY_NAME'), api_key: env('CLOUDINARY_KEY'), api_secret: env('CLOUDINARY_SECRET'), }, actionOptions: { upload: {}, uploadStream: {}, delete: {}, }, }, }, // ... }); ``` --- Language: TypeScript File path: /config/env/production/plugins.ts ```ts export default ({ env }) => ({ // ... upload: { config: { provider: 'aws-s3', providerOptions: { baseUrl: env('CDN_URL'), rootPath: env('CDN_ROOT_PATH'), s3Options: { credentials: { accessKeyId: env('AWS_ACCESS_KEY_ID'), secretAccessKey: env('AWS_ACCESS_SECRET'), }, region: env('AWS_REGION'), params: { ACL: env('AWS_ACL', 'public-read'), signedUrlExpires: env('AWS_SIGNED_URL_EXPIRES', 15 * 60), Bucket: env('AWS_BUCKET'), }, }, }, actionOptions: { upload: {}, uploadStream: {}, delete: {}, }, }, }, // ... }); ``` ## 配置安全中间件 Description: Code example from "配置安全中间件" (Source: https://docs.strapi.io/cloud/advanced/upload#configure-the-security-middleware) Language: JavaScript File path: /config/env/production/middlewares.js ```js module.exports = [ // ... { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'res.cloudinary.com' ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'res.cloudinary.com', ], upgradeInsecureRequests: null, }, }, }, }, // ... ]; ``` --- Language: JavaScript File path: /config/env/production/middlewares.js ```js module.exports = [ // ... { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'yourBucketName.s3.yourRegion.amazonaws.com', ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'yourBucketName.s3.yourRegion.amazonaws.com', ], upgradeInsecureRequests: null, }, }, }, }, // ... ]; ``` Language: TypeScript File path: /config/env/production/middlewares.ts ```ts export default [ // ... { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'res.cloudinary.com' ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'res.cloudinary.com', ], upgradeInsecureRequests: null, }, }, }, }, // ... ]; ``` --- Language: TypeScript File path: /config/env/production/middlewares.ts ```ts export default [ // ... { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'connect-src': ["'self'", 'https:'], 'img-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'yourBucketName.s3.yourRegion.amazonaws.com', ], 'media-src': [ "'self'", 'data:', 'blob:', 'market-assets.strapi.io', 'yourBucketName.s3.yourRegion.amazonaws.com', ], upgradeInsecureRequests: null, }, }, }, }, // ... ]; ``` # Cloud Cli Source: https://docs.strapi.io/cloud/cli/cloud-cli ## Strapi 登录 Description: 🌐 Log in Strapi Cloud. (Source: https://docs.strapi.io/cloud/cli/cloud-cli#strapi-login) Language: Bash File path: N/A ```bash strapi login ``` ## strapi 链接 Description: 🌐 Links project in the current folder to an existing project in Strapi Cloud. (Source: https://docs.strapi.io/cloud/cli/cloud-cli#strapi-link) Language: Bash File path: N/A ```bash strapi link ``` ## strapi 部署 Description: 🌐 Deploy a linked local project (< 100MB) to Strapi Cloud. (Source: https://docs.strapi.io/cloud/cli/cloud-cli#strapi-deploy) Language: Bash File path: N/A ```bash strapi deploy ``` ## strapi 项目 Description: 🌐 Lists all Strapi Cloud projects associated with your account. (Source: https://docs.strapi.io/cloud/cli/cloud-cli#strapi-projects-newbadge-) Language: Bash File path: N/A ```bash strapi projects ``` ## strapi 登出 Description: 🌐 Log out of Strapi Cloud. (Source: https://docs.strapi.io/cloud/cli/cloud-cli#strapi-logout) Language: Bash File path: N/A ```bash strapi logout ``` # Caching Source: https://docs.strapi.io/cloud/getting-started/caching ## Strapi Cloud 中的 Cache-Control 头 Description: 🌐 Responses from dynamic apps served by Strapi Cloud are not cached by default. (Source: https://docs.strapi.io/cloud/getting-started/caching#cache-control-header-in-strapi-cloud) Language: Fish File path: N/A ```fish function myHandler(req, res) { // Set the Cache-Control header to cache responses for 1 day res.setHeader('Cache-Control', 'max-age=86400'); // Add your logic to generate the response here } ``` --- Language: JavaScript File path: N/A ```js import { Request, Response } from 'express'; function myHandler(req: Request, res: Response) { // Set the Cache-Control header to cache responses for 1 day res.setHeader('Cache-Control', 'max-age=86400'); // Add your logic to generate the response here } ``` # Strapi 云端 - CLI 部署 Source: https://docs.strapi.io/cloud/getting-started/deployment-cli ## 登录 Strapi 云 Description: 打开你的终端。 2. (Source: https://docs.strapi.io/cloud/getting-started/deployment-cli#logging-in-to-strapi-cloud) Language: Bash File path: N/A ```bash yarn strapi login ``` --- Language: Bash File path: N/A ```bash npx run strapi login ``` ## 将本地项目链接到 Strapi Cloud Description: 在你的终端中,仍然在你的 Strapi 项目文件夹中,输入以下命令将本地项目链接到你现有的 Strapi Cloud 项目: (Source: https://docs.strapi.io/cloud/getting-started/deployment-cli#linking-your-local-project-to-strapi-cloud) Language: Bash File path: N/A ```bash yarn strapi link ``` --- Language: Bash File path: N/A ```bash npx run strapi link ``` ## 部署你的项目 Description: 在你的终端中,仍然在你的 Strapi 项目文件夹中,输入以下命令来部署项目: (Source: https://docs.strapi.io/cloud/getting-started/deployment-cli#deploying-your-project) Language: Bash File path: N/A ```bash yarn strapi deploy ``` --- Language: Bash File path: N/A ```bash npx run strapi deploy ``` # 管理面板定制 Source: https://docs.strapi.io/cms/admin-panel-customization ## 一般考虑 Description: 🌐 Before deployment, the admin panel needs to be built, by running the following command from the project's root directory: (Source: https://docs.strapi.io/cms/admin-panel-customization#general-considerations) Language: Bash File path: /src/admin/app.js ```bash yarn build ``` --- Language: Bash File path: /src/admin/app.js ```bash npm run build ``` ## 基本示例 Description: 🌐 The following is an example of a basic customization of the admin panel: (Source: https://docs.strapi.io/cms/admin-panel-customization#basic-example) Language: JavaScript File path: /src/admin/app.js ```js import AuthLogo from "./extensions/my-logo.png"; import MenuLogo from "./extensions/logo.png"; import favicon from "./extensions/favicon.png"; export default { config: { // Replace the Strapi logo in auth (login) views auth: { logo: AuthLogo, }, // Replace the favicon head: { favicon: favicon, }, // Add a new locale, other than 'en' locales: ["fr", "de"], // Replace the Strapi logo in the main navigation menu: { logo: MenuLogo, }, // Override or extend the theme theme: { // overwrite light theme properties light: { colors: { primary100: "#f6ecfc", primary200: "#e0c1f4", primary500: "#ac73e6", primary600: "#9736e8", primary700: "#8312d1", danger700: "#b72b1a", }, }, // overwrite dark theme properties dark: { // ... }, }, // Extend the translations translations: { fr: { "Auth.form.email.label": "test", Users: "Utilisateurs", City: "CITY (FRENCH)", // Customize the label of the Content Manager table. Id: "ID french", }, }, // Disable video tutorials tutorials: false, // Disable notifications about new Strapi releases notifications: { releases: false }, }, bootstrap() {}, }; ``` --- Language: JSON File path: /src/admin/tsconfig.json ```json { "compilerOptions": { "types": ["vite/client"] } } ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts import AuthLogo from "./extensions/my-logo.png"; import MenuLogo from "./extensions/logo.png"; import favicon from "./extensions/favicon.png"; export default { config: { // Replace the Strapi logo in auth (login) views auth: { logo: AuthLogo, }, // Replace the favicon head: { favicon: favicon, }, // Add a new locale, other than 'en' locales: ["fr", "de"], // Replace the Strapi logo in the main navigation menu: { logo: MenuLogo, }, // Override or extend the theme theme: { dark:{ colors: { alternative100: '#f6ecfc', alternative200: '#e0c1f4', alternative500: '#ac73e6', alternative600: '#9736e8', alternative700: '#8312d1', buttonNeutral0: '#ffffff', buttonPrimary500: '#7b79ff', // you can see other colors in the link below }, }, light:{ // you can see the light color here just like dark colors https://github.com/strapi/design-system/blob/main/packages/design-system/src/themes/lightTheme/light-colors.ts }, }, }, // Extend the translations // you can see the traslations keys here https://github.com/strapi/strapi/blob/develop/packages/core/admin/admin/src/translations translations: { fr: { "Auth.form.email.label": "test", Users: "Utilisateurs", City: "CITY (FRENCH)", // Customize the label of the Content Manager table. Id: "ID french", }, }, // Disable video tutorials tutorials: false, // Disable notifications about new Strapi releases notifications: { releases: false }, }, bootstrap() {}, }; ``` # 管理面板打包器 Source: https://docs.strapi.io/cms/admin-panel-customization/bundlers ## 快 Description: 🌐 To extend the usage of Vite, define a function that extends its configuration inside /src/admin/vite.config: (Source: https://docs.strapi.io/cms/admin-panel-customization/bundlers#vite) Language: JavaScript File path: /src/admin/vite.config.js ```js const { mergeConfig } = require("vite"); module.exports = (config) => { // Important: always return the modified config return mergeConfig(config, { resolve: { alias: { "@": "/src", }, }, }); }; ``` --- Language: TypeScript File path: /src/admin/vite.config.ts ```ts import { mergeConfig } from "vite"; export default (config) => { // Important: always return the modified config return mergeConfig(config, { resolve: { alias: { "@": "/src", }, }, }); }; ``` ## Webpack Description: 在 Strapi 5 中,默认的打包工具是 Vite。要使用 作为打包工具,你需要将其作为选项传递给 strapi develop 命令: (Source: https://docs.strapi.io/cms/admin-panel-customization/bundlers#webpack) Language: JavaScript File path: /src/admin/webpack.config.js ```js module.exports = (config, webpack) => { // Note: we provide webpack above so you should not `require` it // Perform customizations to webpack config config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)); // Important: return the modified config return config; }; ``` --- Language: TypeScript File path: /src/admin/webpack.config.ts ```ts export default (config, webpack) => { // Note: we provide webpack above so you should not `require` it // Perform customizations to webpack config config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//)); // Important: return the modified config return config; }; ``` Language: Bash File path: /webpack.js ```bash strapi develop --bundler=webpack ``` # 网站图标 Source: https://docs.strapi.io/cms/admin-panel-customization/favicon ## 网站图标 Description: 替换 Strapi 项目根目录下的 favicon.png 文件 用以下代码编辑 strapi::favicon 中间件配置: (Source: https://docs.strapi.io/cms/admin-panel-customization/favicon#favicon) Language: JavaScript File path: /config/middlewares.js ```js // … { name: 'strapi::favicon', config: { path: 'my-custom-favicon.png', }, }, // … ``` # 首页自定义 Source: https://docs.strapi.io/cms/admin-panel-customization/homepage ## 注册自定义小部件 Description: 在他们的插件 package.json 中将 ^5.13.0 设置为其 @strapi/strapi 的 peerDependency。这个 peerDependency 支撑了市场的兼容性检查。 - 或者,在调用 API 之前检查它是否存在: (Source: https://docs.strapi.io/cms/admin-panel-customization/homepage#registering-custom-widgets) Language: JavaScript File path: src/plugins/my-plugin/admin/src/index.js ```js import pluginId from './pluginId'; import MyWidgetIcon from './components/MyWidgetIcon'; export default { register(app) { // Register the plugin itself app.registerPlugin({ id: pluginId, name: 'My Plugin', }); // Register a widget for the Homepage app.widgets.register({ icon: MyWidgetIcon, title: { id: `${pluginId}.widget.title`, defaultMessage: 'My Widget', }, component: async () => { const component = await import('./components/MyWidget'); return component.default; }, /** * Use this instead if you used a named export for your component */ // component: async () => { // const { Component } = await import('./components/MyWidget'); // return Component; // }, id: 'my-custom-widget', pluginId: pluginId, }); }, bootstrap() {}, // ... }; ``` --- Language: TypeScript File path: src/plugins/my-plugin/admin/src/index.ts ```ts import pluginId from './pluginId'; import MyWidgetIcon from './components/MyWidgetIcon'; import type { StrapiApp } from '@strapi/admin/strapi-admin'; export default { register(app: StrapiApp) { // Register the plugin itself app.registerPlugin({ id: pluginId, name: 'My Plugin', }); // Register a widget for the Homepage app.widgets.register({ icon: MyWidgetIcon, title: { id: `${pluginId}.widget.title`, defaultMessage: 'My Widget', }, component: async () => { const component = await import('./components/MyWidget'); return component.default; }, /** * Use this instead if you used a named export for your component */ // component: async () => { // const { Component } = await import('./components/MyWidget'); // return Component; // }, id: 'my-custom-widget', pluginId: pluginId, }); }, bootstrap() {}, // ... }; ``` Language: JavaScript File path: N/A ```js if ('widgets' in app) { // proceed with the registration } ``` ## 创建一个小部件组件 Description: 🌐 Here's how to implement a basic widget component: (Source: https://docs.strapi.io/cms/admin-panel-customization/homepage#creating-a-widget-component) Language: JavaScript File path: src/plugins/my-plugin/admin/src/components/MyWidget/index.js ```js import React, { useState, useEffect } from 'react'; import { Widget } from '@strapi/admin/strapi-admin'; const MyWidget = () => { const [loading, setLoading] = useState(true); const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { // Fetch your data here const fetchData = async () => { try { // Replace with your actual API call const response = await fetch('/my-plugin/data'); const result = await response.json(); setData(result); setLoading(false); } catch (err) { setError(err); setLoading(false); } }; fetchData(); }, []); if (loading) { return ; } if (error) { return ; } if (!data || data.length === 0) { return ; } return (
{/* Your widget content here */}
    {data.map((item) => (
  • {item.name}
  • ))}
); }; export default MyWidget; ``` --- Language: TypeScript File path: src/plugins/my-plugin/admin/src/components/MyWidget/index.tsx ```ts import React, { useState, useEffect } from 'react'; import { Widget } from '@strapi/admin/strapi-admin'; interface DataItem { id: number; name: string; } const MyWidget: React.FC = () => { const [loading, setLoading] = useState(true); const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { // Fetch your data here const fetchData = async () => { try { // Replace with your actual API call const response = await fetch('/my-plugin/data'); const result = await response.json(); setData(result); setLoading(false); } catch (err) { setError(err instanceof Error ? err : new Error(String(err))); setLoading(false); } }; fetchData(); }, []); if (loading) { return ; } if (error) { return ; } if (!data || data.length === 0) { return ; } return (
{/* Your widget content here */}
    {data.map((item) => (
  • {item.name}
  • ))}
); }; export default MyWidget; ``` ## 示例:添加内容指标小部件 Description: 🌐 The following file registers the plugin and the widget: (Source: https://docs.strapi.io/cms/admin-panel-customization/homepage#example-adding-a-content-metrics-widget) Language: JavaScript File path: src/plugins/content-metrics/admin/src/index.js ```js import { PLUGIN_ID } from './pluginId'; import { Initializer } from './components/Initializer'; import { PluginIcon } from './components/PluginIcon'; import { Stethoscope } from '@strapi/icons' export default { register(app) { app.addMenuLink({ to: `plugins/${PLUGIN_ID}`, icon: PluginIcon, intlLabel: { id: `${PLUGIN_ID}.plugin.name`, defaultMessage: PLUGIN_ID, }, Component: () => import('./pages/App'), }); app.registerPlugin({ id: PLUGIN_ID, initializer: Initializer, isReady: false, name: PLUGIN_ID, }); // Registers the widget app.widgets.register({ icon: Stethoscope, title: { id: `${PLUGIN_ID}.widget.metrics.title`, defaultMessage: 'Content Metrics', }, component: async () => { const component = await import('./components/MetricsWidget'); return component.default; }, id: 'content-metrics', pluginId: PLUGIN_ID, }); }, async registerTrads({ locales }) { return Promise.all( locales.map(async (locale) => { try { const { default: data } = await import(`./translations/${locale}.json`); return { data, locale }; } catch { return { data: {}, locale }; } }) ); }, bootstrap() {}, }; ``` --- Language: JavaScript File path: src/plugins/content-metrics/admin/src/components/MetricsWidget/index.js ```js import React, { useState, useEffect } from 'react'; import { Table, Tbody, Tr, Td, Typography, Box } from '@strapi/design-system'; import { Widget } from '@strapi/admin/strapi-admin' const MetricsWidget = () => { const [loading, setLoading] = useState(true); const [metrics, setMetrics] = useState(null); const [error, setError] = useState(null); useEffect(() => { const fetchMetrics = async () => { try { const response = await fetch('/api/content-metrics/count'); const data = await response.json(); console.log("data:", data); const formattedData = {}; if (data && typeof data === 'object') { Object.keys(data).forEach(key => { const value = data[key]; formattedData[key] = typeof value === 'number' ? value : String(value); }); } setMetrics(formattedData); setLoading(false); } catch (err) { console.error(err); setError(err.message || 'An error occurred'); setLoading(false); } }; fetchMetrics(); }, []); if (loading) { return ( ); } if (error) { return ( ); } if (!metrics || Object.keys(metrics).length === 0) { return No content types found; } return ( {Object.entries(metrics).map(([contentType, count], index) => ( ))}
{String(contentType)} {String(count)}
); }; export default MetricsWidget; ``` --- Language: JavaScript File path: src/plugins/content-metrics/server/src/controllers/metrics.js ```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); } } }); ``` --- Language: JavaScript File path: src/plugins/content-metrics/server/src/routes/index.js ```js export default { 'content-api': { type: 'content-api', routes: [ { method: 'GET', path: '/count', handler: 'metrics.getContentCounts', config: { policies: [], }, }, ], }, }; ``` --- Language: TypeScript File path: src/plugins/content-metrics/admin/src/index.ts ```ts import { PLUGIN_ID } from './pluginId'; import { Initializer } from './components/Initializer'; import { PluginIcon } from './components/PluginIcon'; import { Stethoscope } from '@strapi/icons' export default { register(app) { app.addMenuLink({ to: `plugins/${PLUGIN_ID}`, icon: PluginIcon, intlLabel: { id: `${PLUGIN_ID}.plugin.name`, defaultMessage: PLUGIN_ID, }, Component: () => import('./pages/App'), }); app.registerPlugin({ id: PLUGIN_ID, initializer: Initializer, isReady: false, name: PLUGIN_ID, }); // Registers the widget app.widgets.register({ icon: Stethoscope, title: { id: `${PLUGIN_ID}.widget.metrics.title`, defaultMessage: 'Content Metrics', }, component: async () => { const component = await import('./components/MetricsWidget'); return component.default; }, id: 'content-metrics', pluginId: PLUGIN_ID, }); }, async registerTrads({ locales }) { return Promise.all( locales.map(async (locale) => { try { const { default: data } = await import(`./translations/${locale}.json`); return { data, locale }; } catch { return { data: {}, locale }; } }) ); }, bootstrap() {}, }; ``` --- Language: TypeScript File path: src/plugins/content-metrics/admin/src/components/MetricsWidget/index.ts ```ts import React, { useState, useEffect } from 'react'; import { Table, Tbody, Tr, Td, Typography, Box } from '@strapi/design-system'; import { Widget } from '@strapi/admin/strapi-admin' const MetricsWidget = () => { const [loading, setLoading] = useState(true); const [metrics, setMetrics] = useState(null); const [error, setError] = useState(null); useEffect(() => { const fetchMetrics = async () => { try { const response = await fetch('/api/content-metrics/count'); const data = await response.json(); console.log("data:", data); const formattedData = {}; if (data && typeof data === 'object') { Object.keys(data).forEach(key => { const value = data[key]; formattedData[key] = typeof value === 'number' ? value : String(value); }); } setMetrics(formattedData); setLoading(false); } catch (err) { console.error(err); setError(err.message || 'An error occurred'); setLoading(false); } }; fetchMetrics(); }, []); if (loading) { return ( ); } if (error) { return ( ); } if (!metrics || Object.keys(metrics).length === 0) { return No content types found; } return ( {Object.entries(metrics).map(([contentType, count], index) => ( ))}
{String(contentType)} {String(count)}
); }; export default MetricsWidget; ``` --- Language: JavaScript File path: src/plugins/content-metrics/server/src/controllers/metrics.js ```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); } } }); ``` --- Language: JavaScript File path: src/plugins/content-metrics/server/src/routes/index.js ```js export default { 'content-api': { type: 'content-api', routes: [ { method: 'GET', path: '/count', handler: 'metrics.getContentCounts', config: { policies: [], }, }, ], }, }; ``` # 管理面板自定义 - URL、主机和路径配置 Source: https://docs.strapi.io/cms/admin-panel-customization/host-port-path ## 仅更新管理员面板的路径 Description: 🌐 To make the admin panel accessible at another path, for instance at http://localhost:1337/dashboard, define or update the url property in the admin panel configuration file as follows: (Source: https://docs.strapi.io/cms/admin-panel-customization/host-port-path#update-the-admin-panels-path-only) Language: JavaScript File path: /config/server.js ```js module.exports = ({ env }) => ({ host: env("HOST", "0.0.0.0"), port: env.int("PORT", 1337), }); ``` --- Language: TypeScript File path: /config/server.ts ```ts export default ({ env }) => ({ host: env("HOST", "0.0.0.0"), port: env.int("PORT", 1337), }); ``` Language: JavaScript File path: /config/admin.js ```js module.exports = ({ env }) => ({ // … other configuration properties url: "/dashboard", }); ``` ## 更新管理员面板的主机和端口 Description: 🌐 This is done in the admin panel configuration file, for example to host the admin panel on my-host.com:3000 properties should be updated follows: (Source: https://docs.strapi.io/cms/admin-panel-customization/host-port-path#update-the-admin-panels-host-and-port) Language: JavaScript File path: ./config/admin.js ```js module.exports = ({ env }) => ({ host: "my-host.com", port: 3000, // Additionally you can define another path instead of the default /admin one 👇 // url: '/dashboard' }); ``` --- Language: TypeScript File path: ./config/admin.ts ```ts export default ({ env }) => ({ host: "my-host.com", port: 3000, // Additionally you can define another path instead of the default /admin one 👇 // url: '/dashboard' }); ``` # 本地化与翻译 Source: https://docs.strapi.io/cms/admin-panel-customization/locales-translations ## 定义区域设置 Description: 🌐 To update the list of available locales in the admin panel, set the config.locales array in src/admin/app file: (Source: https://docs.strapi.io/cms/admin-panel-customization/locales-translations#defining-locales) Language: JavaScript File path: /src/admin/app.js ```js export default { config: { locales: ["ru", "zh"], }, bootstrap() {}, }; ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts export default { config: { locales: ["ru", "zh"], }, bootstrap() {}, }; ``` ## 扩展翻译 Description: 🌐 These keys can be extended through the config.translations key in src/admin/app file: (Source: https://docs.strapi.io/cms/admin-panel-customization/locales-translations#extending-translations) Language: JavaScript File path: /src/admin/app.js ```js export default { config: { locales: ["fr"], translations: { fr: { "Auth.form.email.label": "test", Users: "Utilisateurs", City: "CITY (FRENCH)", // Customize the label of the Content Manager table. Id: "ID french", }, }, }, bootstrap() {}, }; ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts export default { config: { locales: ["fr"], translations: { fr: { "Auth.form.email.label": "test", Users: "Utilisateurs", City: "CITY (FRENCH)", // Customize the label of the Content Manager table. Id: "ID french", }, }, }, bootstrap() {}, }; ``` Language: JavaScript File path: /src/admin/app.js ```js export default { config: { locales: ["fr"], translations: { fr: { "Auth.form.email.label": "test", // Translate a plugin's key/value pair by adding the plugin's name as a prefix // In this case, we translate the "plugin.name" key of plugin "content-type-builder" "content-type-builder.plugin.name": "Constructeur de Type-Contenu", }, }, }, bootstrap() {}, }; ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts export default { config: { locales: ["fr"], translations: { fr: { "Auth.form.email.label": "test", // Translate a plugin's key/value pair by adding the plugin's name as a prefix // In this case, we translate the "plugin.name" key of plugin "content-type-builder" "content-type-builder.plugin.name": "Constructeur de Type-Contenu", }, }, }, bootstrap() {}, }; ``` # 标志 Source: https://docs.strapi.io/cms/admin-panel-customization/logos ## 更新标志 Description: 🌐 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: (Source: https://docs.strapi.io/cms/admin-panel-customization/logos#updating-logos) Language: JavaScript File path: /src/admin/app.js ```js import AuthLogo from "./extensions/my-auth-logo.png"; import MenuLogo from "./extensions/my-menu-logo.png"; export default { config: { // … other configuration properties auth: { // Replace the Strapi logo in auth (login) views logo: AuthLogo, }, menu: { // Replace the Strapi logo in the main navigation logo: MenuLogo, }, // … other configuration properties bootstrap() {}, }; ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts import AuthLogo from "./extensions/my-auth-logo.png"; import MenuLogo from "./extensions/my-menu-logo.png"; export default { config: { // … other configuration properties auth: { // Replace the Strapi logo in auth (login) views logo: AuthLogo, }, menu: { // Replace the Strapi logo in the main navigation logo: MenuLogo, }, // … other configuration properties bootstrap() {}, }; ``` # 主题扩展 Source: https://docs.strapi.io/cms/admin-panel-customization/theme-extension ## 主题扩展 Description: 🌐 The following example shows how to override the primary color by customizing the light and dark theme keys in the admin panel configuration: (Source: https://docs.strapi.io/cms/admin-panel-customization/theme-extension#theme-extension) Language: JavaScript File path: /src/admin/app.js ```js export default { config: { theme: { light: { colors: { primary600: "#4A6EFF", }, }, dark: { colors: { primary600: "#9DB2FF", }, }, }, }, bootstrap() {}, } ``` --- Language: TypeScript File path: /src/admin/app.ts ```ts export default { config: { theme: { light: { colors: { primary600: '#4A6EFF', }, }, dark: { colors: { primary600: '#9DB2FF', }, }, }, }, bootstrap() {}, } ``` # Docs MCP 服务器 Source: https://docs.strapi.io/cms/ai/docs-mcp-server ## 连接详情 Description: 添加到你的 .cursor/mcp.json 文件中: (Source: https://docs.strapi.io/cms/ai/docs-mcp-server#connection-details) Language: JSON File path: .cursor/mcp.json ```json { "mcpServers": { "strapi-docs": { "url": "https://strapi-docs.mcp.kapa.ai" } } } ``` --- Language: JSON File path: .vscode/mcp.json ```json { "servers": { "strapi-docs": { "type": "http", "url": "https://strapi-docs.mcp.kapa.ai" } } } ``` --- Language: JSON File path: ~/.codeium/windsurf/mcp_config.json ```json { "mcpServers": { "strapi-docs": { "serverUrl": "https://strapi-docs.mcp.kapa.ai" } } } ``` # 面向内容管理的人工智能 Source: https://docs.strapi.io/cms/ai/for-content-managers ## 激活和配置 Description: 🌐 All Strapi AI features can be enabled or disabled globally through the admin panel configuration: (Source: https://docs.strapi.io/cms/ai/for-content-managers#activation) Language: JavaScript File path: /config/admin.js|ts ```js module.exports = { // ... ai: { enabled: true, // set to false to disable all Strapi AI features }, }; ``` # Strapi 客户端 Source: https://docs.strapi.io/cms/api/client ## 安装 Description: 🌐 To use the Strapi Client in your project, install it as a dependency using your preferred package manager: (Source: https://docs.strapi.io/cms/api/client#installation) Language: Bash File path: N/A ```bash yarn add @strapi/client ``` --- Language: Bash File path: N/A ```bash npm install @strapi/client ``` --- Language: Bash File path: N/A ```bash pnpm add @strapi/client ``` ## 基本配置 Description: 🌐 With Javascript, import the strapi function and create a client instance: (Source: https://docs.strapi.io/cms/api/client#basic-configuration) Language: JavaScript File path: N/A ```js import { strapi } from '@strapi/client'; const client = strapi({ baseURL: 'http://localhost:1337/api' }); ``` --- Language: JavaScript File path: N/A ```js import { strapi } from '@strapi/client'; const client = strapi({ baseURL: 'http://localhost:1337/api' }); ``` --- Language: TypeScript File path: ./src/api/[apiName]/routes/[routerName].ts ```ts ``` ## 身份验证 Description: 🌐 If your Strapi instance uses API tokens, configure the Strapi Client as follows: (Source: https://docs.strapi.io/cms/api/client#authentication) Language: JavaScript File path: N/A ```js const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token-here', }); ``` ## 通用抓取 Description: 🌐 The Strapi Client provides access to the underlying JavaScript fetch function to make direct API requests. (Source: https://docs.strapi.io/cms/api/client#general-purpose-fetch) Language: JavaScript File path: N/A ```js const result = await client.fetch('articles', { method: 'GET' }); ``` ## 与集合类型一起工作 Description: Code example from "与集合类型一起工作" (Source: https://docs.strapi.io/cms/api/client#working-with-collection-types) Language: JavaScript File path: N/A ```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'); ``` ## 处理单一类型 Description: Code example from "处理单一类型" (Source: https://docs.strapi.io/cms/api/client#working-with-single-types) Language: JavaScript File path: N/A ```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(); ``` ## find Description: 🌐 The method can be used as follows: (Source: https://docs.strapi.io/cms/api/client#find) Language: JavaScript File path: N/A ```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 }); ``` ## findOne Description: 🌐 The method can be used as follows: (Source: https://docs.strapi.io/cms/api/client#findone) Language: JavaScript File path: N/A ```js // Initialize the client const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token', }); // Find file metadata by ID const file = await client.files.findOne(1); console.log(file.name); console.log(file.url); console.log(file.mime); // The file MIME type ``` ## update Description: 🌐 The methods can be used as follows: (Source: https://docs.strapi.io/cms/api/client#update) Language: JavaScript File path: N/A ```js // Initialize the client const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token', }); // 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', }); ``` ## 方法签名 Description: 🌐 Method Signature (Source: https://docs.strapi.io/cms/api/client#method-signature) Language: TypeScript File path: /github.com/strapi/client/blob/60a0117e361346073bed1959d354c7facfb963b3/src/files/types.ts ```ts const client = strapi({ baseURL: 'http://localhost:1337/api' }); const fileInput = document.querySelector('input[type="file"]'); const file = fileInput.files[0]; try { const result = await client.files.upload(file, { fileInfo: { alternativeText: 'A user uploaded image', caption: 'Uploaded via browser', }, }); console.log('Upload successful:', result); } catch (error) { console.error('Upload failed:', error); } ``` Language: TypeScript File path: /github.com/strapi/client/blob/60a0117e361346073bed1959d354c7facfb963b3/src/files/types.ts ```ts import { readFile } from 'fs/promises'; const client = strapi({ baseURL: 'http://localhost:1337/api' }); const filePath = './image.png'; const mimeType = 'image/png'; const fileContentBuffer = await readFile(filePath); const fileBlob = new Blob([fileContentBuffer], { type: mimeType }); try { const result = await client.files.upload(fileBlob, { fileInfo: { name: 'Image uploaded as Blob', alternativeText: 'Uploaded from Node.js Blob', caption: 'Example upload', }, }); console.log('Blob upload successful:', result); } catch (error) { console.error('Blob upload failed:', error); } ``` --- Language: TypeScript File path: /github.com/strapi/client/blob/60a0117e361346073bed1959d354c7facfb963b3/src/files/types.ts ```ts import { readFile } from 'fs/promises'; const client = strapi({ baseURL: 'http://localhost:1337/api' }); const filePath = './image.png'; const fileContentBuffer = await readFile(filePath); try { const result = await client.files.upload(fileContentBuffer, { filename: 'image.png', mimetype: 'image/png', fileInfo: { name: 'Image uploaded as Buffer', alternativeText: 'Uploaded from Node.js Buffer', caption: 'Example upload', }, }); console.log('Buffer upload successful:', result); } catch (error) { console.error('Buffer upload failed:', error); } ``` Language: TypeScript File path: N/A ```ts async upload(file: Blob, options?: BlobUploadOptions): Promise async upload(file: Buffer, options: BufferUploadOptions): Promise ``` ## 响应结构 Description: 🌐 The strapi.client.files.upload() method returns an array of file objects, each with fields such as: (Source: https://docs.strapi.io/cms/api/client#response-structure) Language: JSON File path: /github.com/strapi/client/blob/60a0117e361346073bed1959d354c7facfb963b3/src/files/types.ts ```json { "id": 1, "name": "image.png", "alternativeText": "Uploaded from Node.js Buffer", "caption": "Example upload", "mime": "image/png", "url": "/uploads/image.png", "size": 12345, "createdAt": "2025-07-23T12:34:56.789Z", "updatedAt": "2025-07-23T12:34:56.789Z" } ``` ## delete Description: 🌐 The method can be used as follows: (Source: https://docs.strapi.io/cms/api/client#delete) Language: TypeScript File path: /github.com/strapi/client/blob/main/src/files/types.ts ```ts // Initialize the client const client = strapi({ baseURL: 'http://localhost:1337/api', auth: 'your-api-token', }); // 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); ``` # 文档服务 API Source: https://docs.strapi.io/cms/api/document-service ## `findOne()` Description: GET strapi.documents().findOne() — findOne() (Source: https://docs.strapi.io/cms/api/document-service#findone) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findOne({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn' }) ``` ## `findFirst()` Description: GET strapi.documents().findFirst() — findFirst() (Source: https://docs.strapi.io/cms/api/document-service#findfirst) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findFirst() ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findFirst( { filters: { name: { $startsWith: "Pizzeria" } } } ) ``` ## `findMany()` Description: GET strapi.documents().findMany() — findMany() (Source: https://docs.strapi.io/cms/api/document-service#findmany) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany() ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany( { filters: { name: { $startsWith: 'Pizzeria' } } } ) ``` ## `create()` Description: GET strapi.documents().create() — create() (Source: https://docs.strapi.io/cms/api/document-service#create) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').create({ data: { name: 'Restaurant B' } }) ``` ## `update()` Description: GET strapi.documents().update() — update() (Source: https://docs.strapi.io/cms/api/document-service#update) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').update({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn', data: { name: "New restaurant name" } }) ``` ## `delete()` Description: GET strapi.documents().delete() — delete() (Source: https://docs.strapi.io/cms/api/document-service#delete) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').delete({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn', }) ``` ## `deleteMany()` Description: GET strapi.documents().deleteMany() — deleteMany() (Source: https://docs.strapi.io/cms/api/document-service#deletemany) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').deleteMany({ filters: { city: { name: { $eq: 'New York' } } } }); ``` ## `publish()` Description: GET strapi.documents().publish() — publish() (Source: https://docs.strapi.io/cms/api/document-service#publish) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').publish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn', }); ``` ## `unpublish()` Description: GET strapi.documents().unpublish() — unpublish() (Source: https://docs.strapi.io/cms/api/document-service#unpublish) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').unpublish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn' }); ``` ## `discardDraft()` Description: GET strapi.documents().discardDraft() — discardDraft() (Source: https://docs.strapi.io/cms/api/document-service#discarddraft) Language: JavaScript File path: N/A ```javascript strapi.documents('api::restaurant.restaurant').discardDraft({ documentId: 'a1b2c3d4e5f6g7h8i9j0klmn', }); ``` ## `count()` Description: GET strapi.documents().count() — count() (Source: https://docs.strapi.io/cms/api/document-service#count) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').count() ``` --- Language: Bash File path: N/A ```bash strapi.documents('api::restaurant.restaurant').count({ status: 'published' }) ``` --- Language: JavaScript File path: N/A ```javascript /** * 计算草稿文档的数量(如果省略状态则为默认值) * 英语(默认语言环境) * 名字以 'Pizzeria' 开头 */strapi.documents('api::restaurant.restaurant').count({ filters: { name: { $startsWith: "Pizzeria" }}}) ``` # 使用文档服务 API 的字段 Source: https://docs.strapi.io/cms/api/document-service/fields ## 使用 `findOne()` 查询选择字段 Description: GET strapi.documents( — Select fields with findOne() (Source: https://docs.strapi.io/cms/api/document-service/fields#findone) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").findOne({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', fields: ["name", "description"], }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", description: "Welcome to Biscotte restaurant! …" } ``` ## 使用 `findFirst()` 查询选择字段 Description: GET strapi.documents( — Select fields with findFirst() (Source: https://docs.strapi.io/cms/api/document-service/fields#findfirst) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").findFirst({ fields: ["name", "description"], }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", description: "Welcome to Biscotte restaurant! …" } ``` ## 使用 `findMany()` 查询选择字段 Description: GET strapi.documents( — Select fields with findMany() (Source: https://docs.strapi.io/cms/api/document-service/fields#findmany) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::restaurant.restaurant").findMany({ fields: ["name", "description"], }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", description: "Welcome to Biscotte restaurant! …" } // ... ] ``` ## 使用 `create()` 查询选择字段 Description: GET strapi.documents( — Select fields with create() (Source: https://docs.strapi.io/cms/api/document-service/fields#create) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").create({ data: { name: "Restaurant B", description: "Description for the restaurant", }, fields: ["name", "description"], }); ``` Language: JSON File path: N/A ```json { id: 4, documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', name: 'Restaurant B', description: 'Description for the restaurant' } ``` ## 使用 `update()` 查询选择字段 Description: GET strapi.documents( — Select fields with update() (Source: https://docs.strapi.io/cms/api/document-service/fields#update) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").update({ documentId: "fmtr6d7ktzpgrijqaqgr6vxs", data: { name: "Restaurant C", }, fields: ["name"], }); ``` Language: JSON File path: N/A ```json { documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', name: 'Restaurant C' } ``` ## 使用 `delete()` 查询选择字段 Description: GET strapi.documents( — Select fields with delete() (Source: https://docs.strapi.io/cms/api/document-service/fields#delete) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").delete({ documentId: "fmtr6d7ktzpgrijqaqgr6vxs", fields: ["name"], }); ``` Language: JSON File path: N/A ```json documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', // All of the deleted document's versions are returned entries: [ { id: 4, documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', name: 'Restaurant C', // … } ] } ``` ## 使用 `publish()` 查询选择字段 Description: GET strapi.documents( — Select fields with publish() (Source: https://docs.strapi.io/cms/api/document-service/fields#publish) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").publish({ documentId: "fmtr6d7ktzpgrijqaqgr6vxs", fields: ["name"], }); ``` Language: JSON File path: N/A ```json { documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', // All of the published locale entries are returned entries: [ { documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', name: 'Restaurant B' } ] } ``` ## 选择包含 `unpublish()` 查询的字段 Description: GET strapi.documents( — Select fields with unpublish() (Source: https://docs.strapi.io/cms/api/document-service/fields#unpublish) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").unpublish({ documentId: "cjld2cjxh0000qzrmn831i7rn", fields: ["name"], }); ``` Language: JSON File path: N/A ```json { documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', // All of the published locale entries are returned entries: [ { documentId: 'fmtr6d7ktzpgrijqaqgr6vxs', name: 'Restaurant B' } ] } ``` ## 选择包含 `discardDraft()` 查询的字段 Description: GET strapi.documents( — Select fields with discardDraft() (Source: https://docs.strapi.io/cms/api/document-service/fields#discarddraft) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").discardDraft({ documentId: "fmtr6d7ktzpgrijqaqgr6vxs", fields: ["name"], }); ``` Language: JSON File path: N/A ```json { documentId: "fmtr6d7ktzpgrijqaqgr6vxs", // All of the discarded draft entries are returned entries: [ { "name": "Restaurant B" } ] } ``` # 在文档服务 API 中使用过滤器 Source: https://docs.strapi.io/cms/api/document-service/filters ## 属性运算符 Description: GET strapi.documents().findMany() — $not (Source: https://docs.strapi.io/cms/api/document-service/filters#attribute-operators) Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $not: { $contains: 'Hello World', }, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $eq: 'Hello World', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript // $eq can be omitted: const entries = await strapi.documents('api::article.article').findMany({ filters: { title: 'Hello World', }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $eqi: 'HELLO World', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $ne: 'ABCD', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $nei: 'abcd', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $in: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript // $in can be omitted when passing an array of values: const entries = await strapi.documents('api::article.article').findMany({ filters: { title: ['Hello', 'Hola', 'Bonjour'], }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notIn: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $lt: 10, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $lte: 10, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $gt: 5, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $gte: 5, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { rating: { $between: [1, 20], }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $contains: 'Hello', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notContains: 'Hello', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $containsi: 'hello', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notContainsi: 'hello', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $startsWith: 'ABCD', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $startsWithi: 'ABCD', // will return the same as filtering with 'abcd' }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $endsWith: 'ABCD', }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $endsWith: 'ABCD', // will return the same as filtering with 'abcd' }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $null: true, }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { title: { $notNull: true, }, }, }); ``` ## 逻辑运算符 Description: GET strapi.documents().findMany() — $and (Source: https://docs.strapi.io/cms/api/document-service/filters#logical-operators) Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { $and: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` --- Language: JavaScript File path: N/A ```javascript // $and will be used implicitly when passing an object with nested conditions: const entries = await strapi.documents('api::article.article').findMany({ filters: { title: 'Hello World', createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { $or: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` --- Language: JavaScript File path: N/A ```javascript const entries = await strapi.documents('api::article.article').findMany({ filters: { $not: { title: 'Hello World', }, }, }); ``` # 在文档服务 API 中使用区域参数 Source: https://docs.strapi.io/cms/api/document-service/locale ## count() 个本地化文档 Description: 🌐 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): (Source: https://docs.strapi.io/cms/api/document-service/locale#count) Language: JavaScript File path: N/A ```js // Count number of published documents in French strapi.documents('api::restaurant.restaurant').count({ locale: 'fr' }); ``` ## 获取带有 `findOne()` 的本地化版本 Description: GET strapi.documents().findOne() — Get a locale version with findOne() (Source: https://docs.strapi.io/cms/api/document-service/locale#findone) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findOne({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr', }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: null, // draft version (default) locale: "fr", // as asked from the parameters // … } ``` ## 获取带有 `findFirst()` 的本地化版本 Description: GET strapi.documents().findFirst() — Get a locale version with findFirst() (Source: https://docs.strapi.io/cms/api/document-service/locale#findfirst) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents('api::article.article').findFirst({ locale: 'fr', }); ``` Language: JSON File path: N/A ```json { "documentId": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article" // … } ``` ## 使用 `findMany()` 获取本地化版本 Description: GET strapi.documents().findMany() — Get locale versions with findMany() (Source: https://docs.strapi.io/cms/api/document-service/locale#findmany) Language: JavaScript File path: N/A ```javascript // Defaults to status: draft await strapi.documents('api::restaurant.restaurant').findMany({ locale: 'fr' }); ``` Language: JSON File path: N/A ```json [ { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Restaurant Biscotte', publishedAt: null, locale: 'fr', // … }, // … ] ``` ## `create()` 为一个区域创建文档 Description: GET strapi.documents().create() — Create a document for a locale (Source: https://docs.strapi.io/cms/api/document-service/locale#create) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').create({ locale: 'es' // if not passed, the draft is created for the default locale data: { name: 'Restaurante B' } }) ``` Language: JSON File path: N/A ```json { documentId: "pw2s0nh5ub1zmnk0d80vgqrh", name: "Restaurante B", publishedAt: null, locale: "es" // … } ``` ## `update()` 本地化版本 Description: GET strapi.documents().update() — Update a locale version (Source: https://docs.strapi.io/cms/api/document-service/locale#update) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').update({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'es', data: { name: 'Nuevo nombre del restaurante' }, }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Nuevo nombre del restaurante", locale: "es", publishedAt: null, // … } ``` ## 删除一个语言版本 Description: GET strapi.documents().delete() — 删除一个语言版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#delete-a-locale-version) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').delete({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', // documentId, locale: 'es', }); ``` ## 删除所有语言版本 Description: GET strapi.documents().delete() — 删除所有语言版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#delete-all-locale-versions) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').delete({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', // documentId, locale: '*', }); // for all existing locales ``` Language: JSON File path: N/A ```json { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", // All of the deleted locale versions are returned "versions": [ { "title": "Test Article" } ] } ``` ## 发布本地化版本 Description: GET strapi.documents().publish() — 发布本地化版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#publish-a-locale-version) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').publish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr', }); ``` Language: JSON File path: N/A ```json { versions: [ { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Restaurant Biscotte', publishedAt: '2024-03-14T18:38:05.674Z', locale: 'fr', // … }, ] } ``` ## 发布所有语言版本 Description: GET strapi.documents().publish() — 发布所有语言版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#publish-all-locale-versions) Language: JavaScript File path: N/A ```javascript await strapi .documents('api::restaurant.restaurant') .publish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: '*' }); ``` Language: JSON File path: N/A ```json { "versions": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "publishedAt": "2024-03-14T18:45:21.857Z", "locale": "en" // … }, { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "publishedAt": "2024-03-14T18:45:21.857Z", "locale": "es" // … }, { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "publishedAt": "2024-03-14T18:45:21.857Z", "locale": "fr" // … } ] } ``` ## 取消发布本地版本 Description: GET strapi.documents().unpublish() — 取消发布本地版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#unpublish-a-locale-version) Language: JavaScript File path: N/A ```javascript await strapi .documents('api::restaurant.restaurant') .unpublish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr' }); ``` Language: JSON File path: N/A ```json { versions: 1 } ``` ## 取消发布所有语言版本 Description: GET strapi.documents().unpublish() — 取消发布所有语言版本 (Source: https://docs.strapi.io/cms/api/document-service/locale#unpublish-all-locale-versions) Language: JavaScript File path: N/A ```javascript await strapi .documents('api::restaurant.restaurant') .unpublish({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: '*' }); ``` --- Language: JavaScript File path: N/A ```javascript const document = await strapi.documents('api::article.article').unpublish({ documentId: 'cjld2cjxh0000qzrmn831i7rn', fields: ['title'], }); ``` Language: JSON File path: N/A ```json { versions: 3 } ``` --- Language: JSON File path: N/A ```json { "documentId": "cjld2cjxh0000qzrmn831i7rn", // All of the unpublished locale versions are returned "versions": [ { "title": "Test Article" } ] } ``` ## 丢弃本地化版本的草稿 Description: GET strapi.documents().discardDraft() — 丢弃本地化版本的草稿 (Source: https://docs.strapi.io/cms/api/document-service/locale#discard-draft-for-a-locale-version) Language: JavaScript File path: N/A ```javascript await strapi .documents('api::restaurant.restaurant') .discardDraft({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr' }); ``` Language: JSON File path: N/A ```json { versions: [ { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Restaurant Biscotte', publishedAt: null, locale: 'fr', // … }, ] } ``` ## 丢弃所有语言版本的草稿 Description: GET strapi.documents().discardDraft() — 丢弃所有语言版本的草稿 (Source: https://docs.strapi.io/cms/api/document-service/locale#discard-drafts-for-all-locale-versions) Language: JavaScript File path: N/A ```javascript await strapi .documents('api::restaurant.restaurant') .discardDraft({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: '*' }); ``` Language: JSON File path: N/A ```json { versions: [ { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Biscotte Restaurant', publishedAt: null, locale: 'en', // … }, { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Restaurant Biscotte', publishedAt: null, locale: 'fr', // … }, { documentId: 'a1b2c3d4e5f6g7h8i9j0klm', name: 'Biscotte Restaurante', publishedAt: null, locale: 'es', // … }, ] } ``` # 扩展文档服务行为 Source: https://docs.strapi.io/cms/api/document-service/middlewares ## context Description: 🌐 The following examples show what context might include depending on the method called: (Source: https://docs.strapi.io/cms/api/document-service/middlewares#context) Language: JSON File path: N/A ```json { uid: "api::restaurant.restaurant", contentType: { kind: "collectionType", collectionName: "restaurants", info: { singularName: "restaurant", pluralName: "restaurants", displayName: "restaurant" }, options: { draftAndPublish: true }, pluginOptions: {}, attributes: { name: { /*...*/ }, description: { /*...*/ }, createdAt: { /*...*/ }, updatedAt: { /*...*/ }, publishedAt: { /*...*/ }, createdBy: { /*...*/ }, updatedBy: { /*...*/ }, locale: { /*...*/ }, }, apiName: "restaurant", globalId: "Restaurants", uid: "api::restaurant.restaurant", modelType: "contentType", modelName: "restaurant", actions: { /*...*/ }, lifecycles: { /*...*/ }, }, action: "findOne", params: { documentId: 'hp7hjvrbt8rcgkmabntu0aoq', locale: undefined, status: "publish" populate: { /*...*/ }, } } ``` --- Language: JSON File path: N/A ```json { uid: "api::restaurant.restaurant", contentType: { kind: "collectionType", collectionName: "restaurants", info: { singularName: "restaurant", pluralName: "restaurants", displayName: "restaurant" }, options: { draftAndPublish: true }, pluginOptions: {}, attributes: { name: { /*...*/ }, description: { /*...*/ }, createdAt: { /*...*/ }, updatedAt: { /*...*/ }, publishedAt: { /*...*/ }, createdBy: { /*...*/ }, updatedBy: { /*...*/ }, locale: { /*...*/ }, }, apiName: "restaurant", globalId: "Restaurants", uid: "api::restaurant.restaurant", modelType: "contentType", modelName: "restaurant", actions: { /*...*/ }, lifecycles: { /*...*/ }, }, action: "findMany", params: { filters: { /*...*/ }, status: "draft", locale: null, fields: ['name', 'description'], } } ``` --- Language: JSON File path: N/A ```json { uid: "api::restaurant.restaurant", contentType: { kind: "collectionType", collectionName: "restaurants", info: { singularName: "restaurant", pluralName: "restaurants", displayName: "restaurant" }, options: { draftAndPublish: true }, pluginOptions: {}, attributes: { name: { /*...*/ }, description: { /*...*/ }, createdAt: { /*...*/ }, updatedAt: { /*...*/ }, publishedAt: { /*...*/ }, createdBy: { /*...*/ }, updatedBy: { /*...*/ }, locale: { /*...*/ }, }, apiName: "restaurant", globalId: "Restaurants", uid: "api::restaurant.restaurant", modelType: "contentType", modelName: "restaurant", actions: { /*...*/ }, lifecycles: { /*...*/ }, }, action: "create", params: { data: { /*...*/ }, status: "draft", populate: { /*...*/ }, } } ``` --- Language: JSON File path: N/A ```json { uid: "api::restaurant.restaurant", contentType: { kind: "collectionType", collectionName: "restaurants", info: { singularName: "restaurant", pluralName: "restaurants", displayName: "restaurant" }, options: { draftAndPublish: true }, pluginOptions: {}, attributes: { name: { /*...*/ }, description: { /*...*/ }, createdAt: { /*...*/ }, updatedAt: { /*...*/ }, publishedAt: { /*...*/ }, createdBy: { /*...*/ }, updatedBy: { /*...*/ }, locale: { /*...*/ }, }, apiName: "restaurant", globalId: "Restaurants", uid: "api::restaurant.restaurant", modelType: "contentType", modelName: "restaurant", actions: { /*...*/ }, lifecycles: { /*...*/ }, }, action: "update", params: { data: { /*...*/ }, documentId: 'hp7hjvrbt8rcgkmabntu0aoq', locale: undefined, status: "draft" populate: { /*...*/ }, } } ``` --- Language: JSON File path: N/A ```json { uid: "api::restaurant.restaurant", contentType: { kind: "collectionType", collectionName: "restaurants", info: { singularName: "restaurant", pluralName: "restaurants", displayName: "restaurant" }, options: { draftAndPublish: true }, pluginOptions: {}, attributes: { name: { /*...*/ }, description: { /*...*/ }, createdAt: { /*...*/ }, updatedAt: { /*...*/ }, publishedAt: { /*...*/ }, createdBy: { /*...*/ }, updatedBy: { /*...*/ }, locale: { /*...*/ }, }, apiName: "restaurant", globalId: "Restaurants", uid: "api::restaurant.restaurant", modelType: "contentType", modelName: "restaurant", actions: { /*...*/ }, lifecycles: { /*...*/ }, }, action: "delete", params: { data: { /*...*/ }, documentId: 'hp7hjvrbt8rcgkmabntu0aoq', locale: "*", populate: { /*...*/ }, } } ``` ## next Description: Code example from "next" (Source: https://docs.strapi.io/cms/api/document-service/middlewares#next) Language: JavaScript File path: N/A ```js strapi.documents.use((context, next) => { return next(); }); ``` ## 用户 Description: 🌐 The middleware must be registered in the general register() lifecycle method: (Source: https://docs.strapi.io/cms/api/document-service/middlewares#users) Language: JavaScript File path: /src/index.js|ts ```js module.exports = { register({ strapi }) { strapi.documents.use((context, next) => { // your logic return next(); }); }, // bootstrap({ strapi }) {}, // destroy({ strapi }) {}, }; ``` ## 插件开发者 Description: 🌐 The middleware must be registered in the plugin's register() lifecycle method: (Source: https://docs.strapi.io/cms/api/document-service/middlewares#plugin-developers) Language: JavaScript File path: /(plugin-root-folder)/strapi-server.js|ts ```js module.exports = { register({ strapi }) { strapi.documents.use((context, next) => { // your logic return next(); }); }, // bootstrap({ strapi }) {}, // destroy({ strapi }) {}, }; ``` ## 例子 Description: 🌐 Examples (Source: https://docs.strapi.io/cms/api/document-service/middlewares#examples) Language: JavaScript File path: N/A ```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 }); ``` # 使用 Document Service API 的 Populate Source: https://docs.strapi.io/cms/api/document-service/populate ## 为所有关系填充 1 级 Description: GET strapi.documents( — 为所有关系填充 1 级 (Source: https://docs.strapi.io/cms/api/document-service/populate#populate-1-level-for-all-relations) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: "*", }); ``` Language: JSON File path: N/A ```json { [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... "headerImage": { "data": { "id": 1, "attributes": { "name": "17520.jpg", "alternativeText": "17520.jpg", "formats": { // ... } // ... } } }, "author": { // ... }, "categories": { // ... } } // ... ] } ``` ## 为特定关系填充 1 级 Description: GET strapi.documents( — 为特定关系填充 1 级 (Source: https://docs.strapi.io/cms/api/document-service/populate#populate-1-level-for-specific-relations) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: ["headerImage"], }); ``` Language: JSON File path: N/A ```json [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... "headerImage": { "id": 2, "name": "17520.jpg" // ... } } // ... ] ``` ## 为特定关系填充多层数据 Description: GET strapi.documents( — 为特定关系填充多层数据 (Source: https://docs.strapi.io/cms/api/document-service/populate#populate-several-levels-deep-for-specific-relations) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: { categories: { populate: ["articles"], }, }, }); ``` Language: JSON File path: N/A ```json [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... "categories": { "id": 1, "name": "Test Category", "slug": "test-category", "description": "Test 1" // ... "articles": [ { "id": 1, "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... } // ... ] } } // ... ] ``` ## 排序已填充的关系 Description: GET strapi.documents( — 排序已填充的关系 (Source: https://docs.strapi.io/cms/api/document-service/populate#sort-populated-relations) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: { categories: { sort: 'name:asc', }, }, }); ``` Language: JSON File path: N/A ```json [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", // ... "categories": [ { "id": 1, "name": "Architecture" // ... }, { "id": 3, "name": "Technology" // ... } ] } // ... ] ``` ## 组件与动态区域 Description: GET strapi.documents( — 填充组件 (Source: https://docs.strapi.io/cms/api/document-service/populate#components--dynamic-zones) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: ["testComp"], }); ``` --- Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ populate: { testDZ: { on: { "test.test-compo": { fields: ["testString"], populate: ["testNestedCompo"], }, }, }, }, }); ``` Language: JSON File path: N/A ```json [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... "testComp": { "id": 1, "name": "Test Component" // ... } } // ... ] ``` --- Language: JSON File path: N/A ```json [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", // ... "testDZ": [ { "id": 3, "__component": "test.test-compo", "testString": "test1", "testNestedCompo": { "id": 3, "testNestedString": "testNested1" } } ] } // ... ] ``` ## 正在用 `create()` 填充 Description: GET strapi.documents( — Populate with create (Source: https://docs.strapi.io/cms/api/document-service/populate#create) Language: JavaScript File path: N/A ```javascript strapi.documents("api::article.article").create({ data: { title: "Test Article", slug: "test-article", body: "Test 1", headerImage: 2, }, populate: ["headerImage"], }); ``` Language: JSON File path: N/A ```json { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", "headerImage": { "id": 2, "name": "17520.jpg" // ... } } ``` ## 正在用 `update()` 填充 Description: GET strapi.documents( — Populate with update (Source: https://docs.strapi.io/cms/api/document-service/populate#update) Language: JavaScript File path: N/A ```javascript strapi.documents("api::article.article").update({ documentId: "cjld2cjxh0000qzrmn831i7rn", data: { title: "Test Article Update", }, populate: ["headerImage"], }); ``` Language: JSON File path: N/A ```json { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article Update", "slug": "test-article", "body": "Test 1", "headerImage": { "id": 2, "name": "17520.jpg" // ... } } ``` ## 正在用 `publish()` 填充 Description: GET strapi.documents( — Populate with publish (Source: https://docs.strapi.io/cms/api/document-service/populate#publish) Language: JavaScript File path: N/A ```javascript strapi.documents("api::article.article").publish({ documentId: "cjld2cjxh0000qzrmn831i7rn", populate: ["headerImage"], }); ``` Language: JSON File path: N/A ```json { "id": "cjld2cjxh0000qzrmn831i7rn", "versions": [ { "id": "cjld2cjxh0001qzrm1q1i7rn", "locale": "en", // ... "headerImage": { "id": 2, "name": "17520.jpg" // ... } } ] } ``` ## 正在用 `delete()` 填充 Description: GET strapi.documents( — Populate with delete (Source: https://docs.strapi.io/cms/api/document-service/populate#delete) Language: JavaScript File path: N/A ```javascript strapi.documents("api::article.article").delete({ documentId: "cjld2cjxh0000qzrmn831i7rn", populate: ["headerImage"], }); ``` Language: JSON File path: N/A ```json { "documentId": "cjld2cjxh0000qzrmn831i7rn", "entries": [ { "id": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1", "headerImage": { "id": 2, "name": "17520.jpg" // ... } // ... } ] } ``` # 在文档服务 API 中使用 publicationFilter Source: https://docs.strapi.io/cms/api/document-service/publication-filter ## 查找从未发布的草稿 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#never-published) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'never-published', }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "New Restaurant", publishedAt: null, locale: "en", // default locale // … } // … ] ``` ## 查找从未在任何地区发布的草稿 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#never-published-document) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'never-published-document', }); ``` Language: JSON File path: N/A ```json [ { documentId: "d41r46wac4xix5vpba7561at", name: "New Restaurant", publishedAt: null, locale: "en", // default locale // … } // … ] ``` ## 查找已修改的文档 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#modified) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'modified', }); ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'published', publicationFilter: 'modified', }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant (updated)", publishedAt: null, locale: "en", // default locale // … } // … ] ``` --- Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } // … ] ``` ## 查找未修改的文档 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#unmodified) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'unmodified', }); ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'published', publicationFilter: 'unmodified', }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: null, locale: "en", // default locale // … } // … ] ``` --- Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } // … ] ``` ## 查找有已发布版本的文档 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#has-published-version) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'has-published-version', }); ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'published', publicationFilter: 'has-published-version', }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: null, locale: "en", // default locale // … } // … ] ``` --- Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } // … ] ``` ## 查找在至少一个地区有已发布版本的文档 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#has-published-version-document) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'draft', publicationFilter: 'has-published-version-document', }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: null, locale: "en", // published in at least one locale // … } // … ] ``` ## 与 `findOne()` 和 `findFirst()` 一起使用 Description: GET strapi.documents().findOne() — findOne() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#findone-findfirst) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findOne({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', status: 'draft', publicationFilter: 'never-published', }); ``` Language: JSON File path: N/A ```json null // the documentId exists, but the document does not match never-published ``` ## 只计算匹配的文档 Description: GET strapi.documents().count() — count() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#count) Language: JavaScript File path: N/A ```javascript const neverPublishedCount = await strapi .documents('api::restaurant.restaurant') .count({ status: 'draft', publicationFilter: 'never-published', }); ``` Language: JSON File path: N/A ```json 12 // the number of never-published drafts ``` ## 诊断值 Description: GET strapi.documents().findMany() — findMany() with publicationFilter: (Source: https://docs.strapi.io/cms/api/document-service/publication-filter#diagnostics) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'published', publicationFilter: 'published-without-draft', }); ``` --- Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findMany({ status: 'published', publicationFilter: 'published-with-draft', }); ``` Language: JSON File path: N/A ```json [ { documentId: "j0klm1n2o3p4q5r6s7t8u9v", name: "Legacy Restaurant", publishedAt: "2024-01-10T09:15:00.000Z", locale: "en", // default locale // … } // … ] ``` --- Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } // … ] ``` # 在文档服务 API 中使用排序和分页 Source: https://docs.strapi.io/cms/api/document-service/sort-pagination ## 按单个字段排序 Description: GET strapi.documents().findMany() — 按单个字段排序 (Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-a-single-field) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ sort: "title:asc", }); ``` Language: JSON File path: N/A ```json [ { "documentId": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1" }, { "documentId": "cjld2cjxh0001qzrm5q1j5q7m", "title": "Test Article 2", "slug": "test-article-2", "body": "Test 2" } ] ``` ## 按多个字段排序 Description: GET strapi.documents().findMany() — 按多个字段排序 (Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#sort-on-multiple-fields) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ sort: [{ title: "asc" }, { slug: "desc" }], }); ``` Language: JSON File path: N/A ```json [ { "documentId": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1" }, { "documentId": "cjld2cjxh0001qzrm5q1j5q7m", "title": "Test Article 2", "slug": "test-article-2", "body": "Test 2" } ] ``` ## 分页 Description: GET strapi.documents().findMany() — 分页 (Source: https://docs.strapi.io/cms/api/document-service/sort-pagination#pagination) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::article.article").findMany({ limit: 10, start: 0, }); ``` Language: JSON File path: N/A ```json [ { "documentId": "cjld2cjxh0000qzrmn831i7rn", "title": "Test Article", "slug": "test-article", "body": "Test 1" }, { "documentId": "cjld2cjxh0001qzrm5q1j5q7m", "title": "Test Article 2", "slug": "test-article-2", "body": "Test 2" } ] ``` # 在文档服务 API 中使用草稿与发布 Source: https://docs.strapi.io/cms/api/document-service/status ## count() 仅草稿或已发布版本 Description: 🌐 To take into account only draft or published versions of documents while counting documents with the Document Service API, pass the corresponding status parameter: (Source: https://docs.strapi.io/cms/api/document-service/status#count) Language: JavaScript File path: N/A ```js // Count draft documents (also actually includes published documents) const draftsCount = await strapi.documents("api::restaurant.restaurant").count({ status: 'draft' }); ``` --- Language: JavaScript File path: N/A ```js // Count only published documents const publishedCount = await strapi.documents("api::restaurant.restaurant").count({ status: 'published' }); ``` ## 获取已发布的版本与 `findOne()` Description: GET strapi.documents().findOne() — findOne() with status: (Source: https://docs.strapi.io/cms/api/document-service/status#findone) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').findOne({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', status: 'published' }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } ``` ## 获取带有 `findFirst()` 的已发布版本 Description: GET strapi.documents().findFirst() — findFirst() with status: (Source: https://docs.strapi.io/cms/api/document-service/status#findfirst) Language: JavaScript File path: N/A ```javascript const document = await strapi.documents("api::restaurant.restaurant").findFirst({ status: 'published', }); ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } ``` ## 获取已发布的版本与 `findMany()` Description: GET strapi.documents().findMany() — findMany() with status: (Source: https://docs.strapi.io/cms/api/document-service/status#findmany) Language: JavaScript File path: N/A ```javascript const documents = await strapi.documents("api::restaurant.restaurant").findMany({ status: 'published' }); ``` Language: JSON File path: N/A ```json [ { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant", publishedAt: "2024-03-14T15:40:45.330Z", locale: "en", // default locale // … } // … ] ``` ## 创建草稿并发布它 Description: GET strapi.documents().create() — create() with status: (Source: https://docs.strapi.io/cms/api/document-service/status#create) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').create({ data: { name: "New Restaurant", }, status: 'published', }) ``` Language: JSON File path: N/A ```json { documentId: "d41r46wac4xix5vpba7561at", name: "New Restaurant", publishedAt: "2024-03-14T17:29:03.399Z", locale: "en" // default locale // … } ``` ## 更新草稿并发布它 Description: GET strapi.documents().update() — update() with status: (Source: https://docs.strapi.io/cms/api/document-service/status#update) Language: JavaScript File path: N/A ```javascript await strapi.documents('api::restaurant.restaurant').update({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', data: { name: "Biscotte Restaurant (closed)", }, status: 'published', }) ``` Language: JSON File path: N/A ```json { documentId: "a1b2c3d4e5f6g7h8i9j0klm", name: "Biscotte Restaurant (closed)", publishedAt: "2024-03-14T17:29:03.399Z", locale: "en" // default locale // … } ``` # 实体服务 API Source: https://docs.strapi.io/cms/api/entity-service ## 基本用法 Description: 🌐 The Entity Service is available through strapi.entityService: (Source: https://docs.strapi.io/cms/api/entity-service#basic-usage) Language: JavaScript File path: N/A ```js const entry = await strapi.entityService.findOne('api::article.article', 1, { populate: { someRelation: true }, }); ``` # 组件与动态区域 Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones ## 创造 Description: 🌐 A component can be created while creating an entry with the Entity Service API: (Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#creation) Language: JavaScript File path: N/A ```js strapi.entityService.create('api::article.article', { data: { myComponent: { foo: 'bar', }, }, }); ``` Language: JavaScript File path: N/A ```js strapi.entityService.create('api::article.article', { data: { myDynamicZone: [ { __component: 'compo.type', foo: 'bar', }, { __component: 'compo.type2', foo: 'bar', }, ], }, }); ``` ## 更新 Description: 🌐 A component can be updated while updating an entry with the Entity Service API. (Source: https://docs.strapi.io/cms/api/entity-service/components-dynamic-zones#update) Language: JavaScript File path: N/A ```js strapi.entityService.update('api::article.article', 1, { data: { myComponent: { id: 1, // will update component with id: 1 (if not specified, would have deleted it and created a new one) foo: 'bar', }, }, }); ``` Language: JavaScript File path: N/A ```js strapi.entityService.update('api::article.article', 1, { data: { myDynamicZone: [ { // will update id: 2, __component: 'compo.type', foo: 'bar', }, { // will add a new & delete old ones __component: 'compo.type2', foo: 'bar2', }, ], }, }); ``` # 增删改查操作 Source: https://docs.strapi.io/cms/api/entity-service/crud ## 例子 Description: 🌐 Example (Source: https://docs.strapi.io/cms/api/entity-service/crud#example-1) Language: JavaScript File path: N/A ```js const entry = await strapi.entityService.findOne('api::article.article', 1, { fields: ['title', 'description'], populate: { category: true }, }); ``` --- Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { fields: ['title', 'description'], filters: { title: 'Hello World' }, sort: { createdAt: 'DESC' }, populate: { category: true }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { publicationState: 'preview', filters: { publishedAt: { $null: true, }, }, }); ::: ## create() Creates one entry and returns it Syntax: `create(uid: string, parameters: Params)` ⇒ `Entry` ### Parameters | Parameter | Description | Type | | ---------- | ----------- | ---------- | | `fields` | Attributes to return | `String[]` | | `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) | | `locale` | Locale code when the Internationalization plugin is enabled. Creates the entry for that locale. | `string` | | `data` | Input data | `Object` | ### Example ``` Language: JavaScript File path: N/A ``` ## update() Updates one entry and returns it. :::note `update()` only performs a partial update, so existing fields that are not included won't be replaced. ::: Syntax: `update(uid: string, id: ID, parameters: Params)` ⇒ `Entry` ### Parameters | Parameter | Description | Type | | ---------- | ------------- | ---------- | | `fields` | Attributes to return | `String[]` | | `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) | | `locale` | Locale code when the Internationalization plugin is enabled. Updates the matching localized variant. | `string` | | `data` | Input data | `object` | ### Example ``` Language: JavaScript File path: N/A ``` ## delete() Deletes one entry and returns it. Syntax: `delete(uid: string, id: ID, parameters: Params)` ⇒ `Entry` ### Parameters | Parameter | Description | Type | | ---------- | --------- | -------- | | `fields` | Attributes to return | `String[]` | | `populate` | Relations, components and dynamic zones to [populate](/cms/api/entity-service/populate) | [`PopulateParameter`](/cms/api/entity-service/populate) | | `locale` | Locale code when the Internationalization plugin is enabled. Deletes the localized variant that matches this locale. | `string` | ### Example ``` # 使用实体服务 API 进行筛选 Source: https://docs.strapi.io/cms/api/entity-service/filter ## $and Description: $and 在传递带有嵌套条件的对象时将被隐式使用: (Source: https://docs.strapi.io/cms/api/entity-service/filter#and) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { $and: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: 'Hello World', createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, }); ``` ## $or Description: Code example from "$or" (Source: https://docs.strapi.io/cms/api/entity-service/filter#or) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { $or: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` ## $not Description: Code example from "$not" (Source: https://docs.strapi.io/cms/api/entity-service/filter#not) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { $not: { title: 'Hello World', }, }, }); ``` --- Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $not: { $contains: 'Hello World', }, }, }, }); ``` ## $eq Description: $eq可以省略: (Source: https://docs.strapi.io/cms/api/entity-service/filter#eq) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $eq: 'Hello World', }, }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: 'Hello World', }, }); ``` ## $eqi Description: Code example from "$eqi" (Source: https://docs.strapi.io/cms/api/entity-service/filter#eqi) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $eqi: 'HELLO World', }, }, }); ``` ## $ne Description: Code example from "$ne" (Source: https://docs.strapi.io/cms/api/entity-service/filter#ne) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $ne: 'ABCD', }, }, }); ``` ## $nei Description: Code example from "$nei" (Source: https://docs.strapi.io/cms/api/entity-service/filter#nei) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $nei: 'abcd', }, }, }); ``` ## $in Description: 在传递一个值数组时可以省略 $in: (Source: https://docs.strapi.io/cms/api/entity-service/filter#in) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $in: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: ['Hello', 'Hola', 'Bonjour'], }, }); ``` ## $notIn Description: Code example from "$notIn" (Source: https://docs.strapi.io/cms/api/entity-service/filter#notin) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $notIn: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` ## $lt Description: Code example from "$lt" (Source: https://docs.strapi.io/cms/api/entity-service/filter#lt) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { rating: { $lt: 10, }, }, }); ``` ## $lte Description: Code example from "$lte" (Source: https://docs.strapi.io/cms/api/entity-service/filter#lte) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { rating: { $lte: 10, }, }, }); ``` ## $gt Description: Code example from "$gt" (Source: https://docs.strapi.io/cms/api/entity-service/filter#gt) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { rating: { $gt: 5, }, }, }); ``` ## $gte Description: Code example from "$gte" (Source: https://docs.strapi.io/cms/api/entity-service/filter#gte) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { rating: { $gte: 5, }, }, }); ``` ## $between Description: Code example from "$between" (Source: https://docs.strapi.io/cms/api/entity-service/filter#between) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { rating: { $between: [1, 20], }, }, }); ``` ## $contains Description: Code example from "$contains" (Source: https://docs.strapi.io/cms/api/entity-service/filter#contains) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $contains: 'Hello', }, }, }); ``` ## $notContains Description: Code example from "$notContains" (Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontains) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $notContains: 'Hello', }, }, }); ``` ## $containsi Description: Code example from "$containsi" (Source: https://docs.strapi.io/cms/api/entity-service/filter#containsi) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $containsi: 'hello', }, }, }); ``` ## $notContainsi Description: Code example from "$notContainsi" (Source: https://docs.strapi.io/cms/api/entity-service/filter#notcontainsi) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $notContainsi: 'hello', }, }, }); ``` ## $startsWith Description: Code example from "$startsWith" (Source: https://docs.strapi.io/cms/api/entity-service/filter#startswith) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $startsWith: 'ABCD', }, }, }); ``` ## $endsWith Description: Code example from "$endsWith" (Source: https://docs.strapi.io/cms/api/entity-service/filter#endswith) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $endsWith: 'ABCD', }, }, }); ``` ## $null Description: Code example from "$null" (Source: https://docs.strapi.io/cms/api/entity-service/filter#null) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $null: true, }, }, }); ``` ## $notNull Description: Code example from "$notNull" (Source: https://docs.strapi.io/cms/api/entity-service/filter#notnull) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { filters: { title: { $notNull: true, }, }, }); ``` # 使用实体服务 API 进行排序和分页 Source: https://docs.strapi.io/cms/api/entity-service/order-pagination ## 单身 Description: 作为 string 用默认的升序排序,或者 - 作为一个 object 来定义字段名和顺序(即 'asc' 表示升序,'desc' 表示降序) (Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#single) Language: JavaScript File path: N/A ```js strapi.entityService.findMany('api::article.article', { sort: 'id', }); // single with direction strapi.entityService.findMany('api::article.article', { sort: { id: 'desc' }, }); ``` ## 多个 Description: 作为字符串数组,使用默认升序对多个字段进行排序,或者 - 作为对象数组来定义字段名称和顺序(即升序为 'asc',降序为 'desc') (Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#multiple) Language: JavaScript File path: N/A ```js strapi.entityService.findMany('api::article.article', { sort: ['publishDate', 'name'], }); // multiple with direction strapi.entityService.findMany('api::article.article', { sort: [{ title: 'asc' }, { publishedAt: 'desc' }], }); ``` ## 关系顺序 Description: 🌐 Fields can also be sorted based on fields from relations: (Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#relational-ordering) Language: JavaScript File path: N/A ```js strapi.entityService.findMany('api::article.article', { sort: { author: { name: 'asc', }, }, }); ``` ## 分页 Description: 🌐 To paginate results returned by the Entity Service API, you can use the start and limit parameters: (Source: https://docs.strapi.io/cms/api/entity-service/order-pagination#pagination) Language: JavaScript File path: N/A ```js strapi.entityService.findMany('api::article.article', { start: 10, limit: 15, }); ``` Language: JavaScript File path: N/A ```js strapi.entityService.findMany('api::article.article', { page: 1, pageSize: 15, }); ``` # 使用实体服务 API 填充 Source: https://docs.strapi.io/cms/api/entity-service/populate ## 基本填充 Description: 🌐 To populate all the root level relations, use populate: '*': (Source: https://docs.strapi.io/cms/api/entity-service/populate#basic-populating) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { populate: '*', }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { populate: ['componentA', 'relationA'], }); ``` ## 高级填充 Description: 🌐 An object can be passed for more advanced populating: (Source: https://docs.strapi.io/cms/api/entity-service/populate#advanced-populating) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { populate: { relationA: true, repeatableComponent: { fields: ['fieldA'], filters: {}, sort: 'fieldA:asc', populate: { relationB: true, }, }, }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { populate: { relationA: { filters: { name: { $contains: 'Strapi', }, }, }, repeatableComponent: { fields: ['someAttributeName'], sort: ['someAttributeName'], populate: { componentRelationA: true, }, }, }, }); ``` ## 填充碎片 Description: 🌐 When dealing with polymorphic content structures (dynamic zones, polymorphic relations, etc...), it is possible to use populate fragments to have a better granularity on the populate strategy. (Source: https://docs.strapi.io/cms/api/entity-service/populate#populate-fragments) Language: JavaScript File path: N/A ```js const entries = await strapi.entityService.findMany('api::article.article', { populate: { dynamicZone: { on: { 'components.foo': { fields: ['title'], filters: { title: { $contains: 'strapi' } }, }, 'components.bar': { fields: ['name'], }, }, }, morphAuthor: { on: { 'plugin::users-permissions.user': { fields: ['username'], }, 'api::author.author': { fields: ['name'], }, }, }, }, }); ``` # GraphQL API Source: https://docs.strapi.io/cms/api/graphql ## GraphQL API Description: 🌐 To use the GraphQL API, install the GraphQL plugin: (Source: https://docs.strapi.io/cms/api/graphql#graphql-api) Language: Bash File path: N/A ```bash yarn add @strapi/plugin-graphql ``` --- Language: Bash File path: N/A ```bash npm install @strapi/plugin-graphql ``` Language: JavaScript File path: /config/plugins.js|ts ```js export default { shadowCRUD: true, endpoint: '/graphql', // <— single GraphQL endpoint subscriptions: false, maxLimit: -1, apolloServer: {}, v4CompatibilityMode: process.env.STRAPI_GRAPHQL_V4_COMPATIBILITY_MODE ?? false, }; ``` ## 获取单个文档 Description: 可以通过它们的 documentId 获取文档 。 (Source: https://docs.strapi.io/cms/api/graphql#fetch-a-single-document) Language: JSON File path: Example ```json { restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl") { name description } } ``` ## 获取多个文档 Description: 🌐 To fetch multiple documents you can use flat queries like the following: (Source: https://docs.strapi.io/cms/api/graphql#fetch-multiple-documents) Language: GRAPHQL File path: Example ```graphql restaurants { documentId title } ``` --- Language: GRAPHQL File path: Example ```graphql { restaurants_connection { nodes { documentId name } pageInfo { pageSize page pageCount total } } } ``` ## 获取关系 Description: 🌐 The following example fetches all documents from the "Restaurant" content-type, and for each of them, also returns some fields for the many-to-many relation with the "Category" content-type: (Source: https://docs.strapi.io/cms/api/graphql#fetch-relations) Language: GRAPHQL File path: Example ```graphql { restaurants { documentId name description # categories is a many-to-many relation categories { documentId name } } } ``` --- Language: GRAPHQL File path: Example ```graphql { restaurants_connection { nodes { documentId name description # categories is a many-to-many relation categories_connection { nodes { documentId name } } } pageInfo { page pageCount pageSize total } } } ``` --- Language: GRAPHQL File path: N/A ```graphql { restaurants_connection { nodes { documentId name description # many-to-many relation categories_connection { nodes { documentId name } } } pageInfo { page pageCount pageSize total } } } ``` --- Language: GRAPHQL File path: N/A ```graphql { restaurants_connection { nodes { documentId name description # many-to-many relation categories_connection { nodes { documentId name } # not supported pageInfo { page pageCount pageSize total } } } pageInfo { page pageCount pageSize total } } }} ``` ## 获取媒体字段 Description: 🌐 The following example fetches the url attribute value for each cover media field attached to each document from the "Restaurants" content-type: (Source: https://docs.strapi.io/cms/api/graphql#fetch-media-fields) Language: GRAPHQL File path: N/A ```graphql { restaurants { images_connection { nodes { documentId url } } } } ``` --- Language: GRAPHQL File path: N/A ```graphql { restaurants { images_connection { nodes { documentId url } } } } ``` Language: GRAPHQL File path: N/A ```graphql { restaurants { images { documentId url } } } ``` ## 获取组件 Description: 🌐 The following example fetches the label, startdate, and enddate attributes values for each closingPeriod component added to each document from the "Restaurants" content-type: (Source: https://docs.strapi.io/cms/api/graphql#fetch-components) Language: GRAPHQL File path: N/A ```graphql { restaurants { closingPeriod { label start_date end_date } } } ``` ## 获取动态区域数据 Description: 🌐 The following example fetches data for the label attribute of a "Closingperiod" component from the "Default" components category that can be added to the "dz" dynamic zone: (Source: https://docs.strapi.io/cms/api/graphql#fetch-dynamic-zone-data) Language: GRAPHQL File path: N/A ```graphql { restaurants { dz { __typename ...on ComponentDefaultClosingperiod { # define which attributes to return for the component label } } } } ``` ## 获取草稿或已发布的版本 Description: 如果内容类型启用了 Draft & Publish 功能,你可以在查询中添加 status 参数以获取文档的草稿或已发布版本 : (Source: https://docs.strapi.io/cms/api/graphql#status) Language: GRAPHQL File path: Example: ```graphql query Query($status: PublicationStatus) { restaurants(status: DRAFT) { documentId name publishedAt # should return null } } ``` --- Language: GRAPHQL File path: Example: ```graphql query Query($status: PublicationStatus) { restaurants(status: PUBLISHED) { documentId name publishedAt } } ``` ## 使用 publicationFilter 过滤 Description: Code example from "使用 publicationFilter 过滤" (Source: https://docs.strapi.io/cms/api/graphql#publication-filter) Language: GRAPHQL File path: Example: ```graphql query Query($status: PublicationStatus, $publicationFilter: PublicationFilter) { restaurants(status: DRAFT, publicationFilter: NEVER_PUBLISHED) { documentId name publishedAt } } ``` --- Language: GRAPHQL File path: Example: ```graphql query Query { restaurants(publicationFilter: MODIFIED) { documentId name publishedAt } } ``` ## 创建新文档 Description: 🌐 The following example creates a new document for the "Restaurant" content-type and returns its name and documentId: (Source: https://docs.strapi.io/cms/api/graphql#create-a-new-document) Language: GRAPHQL File path: N/A ```graphql mutation CreateRestaurant($data: RestaurantInput!) { createRestaurant(data: { name: "Pizzeria Arrivederci" }) { name documentId } } ``` Language: GRAPHQL File path: N/A ```graphql mutation CreateCategory { createCategory(data: { Name: "Italian Food" restaurants: ["a1b2c3d4e5d6f7g8h9i0jkl", "bf97tfdumkcc8ptahkng4puo"] }) { documentId Name restaurants { documentId name } } } ``` ## 更新现有文档 Description: 🌐 For instance, the following example updates an existing document from the "Restaurants" content-type and give it a new name: (Source: https://docs.strapi.io/cms/api/graphql#update-an-existing-document) Language: GRAPHQL File path: N/A ```graphql mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!) { updateRestaurant( documentId: "bf97tfdumkcc8ptahkng4puo", data: { name: "Pizzeria Amore" } ) { documentId name } } ``` ## 更新关系 Description: 🌐 For instance, the following example updates a document from the "Restaurant" content-type and adds a relation to a document from the "Category" content-type through the categories relation field: (Source: https://docs.strapi.io/cms/api/graphql#update-relations) Language: GRAPHQL File path: N/A ```graphql mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!) { updateRestaurant( documentId: "slwsiopkelrpxpvpc27953je", data: { categories: ["kbbvj00fjiqoaj85vmylwi17"] } ) { documentId name categories { documentId Name } } } ``` ## 删除文档 Description: 要删除文档 ,传入其 documentId : (Source: https://docs.strapi.io/cms/api/graphql#delete-a-document) Language: GRAPHQL File path: N/A ```graphql mutation DeleteRestaurant { deleteRestaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl") { documentId } } ``` ## 更新已上传的媒体文件 Description: 🌐 For instance, the following example updates the alternativeText attribute for a media file whose id is 3: (Source: https://docs.strapi.io/cms/api/graphql#update-an-uploaded-media-file) Language: GRAPHQL File path: N/A ```graphql mutation Mutation($updateUploadFileId: ID!, $info: FileInfoInput) { updateUploadFile( id: 3, info: { alternativeText: "New alt text" } ) { documentId url alternativeText } } ``` ## 删除已上传的媒体文件 Description: 🌐 When deleting an uploaded media file, pass the media's id (not its documentId). (Source: https://docs.strapi.io/cms/api/graphql#delete-an-uploaded-media-file) Language: GRAPHQL File path: Example: ```graphql mutation DeleteUploadFile($deleteUploadFileId: ID!) { deleteUploadFile(id: 4) { documentId # return its documentId } } ``` ## 过滤器 Description: Code example from "过滤器" (Source: https://docs.strapi.io/cms/api/graphql#filters) Language: GRAPHQL File path: Simple ```graphql # eq - returns restaurants with the exact name "Biscotte" { restaurants(filters: { name: { eq: "Biscotte" } }) { name } } # eqi - returns restaurants whose name equals "Biscotte", # comparison is case-insensitive { restaurants(filters: { name: { eqi: "Biscotte" } }) { name } } # ne - returns restaurants whose name is not "Biscotte" { restaurants(filters: { name: { ne: "Biscotte" } }) { name } } # nei - returns restaurants whose name is not "Biscotte", # comparison is case-insensitive { restaurants(filters: { name: { nei: "Biscotte" } }) { name } } # lt - returns restaurants with averagePrice less than 20 { restaurants(filters: { averagePrice: { lt: 20 } }) { name } } # lte - returns restaurants with averagePrice less than or equal to 20 { restaurants(filters: { averagePrice: { lte: 20 } }) { name } } # gt - returns restaurants with averagePrice greater than 20 { restaurants(filters: { averagePrice: { gt: 20 } }) { name } } # gte - returns restaurants with averagePrice greater than or equal to 20 { restaurants(filters: { averagePrice: { gte: 20 } }) { name } } # between - returns restaurants with averagePrice between 10 and 30 { restaurants(filters: { averagePrice: { between: [10, 30] } }) { name } } ``` --- Language: GRAPHQL File path: Simple ```graphql # in - returns restaurants with category either "pizza" or "burger" { restaurants(filters: { category: { in: ["pizza", "burger"] } }) { name } } # notIn - returns restaurants whose category is neither "pizza" nor "burger" { restaurants(filters: { category: { notIn: ["pizza", "burger"] } }) { name } } ``` --- Language: GRAPHQL File path: Simple ```graphql # contains - returns restaurants whose name contains "Pizzeria" { restaurants(filters: { name: { contains: "Pizzeria" } }) { name } } # notContains - returns restaurants whose name does NOT contain "Pizzeria" { restaurants(filters: { name: { notContains: "Pizzeria" } }) { name } } # containsi - returns restaurants whose name contains "pizzeria" (case‑insensitive) { restaurants(filters: { name: { containsi: "pizzeria" } }) { name } } # notContainsi - returns restaurants whose name does NOT contain "pizzeria" (case‑insensitive) { restaurants(filters: { name: { notContainsi: "pizzeria" } }) { name } } # startsWith - returns restaurants whose name starts with "Pizza" { restaurants(filters: { name: { startsWith: "Pizza" } }) { name } } # endsWith - returns restaurants whose name ends with "Inc" { restaurants(filters: { name: { endsWith: "Inc" } }) { name } } ``` --- Language: GRAPHQL File path: Simple ```graphql # null - returns restaurants where description is null { restaurants(filters: { description: { null: true } }) { name } } # notNull - returns restaurants where description is not null { restaurants(filters: { description: { notNull: true } }) { name } } ``` --- Language: GRAPHQL File path: Simple ```graphql # and - both category must be "pizza" AND averagePrice must be < 20 { restaurants(filters: { and: [ { category: { eq: "pizza" } }, { averagePrice: { lt: 20 } } ] }) { name } } # or - category is "pizza" OR category is "burger" { restaurants(filters: { or: [ { category: { eq: "pizza" } }, { category: { eq: "burger" } } ] }) { name } } # not - category must NOT be "pizza" { restaurants(filters: { not: { category: { eq: "pizza" } } }) { name } } ``` Language: JSON File path: Example ```json { restaurants( filters: { and: [ { not: { averagePrice: { gte: 20 } } } { or: [ { name: { eq: "Pizzeria" } } { name: { startsWith: "Pizzeria" } } ] } ] } ) { documentId name averagePrice } } ``` ## 排序 Description: 🌐 The sorting order can be defined with :asc (ascending order, default, can be omitted) or :desc (for descending order). (Source: https://docs.strapi.io/cms/api/graphql#sorting) Language: JSON File path: Example: ```json { restaurants(sort: "name") { documentId name } } ``` --- Language: JSON File path: Example: ```json { restaurants(sort: "averagePrice:desc") { documentId name averagePrice } } ``` Language: JSON File path: N/A ```json { restaurants(sort: ["name:asc", "averagePrice:desc"]) { documentId name averagePrice } } ``` ## 按页分页 Description: | 参数 | 描述 | 默认值 | | --- | --- | --- | | pagination.page | 页码 | 1 | | pagination.pageSize | 每页条数 | 10 | (Source: https://docs.strapi.io/cms/api/graphql#pagination-by-page) Language: JSON File path: Example ```json { restaurants_connection(pagination: { page: 1, pageSize: 10 }) { nodes { documentId name } pageInfo { page pageSize pageCount total } } } ``` ## 按偏移量分页 Description: | 参数 | 描述 | 默认值 | 最大值 | | --- | --- | --- | --- | | pagination.start | 起始值 | 0 | - | | pagination.limit | 返回的实体数量 | 10 | -1 | (Source: https://docs.strapi.io/cms/api/graphql#pagination-by-offset) Language: JSON File path: Example ```json { restaurants_connection(pagination: { start: 10, limit: 19 }) { nodes { documentId name } pageInfo { page pageSize pageCount total } } } ``` ## 获取特定区域的所有文档 Description: Request (Source: https://docs.strapi.io/cms/api/graphql#locale-fetch-all) Language: GRAPHQL File path: N/A ```graphql query { restaurants(locale: "fr") { documentId name locale } } ``` Language: JSON File path: N/A ```json { "data": { "restaurants": [ { "documentId": "a1b2c3d4e5d6f7g8h9i0jkl", "name": "Restaurant Biscotte", "locale": "fr" }, { "documentId": "m9n8o7p6q5r4s3t2u1v0wxyz", "name": "Pizzeria Arrivederci", "locale": "fr" }, ] } } ``` Language: GRAPHQL File path: ./config/plugins.js ```graphql query { restaurants(locale: "fr") { documentId name locale } } ``` --- Language: JSON File path: ./config/plugins.js ```json { "data": { "restaurants": [ { "documentId": "a1b2c3d4e5d6f7g8h9i0jkl", "name": "Restaurant Biscotte", "locale": "fr" }, { "documentId": "m9n8o7p6q5r4s3t2u1v0wxyz", "name": "Pizzeria Arrivederci", "locale": "fr" }, ] } } ``` ## 获取特定语言环境的文档 Description: Request (Source: https://docs.strapi.io/cms/api/graphql#locale-fetch) Language: GRAPHQL File path: N/A ```graphql query Restaurant($documentId: ID!, $locale: I18NLocaleCode) { restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl", locale: "fr") { documentId name description locale } } ``` Language: JSON File path: N/A ```json { "data": { "restaurant": { "documentId": "lviw819d5htwvga8s3kovdij", "name": "Restaurant Biscotte", "description": "Bienvenue au restaurant Biscotte!", "locale": "fr" } } } ``` Language: GRAPHQL File path: N/A ```graphql query Restaurant($documentId: ID!, $locale: I18NLocaleCode) { restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl", locale: "fr") { documentId name description locale } } ``` --- Language: JSON File path: N/A ```json { "data": { "restaurant": { "documentId": "lviw819d5htwvga8s3kovdij", "name": "Restaurant Biscotte", "description": "Bienvenue au restaurant Biscotte!", "locale": "fr" } } } ``` ## 创建一个新的本地化文档 Description: locale 字段可以传递以创建针对特定语言环境的本地化文档 (有关使用 GraphQL 进行变更的更多信息,请参阅 GraphQL API 文档)。 (Source: https://docs.strapi.io/cms/api/graphql#locale-create) Language: GRAPHQL File path: Example: ```graphql mutation CreateRestaurant($data: RestaurantInput!, $locale: I18NLocaleCode) { createRestaurant( data: { name: "Brasserie Bonjour", description: "Description in French goes here" }, locale: "fr" ) { documentId name description locale } ``` ## 为特定地区更新文档 Description: 可以在变更中传入 locale 参数以更新给定语言环境的文档 (有关使用 GraphQL 的变更的更多信息,请参阅 GraphQL API 文档)。 (Source: https://docs.strapi.io/cms/api/graphql#locale-update) Language: GRAPHQL File path: Example: ```graphql mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!, $locale: I18NLocaleCode) { updateRestaurant( documentId: "a1b2c3d4e5d6f7g8h9i0jkl" data: { description: "New description in French" }, locale: "fr" ) { documentId name description locale } ``` ## 删除文档的语言区域 Description: 在变更中传递 locale 参数以删除文档的特定本地化 : (Source: https://docs.strapi.io/cms/api/graphql#locale-delete) Language: GRAPHQL File path: N/A ```graphql mutation DeleteRestaurant($documentId: ID!, $locale: I18NLocaleCode) { deleteRestaurant(documentId: "xzmzdo4k0z73t9i68a7yx2kk", locale: "fr") { documentId } } ``` # GraphQL 的高级策略 Source: https://docs.strapi.io/cms/api/graphql/advanced-policies ## 条件可见性 Description: 🌐 To limit the number of returned entries for unauthenticated users you can write a policy that modifies resolver arguments: (Source: https://docs.strapi.io/cms/api/graphql/advanced-policies#conditional-visibility) Language: TypeScript File path: /src/policies/limit-public-results.ts ```ts export default async (policyContext, config, { strapi }) => { const { state, args } = policyContext; if (!state.user) { args.limit = 4; // only return 4 results for public } return true; }; ``` Language: TypeScript File path: /config/policies.ts ```ts export default { 'api::restaurant.restaurant': { find: [ 'global::limit-public-results' ], }, }; ``` ## 群体成员资格 Description: 🌐 Policies can access policyContext.state.user to check group membership, as in the following example: (Source: https://docs.strapi.io/cms/api/graphql/advanced-policies#group-membership) Language: TypeScript File path: /src/policies/is-group-member.ts ```ts export default async ({ state }, config, { strapi }) => { const userGroups = await strapi.query('plugin::users-permissions.group').findMany({ where: { users: { id: state.user.id } }, }); return userGroups.some(g => g.name === config.group); }; ``` Language: TypeScript File path: /config/policies.ts ```ts export default { 'api::restaurant.restaurant': { find: [{ name: 'global::is-group-member', config: { group: 'editors' } }], }, }; ``` # GraphQL 的高级查询 Source: https://docs.strapi.io/cms/api/graphql/advanced-queries ## 多层查询 Description: 🌐 Use nested selection sets to fetch relations several levels deep, as in the following example: (Source: https://docs.strapi.io/cms/api/graphql/advanced-queries#multi-level-queries) Language: GRAPHQL File path: N/A ```graphql { restaurants { documentId name categories { documentId name parent { documentId name } } } } ``` ## 解析器链 Description: 🌐 Custom resolvers can call other resolvers to reuse existing logic. (Source: https://docs.strapi.io/cms/api/graphql/advanced-queries#resolver-chains) Language: TypeScript File path: /src/api/restaurant/resolvers/restaurant.ts ```ts export default { Query: { restaurants: async (parent, args, ctx) => { const documents = await strapi.documents('api::restaurant.restaurant').findMany(args); return documents.map(doc => ctx.request.graphql.resolve('Restaurant', doc)); }, }, }; ``` # 在 GraphQL API 中使用 locale 参数 Source: https://docs.strapi.io/cms/api/graphql/locale ## 获取特定区域的所有文档 Description: Request (Source: https://docs.strapi.io/cms/api/graphql/locale#graphql-fetch-all) Language: GRAPHQL File path: N/A ```graphql query { restaurants(locale: "fr") { documentId name locale } } ``` Language: JSON File path: N/A ```json { "data": { "restaurants": [ { "documentId": "a1b2c3d4e5d6f7g8h9i0jkl", "name": "Restaurant Biscotte", "locale": "fr" }, { "documentId": "m9n8o7p6q5r4s3t2u1v0wxyz", "name": "Pizzeria Arrivederci", "locale": "fr" }, ] } } ``` Language: GRAPHQL File path: N/A ```graphql query { restaurants(locale: "fr") { documentId name locale } } ``` --- Language: JSON File path: N/A ```json { "data": { "restaurants": [ { "documentId": "a1b2c3d4e5d6f7g8h9i0jkl", "name": "Restaurant Biscotte", "locale": "fr" }, { "documentId": "m9n8o7p6q5r4s3t2u1v0wxyz", "name": "Pizzeria Arrivederci", "locale": "fr" }, ] } } ``` ## 在特定区域获取文档 Description: Request (Source: https://docs.strapi.io/cms/api/graphql/locale#graphql-fetch) Language: GRAPHQL File path: N/A ```graphql query Restaurant($documentId: ID!, $locale: I18NLocaleCode) { restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl", locale: "fr") { documentId name description locale } } ``` Language: JSON File path: N/A ```json { "data": { "restaurant": { "documentId": "lviw819d5htwvga8s3kovdij", "name": "Restaurant Biscotte", "description": "Bienvenue au restaurant Biscotte!", "locale": "fr" } } } ``` Language: GRAPHQL File path: N/A ```graphql query Restaurant($documentId: ID!, $locale: I18NLocaleCode) { restaurant(documentId: "a1b2c3d4e5d6f7g8h9i0jkl", locale: "fr") { documentId name description locale } } ``` --- Language: JSON File path: N/A ```json { "data": { "restaurant": { "documentId": "lviw819d5htwvga8s3kovdij", "name": "Restaurant Biscotte", "description": "Bienvenue au restaurant Biscotte!", "locale": "fr" } } } ``` ## 创建一个新的本地化文档 Description: locale 字段可以传递以创建针对特定语言环境的本地化文档 (有关使用 GraphQL 进行变更的更多信息,请参阅 GraphQL API 文档)。 (Source: https://docs.strapi.io/cms/api/graphql/locale#graphql-create) Language: GRAPHQL File path: Example: ```graphql mutation CreateRestaurant($data: RestaurantInput!, $locale: I18NLocaleCode) { createRestaurant( data: { name: "Brasserie Bonjour", description: "Description in French goes here" }, locale: "fr" ) { documentId name description locale } ``` ## 为特定地区更新文档 Description: 可以在变更中传入 locale 参数以更新给定语言环境的文档 (有关使用 GraphQL 的变更的更多信息,请参阅 GraphQL API 文档)。 (Source: https://docs.strapi.io/cms/api/graphql/locale#graphql-update) Language: GRAPHQL File path: Example: ```graphql mutation UpdateRestaurant($documentId: ID!, $data: RestaurantInput!, $locale: I18NLocaleCode) { updateRestaurant( documentId: "a1b2c3d4e5d6f7g8h9i0jkl" data: { description: "New description in French" }, locale: "fr" ) { documentId name description locale } ``` ## 删除文档的某个语言环境 Description: 在变更中传递 locale 参数以删除文档的特定本地化 : (Source: https://docs.strapi.io/cms/api/graphql/locale#graphql-delete) Language: GRAPHQL File path: N/A ```graphql mutation DeleteRestaurant($documentId: ID!, $locale: I18NLocaleCode) { deleteRestaurant(documentId: "xzmzdo4k0z73t9i68a7yx2kk", locale: "fr") { documentId } } ``` # OpenAPI 规范 Source: https://docs.strapi.io/cms/api/openapi ## 命令行接口使用 Description: 🌐 Executing the command without any arguments will generate a specification.json file at the root of your Strapi folder project: (Source: https://docs.strapi.io/cms/api/openapi#cli-usage) Language: Bash File path: N/A ```bash yarn strapi openapi generate ``` --- Language: Bash File path: N/A ```bash npm run strapi openapi generate ``` Language: Bash File path: N/A ```bash yarn strapi openapi generate --output ./docs/api-spec.json ``` --- Language: Bash File path: N/A ```bash npm run strapi openapi generate -- --output ./docs/api-spec.json ``` ## 规范结构和内容 Description: Code example from "规范结构和内容" (Source: https://docs.strapi.io/cms/api/openapi#specification-structure-and-content) Language: JSON File path: N/A ```json { "openapi": "3.1.0", "x-powered-by": "strapi", "x-strapi-version": "5.21.0", "info": { "title": "My Strapi API", "description": "API documentation for My Strapi API", "version": "1.0.0" }, "paths": { "/api/articles": { "get": { "operationId": "article/get/articles", "parameters": [ { "name": "fields", "in": "query", "schema": { "type": "array", "items": { "type": "string" } } } ], "responses": { "200": { "description": "Successful response", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Article" } } } } } } } } } } }, "components": { "schemas": { "Article": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "content": { "type": "string" } } } } } } ``` ## HTTP 端点访问 Description: 🌐 The following example exposes both endpoints: (Source: https://docs.strapi.io/cms/api/openapi#http-endpoint-access) Language: JavaScript File path: /config/server.js ```js module.exports = { openapi: { 'content-api': { access: 'public', }, admin: { access: 'authenticated', }, }, }; ``` --- Language: TypeScript File path: /config/server.ts ```ts export default { openapi: { 'content-api': { access: 'public', }, admin: { access: 'authenticated', }, }, }; ``` ## 与 Swagger UI 集成 Description: 使用以下代码更新 /config/middlewares.js 配置文件: (Source: https://docs.strapi.io/cms/api/openapi#integrating-with-swagger-ui) Language: Bash File path: /localhost:1337/api/openapi.js ```bash yarn strapi openapi generate --output ./public/swagger-spec.json ``` --- Language: Bash File path: /localhost:1337/api/openapi.js ```bash npm run strapi openapi generate -- --output ./public/swagger-spec.json ``` Language: JavaScript File path: /config/middlewares.js ```js module.exports = [ 'strapi::logger', 'strapi::errors', { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'script-src': ["'self'", "'unsafe-inline'", 'https://unpkg.com'], 'style-src': ["'self'", "'unsafe-inline'", 'https://unpkg.com'], 'connect-src': ["'self'", 'https:'], 'img-src': ["'self'", 'data:', 'blob:', 'https:'], 'media-src': ["'self'", 'data:', 'blob:'], upgradeInsecureRequests: null, }, }, }, }, 'strapi::cors', 'strapi::poweredBy', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` --- Language: TypeScript File path: /config/middlewares.ts ```ts export default [ 'strapi::logger', 'strapi::errors', { name: 'strapi::security', config: { contentSecurityPolicy: { useDefaults: true, directives: { 'script-src': ["'self'", "'unsafe-inline'", 'https://unpkg.com'], 'style-src': ["'self'", "'unsafe-inline'", 'https://unpkg.com'], 'connect-src': ["'self'", 'https:'], 'img-src': ["'self'", 'data:', 'blob:', 'https:'], 'media-src': ["'self'", 'data:', 'blob:'], upgradeInsecureRequests: null, }, }, }, }, 'strapi::cors', 'strapi::poweredBy', 'strapi::query', 'strapi::body', 'strapi::session', 'strapi::favicon', 'strapi::public', ]; ``` Language: JavaScript File path: /config/middlewares.js ```js API Documentation
``` # 查询引擎 API Source: https://docs.strapi.io/cms/api/query-engine ## 基本用法 Description: 🌐 The Query Engine is available through strapi.db.query: (Source: https://docs.strapi.io/cms/api/query-engine#basic-usage) Language: JavaScript File path: N/A ```js strapi.db.query('api::blog.article').findMany({ // uid syntax: 'api::api-name.content-type-name' where: { title: { $startsWith: '2021', $endsWith: 'v4', }, }, populate: { category: true, }, }); ``` # 批量操作 Source: https://docs.strapi.io/cms/api/query-engine/bulk-operations ## 例子 Description: 🌐 Example (Source: https://docs.strapi.io/cms/api/query-engine/bulk-operations#example-2) Language: JavaScript File path: N/A ```js await strapi.db.query("api::blog.article").createMany({ data: [ { title: "ABCD", }, { title: "EFGH", }, ], }); // { count: 2 , ids: [1,2]} ``` --- Language: JavaScript File path: N/A ```js await strapi.db.query("api::shop.article").updateMany({ where: { price: 20, }, data: { price: 18, }, }); // { count: 42 } ``` --- Language: JavaScript File path: N/A ```js await strapi.db.query("api::blog.article").deleteMany({ where: { title: { $startsWith: "v3", }, }, }); // { count: 42 } ``` ## 参数 Description: | 参数 | 类型 | 描述 | | --- | --- | --- | | where | WhereParameter | 使用的过滤器 | (Source: https://docs.strapi.io/cms/api/query-engine/bulk-operations#parameters-3) Language: JavaScript File path: N/A ```js const count = await strapi.db.query("api::blog.article").count({ where: { title: { $startsWith: "v3", }, }, }); // 12 ``` # 使用查询引擎 API 进行过滤 Source: https://docs.strapi.io/cms/api/query-engine/filtering ## $and Description: 在传递具有嵌套条件的对象时,会隐式使用 $and: (Source: https://docs.strapi.io/cms/api/query-engine/filtering#and) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { $and: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: 'Hello World', createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, }); ``` ## $or Description: Code example from "$or" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#or) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { $or: [ { title: 'Hello World', }, { createdAt: { $gt: '2021-11-17T14:28:25.843Z' }, }, ], }, }); ``` ## $not Description: Code example from "$not" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#not) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { $not: { title: 'Hello World', }, }, }); ``` --- Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $not: { $contains: 'Hello World', }, }, }, }); ``` ## $eq Description: $eq可以省略: (Source: https://docs.strapi.io/cms/api/query-engine/filtering#eq) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $eq: 'Hello World', }, }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: 'Hello World', }, }); ``` ## $eqi Description: Code example from "$eqi" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#eqi) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $eqi: 'HELLO World', }, }, }); ``` ## $ne Description: Code example from "$ne" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#ne) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $ne: 'ABCD', }, }, }); ``` ## $nei Description: Code example from "$nei" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#nei) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $nei: 'abcd', }, }, }); ``` ## $in Description: 在传递一个值数组时可以省略 $in: (Source: https://docs.strapi.io/cms/api/query-engine/filtering#in) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $in: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: ['Hello', 'Hola', 'Bonjour'], }, }); ``` ## $notIn Description: Code example from "$notIn" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#notin) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $notIn: ['Hello', 'Hola', 'Bonjour'], }, }, }); ``` ## $lt Description: Code example from "$lt" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#lt) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { rating: { $lt: 10, }, }, }); ``` ## $lte Description: Code example from "$lte" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#lte) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { rating: { $lte: 10, }, }, }); ``` ## $gt Description: Code example from "$gt" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#gt) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { rating: { $gt: 5, }, }, }); ``` ## $gte Description: Code example from "$gte" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#gte) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { rating: { $gte: 5, }, }, }); ``` ## $between Description: Code example from "$between" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#between) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { rating: { $between: [1, 20], }, }, }); ``` ## $contains Description: Code example from "$contains" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#contains) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $contains: 'Hello', }, }, }); ``` ## $notContains Description: Code example from "$notContains" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#notcontains) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $notContains: 'Hello', }, }, }); ``` ## $containsi Description: Code example from "$containsi" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#containsi) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $containsi: 'hello', }, }, }); ``` ## $notContainsi Description: Code example from "$notContainsi" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#notcontainsi) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $notContainsi: 'hello', }, }, }); ``` ## $startsWith Description: Code example from "$startsWith" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#startswith) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $startsWith: 'ABCD', }, }, }); ``` ## $endsWith Description: Code example from "$endsWith" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#endswith) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $endsWith: 'ABCD', }, }, }); ``` ## $null Description: Code example from "$null" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#null) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $null: true, }, }, }); ``` ## $notNull Description: Code example from "$notNull" (Source: https://docs.strapi.io/cms/api/query-engine/filtering#notnull) Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::article.article').findMany({ where: { title: { $notNull: true, }, }, }); ``` # 使用查询引擎 API 进行排序和分页 Source: https://docs.strapi.io/cms/api/query-engine/order-pagination ## 单身 Description: 🌐 Single (Source: https://docs.strapi.io/cms/api/query-engine/order-pagination#single) Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ orderBy: 'id', }); // single with direction strapi.db.query('api::article.article').findMany({ orderBy: { id: 'asc' }, }); ``` ## 多个 Description: 🌐 Multiple (Source: https://docs.strapi.io/cms/api/query-engine/order-pagination#multiple) Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ orderBy: ['id', 'name'], }); // multiple with direction strapi.db.query('api::article.article').findMany({ orderBy: [{ title: 'asc' }, { publishedAt: 'desc' }], }); ``` ## 关系顺序 Description: 🌐 Relational ordering (Source: https://docs.strapi.io/cms/api/query-engine/order-pagination#relational-ordering) Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ orderBy: { author: { name: 'asc', }, }, }); ``` ## 分页 Description: 🌐 To paginate results returned by the Query Engine API, use the offset and limit parameters: (Source: https://docs.strapi.io/cms/api/query-engine/order-pagination#pagination) Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ offset: 15, limit: 10, }); ``` # 使用查询引擎 API 填充 Source: https://docs.strapi.io/cms/api/query-engine/populating ## 使用查询引擎 API 填充 Description: 🌐 To populate all the root level relations, use populate: true: (Source: https://docs.strapi.io/cms/api/query-engine/populating#populating-with-the-query-engine-api) Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ populate: true, }); ``` Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ populate: ['componentA', 'relationA'], }); ``` Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ populate: { componentB: true, dynamiczoneA: true, relation: someLogic || true, }, }); ``` Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany({ populate: { relationA: { where: { name: { $contains: 'Strapi', }, }, }, repeatableComponent: { select: ['someAttributeName'], orderBy: ['someAttributeName'], populate: { componentRelationA: true, }, }, dynamiczoneA: true, }, }); ``` Language: JavaScript File path: N/A ```js strapi.db.query('api::article.article').findMany('api::article.article', { populate: { dynamicZone: { on: { 'components.foo': { select: ['title'], where: { title: { $contains: 'strapi' } }, }, 'components.bar': { select: ['name'], }, }, }, morphAuthor: { on: { 'plugin::users-permissions.user': { select: ['username'], }, 'api::author.author': { select: ['name'], }, }, }, }, }); ``` # 单次操作 Source: https://docs.strapi.io/cms/api/query-engine/single-operations ## 例子 Description: 🌐 Example (Source: https://docs.strapi.io/cms/api/query-engine/single-operations#example-5) Language: JavaScript File path: N/A ```js const entry = await strapi.db.query('api::blog.article').findOne({ select: ['title', 'description'], where: { title: 'Hello World' }, populate: { category: true }, }); ``` --- Language: JavaScript File path: N/A ```js const entries = await strapi.db.query('api::blog.article').findMany({ select: ['title', 'description'], where: { title: 'Hello World' }, orderBy: { publishedAt: 'DESC' }, populate: { category: true }, }); ``` --- Language: JavaScript File path: N/A ```js const [entries, count] = await strapi.db.query('api::blog.article').findWithCount({ select: ['title', 'description'], where: { title: 'Hello World' }, orderBy: { title: 'DESC' }, populate: { category: true }, }); ``` --- Language: JavaScript File path: N/A ```js const entry = await strapi.db.query('api::blog.article').create({ data: { title: 'My Article', }, }); ``` --- Language: JavaScript File path: N/A ```js const entry = await strapi.db.query('api::blog.article').update({ where: { id: 1 }, data: { title: 'xxx', }, }); ``` --- Language: JavaScript File path: N/A ```js const entry = await strapi.db.query('api::blog.article').delete({ where: { id: 1 }, }); ``` # REST API参考 Source: https://docs.strapi.io/cms/api/rest ## 获取文档 Description: { name: 'pagination[pageSize]', type: 'integer', required: false, description: 'Items per page. (Source: https://docs.strapi.io/cms/api/rest#get) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/restaurants', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants/znrlzntu9ei5onjvwfaalu2v' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/restaurants/znrlzntu9ei5onjvwfaalu2v', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 2, "documentId": "hgv1vny5cebq2l3czil1rpb3", "Name": "BMK Paris Bamako", "Description": null, "createdAt": "2024-03-06T13:42:05.098Z", "updatedAt": "2024-03-06T13:42:05.098Z", "publishedAt": "2024-03-06T13:42:05.103Z", "locale": "en" }, { "id": 4, "documentId": "znrlzntu9ei5onjvwfaalu2v", "Name": "Biscotte Restaurant", "createdAt": "2024-03-06T13:43:30.172Z", "updatedAt": "2024-03-06T13:43:30.172Z", "publishedAt": "2024-03-06T13:43:30.175Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 2 } } } ``` Language: JSON File path: N/A ```json { "data": { "id": 6, "documentId": "znrlzntu9ei5onjvwfaalu2v", "Name": "Biscotte Restaurant", "Description": [ { "type": "paragraph", "children": [{ "type": "text", "text": "Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products." }] } ], "createdAt": "2024-02-27T10:19:04.953Z", "updatedAt": "2024-03-05T15:52:05.591Z", "publishedAt": "2024-03-05T15:52:05.600Z", "locale": "en" }, "meta": {} } ``` --- Language: JSON File path: N/A ```json { "data": null, "error": { "status": 404, "name": "NotFoundError", "message": "Document not found" } } ``` ## 创建文档 Description: description="创建一个新文档并返回它。在请求正文中将字段值放入数据对象中。" paramTitle="Body Parameters" params={[ { name: 'data', type: 'object', required: true, description: 'Object containing the field values for the new document' }, ]}> (Source: https://docs.strapi.io/cms/api/rest#create) Language: Bash File path: N/A ```bash curl -X POST \ 'http://localhost:1337/api/restaurants' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Name": "Restaurant D", "Description": [ { "type": "paragraph", "children": [{ "type": "text", "text": "A very short description goes here." }] } ] } }' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/restaurants', { method: 'POST', headers: { Authorization: 'Bearer ', 'Content-Type': 'application/json', }, body: JSON.stringify({ data: { Name: 'Restaurant D', Description: [ { type: 'paragraph', children: [{ type: 'text', text: 'A very short description goes here.' }], }, ], }, }), } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": { "documentId": "bw64dnu97i56nq85106yt4du", "Name": "Restaurant D", "Description": [ { "type": "paragraph", "children": [{ "type": "text", "text": "A very short description goes here." }] } ], "createdAt": "2024-03-05T16:44:47.689Z", "updatedAt": "2024-03-05T16:44:47.689Z", "publishedAt": "2024-03-05T16:44:47.687Z", "locale": "en" }, "meta": {} } ``` ## 更新文档 Description: description="通过 documentId 部分更新文档并返回其值。发送 null 值以清除字段。" paramTitle="Body Parameters" params={[ { name: 'data', type: 'object', required: true, description: 'Object containing the field values to update' }, ]}> (Source: https://docs.strapi.io/cms/api/rest#update) Language: Bash File path: N/A ```bash curl -X PUT \ 'http://localhost:1337/api/restaurants/hgv1vny5cebq2l3czil1rpb3' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Name": "BMK Paris Bamako", "Description": [ { "type": "paragraph", "children": [{ "type": "text", "text": "A very short description goes here." }] } ] } }' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/restaurants/hgv1vny5cebq2l3czil1rpb3', { method: 'PUT', headers: { Authorization: 'Bearer ', 'Content-Type': 'application/json', }, body: JSON.stringify({ data: { Name: 'BMK Paris Bamako', Description: [ { type: 'paragraph', children: [{ type: 'text', text: 'A very short description goes here.' }], }, ], }, }), } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": { "id": 9, "documentId": "hgv1vny5cebq2l3czil1rpb3", "Name": "BMK Paris Bamako", "Description": [ { "type": "paragraph", "children": [{ "type": "text", "text": "A very short description goes here." }] } ], "createdAt": "2024-03-06T13:42:05.098Z", "updatedAt": "2024-03-06T14:16:56.883Z", "publishedAt": "2024-03-06T14:16:56.895Z", "locale": "en" }, "meta": {} } ``` ## 删除文档 Description: paramTitle="Path Parameters" params={[ { name: 'pluralApiId', type: 'string', required: true, description: 'Plural API ID (e.g. (Source: https://docs.strapi.io/cms/api/rest#delete) Language: Bash File path: N/A ```bash curl -X DELETE \ 'http://localhost:1337/api/restaurants/bw64dnu97i56nq85106yt4du' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/restaurants/bw64dnu97i56nq85106yt4du', { method: 'DELETE', headers: { Authorization: 'Bearer ', }, } ); ``` # 过滤器 Source: https://docs.strapi.io/cms/api/rest/filters ## 示例:查找名字为 'John' 的用户 Description: id="find-users-eq" method="GET" path="/api/users?filters[username][$eq]=John" title="查找名字为 'John' 的用户" description="使用 $eq 过滤操作符查找完全匹配。"> (Source: https://docs.strapi.io/cms/api/rest/filters#example-find-users-having-john-as-a-first-name) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/users?filters[username][$eq]=John' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ filters: { username: { $eq: 'John', }, }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/users?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "znrlzntu9ei5onjvwfaalu2v", "username": "John", "email": "john@test.com", "provider": "local", "confirmed": true, "blocked": false, "createdAt": "2021-12-03T20:08:17.740Z", "updatedAt": "2021-12-03T20:08:17.740Z" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 示例:查找 ID 为 3、6、8 的多家餐厅 Description: id="find-restaurants-in" method="GET" path="/api/restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8" title="查找ID为3、6、8的多家餐厅" description="使用 $in 过滤运算符和一个值数组来查找多个精确值。"> (Source: https://docs.strapi.io/cms/api/rest/filters#example-find-multiple-restaurants-with-ids-3-68) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ filters: { id: { $in: [3, 6, 8], }, }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 3, "documentId": "ethwxjxtvuxl89jq720e38uk", "name": "test3" }, { "id": 6, "documentId": "ethwxjxtvuxl89jq720e38uk", "name": "test6" }, { "id": 8, "documentId": "cf07g1dbusqr8mzmlbqvlegx", "name": "test8" } ], "meta": {} } ``` ## 复杂筛选 Description: Code example from "复杂筛选" (Source: https://docs.strapi.io/cms/api/rest/filters#complex-filtering) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/books?filters[$and][0][$or][0][date][$eq]=2020-01-01&filters[$and][0][$or][1][date][$eq]=2020-01-02&filters[$and][1][author][name][$eq]=Kai%20doe' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ filters: { $and: [ { $or: [ { date: { $eq: '2020-01-01', }, }, { date: { $eq: '2020-01-02', }, }, ], }, { author: { name: { $eq: 'Kai doe', }, }, }, ], }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/books?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "rxngxzclq0zdaqtvz67hj38d", "name": "test1", "date": "2020-01-01" }, { "id": 2, "documentId": "kjkhff4e269a50b4vi16stst", "name": "test2", "date": "2020-01-02" } ], "meta": {} } ``` ## 深度过滤 Description: Code example from "深度过滤" (Source: https://docs.strapi.io/cms/api/rest/filters#deep-filtering) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?filters[chef][restaurants][stars][$eq]=5' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ filters: { chef: { restaurants: { stars: { $eq: 5, }, }, }, }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "cvsz61qg33rtyv1qljb1nrtg", "name": "GORDON RAMSAY STEAK", "stars": 5 }, { "id": 2, "documentId": "uh17h7ibw0g8thit6ivi71d8", "name": "GORDON RAMSAY BURGER", "stars": 5 } ], "meta": {} } ``` # 如何填写创建者字段 Source: https://docs.strapi.io/cms/api/rest/guides/populate-creator-fields ## 如何填写创建者字段,例如 createdBy 和 updatedBy Description: 打开内容类型为 schema.json 的文件。 2. (Source: https://docs.strapi.io/cms/api/rest/guides/populate-creator-fields#icon-namewrench--how-to-populate-creator-fields-such-as-createdby-and-updatedby) Language: JSON File path: N/A ```json "options": { "draftAndPublish": true, "populateCreatorFields": true }, ``` Language: JavaScript File path: ./src/api/test/middlewares/defaultTestPopulate.js ```js "use strict"; module.exports = (config, { strapi }) => { return async (ctx, next) => { if (!ctx.query.populate) { ctx.query.populate = ["createdBy", "updatedBy"]; } await next(); }; }; ``` Language: JavaScript File path: ./src/api/test/routes/test.js ```js "use strict"; const { createCoreRouter } = require("@strapi/strapi").factories; module.exports = createCoreRouter("api::test.test", { config: { find: { middlewares: ["api::test.default-test-populate"], }, findOne: { middlewares: ["api::test.default-test-populate"], }, }, }); ``` # 理解填充 Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate ## 示例:没有 populate Description: id="without-populate" method="GET" path="/api/articles" title="未填充" description="仅返回默认属性,不包含任何媒体字段、关系、组件或动态区域。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-without-populate-1) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "t3q2i3v1z2j7o8p6d0o4xxg", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "// truncated content" }, { "id": 2, "documentId": "k2r5l0i9g3u2j3b4p7f0sed", "title": "What are chinese hamburgers and why aren't you eating them?", "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", "createdAt": "2021-11-11T13:33:19.948Z", "updatedAt": "2023-06-01T14:32:50.984Z", "publishedAt": "2022-09-22T12:36:48.312Z", "locale": "en", "ckeditor_content": "// truncated content" }, { "id": 3, "documentId": "k6m6l9q0n6v9z2m3i0z5jah", "title": "7 Places worth visiting for the food alone", "slug": "7-places-worth-visiting-for-the-food-alone", "createdAt": "2021-11-12T13:33:19.948Z", "updatedAt": "2023-06-02T11:30:00.075Z", "publishedAt": "2023-06-02T11:30:00.075Z", "locale": "en", "ckeditor_content": "// truncated content" }, { "id": 4, "documentId": "d5m4b6z6g5d9e3v1k9n5gbn", "title": "If you don't finish your plate in these countries, you might offend someone", "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", "createdAt": "2021-11-15T13:33:19.948Z", "updatedAt": "2023-06-02T10:59:35.148Z", "publishedAt": "2022-09-22T12:35:53.899Z", "locale": "en", "ckeditor_content": "// truncated content" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "x2m0d7d9o4m2z3u2r2l9yes", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…" }, { "id": 2, "documentId": "k6m6l9q0n6v9z2m3i0z5jah", "title": "What are chinese hamburgers and why aren't you eating them?", "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", "createdAt": "2021-11-11T13:33:19.948Z", "updatedAt": "2023-06-01T14:32:50.984Z", "publishedAt": "2022-09-22T12:36:48.312Z", "locale": "en", "ckeditor_content": "…" }, { "id": 3, "documentId": "o5d4b0l4p8l4o4k5n1l3rxa", "title": "7 Places worth visiting for the food alone", "slug": "7-places-worth-visiting-for-the-food-alone", "createdAt": "2021-11-12T13:33:19.948Z", "updatedAt": "2023-06-02T11:30:00.075Z", "publishedAt": "2023-06-02T11:30:00.075Z", "locale": "en", "ckeditor_content": "…" }, { "id": 4, "documentId": "t3q2i3v1z2j7o8p6d0o4xxg", "title": "If you don't finish your plate in these countries, you might offend someone", "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", "createdAt": "2021-11-15T13:33:19.948Z", "updatedAt": "2023-06-02T10:59:35.148Z", "publishedAt": "2022-09-22T12:35:53.899Z", "locale": "en", "ckeditor_content": "…" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 示例:使用 populate=* Description: id="with-populate-all" method="GET" path="/api/articles" title="使用 populate=*" description="返回所有媒体字段、一阶关系、组件和动态区域。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-with-populate) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate=*' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate=*', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "// truncated content", "image": { "data": { "id": 12, "documentId": "o5d4b0l4p8l4o4k5n1l3rxa", "name": "Basque dish", "alternativeText": "Basque dish", "caption": "Basque dish", "width": 758, "height": 506, "formats": { "thumbnail": { "name": "thumbnail_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", "hash": "thumbnail_basque_cuisine_17fa4567e0_f033424240", "ext": ".jpeg", "mime": "image/jpeg", "width": 234, "height": 156, "size": 11.31, "path": null, "url": "/uploads/thumbnail_basque_cuisine_17fa4567e0_f033424240.jpeg" }, "medium": { "name": "medium_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", "hash": "medium_basque_cuisine_17fa4567e0_f033424240", "ext": ".jpeg", "mime": "image/jpeg", "width": 750, "height": 501, "size": 82.09, "path": null, "url": "/uploads/medium_basque_cuisine_17fa4567e0_f033424240.jpeg" }, "small": { "name": "small_https://4d40-2a01-cb00-c8b-1800-7cbb-7da-ea9d-2011.ngrok.io/uploads/basque_cuisine_17fa4567e0.jpeg", "hash": "small_basque_cuisine_17fa4567e0_f033424240", "ext": ".jpeg", "mime": "image/jpeg", "width": 500, "height": 334, "size": 41.03, "path": null, "url": "/uploads/small_basque_cuisine_17fa4567e0_f033424240.jpeg" } }, "hash": "basque_cuisine_17fa4567e0_f033424240", "ext": ".jpeg", "mime": "image/jpeg", "size": 58.209999999999994, "url": "/uploads/basque_cuisine_17fa4567e0_f033424240.jpeg", "previewUrl": null, "provider": "local", "provider_metadata": null, "createdAt": "2021-11-23T14:05:33.460Z", "updatedAt": "2021-11-23T14:05:46.084Z" } } }, "blocks": [ { "id": 2, "__component": "blocks.related-articles" }, { "id": 2, "documentId": "w8r5k8o8v0t9l9e0d7y6vco", "__component": "blocks.cta-command-line", "theme": "primary", "title": "Want to give a try to a Strapi starter?", "text": "❤️", "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" } ], "seo": { "id": 1, "documentId": "h7c8d0u3i3q5v1j3j3r4cxf", "metaTitle": "Articles - FoodAdvisor", "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", "keywords": "food", "metaRobots": null, "structuredData": null, "metaViewport": null, "canonicalURL": null }, "category": { "data": { "id": 4, "documentId": "t1t3d9k6n1k5a6r8l7f8rox", "name": "European", "slug": "european", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } }, "localizations": { "data": [ { "id": 10, "documentId": "h7c8d0u3i3q5v1j3j3r4cxf", "title": "Voici pourquoi il faut essayer la cuisine basque, selon un chef basque", "slug": "voici-pourquoi-il-faut-essayer-la-cuisine-basque-selon-un-chef-basque", "createdAt": "2021-11-18T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.606Z", "publishedAt": "2022-09-22T13:00:00.069Z", "locale": "fr-FR", "ckeditor_content": "// truncated content" } ] } } }, { "id": 2, "// truncated content": true }, { "id": 3, "// truncated content": true }, { "id": 4, "// truncated content": true } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 填充特定的关系和字段 Description: Code example from "填充特定的关系和字段" (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#populate-specific-relations-and-fields) Language: JSON File path: N/A ```json { populate: { category: { populate: ['restaurants'], }, }, } ``` --- Language: JSON File path: N/A ```json { populate: [ 'articles', 'restaurants' ], } ``` ## 示例:使用 populate[0]=category Description: id="with-populate-category" method="GET" path="/api/articles" title="使用 populate[0]=category" description="返回填充了相关类别数据的文章。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-with-populate0category) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[0]=category' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[0]=category', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "w8r5k8o8v0t9l9e0d7y6vco", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 4, "documentId": "u6x8u7o7j5q1l5y3t8j9yxi", "name": "European", "slug": "european", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 2, "documentId": "k6m6l9q0n6v9z2m3i0z5jah", "title": "What are chinese hamburgers and why aren't you eating them?", "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", "createdAt": "2021-11-11T13:33:19.948Z", "updatedAt": "2023-06-01T14:32:50.984Z", "publishedAt": "2022-09-22T12:36:48.312Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 13, "documentId": "x2m0d7d9o4m2z3u2r2l9yes", "name": "Chinese", "slug": "chinese", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 3, "title": "7 Places worth visiting for the food alone", "slug": "7-places-worth-visiting-for-the-food-alone", "createdAt": "2021-11-12T13:33:19.948Z", "updatedAt": "2023-06-02T11:30:00.075Z", "publishedAt": "2023-06-02T11:30:00.075Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 3, "documentId": "h7c8d0u3i3q5v1j3j3r4cxf", "name": "International", "slug": "international", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 4, "documentId": "t1t3d9k6n1k5a6r8l7f8rox", "title": "If you don't finish your plate in these countries, you might offend someone", "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", "createdAt": "2021-11-15T13:33:19.948Z", "updatedAt": "2023-06-02T10:59:35.148Z", "publishedAt": "2022-09-22T12:35:53.899Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 3, "documentId": "u6x8u7o7j5q1l5y3t8j9yxi", "name": "International", "slug": "international", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 为特定关系填充多层数据 Description: 🌐 The syntax for advanced query parameters can be quite complex to build manually. (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#populate-several-levels-deep-for-specific-relations) Language: JSON File path: N/A ```json { populate: { category: { populate: ['restaurants'], }, }, } ``` ## 示例:具有 1 级深度的人口 Description: id="with-1-level-deep-population" method="GET" path="/api/articles" title="具有1级深度的人口" description="填充类别关系,深入1级。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-with-1-level-deep-population) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[0]=category' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[0]=category', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "9ih6hy1bnma3q3066kdwt3", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 4, "name": "European", "slug": "european", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 2, "documentId": "sen6qfgxcac13pwchf8xbu", "title": "What are chinese hamburgers and why aren't you eating them?", "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", "createdAt": "2021-11-11T13:33:19.948Z", "updatedAt": "2023-06-01T14:32:50.984Z", "publishedAt": "2022-09-22T12:36:48.312Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 13, "documentId": "r3rhzcxd7gjx07vkq3pia5", "name": "Chinese", "slug": "chinese", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 3, "documentId": "s9uu7rkukhfcsmj2e60b67", "title": "7 Places worth visiting for the food alone", "slug": "7-places-worth-visiting-for-the-food-alone", "createdAt": "2021-11-12T13:33:19.948Z", "updatedAt": "2023-06-02T11:30:00.075Z", "publishedAt": "2023-06-02T11:30:00.075Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 3, "documentId": "4sevz15w6bdol6y4t8kblk", "name": "International", "slug": "international", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } }, { "id": 4, "documentId": "iy5ifm3xj8q0t8vlq6l23h", "title": "If you don't finish your plate in these countries, you might offend someone", "slug": "if-you-don-t-finish-your-plate-in-these-countries-you-might-offend-someone", "createdAt": "2021-11-15T13:33:19.948Z", "updatedAt": "2023-06-02T10:59:35.148Z", "publishedAt": "2022-09-22T12:35:53.899Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 3, "documentId": "0eor603u8qej933maphdv3", "name": "International", "slug": "international", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z" } } } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 示例:具有两级深度的人口 Description: id="with-2-level-deep-population" method="GET" path="/api/articles" title="具有两级深度的人口" description="填充类别关系和嵌套的餐厅关系,深度为2级。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-with-2-level-deep-population) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[category][populate][0]=restaurants' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[category][populate][0]=restaurants', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "iy5ifm3xj8q0t8vlq6l23h", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…", "category": { "data": { "id": 4, "name": "European", "slug": "european", "createdAt": "2021-11-09T13:33:20.123Z", "updatedAt": "2021-11-09T13:33:20.123Z", "restaurants": { "data": [ { "id": 1, "documentId": "ozlqrdxpnjb7wtvf6lp74v", "name": "Mint Lounge", "slug": "mint-lounge", "price": "p3", "createdAt": "2021-11-09T14:07:47.125Z", "updatedAt": "2021-11-23T16:41:30.504Z", "publishedAt": "2021-11-23T16:41:30.501Z", "locale": "en" }, { "id": 9, "// truncated content": true }, { "id": 10, "// truncated content": true }, { "id": 12, "// truncated content": true }, { "id": 21, "// truncated content": true }, { "id": 26, "// truncated content": true } ] } } } }, { "id": 2, "// truncated content": true }, { "id": 3, "// truncated content": true }, { "id": 4, "// truncated content": true } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 填充组件 Description: 🌐 The syntax for advanced query parameters can be quite complex to build manually. (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#populate-components) Language: JSON File path: N/A ```json { populate: [ 'seoData', 'seoData.sharedImage', 'seoData.sharedImage.media', ], }, ``` ## 示例:仅第一级组件 Description: id="only-1st-level-component" method="GET" path="/api/articles" title="仅一级组件" description="仅填充 SEO 组件,深度为1级。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-only-1st-level-component) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[0]=seo' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[0]=seo', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "md60m5cy3dula5g87x1uar", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…", "seo": { "id": 1, "documentId": "kqcwhq6hes25kt9ebj8x7j", "metaTitle": "Articles - FoodAdvisor", "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", "keywords": "food", "metaRobots": null, "structuredData": null, "metaViewport": null, "canonicalURL": null } }, { "id": 2, "// truncated content": true }, { "id": 3, "// truncated content": true }, { "id": 4, "// truncated content": true } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 示例:一级组件和二级组件 Description: id="1st-level-and-2nd-level-component" method="GET" path="/api/articles" title="一级组件和二级组件" description="填充 seo 组件和嵌套的 metaSocial 组件。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example-1st-level-and-2nd-level-component) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[0]=seo&populate[1]=seo.metaSocial' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[0]=seo&populate[1]=seo.metaSocial', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "c2imt19iywk27hl2ftph7s", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "…", "seo": { "id": 1, "documentId": "e8cnux5ejxyqrejd5addfv", "metaTitle": "Articles - FoodAdvisor", "metaDescription": "Discover our articles about food, restaurants, bars and more! - FoodAdvisor", "keywords": "food", "metaRobots": null, "structuredData": null, "metaViewport": null, "canonicalURL": null, "metaSocial": [ { "id": 1, "documentId": "ks7xsp9fewoi0qljcz9qa0", "socialNetwork": "Facebook", "title": "Browse our best articles about food and restaurants ", "description": "Discover our articles about food, restaurants, bars and more!" } ] } }, { "id": 2, "// truncated content": true }, { "id": 3, "// truncated content": true }, { "id": 4, "// truncated content": true } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 填充动态区域 Description: 🌐 The syntax for advanced query parameters can be quite complex to build manually. (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#populate-dynamic-zones) Language: JSON File path: N/A ```json { populate: { blocks: { // asking to populate the blocks dynamic zone on: { // using a detailed population strategy to explicitly define what you want 'blocks.related-articles': { populate: { 'articles': { populate: ['image'] } } }, 'blocks.cta-command-line': { populate: '*' } }, }, }, } ``` ## 例子 Description: id="detailed-population-dynamic-zones" method="GET" path="/api/articles" title="动态区域的详细人口策略" description="使用详细的逐组件填充策略来填充区块的动态区域。"> (Source: https://docs.strapi.io/cms/api/rest/guides/understanding-populate#example) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[blocks][on][blocks.related-articles][populate][articles][populate][0]=image&populate[blocks][on][blocks.cta-command-line][populate]=*' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const response = await fetch( 'http://localhost:1337/api/articles?populate[blocks][on][blocks.related-articles][populate][articles][populate][0]=image&populate[blocks][on][blocks.cta-command-line][populate]=*', { headers: { Authorization: 'Bearer ', }, } ); const data = await response.json(); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "it9bbhcgc6mcfsqas7h1dp", "title": "Here's why you have to try basque cuisine, according to a basque chef", "slug": "here-s-why-you-have-to-try-basque-cuisine-according-to-a-basque-chef", "createdAt": "2021-11-09T13:33:19.948Z", "updatedAt": "2023-06-02T10:57:19.584Z", "publishedAt": "2022-09-22T09:30:00.208Z", "locale": "en", "ckeditor_content": "// truncated content", "blocks": [ { "id": 2, "documentId": "e8cnux5ejxyqrejd5addfv", "__component": "blocks.related-articles", "articles": { "data": [ { "id": 2, "documentId": "wkgojrcg5bkz8teqx1foz7", "title": "What are chinese hamburgers and why aren't you eating them?", "slug": "what-are-chinese-hamburgers-and-why-aren-t-you-eating-them", "createdAt": "2021-11-11T13:33:19.948Z", "updatedAt": "2023-06-01T14:32:50.984Z", "publishedAt": "2022-09-22T12:36:48.312Z", "locale": "en", "ckeditor_content": "// truncated content", "image": { "data": { "// …": true } } } }, { "id": 3, "// …": true }, { "id": 4, "// …": true } ] } }, { "id": 2, "__component": "blocks.cta-command-line", "theme": "primary", "title": "Want to give a try to a Strapi starter?", "text": "❤️", "commandLine": "git clone https://github.com/strapi/nextjs-corporate-starter.git" } ] }, { "id": 2, "// …": true }, { "id": 3, "documentId": "z5jnfvyuj07fogzh1kcbd3", "title": "7 Places worth visiting for the food alone", "slug": "7-places-worth-visiting-for-the-food-alone", "createdAt": "2021-11-12T13:33:19.948Z", "updatedAt": "2023-06-02T11:30:00.075Z", "publishedAt": "2023-06-02T11:30:00.075Z", "locale": "en", "ckeditor_content": "// truncated content", "blocks": [ { "id": 1, "documentId": "ks7xsp9fewoi0qljcz9qa0", "__component": "blocks.related-articles", "articles": { "// …": true } }, { "id": 1, "documentId": "c2imt19iywk27hl2ftph7s", "__component": "blocks.cta-command-line", "theme": "secondary", "title": "Want to give it a try with a brand new project?", "text": "Up & running in seconds 🚀", "commandLine": "npx create-strapi-app my-project --quickstart" } ] }, { "id": 4, "// …": true } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` # 本地化 Source: https://docs.strapi.io/cms/api/rest/locale ## GET 获取特定语言环境中的所有文档 Description: id="rest-get-all-endpoint" method="GET" path="/api/restaurants?locale=fr" title="获取特定语言环境下的所有文档" description="返回给定语言环境的所有文档。"> (Source: https://docs.strapi.io/cms/api/rest/locale#rest-get-all) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?locale=fr' \ -H 'Authorization: Bearer ' ``` Language: JSON File path: N/A ```json { "data": [ { "id": 5, "documentId": "h90lgohlzfpjf3bvan72mzll", "Title": "Meilleures pizzas", "Body": [ { "type": "paragraph", "children": [ { "type": "text", "text": "On déguste les meilleures pizzas de la ville à la Pizzeria Arrivederci." } ] } ], "createdAt": "2024-03-06T22:08:59.643Z", "updatedAt": "2024-03-06T22:10:21.127Z", "publishedAt": "2024-03-06T22:10:21.130Z", "locale": "fr" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 集合类型 Description: id="get-one-collection-type-endpoint" method="GET" path="/api/restaurants/:documentId?locale=fr" title="获取特定区域(集合类型)的文档" description="返回特定区域设置的集合类型中的特定文档。"> (Source: https://docs.strapi.io/cms/api/rest/locale#get-one-collection-type) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr' \ -H 'Authorization: Bearer ' ``` Language: JSON File path: N/A ```json { "data": [ { "id": 22, "documentId": "lr5wju2og49bf820kj9kz8c3", "Name": "Biscotte Restaurant", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "Bienvenue au restaurant Biscotte! Le Restaurant Biscotte propose une cuisine à base de produits frais et de qualité, souvent locaux, biologiques lorsque cela est possible, et toujours produits par des producteurs passionnés." } ] } ], "locale": "fr" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 3 } } } ``` ## 单类型 Description: id="get-one-single-type-endpoint" method="GET" path="/api/homepage?locale=fr" title="获取特定区域的文档(单一类型)" description="返回给定区域设置的特定单类型文档。"> (Source: https://docs.strapi.io/cms/api/rest/locale#get-one-single-type) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/homepage?locale=fr' \ -H 'Authorization: Bearer ' ``` Language: JSON File path: N/A ```json { "data": { "id": 10, "documentId": "ukbpbnu8kbutpn98rsanyi50", "Title": "Page d'accueil", "Body": null, "createdAt": "2024-03-07T13:28:26.349Z", "updatedAt": "2024-03-07T13:28:26.349Z", "publishedAt": "2024-03-07T13:28:26.353Z", "locale": "fr" }, "meta": {} } ``` ## 对于默认区域设置 Description: id="rest-create-default-locale-endpoint" method="POST" path="/api/restaurants" title="为默认语言环境创建文档" description="使用默认语言环境创建新文档。"> (Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-default-locale) Language: Bash File path: N/A ```bash curl -X POST \ 'http://localhost:1337/api/restaurants' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Name": "Oplato" } }' ``` Language: JSON File path: N/A ```json { "data": { "id": 13, "documentId": "jae8klabhuucbkgfe2xxc5dj", "Name": "Oplato", "Description": null, "createdAt": "2024-03-06T22:19:54.646Z", "updatedAt": "2024-03-06T22:19:54.646Z", "publishedAt": "2024-03-06T22:19:54.649Z", "locale": "en" }, "meta": {} } ``` ## 针对特定区域 Description: id="rest-create-specific-locale-endpoint" method="POST" path="/api/restaurants?locale=fr" title="为特定地区创建文档" description="为指定的语言环境创建一个新文档。"> (Source: https://docs.strapi.io/cms/api/rest/locale#rest-create-specific-locale) Language: Bash File path: N/A ```bash curl -X POST \ 'http://localhost:1337/api/restaurants?locale=fr' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Name": "She'\''s Cake" } }' ``` Language: JSON File path: N/A ```json { "data": { "id": 15, "documentId": "ldcmn698iams5nuaehj69j5o", "Name": "She's Cake", "Description": null, "createdAt": "2024-03-06T22:21:18.373Z", "updatedAt": "2024-03-06T22:21:18.373Z", "publishedAt": "2024-03-06T22:21:18.378Z", "locale": "fr" }, "meta": {} } ``` ## 在集合类型 中 Description: id="rest-put-collection-type-endpoint" method="PUT" path="/api/restaurants/:documentId?locale=fr" title="创建或更新本地版本(集合类型)" description="为现有餐厅创建法语区域设置,如果已存在则更新它。"> (Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-collection-type) Language: Bash File path: N/A ```bash curl -X PUT \ 'http://localhost:1337/api/restaurants/lr5wju2og49bf820kj9kz8c3?locale=fr' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Name": "She'\''s Cake in French" } }' ``` Language: JSON File path: N/A ```json { "data": { "id": 19, "documentId": "lr5wju2og49bf820kj9kz8c3", "Name": "She's Cake in French", "Description": null, "createdAt": "2024-03-07T12:13:09.551Z", "updatedAt": "2024-03-07T12:13:09.551Z", "publishedAt": "2024-03-07T12:13:09.554Z", "locale": "fr" }, "meta": {} } ``` Language: Bash File path: N/A ```bash curl -X DELETE \ 'http://localhost:1337/api/restaurants/abcdefghijklmno456?locale=fr' \ -H 'Authorization: Bearer ' ``` ## 在单一类型中 Description: id="rest-put-single-type-endpoint" method="PUT" path="/api/homepage?locale=fr" title="创建或更新本地化版本(单类型)" description="为现有的主页单一类型创建一个法语区域设置,如果它已存在则更新它。"> (Source: https://docs.strapi.io/cms/api/rest/locale#rest-delete-single-type) Language: Bash File path: N/A ```bash curl -X PUT \ 'http://localhost:1337/api/homepage?locale=fr' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "data": { "Title": "Page d'\''accueil" } }' ``` Language: JSON File path: N/A ```json { "data": { "id": 10, "documentId": "ukbpbnu8kbutpn98rsanyi50", "Title": "Page d'accueil", "Body": null, "createdAt": "2024-03-07T13:28:26.349Z", "updatedAt": "2024-03-07T13:28:26.349Z", "publishedAt": "2024-03-07T13:28:26.353Z", "locale": "fr" }, "meta": {} } ``` Language: Bash File path: N/A ```bash curl -X DELETE \ 'http://localhost:1337/api/homepage?locale=fr' \ -H 'Authorization: Bearer ' ``` # 填充并选择 Source: https://docs.strapi.io/cms/api/rest/populate-select ## 字段选择 Description: id="field-selection-example" method="GET" path="/api/restaurants?fields[0]=name&fields[1]=description" title="只返回名称和描述字段" description="使用 fields 参数仅选择响应中的特定字段。"> (Source: https://docs.strapi.io/cms/api/rest/populate-select#field-selection) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?fields[0]=name&fields[1]=description' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify( { fields: ['name', 'description'], }, { encodeValuesOnly: true, // prettify URL } ); await request(`/api/users?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 4, "Name": "Pizzeria Arrivederci", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "Specialized in pizza, we invite you to rediscover our classics, such as 4 Formaggi or Calzone, and our original creations such as Do Luigi or Nduja." } ] } ], "documentId": "lr5wju2og49bf820kj9kz8c3" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` ## 用字段选择填充 Description: Code example from "用字段选择填充" (Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-field-selection) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?fields[0]=title&fields[1]=slug&populate[headerImage][fields][0]=name&populate[headerImage][fields][1]=url' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify( { fields: ['title', 'slug'], populate: { headerImage: { fields: ['name', 'url'], }, }, }, { encodeValuesOnly: true, // prettify URL } ); await request(`/api/articles?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "h90lgohlzfpjf3bvan72mzll", "title": "Test Article", "slug": "test-article", "headerImage": { "id": 1, "documentId": "cf07g1dbusqr8mzmlbqvlegx", "name": "17520.jpg", "url": "/uploads/17520_73c601c014.jpg" } } ], "meta": {} } ``` ## 填充并筛选 Description: Code example from "填充并筛选" (Source: https://docs.strapi.io/cms/api/rest/populate-select#populate-with-filtering) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?populate[categories][sort][0]=name%3Aasc&populate[categories][filters][name][$eq]=Cars' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify( { populate: { categories: { sort: ['name:asc'], filters: { name: { $eq: 'Cars', }, }, }, }, }, { encodeValuesOnly: true, // prettify URL } ); await request(`/api/articles?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "a1b2c3d4e5d6f7g8h9i0jkl", "title": "Test Article", "categories": { "data": [ { "id": 2, "documentId": "jKd8djla9ndalk98hflj3", "name": "Cars" } ] } } ], "meta": {} } ``` # REST API:publicationFilter Source: https://docs.strapi.io/cms/api/rest/publication-filter ## 查找从未发布的草稿 Description: Code example from "查找从未发布的草稿" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#never-published) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'never-published', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "New Restaurant", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 查找从未在任何地区发布的草稿 Description: Code example from "查找从未在任何地区发布的草稿" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#never-published-document) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=never-published-document' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'never-published-document', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "d41r46wac4xix5vpba7561at", "name": "New Restaurant", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 查找已修改的文档 Description: Code example from "查找已修改的文档" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#modified) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=modified' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'modified', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?publicationFilter=modified' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ publicationFilter: 'modified', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant (updated)", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": "2024-03-14T15:40:45.330Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 查找未修改的文档 Description: Code example from "查找未修改的文档" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#unmodified) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=unmodified' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'unmodified', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?publicationFilter=unmodified' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ publicationFilter: 'unmodified', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": "2024-03-14T15:40:45.330Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 查找有已发布版本的文档 Description: Code example from "查找有已发布版本的文档" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#has-published-version) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'has-published-version', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?publicationFilter=has-published-version' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ publicationFilter: 'has-published-version', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": "2024-03-14T15:40:45.330Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 查找在至少一个地区有已发布版本的文档 Description: Code example from "查找在至少一个地区有已发布版本的文档" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#has-published-version-document) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?status=draft&publicationFilter=has-published-version-document' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', publicationFilter: 'has-published-version-document', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` ## 诊断值 Description: Code example from "诊断值" (Source: https://docs.strapi.io/cms/api/rest/publication-filter#diagnostics) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?publicationFilter=published-without-draft' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ publicationFilter: 'published-without-draft', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?publicationFilter=published-with-draft' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ publicationFilter: 'published-with-draft', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "j0klm1n2o3p4q5r6s7t8u9v", "name": "Legacy Restaurant", "publishedAt": "2024-01-10T09:15:00.000Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "documentId": "a1b2c3d4e5f6g7h8i9j0klm", "name": "Biscotte Restaurant", "publishedAt": "2024-03-14T15:40:45.330Z", "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 } } } ``` # 关系 Source: https://docs.strapi.io/cms/api/rest/relations ## 管理与 API 请求的关系 Description: 🌐 When Internationalization (i18n) 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: (Source: https://docs.strapi.io/cms/api/rest/relations#managing-relations-with-api-requests) Language: JavaScript File path: N/A ```js await strapi.documents('api::restaurant.restaurant').update({ documentId: 'a1b2c3d4e5f6g7h8i9j0klm', locale: 'fr', data: { category: { connect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv'] } } }) ``` Language: JavaScript File path: N/A ```js await strapi.documents('api::cart.cart').update({ documentId: cart.documentId, data: { status: 'checked_out', order: { connect: [order.documentId] } as any, // Temporary workaround }, }); ``` ## connect Description: PUT http://localhost:1337/api/restaurants/a1b2c3d4e5f6g7h8i9j0klm (Source: https://docs.strapi.io/cms/api/rest/relations#connect) Language: JSON File path: N/A ```json { data: { categories: { connect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv'] } } } ``` --- Language: JavaScript File path: N/A ```js const fetch = require('node-fetch'); const response = await fetch( 'http://localhost:1337/api/restaurants/a1b2c3d4e5f6g7h8i9j0klm', { method: 'put', body: { data: { categories: { connect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv'] } } } } ); ``` --- Language: JSON File path: N/A ```json { data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } ] } } } ``` --- Language: JavaScript File path: N/A ```js const fetch = require('node-fetch'); const response = await fetch( 'http://localhost:1337/api/restaurants/a1b2c3d4e5f6g7h8i9j0klm', { method: 'put', body: { data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } ] } } } } ); ``` ## 关系重新排序 Description: 🌐 Consider the following record in the database: (Source: https://docs.strapi.io/cms/api/rest/relations#relations-reordering) Language: JavaScript File path: N/A ```js categories: [ { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' } ] ``` --- Language: JSON File path: N/A ```json { data: { categories: { connect: [ { documentId: 'ma12bc34de56fg78hi90jkl', position: { before: 'z0y2x4w6v8u1t3s5r7q9onm' } }, ] } } } ``` --- Language: JavaScript File path: N/A ```js categories: [ { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' } ] ``` --- Language: JSON File path: N/A ```json { data: { categories: { connect: [ { id: '6u86wkc6x3parjd4emikhmx', position: { after: 'j9k8l7m6n5o4p3q2r1s0tuv'} }, { id: '3r1wkvyjwv0b9b36s7hzpxl', position: { before: 'z0y2x4w6v8u1t3s5r7q9onm' } }, { id: 'rkyqa499i84197l29sbmwzl', position: { end: true } }, { id: 'srkvrr77k96o44d9v6ef1vu' }, { id: 'nyk7047azdgbtjqhl7btuxw', position: { start: true } }, ] } } } ``` --- Language: JavaScript File path: N/A ```js categories: [ { id: 'nyk7047azdgbtjqhl7btuxw' }, { id: 'j9k8l7m6n5o4p3q2r1s0tuv' }, { id: '6u86wkc6x3parjd4emikhmx6' }, { id: '3r1wkvyjwv0b9b36s7hzpxl7' }, { id: 'a1b2c3d4e5f6g7h8i9j0klm' }, { id: 'rkyqa499i84197l29sbmwzl' }, { id: 'srkvrr77k96o44d9v6ef1vu9' } ] ``` ## 边缘情况:草稿与发布或国际化禁用 Description: 🌐 In this situation you can select which locale you are connecting to: (Source: https://docs.strapi.io/cms/api/rest/relations#edge-cases-draft--publish-or-i18n-disabled) Language: JavaScript File path: N/A ```js data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', locale: 'en' }, // Connect to the same document id but with a different locale 👇 { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', locale: 'fr' }, ] } } ``` Language: JavaScript File path: N/A ```js data: { categories: { connect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', status: 'draft' }, // Connect to the same document id but with different publication states 👇 { documentId: 'z0y2x4w6v8u1t3s5r7q9onm', status: 'published' }, ] } } ``` ## disconnect Description: PUT http://localhost:1337/api/restaurants/a1b2c3d4e5f6g7h8i9j0klm (Source: https://docs.strapi.io/cms/api/rest/relations#disconnect) Language: JSON File path: N/A ```json { data: { categories: { disconnect: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv'], } } } ``` --- Language: JSON File path: N/A ```json { data: { categories: { disconnect: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } ], } } } ``` ## set Description: PUT http://localhost:1337/api/restaurants/a1b2c3d4e5f6g7h8i9j0klm (Source: https://docs.strapi.io/cms/api/rest/relations#set) Language: JSON File path: N/A ```json { data: { categories: { set: ['z0y2x4w6v8u1t3s5r7q9onm', 'j9k8l7m6n5o4p3q2r1s0tuv4'], } } } ``` --- Language: JSON File path: N/A ```json { data: { categories: { set: [ { documentId: 'z0y2x4w6v8u1t3s5r7q9onm' }, { documentId: 'j9k8l7m6n5o4p3q2r1s0tuv' } ], } } } ``` # 排序与分页 Source: https://docs.strapi.io/cms/api/rest/sort-pagination ## 示例:使用两个字段排序 Description: id="sort-using-2-fields" method="GET" path="/api/restaurants?sort[0]=Description&sort[1]=Name" title="使用两个字段排序" description="按描述和名称字段排序结果。"> (Source: https://docs.strapi.io/cms/api/rest/sort-pagination#example-sort-using-2-fields) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?sort[0]=Description&sort[1]=Name' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ sort: ['Description', 'Name'], }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 9, "documentId": "hgv1vny5cebq2l3czil1rpb3", "Name": "BMK Paris Bamako", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "A very short description goes here." } ] } ] // … }, { "id": 8, "documentId": "flzc8qrarj19ee0luix8knxn", "Name": "Restaurant D", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "A very short description goes here." } ] } ] // … } // … ], "meta": { // … } } ``` ## 示例:使用两个字段排序并设置顺序 Description: id="sort-using-2-fields-and-set-the-order" method="GET" path="/api/restaurants?sort[0]=Description:asc&sort[1]=Name:desc" title="使用两个字段进行排序并设置顺序" description="按描述升序排列结果,按名称降序排列。"> (Source: https://docs.strapi.io/cms/api/rest/sort-pagination#example-sort-using-2-fields-and-set-the-order) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/restaurants?sort[0]=Description:asc&sort[1]=Name:desc' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ sort: ['Description:asc', 'Name:desc'], }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/restaurants?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 8, "documentId": "flzc8qrarj19ee0luix8knxn", "Name": "Restaurant D", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "A very short description goes here." } ] } ] // … }, { "id": 9, "documentId": "hgv1vny5cebq2l3czil1rpb3", "Name": "BMK Paris Bamako", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "A very short description goes here." } ] } ] // … } // … ], "meta": { // … } } ``` ## 按页分页 Description: Code example from "按页分页" (Source: https://docs.strapi.io/cms/api/rest/sort-pagination#pagination-by-page) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?pagination[page]=1&pagination[pageSize]=10' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ pagination: { page: 1, pageSize: 10, }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/articles?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ // ... ], "meta": { "pagination": { "page": 1, "pageSize": 10, "pageCount": 5, "total": 48 } } } ``` ## 按偏移量分页 Description: Code example from "按偏移量分页" (Source: https://docs.strapi.io/cms/api/rest/sort-pagination#pagination-by-offset) Language: JavaScript File path: ./config/api.js ```js curl 'http://localhost:1337/api/articles?pagination[start]=0&pagination[limit]=10' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: ./config/api.js ```js const qs = require('qs'); const query = qs.stringify({ pagination: { start: 0, limit: 10, }, }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/articles?${query}`); ``` Language: JSON File path: ./config/api.js ```json { "data": [ // ... ], "meta": { "pagination": { "start": 0, "limit": 10, "total": 42 } } } ``` # 状态 Source: https://docs.strapi.io/cms/api/rest/status ## REST API:status Description: id="get-draft-versions" method="GET" path="/api/articles?status=draft" title="获取餐厅草稿版本" description="通过传递 status=draft 查询参数返回文档的草稿版本。"> (Source: https://docs.strapi.io/cms/api/rest/status#rest-api-status) Language: Bash File path: N/A ```bash curl 'http://localhost:1337/api/articles?status=draft' \ -H 'Authorization: Bearer ' ``` --- Language: JavaScript File path: N/A ```js const qs = require('qs'); const query = qs.stringify({ status: 'draft', }, { encodeValuesOnly: true, // prettify URL }); await request(`/api/articles?${query}`); ``` Language: JSON File path: N/A ```json { "data": [ { "id": 5, "documentId": "znrlzntu9ei5onjvwfaalu2v", "Name": "Biscotte Restaurant", "Description": [ { "type": "paragraph", "children": [ { "type": "text", "text": "This is the draft version." } ] } ], "createdAt": "2024-03-06T13:43:30.172Z", "updatedAt": "2024-03-06T21:38:46.353Z", "publishedAt": null, "locale": "en" } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 4 } } } ``` # 上传文件 Source: https://docs.strapi.io/cms/api/rest/upload ## 获取所有文件 Description: GET /api/upload/files 返回媒体库中所有文件的扁平数组: (Source: https://docs.strapi.io/cms/api/rest/upload#get-all-files) Language: JSON File path: N/A ```json [ { "id": 1, "documentId": "a1b2c3...", "name": "photo.jpg", "url": "/uploads/photo.jpg", "mime": "image/jpeg", "size": 12.34 // ...other file fields } // ... ] ``` ## 获取分页文件列表 Description: Response (Source: https://docs.strapi.io/cms/api/rest/upload#get-a-paginated-list-of-files) Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "a1b2c3...", "name": "photo.jpg", "url": "/uploads/photo.jpg", "mime": "image/jpeg", "size": 12.34 // ...other file fields } ], "meta": { "pagination": { "page": 2, "pageSize": 10, "pageCount": 4, "total": 100 } } } ``` --- Language: JSON File path: N/A ```json { "data": [ // ... ], "meta": { "pagination": { "start": 20, "limit": 5 } } } ``` Language: JSON File path: N/A ```json { "data": [ { "id": 1, "documentId": "a1b2c3...", "name": "photo.jpg", "url": "/uploads/photo.jpg", "mime": "image/jpeg", "size": 12.34 // ...other file fields } ], "meta": { "pagination": { "page": 2, "pageSize": 10, "pageCount": 4, "total": 100 } } } ``` --- Language: JSON File path: N/A ```json { "data": [ // ... ], "meta": { "pagination": { "start": 20, "limit": 5 } } } ``` ## 上传文件 Description: Code example from "上传文件" (Source: https://docs.strapi.io/cms/api/rest/upload#upload-files) Language: HTML File path: N/A ```html
``` --- Language: JavaScript File path: N/A ```js import { FormData } from 'formdata-node'; import fetch, { blobFrom } from 'node-fetch'; const file = await blobFrom('./1.png', 'image/png'); const form = new FormData(); form.append('files', file, "1.png"); form.append( 'fileInfo', JSON.stringify({ name: 'Homepage hero', alternativeText: 'Person smiling while holding laptop', caption: 'Hero image used on the homepage', }) ); const response = await fetch('http://localhost:1337/api/upload', { method: 'post', body: form, }); ``` ## 上传入口文件 Description: 🌐 For example, given the Restaurant model attributes: (Source: https://docs.strapi.io/cms/api/rest/upload#upload-entry-files) Language: JSON File path: /src/api/restaurant/content-types/restaurant/schema.json ```json { // ... "attributes": { "name": { "type": "string" }, "cover": { "type": "media", "multiple": false, } } // ... } ``` Language: HTML File path: N/A ```html
``` ## 更新文件信息 Description: fileInfo 是唯一被接受的参数,用于描述要更新的文件信息: (Source: https://docs.strapi.io/cms/api/rest/upload#update-fileinfo) Language: JavaScript File path: N/A ```js import { FormData } from 'formdata-node'; import fetch from 'node-fetch'; 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, }); ``` ## 模型定义 Description: 🌐 The following example lets you upload and attach one file to the avatar attribute: (Source: https://docs.strapi.io/cms/api/rest/upload#models-definition) Language: JSON File path: /src/api/restaurant/content-types/restaurant/schema.json ```json { // ... { "attributes": { "pseudo": { "type": "string", "required": true }, "email": { "type": "email", "required": true, "unique": true }, "avatar": { "type": "media", "multiple": false, } } } // ... } ``` Language: JSON File path: /src/api/restaurant/content-types/restaurant/schema.json ```json { // ... { "attributes": { "name": { "type": "string", "required": true }, "covers": { "type": "media", "multiple": true, } } } // ... } ``` # 控制器 Source: https://docs.strapi.io/cms/backend-customization/controllers ## 添加新控制器 Description: 使用 交互式 CLI 命令 strapi generate - 或通过创建 JavaScript 文件手动: - 在 ./src/api/[api-name]/controllers/ 中用于 API 控制器(此位置很重要,因为 Strapi 会从这里自动加载控制器) - 或者在像 ./src/plugins/[plugin-name]/server/controllers/ 这样的文件夹中用于插件控制器,尽管它们可以创建在其他地方,只要插件接口在 strapi-server.js 文件中正确导出(参见 插件的服务器 API 文档) (Source: https://docs.strapi.io/cms/backend-customization/controllers#adding-a-new-controller) Language: JavaScript File path: ./src/api/restaurant/controllers/restaurant.js ```js const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ // Method 1: Creating an entirely custom action async exampleAction(ctx) { try { ctx.body = 'ok'; } catch (err) { ctx.body = err; } }, // Method 2: Wrapping a core action (leaves core logic in place) async find(ctx) { // some custom logic here ctx.query = { ...ctx.query, local: 'en' } // Calling the default core action const { data, meta } = await super.find(ctx); // some more custom logic meta.date = Date.now() return { data, meta }; }, // Method 3: Replacing a core action with proper sanitization async find(ctx) { // validateQuery (optional) // to throw an error on query params that are invalid or the user does not have access to await this.validateQuery(ctx); // sanitizeQuery to remove any query params that are invalid or the user does not have access to // It is strongly recommended to use sanitizeQuery even if validateQuery is used const sanitizedQueryParams = await this.sanitizeQuery(ctx); const { results, pagination } = await strapi.service('api::restaurant.restaurant').find(sanitizedQueryParams); const sanitizedResults = await this.sanitizeOutput(results, ctx); return this.transformResponse(sanitizedResults, { pagination }); } })); ``` --- Language: TypeScript File path: ./src/api/restaurant/controllers/restaurant.ts ```ts import { factories } from '@strapi/strapi'; export default factories.createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ // Method 1: Creating an entirely custom action async exampleAction(ctx) { try { ctx.body = 'ok'; } catch (err) { ctx.body = err; } }, // Method 2: Wrapping a core action (leaves core logic in place) async find(ctx) { // some custom logic here ctx.query = { ...ctx.query, local: 'en' } // Calling the default core action const { data, meta } = await super.find(ctx); // some more custom logic meta.date = Date.now() return { data, meta }; }, // Method 3: Replacing a core action with proper sanitization async find(ctx) { // validateQuery (optional) // to throw an error on query params that are invalid or the user does not have access to await this.validateQuery(ctx); // sanitizeQuery to remove any query params that are invalid or the user does not have access to // It is strongly recommended to use sanitizeQuery even if validateQuery is used const sanitizedQueryParams = await this.sanitizeQuery(ctx); const { results, pagination } = await strapi.service('api::restaurant.restaurant').find(sanitizedQueryParams); // sanitizeOutput to ensure the user does not receive any data they do not have access to const sanitizedResults = await this.sanitizeOutput(results, ctx); return this.transformResponse(sanitizedResults, { pagination }); } })); ``` Language: JavaScript File path: N/A ```js module.exports = { routes: [ { method: 'GET', path: '/hello', handler: 'api::hello.hello.index', } ] } ``` --- Language: JavaScript File path: ./src/api/hello/controllers/hello.js ```js module.exports = { async index(ctx, next) { // called by GET /hello ctx.body = 'Hello World!'; // we could also send a JSON }, }; ``` --- Language: TypeScript File path: ./src/api/hello/routes/hello.ts ```ts export default { routes: [ { method: 'GET', path: '/hello', handler: 'api::hello.hello.index', } ] } ``` --- Language: TypeScript File path: ./src/api/hello/controllers/hello.ts ```ts export default { async index(ctx, next) { // called by GET /hello ctx.body = 'Hello World!'; // we could also send a JSON }, }; ``` ## 控制器与路由:路由如何到达控制器动作 Description: 🌐 The example below adds a new controller action and exposes it through a custom route without duplicating the existing CRUD route definitions: (Source: https://docs.strapi.io/cms/backend-customization/controllers#controllers--routes-how-routes-reach-controller-actions) Language: JavaScript File path: ./src/api/restaurant/controllers/restaurant.js ```js const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ async exampleAction(ctx) { const specials = await strapi.service('api::restaurant.restaurant').find({ filters: { isSpecial: true } }); return this.transformResponse(specials.results); }, })); ``` Language: JavaScript File path: ./src/api/restaurant/routes/01-custom-restaurant.js ```js module.exports = { routes: [ { method: 'GET', path: '/restaurants/specials', handler: 'api::restaurant.restaurant.exampleAction', }, ], }; ``` ## 使用控制器工厂时的消毒 Description: Code example from "使用控制器工厂时的消毒" (Source: https://docs.strapi.io/cms/backend-customization/controllers#sanitization-when-utilizing-controller-factories) Language: JavaScript File path: ./src/api/restaurant/controllers/restaurant.js ```js const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ async find(ctx) { await this.validateQuery(ctx); const sanitizedQueryParams = await this.sanitizeQuery(ctx); const { results, pagination } = await strapi.service('api::restaurant.restaurant').find(sanitizedQueryParams); const sanitizedResults = await this.sanitizeOutput(results, ctx); return this.transformResponse(sanitizedResults, { pagination }); } })); ``` --- Language: TypeScript File path: ./src/api/restaurant/controllers/restaurant.ts ```ts import { factories } from '@strapi/strapi'; export default factories.createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ async find(ctx) { const sanitizedQueryParams = await this.sanitizeQuery(ctx); const { results, pagination } = await strapi.service('api::restaurant.restaurant').find(sanitizedQueryParams); const sanitizedResults = await this.sanitizeOutput(results, ctx); return this.transformResponse(sanitizedResults, { pagination }); } })); ``` ## 构建自定义控制器时的清理和验证 Description: Code example from "构建自定义控制器时的清理和验证" (Source: https://docs.strapi.io/cms/backend-customization/controllers#sanitize-validate-custom-controllers) Language: JavaScript File path: ./src/api/restaurant/controllers/restaurant.js ```js module.exports = { async findCustom(ctx) { const contentType = strapi.contentType('api::test.test'); await strapi.contentAPI.validate.query(ctx.query, contentType, { auth: ctx.state.auth }); const sanitizedQueryParams = await strapi.contentAPI.sanitize.query(ctx.query, contentType, { auth: ctx.state.auth }); const documents = await strapi.documents(contentType.uid).findMany(sanitizedQueryParams); return await strapi.contentAPI.sanitize.output(documents, contentType, { auth: ctx.state.auth }); } } ``` --- Language: TypeScript File path: ./src/api/restaurant/controllers/restaurant.ts ```ts export default { async findCustom(ctx) { const contentType = strapi.contentType('api::test.test'); await strapi.contentAPI.validate.query(ctx.query, contentType, { auth: ctx.state.auth }); const sanitizedQueryParams = await strapi.contentAPI.sanitize.query(ctx.query, contentType, { auth: ctx.state.auth }); const documents = await strapi.documents(contentType.uid).findMany(sanitizedQueryParams); return await strapi.contentAPI.sanitize.output(documents, contentType, { auth: ctx.state.auth }); } } ``` ## 扩展核心控制器 Description: Code example from "扩展核心控制器" (Source: https://docs.strapi.io/cms/backend-customization/controllers#extending-core-controllers) Language: JavaScript File path: N/A ```js async find(ctx) { // some logic here const { data, meta } = await super.find(ctx); // some more logic return { data, meta }; } ``` --- Language: JavaScript File path: N/A ```js async findOne(ctx) { // some logic here const response = await super.findOne(ctx); // some more logic return response; } ``` --- Language: JavaScript File path: N/A ```js async create(ctx) { // some logic here const response = await super.create(ctx); // some more logic return response; } ``` --- Language: JavaScript File path: N/A ```js async update(ctx) { // some logic here const response = await super.update(ctx); // some more logic return response; } ``` --- Language: JavaScript File path: N/A ```js async delete(ctx) { // some logic here const response = await super.delete(ctx); // some more logic return response; } ``` Language: JavaScript File path: N/A ```js async find(ctx) { // some logic here const response = await super.find(ctx); // some more logic return response; } ``` --- Language: JavaScript File path: N/A ```js async update(ctx) { // some logic here const response = await super.update(ctx); // some more logic return response; } ``` --- Language: JavaScript File path: N/A ```js async delete(ctx) { // some logic here const response = await super.delete(ctx); // some more logic return response; } ``` ## 使用 Description: 🌐 Controllers are declared and attached to a route. (Source: https://docs.strapi.io/cms/backend-customization/controllers#usage) Language: JavaScript File path: N/A ```js // access an API controller strapi.controller('api::api-name.controller-name'); // access a plugin controller strapi.controller('plugin::plugin-name.controller-name'); ``` # 使用 JWT 的身份验证流程 Source: https://docs.strapi.io/cms/backend-customization/examples/authentication ## 示例手册:使用 JWT 的认证流程 Description: 🌐 This file uses the formik package - install it using yarn add formik and restart the dev server. (Source: https://docs.strapi.io/cms/backend-customization/examples/authentication#examples-cookbook-authentication-flow-with-jwt) Language: JavaScript File path: /client/pages/auth/login.js ```js import React from 'react'; import { useFormik } from 'formik'; import { Button, Input } from '@nextui-org/react'; import Layout from '@/components/layout'; import { getStrapiURL } from '@/utils'; const Login = () => { const { handleSubmit, handleChange } = useFormik({ initialValues: { identifier: '', password: '', }, onSubmit: async (values) => { /** * API URLs in Strapi are by default prefixed with /api, * but because the API prefix can be configured * with the rest.prefix property in the config/api.js file, * we use the getStrapiURL() method to build the proper full auth URL. **/ const res = await fetch(getStrapiURL('/auth/local'), { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(values), }); /** * Gets the JWT from the server response. * The actual response is { jwt, user }, but we only need the JWT here. */ const { jwt } = await res.json(); /** * Stores the JWT in the localStorage of the browser. * A better implementation would be to do this with an authentication context provider * or something more sophisticated, but it's not the purpose of this tutorial. */ localStorage.setItem('token', jwt); }, }); /** * The following code renders a basic login form * accessible from the localhost:3000/auth/login page. */ return (

Login

); }; export default Login; ``` ## 配置 Description: 🌐 First, enable session management in your /config/plugins.js: (Source: https://docs.strapi.io/cms/backend-customization/examples/authentication#configuration) Language: JavaScript File path: /config/plugins.js ```js module.exports = ({ env }) => ({ 'users-permissions': { config: { jwtManagement: 'refresh', sessions: { accessTokenLifespan: 600, // 10 minutes (default) maxRefreshTokenLifespan: 2592000, // 30 days (default) idleRefreshTokenLifespan: 1209600, // 14 days (default) maxSessionLifespan: 86400, // 1 day (default) idleSessionLifespan: 7200, // 2 hours (default) }, }, }, }); ``` ## 增强的登录组件 Description: 🌐 Here's an updated login component that handles both JWT and refresh tokens: (Source: https://docs.strapi.io/cms/backend-customization/examples/authentication#enhanced-login-component) Language: JavaScript File path: /client/pages/auth/enhanced-login.js ```js import React, { useState } from 'react'; import { useFormik } from 'formik'; import { Button, Input } from '@nextui-org/react'; import Layout from '@/components/layout'; import { getStrapiURL } from '@/utils'; const EnhancedLogin = () => { const [isLoading, setIsLoading] = useState(false); const { handleSubmit, handleChange } = useFormik({ initialValues: { identifier: '', password: '', }, onSubmit: async (values) => { setIsLoading(true); try { const res = await fetch(getStrapiURL('/auth/local'), { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(values), }); const data = await res.json(); if (res.ok) { // Store both tokens (session management mode) if (data.refreshToken) { localStorage.setItem('accessToken', data.jwt); localStorage.setItem('refreshToken', data.refreshToken); } else { // Legacy mode - single JWT localStorage.setItem('token', data.jwt); } // Redirect to protected area window.location.href = '/dashboard'; } else { console.error('Login failed:', data.error); } } catch (error) { console.error('Login error:', error); } finally { setIsLoading(false); } }, }); return (

Enhanced Login

); }; export default EnhancedLogin; ``` # 自定义中间件 Source: https://docs.strapi.io/cms/backend-customization/examples/middlewares ## 使用自定义中间件在 Google 表格中填充分析仪表板 Description: 在 FoodAdvisor 项目的 /api 文件夹中,创建一个自定义的 analytics 中间件,并使用以下代码: (Source: https://docs.strapi.io/cms/backend-customization/examples/middlewares#populating-an-analytics-dashboard-in-google-sheets-with-a-custom-middleware) Language: JavaScript File path: src/api/restaurant/middlewares/utils.js ```js const { google } = require('googleapis'); const createGoogleSheetClient = async ({ keyFile, sheetId, tabName, range, }) => { async function getGoogleSheetClient() { const auth = new google.auth.GoogleAuth({ keyFile, scopes: ['https://www.googleapis.com/auth/spreadsheets'], }); const authClient = await auth.getClient(); return google.sheets({ version: 'v4', auth: authClient, }); } const googleSheetClient = await getGoogleSheetClient(); const writeGoogleSheet = async (data) => { googleSheetClient.spreadsheets.values.append({ spreadsheetId: sheetId, range: `${tabName}!${range}`, valueInputOption: 'USER_ENTERED', insertDataOption: 'INSERT_ROWS', resource: { majorDimension: 'ROWS', values: data, }, }); }; const updateoogleSheet = async (cell, data) => { googleSheetClient.spreadsheets.values.update({ spreadsheetId: sheetId, range: `${tabName}!${cell}`, valueInputOption: 'USER_ENTERED', resource: { majorDimension: 'ROWS', values: data, }, }); }; const readGoogleSheet = async () => { const res = await googleSheetClient.spreadsheets.values.get({ spreadsheetId: sheetId, range: `${tabName}!${range}`, }); return res.data.values; }; return { writeGoogleSheet, updateoogleSheet, readGoogleSheet, }; }; module.exports = { createGoogleSheetClient, }; ``` Language: JavaScript File path: src/api/restaurant/middlewares/analytics.js ```js 'use strict'; const { createGoogleSheetClient } = require('./utils'); const serviceAccountKeyFile = './gs-keys.json'; // Replace the sheetId value with the corresponding id found in your own URL const sheetId = '1P7Oeh84c18NlHp1Zy-5kXD8zgpoA1WmvYL62T4GWpfk'; const tabName = 'Restaurants'; const range = 'A2:C'; const VIEWS_CELL = 'C'; const transformGSheetToObject = (response) => response.reduce( (acc, restaurant) => ({ ...acc, [restaurant[0]]: { id: restaurant[0], name: restaurant[1], views: restaurant[2], cellNum: Object.keys(acc).length + 2 // + 2 because we need to consider the header and that the initial length is 0, so our first real row would be 2, }, }), {} ); module.exports = (config, { strapi }) => { return async (context, next) => { // Generating google sheet client const { readGoogleSheet, updateoogleSheet, writeGoogleSheet } = await createGoogleSheetClient({ keyFile: serviceAccountKeyFile, range, sheetId, tabName, }); // Get the restaurant documentId from the params in the URL const restaurantId = context.params.id; const restaurant = await strapi.documents('api::restaurant.restaurant').findOne({ documentId: restaurantId, }); // Read the spreadsheet to get the current data const restaurantAnalytics = await readGoogleSheet(); /** * The returned data comes in the shape [1, "Mint Lounge", 23], * and we need to transform it into an object: {id: 1, name: "Mint Lounge", views: 23, cellNum: 2} */ const requestedRestaurant = transformGSheetToObject(restaurantAnalytics)[restaurantId]; if (requestedRestaurant) { await updateoogleSheet( `${VIEWS_CELL}${requestedRestaurant.cellNum}:${VIEWS_CELL}${requestedRestaurant.cellNum}`, [[Number(requestedRestaurant.views) + 1]] ); } else { /** If we don't have the restaurant in the spreadsheet already, * we create it with 1 view. */ const newRestaurant = [[restaurant.id, restaurant.name, 1]]; await writeGoogleSheet(newRestaurant); } // Call next to continue with the flow and get to the controller await next(); }; }; ``` Language: JavaScript File path: src/api/restaurant/routes/restaurant.js ```js 'use strict'; const { createCoreRouter } = require('@strapi/strapi').factories; module.exports = createCoreRouter('api::restaurant.restaurant', { config: { findOne: { auth: false, policies: [], middlewares: ['api::restaurant.analytics'], }, }, }); ``` # 自定义策略 Source: https://docs.strapi.io/cms/backend-customization/examples/policies ## 创建自定义策略 Description: 在 项目的/api文件夹中,创建一个新的src/api/review/policies/is-owner-review.js文件,并输入以下代码: (Source: https://docs.strapi.io/cms/backend-customization/examples/policies#creating-a-custom-policy) Language: JavaScript File path: src/api/review/policies/is-owner-review.js ```js module.exports = async (policyContext, config, { strapi }) => { const { body } = policyContext.request; const { user } = policyContext.state; // Return an error if there is no authenticated user with the request if (!user) { return false; } /** * Queries the Restaurants collection type * using the Document Service API * to retrieve information about the restaurant's owner. */ const [restaurant] = await strapi.documents('api::restaurant.restaurant').findMany({ filters: { slug: body.restaurant, }, populate: ['owner'], }); if (!restaurant) { return false; } /** * If the user submitting the request is the restaurant's owner, * we don't allow the review creation. */ if (user.id === restaurant.owner.id) { return false; } return true; }; ``` ## 通过策略发送自定义错误 Description: 在 项目的 /api 文件夹中,按如下方式更新之前创建的 is-owner-review 自定义策略(高亮显示的行是唯一修改的行): (Source: https://docs.strapi.io/cms/backend-customization/examples/policies#sending-custom-errors-through-policies) Language: JSON File path: N/A ```json { "data": null, "error": { "status": 403, "name": "PolicyError", "message": "Policy Failed", "details": {} } } ``` --- Language: JSON File path: N/A ```json { "data": null, "error": { "status": 403, "name": "PolicyError", "message": "The owner of the restaurant cannot submit reviews", "details": { "policy": "is-owner-review", "errCode": "RESTAURANT_OWNER_REVIEW" } } } ``` Language: JavaScript File path: src/api/review/policies/is-owner-review.js ```js const { errors } = require('@strapi/utils'); const { PolicyError } = errors; module.exports = async (policyContext, config, { strapi }) => { const { body } = policyContext.request; const { user } = policyContext.state; // Return an error if there is no authenticated user with the request if (!user) { return false; } /** * Queries the Restaurants collection type * using the Document Service API * to retrieve information about the restaurant's owner. */ const filteredRestaurants = await strapi.documents('api::restaurant.restaurant').findMany({ filters: { slug: body.restaurant, }, populate: ['owner'], }); const restaurant = filteredRestaurants[0]; if (!restaurant) { return false; } /** * If the user submitting the request is the restaurant's owner, * we don't allow the review creation. */ if (user.id === restaurant.owner.id) { // highlight-start /** * Throws a custom policy error * instead of just returning false * (which would result into a generic Policy Error). */ throw new PolicyError('The owner of the restaurant cannot submit reviews', { errCode: 'RESTAURANT_OWNER_REVIEW', // can be useful for identifying different errors on the front end }); // highlight-end } return true; }; ``` ## 在前端使用自定义错误 Description: 用于显示自定义错误或成功创建评论的 toast 通知的示例前端代码: (Source: https://docs.strapi.io/cms/backend-customization/examples/policies#using-custom-errors-on-the-front-end) Language: JavaScript File path: /client/components/pages/restaurant/RestaurantContent/Reviews/new-review.js ```js import { Button, Input, Textarea } from '@nextui-org/react'; import { useFormik } from 'formik'; import { useRouter } from 'next/router'; import React from 'react'; import { getStrapiURL } from '../../../../../utils'; // highlight-start /** * A notification will be displayed on the front-end using React Hot Toast * (See https://github.com/timolins/react-hot-toast). * React Hot Toast should be added to your project's dependencies; * Use yarn or npm to install it and it will be added to your package.json file. */ import toast from 'react-hot-toast'; class UnauthorizedError extends Error { constructor(message) { super(message); } } // highlight-end const NewReview = () => { const router = useRouter(); const { handleSubmit, handleChange, values } = useFormik({ initialValues: { note: '', content: '', }, onSubmit: async (values) => { // highlight-start /** * The previously added code is wrapped in a try/catch block. */ try { // highlight-end const res = await fetch(getStrapiURL('/reviews'), { method: 'POST', body: JSON.stringify({ restaurant: router.query.slug, ...values, }), headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, }); // highlight-start const { data, error } = await res.json(); /** * If the Strapi backend server returns an error, * we use the custom error message to throw a custom error. * If the request is a success, we display a success message. * In both cases, a toast notification is displayed on the front-end. */ if (error) { throw new UnauthorizedError(error.message); } toast.success('Review created!'); return data; } catch (err) { toast.error(err.message); console.error(err); } }, // highlight-end }); return (

Write your review