Skip to main content

GraphQL 插件

¥GraphQL plugin

默认情况下,Strapi 为你的每种内容类型创建 REST 端点。使用 GraphQL 插件,你将能够添加 GraphQL 端点来获取和更改你的内容。

¥By default Strapi create REST endpoints for each of your content-types. With the GraphQL plugin, you will be able to add a GraphQL endpoint to fetch and mutate your content.

🤓 正在寻找 GraphQL API 文档?

GraphQL API 参考 描述了可用于使用 Strapi 的 GraphQL 插件与 API 进行交互的查询、突变和参数。

¥The GraphQL API reference describes queries, mutations and parameters you can use to interact with your API using Strapi's GraphQL plugin.

用法

¥Usage

要开始在你的应用中使用 GraphQL,请先安装该插件。为此,请打开终端并运行以下命令:

¥To get started with GraphQL in your application, please install the plugin first. To do that, open your terminal and run the following command:

yarn add @strapi/plugin-graphql

然后,启动你的应用并在 http://localhost:1337/graphql 打开浏览器。你现在应该能够访问 GraphQL Playground,它将帮助你编写 GraphQL 查询和突变。

¥Then, start your app and open your browser at http://localhost:1337/graphql. You should now be able to access the GraphQL Playground that will help you to write your GraphQL queries and mutations.

✏️ 注意

GraphQL Playground 在开发和登台环境中默认启用,但在生产环境中禁用。将 playgroundAlways 配置选项设置为 true,以便在生产环境中也启用 GraphQL Playground(请参阅 插件配置文档)。

¥The GraphQL Playground is enabled by default for both the development and staging environments, but disabled in production environments. Set the playgroundAlways configuration option to true to also enable the GraphQL Playground in production environments (see plugins configuration documentation).

配置

¥Configuration

插件配置在 config/plugins.js 文件中定义。此配置文件可以包含 graphql.config 对象来定义 GraphQL 插件的特定配置(请参阅 插件配置文档)。

¥Plugins configuration are defined in the config/plugins.js file. This configuration file can include a graphql.config object to define specific configurations for the GraphQL plugin (see plugins configuration documentation).

Apollo 服务器 选项可与 graphql.config.apolloServer 配置对象 一起设置。例如,Apollo 服务器选项可用于启用 追踪功能,GraphQL 在线运行支持该功能来跟踪查询每个部分的响应时间。从 Apollo Server 版本 3.9 开始,默认缓存选项是 cache: 'bounded'。你可以在 apolloServer 配置中更改它。欲了解更多信息,请访问 Apollo 服务器文档

¥Apollo Server options can be set with the graphql.config.apolloServer configuration object. Apollo Server options can be used for instance to enable the tracing feature, which is supported by the GraphQL playground to track the response time of each part of your query. From Apollo Server version 3.9 default cache option is cache: 'bounded'. You can change it in the apolloServer configuration. For more information visit Apollo Server Docs.

提醒

默认情况下,响应返回的最大项目数限制为 100。可以使用 amountLimit 配置选项更改此值,但只有在仔细考虑后才应更改:大型查询可能会导致 DDoS(分布式拒绝服务),并可能导致 Strapi 服务器以及数据库服务器负载异常。

¥The maximum number of items returned by the response is limited to 100 by default. This value can be changed using the amountLimit configuration option, but should only be changed after careful consideration: a large query can cause a DDoS (Distributed Denial of Service) and may cause abnormal load on your Strapi server, as well as your database server.

./config/plugins.js

module.exports = {
//
graphql: {
config: {
endpoint: '/graphql',
shadowCRUD: true,
playgroundAlways: false,
depthLimit: 7,
amountLimit: 100,
apolloServer: {
tracing: false,
},
},
},
};

影子增删改查

¥Shadow CRUD

为了简化和自动化 GraphQL 模式的构建,我们引入了 Shadow CRUD 功能。它根据你的模型自动生成类型定义、查询、突变和解析器。

¥To simplify and automate the build of the GraphQL schema, we introduced the Shadow CRUD feature. It automatically generates the type definitions, queries, mutations and resolvers based on your models.

示例:

¥Example:

如果你使用 交互式 strapi generate CLI 或管理面板生成了名为 Document 的 API,则你的模型如下所示:

¥If you've generated an API called Document using the interactive strapi generate CLI or the administration panel, your model looks like this:

./src/api/[api-name]/content-types/document/schema.json

