v4 代码迁移:更新 GraphQL 解析器
¥v4 code migration: Updating GraphQL resolvers
本指南是 v4 代码迁移指南 的一部分,旨在帮助你将 Strapi 应用的代码从 v3.6.x 迁移到 v4.0.x
¥This guide is part of the v4 code migration guide designed to help you migrate the code of a Strapi application from v3.6.x to v4.0.x
在 Strapi v3 中,GraphQL 解析器要么自动绑定到 REST 控制器(来自核心 API),要么使用 ./api/<api-name>/config/schema.graphql.js
文件进行自定义。
¥In Strapi v3, GraphQL resolvers are either automatically bound to REST controllers (from the core API) or customized using the ./api/<api-name>/config/schema.graphql.js
files.
在 Strapi v4 中,会自动为每个 API 的基本 CRUD 操作创建 GraphQL 专用核心解析器。其他解析器可以是使用 GraphQL 扩展服务的 以编程方式定制,可使用 strapi.plugin(’graphql’).service(’extension’)
进行访问。
¥In Strapi v4, GraphQL dedicated core resolvers are automatically created for the basic CRUD operations for each API. Additional resolvers can be customized programmatically using GraphQL’s extension service, accessible using strapi.plugin(’graphql’).service(’extension’)
.
将 GraphQL 解析器迁移到 Strapi v4 需要:
¥Migrating GraphQL resolvers to Strapi v4 requires:
将
./api/<api-name>/config/schema.graphql.js
文件中找到的 Strapi v3 逻辑移动到 Strapi v4 的./src/index.js
文件中找到的register
方法¥moving the Strapi v3 logic, found in
./api/<api-name>/config/schema.graphql.js
files, to theregister
method found in the./src/index.js
file of Strapi v4并调整现有的 Strapi v3 代码以利用 Strapi v4 中引入的 GraphQL 扩展服务,可通过
strapi.plugin(’graphql’).service(’extension’)
访问。¥and adapting the existing Strapi v3 code to take advantage of the GraphQL extension service introduced in Strapi v4, accessible through
strapi.plugin(’graphql’).service(’extension’)
.
Strapi v4 GraphQL 解析器的整个逻辑不需要位于 ./src/index.js
的 register
方法中,但应该从那里引用。
¥The entire logic for Strapi v4 GraphQL resolvers doesn’t need to be in the register
method of ./src/index.js
but it should be referenced from there.
以下文档提供了将 Strapi v3 代码转换为使用 GraphQL 扩展服务的 Strapi v4 代码的用例示例。GraphQL 扩展服务允许添加类型、查询和突变的新定义,替换解析器,禁用 API 和 API 中的字段,以及添加策略、中间件和授权。
¥The following documentation provides use case examples of transforming Strapi v3 code to Strapi v4 code that uses the GraphQL extension service. The GraphQL extension service allows adding new definitions for types, queries, and mutations, replacing resolvers, disabling APIs and fields from APIs, and adding policies, middlewares and authorization.
添加新定义
¥Adding new definitions
在 Strapi v4 中添加新的 types、queries 或 mutations 定义是通过 GraphQL 扩展服务 的 use()
方法完成的。
¥Adding new types, queries or mutations definitions in Strapi v4 is done through the use()
method of the GraphQL extension service.
类型
¥Types
Strapi v3:
以下代码示例向 Strapi v3 添加新的 MyEnum
类型定义:
¥The following code example adds a new MyEnum
type definition to Strapi v3:
module.exports = {
definition: `
enum MyEnum {
a
b
c
}
`,
}
Strapi v4:
上面的 Strapi v3 代码示例应替换为 Strapi v4 中的以下代码:
¥The Strapi v3 code example above should be replaced by the following code in Strapi v4:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => {
const MyEnum = nexus.enumType({
name: 'MyEnum',
members: ['a', 'b', 'c'],
});
return { types: [MyEnum] };
});
}
}
建议使用 nexus 定义而不是原始 SDL,但仍然可以使用 typeDefs
来编写原始 SDL。
¥It's recommended to use the nexus definition instead of raw SDL, but it’s still possible to use typeDefs
to write raw SDL.
查询
¥Queries
Strapi v3:
以下代码示例向 Strapi v3 添加新的查询定义:
¥The following code example adds a new query definition to Strapi v3:
module.exports = {
query: `
myQuery(id: ID, status: MyInput, limit: Int): [MyQuery]!
`,
resolver: {
Query: {
myQuery: {
resolver: 'application::api-name.content-type-name.customFind',
// OR
resolver: async (obj, options, { context }) => {
await strapi.controllers.content-type-name.customFind(context);
return context.body.myQuery || [];
}
}
}
},
}
Strapi v4:
上面的 Strapi v3 代码示例应替换为 Strapi v4 中的以下代码:
¥The Strapi v3 code example above should be replaced by the following code in Strapi v4:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => {
const myQuery = nexus.extendType({
type: 'Query',
definition(t) {
// myQuery definition
t.field('myQuery', {
// Response type
type: nexus.nonNull(nexus.list('MyQuery')),
// Args definition
args: { id: 'ID', status: 'MyInput', limit: 'Int' },
// Resolver definition
resolve(parent, args, context) {
const { id, status, limit } = args;
return strapi.service('api::api-name.content-type-name').customFind(id, status, limit);
}
});
}
});
return { types: [myQuery] };
});
}
}
在 Strapi v4 中,REST 控制器和 GraphQL 解析器不再耦合。业务逻辑在服务中实现,并从控制器或解析器调用。这种方法将业务逻辑保留在一个地方,因此 REST 和 GraphQL 都可以按照你想要的方式进行自定义。
¥In Strapi v4, REST controllers and GraphQL resolvers are not coupled anymore. The business logic is implemented in services and called either from the controller or the resolver. This approach keeps the business logic in one place so both REST and GraphQL can be customized the way you want.
在 Strapi v4 中,不建议直接从 GraphQL 解析器引用 REST 控制器。但是,你仍然可以从解析器定义中以编程方式调用它。
¥In Strapi v4, it’s not recommended to reference a REST controller directly from the GraphQL resolver. However, you can still call it programmatically from the resolver definition.
Strapi 提供的用于执行查询的服务称为 实体服务,可在 strapi.entityService
中使用。它可用于创建查询或突变。
¥The service that Strapi provides to perform queries is called the Entity Service and is available with strapi.entityService
. It can be used to create queries or mutations.
突变
¥Mutations
Strapi v3:
以下代码示例向 Strapi v3 添加新的突变定义:
¥The following code example adds a new mutation definition to Strapi v3:
module.exports = {
mutation: `
sendItemByEmail(itemID: ID!, email: String!): Boolean!
`,
resolver: {
Mutation: {
attachRestaurantToChef: {
resolver: 'application::api-name.content-type-name.sendItemByEmail',
// OR
resolver: async (obj, options, { context }) {
await strapi.controllers.content-type-name.sendItemByEmail(context);
return context.body || false;
}
},
},
}
}
Strapi v4:
上面的 Strapi v3 代码示例应替换为 Strapi v4 中的以下代码:
¥The Strapi v3 code example above should be replaced by the following code in Strapi v4:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => {
const sendItemByEmailMutation = nexus.extendType({
type: 'Mutation',
definition(t) {
// "sendItemByEmail" query definition
t.field('sendItemByEmail', {
// Response type
type: nexus.nonNull('Boolean'),
// Args definition
args: { ItemID: nexus.nonNull('ID'), email: nexus.nonNull('String') },
// Resolver definition
resolve(parent, args, context) {
const { ItemID, email } = args;
return strapi.service('api::api-name.content-type-name').sendItemByEmail(itemID, email);
}
});
}
});
return { types: [sendItemByEmailMutation] };
});
}
}
Strapi 提供的用于执行查询的服务称为 实体服务,可在 strapi.entityService
中使用。它可用于创建查询或突变。
¥The service that Strapi provides to perform queries is called the Entity Service and is available with strapi.entityService
. It can be used to create queries or mutations.
更换解析器
¥Replacing resolvers
Strapi v3:
Strapi v3 提供了 2 种替换查询或突变解析器行为的方法:让解析器指向 REST 控制器,或者创建一个新的自定义 GraphQL 解析器,然后将该解析器关联到现有查询或突变。
¥Strapi v3 offers 2 ways of replacing the behavior of a query or mutation resolver: have the resolver point to a REST controller, or create a new custom GraphQL resolver then associate the resolver to an existing query or mutation.
Example of a Strapi v3 resolver pointing to a REST controller
module.exports = {
query: `
testQuery: myQuery
`,
resolver: {
Query: {
testQuery: {
resolver: 'application::api-name.content-type-name.find',
},
},
},
};
Example of creating a new custom resolver and associating it to an existing query in Strapi v3
module.exports = {
query: `
testQuery: myQuery
`,
resolver: {
Query: {
testQuery: {
resolver: async (obj, args) => {
// custom logic here
// ...
// Return response.
return { myResult: 'some data' };
},
},
},
},
};
Strapi v4:
在 Strapi v4 中,替换或自定义解析器的推荐方法是使用新 GraphQL 扩展服务的 resolvers
字段:
¥In Strapi v4, the recommended way to replace or customize a resolver is to use the resolvers
field of the new GraphQL extension service:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => ({
resolvers: {
Query: {
testQuery: async (obj, args) => {
// custom logic here
// ...
// return response
return { myResult: 'some data' };
},
}
});
}
}
禁用 API 和 API 中的字段
¥Disabling APIs and fields from APIs
Strapi v3:
在 Strapi v3 中,通过将查询解析器、突变解析器或字段设置为 false
来禁用它:
¥In Strapi v3, a query resolver, a mutation resolver or a field is disabled by setting it to false
:
module.exports = {
// disable a query resolver
resolver: {
Query: {
myQuery: false,
},
},
// disable a field
type: {
myTypeQuery: {
myField: false,
},
},
};
Strapi v4:
Strapi v4 使用 禁用查询、突变、操作或字段 的编程 API。上面的 Strapi v3 代码示例应替换为 Strapi v4 中的以下代码:
¥Strapi v4 uses programmatic APIs to disable queries, mutation, actions or fields. The Strapi v3 code example above should be replaced by the following code in Strapi v4:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
// disable an action on a query
extensionService.shadowCRUD('api::api-name.content-type-name').disableAction('find');
// disable a field
extensionService.shadowCRUD('api::api-name.content-type-name').field('myField').disable();
}
}
添加策略
¥Adding policies
Strapi v3:
在 Strapi v3 中,应用于解析器的策略是为 REST 控制器或在 schema.graphql.js
自定义文件中定义的:
¥In Strapi v3, policies applied to a resolver are defined either for the REST controller or in the schema.graphql.js
customization file:
module.exports = {
resolver: {
Query: {
findItems: {
policies: ['admin::isAuthenticatedAdmin'],
},
},
},
};
Strapi v4:
在 Strapi v4 中,应用于解析器的策略在 resolversConfig
对象中显式定义(请参阅 GraphQL 策略文档),并通过 GraphQL 扩展服务应用。上面的 Strapi v3 代码示例应替换为 Strapi v4 中的以下代码:
¥In Strapi v4, policies applied to a resolver are explicitly defined in a resolversConfig
object (see GraphQL policies documentation) and applied through the GraphQL extension service. The Strapi v3 code example above should be replaced by the following code in Strapi v4:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => ({
resolversConfig: {
'Query.findItems': {
policies: ['admin::isAuthenticatedAdmin']
}
}
}));
}
}
Strapi v4 策略不再从控制器继承,因为解析器是独立的。
¥Strapi v4 policies are not inherited from controllers anymore since the resolvers are stand-alone.
添加中间件
¥Adding middlewares
在 Strapi v3 中,应用于解析器的中间件继承自与 REST 控制器关联的中间件。
¥In Strapi v3, middlewares applied to a resolver are inherited from middlewares associated to the REST controller.
在 Strapi v4 中,应用于解析器的中间件是明确的 在 resolversConfig
对象中定义 并通过 GraphQL 扩展服务应用:
¥In Strapi v4, middlewares applied to a resolver are explicitly defined in a resolversConfig
object and applied through the GraphQL extension service:
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin('graphql').service('extension');
extensionService.use(({ nexus }) => ({
resolversConfig: {
'Query.findItems': {
middlewares: [
(resolve, ...args) => {
console.log("We're in a middleware");
return resolve(...args);
}
]
}
}
}));
}
}
添加授权
¥Adding authorization
Strapi v4 中自动生成的解析器受到授权策略的保护。可以通过 resolversConfig
对象自定义操作并禁用授权(请参阅 GraphQL 授权文档)。
¥The resolvers automatically generated in Strapi v4 are protected by authorization strategies. The actions can be customized and the authorization can be disabled through the resolversConfig
object (see GraphQL authorization documentation).