{
"kind": "collectionType",
"collectionName": "documents",
"info": {
"singularName": "document",
"pluralName": "documents",
"displayName": "document",
"name": "document"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"name": {
"type": "string"
},
"description": {
"type": "richtext"
},
"locked": {
"type": "boolean"
}
}
}
Generated GraphQL type and queries
# Document's Type definition
input DocumentFiltersInput {
name: StringFilterInput
description: StringFilterInput
locked: BooleanFilterInput
createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput
publishedAt: DateTimeFilterInput
and: [DocumentFiltersInput]
or: [DocumentFiltersInput]
not: DocumentFiltersInput
}

input DocumentInput {
name: String
description: String
locked: Boolean
createdAt: DateTime
updatedAt: DateTime
publishedAt: DateTime
}

type Document {
name: String
description: String
locked: Boolean
createdAt: DateTime
updatedAt: DateTime
publishedAt: DateTime
}

type DocumentEntity {
id: ID
attributes: Document
}

type DocumentEntityResponse {
data: DocumentEntity
}

type DocumentEntityResponseCollection {
data: [DocumentEntity!]!
meta: ResponseCollectionMeta!
}

type DocumentRelationResponseCollection {
data: [DocumentEntity!]!
}

# Queries to retrieve one or multiple restaurants.
type Query {
document(id: ID): DocumentEntityResponse
documents(
filters: DocumentFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
publicationState: PublicationState = LIVE
):DocumentEntityResponseCollection
}

# Mutations to create, update or delete a restaurant.
type Mutation {
createDocument(data: DocumentInput!): DocumentEntityResponse
updateDocument(id: ID!, data: DocumentInput!): DocumentEntityResponse
deleteDocument(id: ID!): DocumentEntityResponse
}

定制化

¥Customization

Strapi 提供了一个编程 API 来自定义 GraphQL,它允许:

¥Strapi provides a programmatic API to customize GraphQL, which allows:

Example of GraphQL customizations
./src/index.js

module.exports = {
/**

* An asynchronous register function that runs before

* your application is initialized.

* * This gives you an opportunity to extend code.
*/
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');

extensionService.shadowCRUD('api::restaurant.restaurant').disable();
extensionService.shadowCRUD('api::category.category').disableQueries();
extensionService.shadowCRUD('api::address.address').disableMutations();
extensionService.shadowCRUD('api::document.document').field('locked').disable();
extensionService.shadowCRUD('api::like.like').disableActions(['create', 'update', 'delete']);

const extension = ({ nexus }) => ({
// Nexus
types: [
nexus.objectType({
name: 'Book',
definition(t) {
t.string('title');
},
}),
],
plugins: [
nexus.plugin({
name: 'MyPlugin',
onAfterBuild(schema) {
console.log(schema);
},
}),
],
// GraphQL SDL
typeDefs: `
type Article {
name: String
}
`,
resolvers: {
Query: {
address: {
resolve() {
return { value: { city: 'Montpellier' } };
},
},
},
},
resolversConfig: {
'Query.address': {
auth: false,
},
},
});
extensionService.use(extension);
},
};

禁用 Shadow CRUD 中的操作

¥Disabling operations in the Shadow CRUD

GraphQL 插件提供的 extension service 公开了可用于禁用内容类型操作的函数:

¥The extension service provided with the GraphQL plugin exposes functions that can be used to disable operations on Content-Types:

内容类型功能描述参数类型可能的参数值
disable()完全禁用内容类型**
disableQueries()仅禁用 Content-Type 的查询**
disableMutations()仅禁用 Content-Type 的突变**
disableAction()禁用 Content-Type 的特定操作字符串列表中的一个值:
  • create
  • find
  • findOne
  • update
  • delete
disableActions()禁用 Content-Type 的特定操作字符串数组列表中的多个值:
  • create
  • find
  • findOne
  • update
  • delete

还可以在字段级别禁用操作,具有以下功能:

¥Actions can also be disabled at the field level, with the following functions:

字段函数描述
disable()完全禁用该字段
disableOutput()禁用字段上的输出
disableInput()禁用字段上的输入
disableFilters()禁用字段上的过滤器输入

示例:

¥Examples:

// Disable the 'find' operation on the 'restaurant' content-type in the 'restaurant' API
strapi
.plugin('graphql')
.service('extension')
.shadowCRUD('api::restaurant.restaurant')
.disableAction('find')

// Disable the 'name' field on the 'document' content-type in the 'document' API
strapi
.plugin('graphql')
.service('extension')
.shadowCRUD('api::document.document')
.field('name')
.disable()

使用获取器

¥Using getters

以下 getter 可用于检索有关内容类型上允许的操作的信息:

¥The following getters can be used to retrieve information about operations allowed on content-types:

内容类型 getter描述参数类型可能的参数值
isEnabled()返回是否启用内容类型**
isDisabled()返回内容类型是否被禁用**
areQueriesEnabled()返回是否在内容类型上启用查询**
areQueriesDisabled()返回是否在内容类型上禁用查询**
areMutationsEnabled()返回是否在内容类型上启用突变**
areMutationsDisabled()返回是否在内容类型上禁用突变**
isActionEnabled(action)返回传递的 action 是否在内容类型上启用字符串列表中的一个值:
  • create
  • find
  • findOne
  • update
  • delete
isActionDisabled(action)返回传递的 action 是否在内容类型上禁用字符串列表中的一个值:
  • create
  • find
  • findOne
  • update
  • delete

以下 getter 可用于检索有关字段上允许的操作的信息:

¥The following getters can be used to retrieve information about operations allowed on fields:

字段获取器描述
isEnabled()返回字段是否启用
isDisabled()返回字段是否被禁用
hasInputEnabled()返回字段是否启用输入
hasOutputEnabled()返回字段是否启用输出
hasFiltersEnabled()返回字段是否启用过滤

扩展架构

¥Extending the schema

可以通过注册扩展来扩展 Content API 生成的架构。

¥The schema generated by the Content API can be extended by registering an extension.

此扩展定义为对象或返回对象的函数,将由 GraphQL 插件提供的 extension service 公开的 use() 函数使用。

¥This extension, defined either as an object or a function returning an object, will be used by the use() function exposed by the extension service provided with the GraphQL plugin.

描述扩展的对象接受以下参数:

¥The object describing the extension accepts the following parameters:

范围类型描述
types数组允许使用基于 关系 的类型定义扩展架构类型
typeDefs字符串允许使用 GraphQL SDL 扩展架构类型
plugins数组允许使用 Nexus plugins 扩展架构
resolvers目的定义自定义解析器
resolversConfig目的定义 解析器的配置选项,例如 authorizationpoliciesmiddlewares
💡 提示

typesplugins 参数基于 关系。要使用它们,请将扩展注册为以 nexus 作为参数的函数:

¥The types and plugins parameters are based on Nexus. To use them, register the extension as a function that takes nexus as a parameter:

Example:
./src/index.js

module.exports = {
register({ strapi }) {
const extension = ({ nexus }) => ({
types: [
nexus.objectType({

}),
],
plugins: [
nexus.plugin({

})
]
})

strapi.plugin('graphql').service('extension').use(extension)
}
}

解析器的自定义配置

¥Custom configuration for resolvers

解析器是 GraphQL 查询或突变处理程序(即为 GraphQL 查询或突变生成响应的函数或函数集合)。每个字段都有一个默认解析器。

¥A resolver is a GraphQL query or mutation handler (i.e. a function, or a collection of functions, that generate(s) a response for a GraphQL query or mutation). Each field has a default resolver.

扩展 GraphQL 架构 时,resolversConfig 键可用于定义解析器的自定义配置,其中可以包括:

¥When extending the GraphQL schema, the resolversConfig key can be used to define a custom configuration for a resolver, which can include:

授权配置

¥Authorization configuration

默认情况下,GraphQL 请求的授权由注册的授权策略处理,可以是 API 令牌 或通过 用户和权限插件。用户和权限插件提供了更精细的控制。

¥By default, the authorization of a GraphQL request is handled by the registered authorization strategy that can be either API token or through the Users & Permissions plugin. The Users & Permissions plugin offers a more granular control.

Authorization with the Users & Permissions plugin

使用用户和权限插件,如果授予适当的权限,则允许 GraphQL 请求。

¥With the Users & Permissions plugin, a GraphQL request is allowed if the appropriate permissions are given.

例如,如果 '类别' 内容类型存在,并且通过 GraphQL 使用 Query.categories 处理程序进行查询,则如果为 '类别' 内容类型提供了适当的 find 权限,则允许该请求。

¥For instance, if a 'Category' content-type exists and is queried through GraphQL with the Query.categories handler, the request is allowed if the appropriate find permission for the 'Categories' content-type is given.

要查询单个类别(使用 Query.category 处理程序完成),如果给出 findOne 权限,则允许该请求。

¥To query a single category, which is done with the Query.category handler, the request is allowed if the the findOne permission is given.

请参阅用户指南了解如何 使用用户和权限插件定义权限

¥Please refer to the user guide on how to define permissions with the Users & Permissions plugin.

要更改授权的配置方式,请使用 resolversConfig.[MyResolverName] 处定义的解析器配置。授权可以配置:

¥To change how the authorization is configured, use the resolver configuration defined at resolversConfig.[MyResolverName]. The authorization can be configured:

  • 要么使用 auth: false 完全绕过授权系统并允许所有请求,

    ¥either with auth: false to fully bypass the authorization system and allow all requests,

  • 或者使用接受字符串数组的 scope 属性来定义授权请求所需的权限。

    ¥or with a scope attribute that accepts an array of strings to define the permissions required to authorize the request.

Examples of authorization configuration
./src/index.js

module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');

extensionService.use({
resolversConfig: {
'Query.categories': {
/**

* Querying the Categories content-type

* bypasses the authorization system.
*/
auth: false
},
'Query.restaurants': {
/**

* Querying the Restaurants content-type

* requires the find permission

* on the 'Address' content-type

* of the 'Address' API
*/
auth: {
scope: ['api::address.address.find']
}
},
}
})
}
}

政策

¥Policies

政策 可以通过 resolversConfig.[MyResolverName].policies 键应用于 GraphQL 解析器。

¥Policies can be applied to a GraphQL resolver through the resolversConfig.[MyResolverName].policies key.

policies 键是一个接受策略列表的数组,该列表中的每一项要么是对已注册策略的引用,要么是直接传递的实现(请参阅 策略配置文档)。

¥The policies key is an array accepting a list of policies, each item in this list being either a reference to an already registered policy or an implementation that is passed directly (see policies configuration documentation).

resolversConfig 中直接实现的策略是以 context 对象和 strapi 实例作为参数的函数。context 对象可以访问:

¥Policies directly implemented in resolversConfig are functions that take a context object and the strapi instance as arguments. The context object gives access to:

  • GraphQL 解析器的 parentargscontextinfo 参数,

    ¥the parent, args, context and info arguments of the GraphQL resolver,

  • Koa 的 context 搭配 context.httpstate 搭配 context.state

    ¥Koa's context with context.http and state with context.state.

Example of GraphQL policies applied to resolvers
./src/index.js

module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');

extensionService.use({
resolversConfig: {
'Query.categories': {
policies: [
(context, { strapi }) => {
console.log('hello', context.parent)
/**

* If 'categories' have a parent, the function returns true,

* so the request won't be blocked by the policy.
*/
return context.parent !== undefined;
}
/**

* Uses a policy already created in Strapi.
*/
"api::model.policy-name",

/**

* Uses a policy already created in Strapi with a custom configuration
*/
{name:"api::model.policy-name", config: {/* all config values I want to pass to the strapi policy */} },
],
auth: false,
},
}
})
}
}
中间件

¥Middlewares

中间件 可以通过 resolversConfig.[MyResolverName].middlewares 键应用于 GraphQL 解析器。GraphQL 和 REST 实现之间的唯一区别是 config 键变为 options

¥Middlewares can be applied to a GraphQL resolver through the resolversConfig.[MyResolverName].middlewares key. The only difference between the GraphQL and REST implementations is that the config key becomes options.

middlewares 键是一个接受中间件列表的数组,该列表中的每个项目要么是对已注册中间件的引用,要么是直接传递的实现(请参阅 中间件配置文档)。

¥The middlewares key is an array accepting a list of middlewares, each item in this list being either a reference to an already registered middleware or an implementation that is passed directly (see middlewares configuration documentation).

直接在 resolversConfig 中实现的中间件可以将 GraphQL 解析器的 parentargscontextinfo 对象 作为参数。

¥Middlewares directly implemented in resolversConfig can take the GraphQL resolver's parent, args, context and info objects as arguments.

💡 提示

使用 GraphQL 的中间件甚至可以作用于嵌套解析器,这提供了比 REST 更精细的控制。

¥Middlewares with GraphQL can even act on nested resolvers, which offer a more granular control than with REST.

Examples of GraphQL middlewares applied to a resolver

module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');

extensionService.use({
resolversConfig: {
'Query.categories': {
middlewares: [
/**

* Basic middleware example #1

* Log resolving time in console
*/
async (next, parent, args, context, info) => {
console.time('Resolving categories');

// call the next resolver
const res = await next(parent, args, context, info);

console.timeEnd('Resolving categories');

return res;
},
/**

* Basic middleware example #2

* Enable server-side shared caching
*/
async (next, parent, args, context, info) => {
info.cacheControl.setCacheHint({ maxAge: 60, scope: "PUBLIC" });
return next(parent, args, context, info);
},
/**

* Basic middleware example #3

* change the 'name' attribute of parent with id 1 to 'foobar'
*/
(resolve, parent, ...rest) => {
if (parent.id === 1) {
return resolve({...parent, name: 'foobar' }, ...rest);
}

return resolve(parent, ...rest);
}
/**

* Basic middleware example #4

* Uses a middleware already created in Strapi.
*/
"api::model.middleware-name",

/**

* Basic middleware example #5

* Uses a middleware already created in Strapi with a custom configuration
*/
{ name: "api::model.middleware-name", options: { /* all config values I want to pass to the strapi middleware */ } },
],
auth: false,
},
}
})
}
}

与用户和权限插件一起使用

¥Usage with the Users & Permissions plugin

用户和权限插件 是一个可选插件,允许通过完整的身份验证过程来保护 API。

¥The Users & Permissions plugin is an optional plugin that allows protecting the API with a full authentication process.

注册

¥Registration

通常,你需要注册或注册才能被识别为用户,然后执行授权请求。

¥Usually you need to sign up or register before being recognized as a user then perform authorized requests.

Mutation
mutation {
register(input: { username: "username", email: "email", password: "password" }) {
jwt
user {
username
email
}
}
}

你应该会看到在 Strapi 管理面板的 Users 集合类型中创建了一个新用户。

¥You should see a new user is created in the Users collection type in your Strapi admin panel.

验证

¥Authentication

要执行授权请求,你必须首先获取 JWT:

¥To perform authorized requests, you must first get a JWT:

Mutation
mutation {
login(input: { identifier: "email", password: "password" }) {
jwt
}
}

然后,在每个请求中,以 { "Authorization": "Bearer YOUR_JWT_GOES_HERE" } 的形式发送 Authorization 标头。这可以在 GraphQL Playground 的 HTTP headers 部分中设置。

¥Then on each request, send along an Authorization header in the form of { "Authorization": "Bearer YOUR_JWT_GOES_HERE" }. This can be set in the HTTP Headers section of your GraphQL Playground.

API 令牌

¥API tokens

要使用 API 令牌进行身份验证,请使用 Bearer your-api-token 格式在 Authorization 标头中传递令牌。

¥To use API tokens for authentication, pass the token in the Authorization header using the format Bearer your-api-token.

✏️ 注意

在 GraphQL Playground 中使用 API 令牌需要在 HTTP HEADERS 选项卡中添加带有令牌的授权标头:

¥Using API tokens in the the GraphQL playground requires adding the authorization header with your token in the HTTP HEADERS tab:

{
"Authorization" : "Bearer <TOKEN>"
}

<TOKEN> 替换为 Strapi 管理面板中生成的 API 令牌。

¥Replace <TOKEN> with your API token generated in the Strapi Admin panel.

安全

¥Security

GraphQL 是一种查询语言,允许用户使用比传统 REST API 更广泛的输入面板。GraphQL API 本质上容易出现安全风险,例如凭证泄露和拒绝服务攻击,可以通过采取适当的预防措施来减少这些风险。

¥GraphQL is a query language allowing users to use a broader panel of inputs than traditional REST APIs. GraphQL APIs are inherently prone to security risks, such as credential leakage and denial of service attacks, that can be reduced by taking appropriate precautions.

在生产中禁用自省和在线运行

¥Disable introspection and playground in production

在生产环境中,建议禁用 GraphQL Playground 和自省查询。如果你尚未编辑 配置文件,则默认情况下它已在生产中禁用。

¥In production environments, disabling the GraphQL Playground and the introspection query is recommended. If you haven't edited the configuration file, it is already disabled in production by default.

限制最大深度和复杂性

¥Limit max depth and complexity

恶意用户可能会发送深度非常高的查询,这可能会使你的服务器过载。使用 depthLimit 配置参数 来限制单个请求中可以查询的嵌套字段的最大数量。默认情况下,depthLimit 设置为 10,但在测试和开发期间可以设置为更高的值。

¥A malicious user could send a query with a very high depth, which could overload your server. Use the depthLimit configuration parameter to limit the maximum number of nested fields that can be queried in a single request. By default, depthLimit is set to 10 but can be set to a higher value during testing and development.

💡 提示

为了进一步提高 GraphQL 安全性,可以使用第三方工具。请参阅有关 在论坛上将 GraphQL Armor 与 Strapi 结合使用 的指南。

¥To increase GraphQL security even further, 3rd-party tools can be used. See the guide about using GraphQL Armor with Strapi on the forum.