GraphQL 突变为嵌套响应返回 null
GraphQL mutation is returning null for nested responses
我有 2 个模型,公司和用户。从数据库的角度来看,一家公司有很多用户。创建单个用户时,我想通过返回与用户关联的公司来利用 graphQL 的强大功能。但是,这仅在进行查询时有效。尝试突变时,对象发生突变,但请求的关系数据始终 returns null
在模型中,我们声明了一个 -> 多关系,并将公司模型模式包含在我们的用户模型模式中以访问数据
用户模型架构
type User {
clients: [Client!]
company: Company <------- Company Relation
companyId: UUID
confirmed: Boolean
defaultPortfolioSize: Int
email: String!
firstName: String!
lastLogin: String!
lastName: String!
id: UUID!
isActive: Boolean
isStaff: Boolean
isSuperuser: Boolean
password: String
phoneNumber: String
priceNotification: Boolean
priceThreshold: Float
sentimentNotification: Boolean
sentimentThreshold: Float
token: String
clientCount: Int
notificationCount: Int
portfolioCount: Int
stockAverageCount: Float
totalValue: Float
stockList: [PortfolioStock!]
}
在用户突变中,我们传递一个公司 ID,我们用它来将用户连接到关联的公司对象
用户突变
user(
companyId: UUID <---- Company ID for relation
confirmed: Boolean
defaultPortfolioSize: Int
delete: Boolean
email: String
firstName: String
lastName: String
id: UUID
isActive: Boolean
isStaff: Boolean
isSuperuser: Boolean
password: String
phoneNumber: String
priceNotification: Boolean
priceThreshold: Float
sentimentNotification: Boolean
sentimentThreshold: Float
username: String
): User!
解析器非常简单。我们验证授权,然后继续请求。
用户突变解析器
user: async (_, params, { user }) => {
if (params.id) {
await authorize(user, Permission.MODIFY_USER, { userId: params.id });
} else {
// Anyone can register
}
return await userDataLoader.upsertUser(user, params);
},
数据加载器是魔法发生的地方。我们调用 upsertUser 来创建、更新和删除任何对象。在这里我们成功创建了一个用户,并且可以在数据库中验证创建。
用户数据加载器
upsertUser: async (user, params) => {
...
/* Register */
if (!params.companyId) {
throw new UserInputError("Missing 'companyId' parameter");
}
if (!params.password) {
throw new UserInputError("Missing 'password' parameter");
}
let newUser = new User({
billingAddressId: 0,
dateJoined: new Date(),
defaultPortfolioSize: 45,
isActive: true,
isStaff: false,
isSuperuser: false,
lastLogin: new Date(),
phoneNumber: '',
priceNotification: false,
priceThreshold: 0,
sentimentNotification: false,
sentimentThreshold: 0,
subscriptionStatus: false,
...params,
});
newUser = await newUser.save();
newUser.token = getJWT(newUser.email, newUser.id);
EmailManager(
EmailTemplate.CONFIRM_ACCOUNT,
`${config.emailBaseUrl}authentication/account-confirmation/?key=${
newUser.token
}`,
newUser.email
);
return newUser;
},
// Including the users query dataloader for reference
users: async params => {
return await User.findAll(get({ ...defaultParams(), ...params }));
},
这是一个示例突变,我们在其中创建一个用户对象并请求具有嵌套公司关系的响应。
示例突变
mutation {
user(
companyId: "16a94e71-d023-4332-8263-3feacf1ad4dc",
firstName: "Test"
lastName: "User"
email: "test@gmail.com"
password: "PleaseWork"
) {
id
company {
id
name
}
email
firstName
lastName
}
}
但是,当请求包含在响应对象中的关系时,api returns null 而不是对象。
示例响应
ACTUAL:
{
"data": {
"user": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"company": null,
"email": "test@gmail.com",
"firstName": "Test",
"lastName": "User"
}
}
}
EXPECTED:
{
"data": {
"user": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"company": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"name": "Test Company",
},
"email": "test@gmail.com",
"firstName": "Test",
"lastName": "User"
}
}
}
我想我有点困惑为什么 graphQL 不能在突变期间绘制我的嵌套对象,但可以通过查询来绘制。
问题出在 Sequelize 上。由于 table 的突变不与其关联共享,因此突变对象不包含典型查询可能具有的所述关联。因此,从变异对象请求的任何关联都将 return 为空,因为该对象不直接存在于模型中。
也就是说,有几种方法可以补充这个问题...
- 创建 - 插入新行时,您可以专门包括与 Sequelize 的创建或构建方法之一的关联。像这样:
let client = new Client(
{
...params
},
{ include: [ClientGroup] }
);
return client.save()
使用方法中的选项参数,我们可以将包含参数与关联模型一起传递。这将return与协会
- 更新 - 这个有点棘手,因为关联不在正在变异的模型中。因此 returned 的对象将不包含这些关联。此外,Sequelize 的更新方法不提供像我们第一次创建对象时那样包含模型关联的选项。这是一个快速解决方案:
await Client.update(params, {
// @ts-ignore: Unknown property 'plain'
plain: true,
returning: true,
where: { id: params.id },
});
return await Client.findOne({
include: [ClientGroup],
where: { id: params.id },
});
首先,我们使用更新方法来改变对象。更新后,我们使用 findOne 方法获取具有关联的变异对象。
虽然这解决了问题,但肯定还有其他方法可以解决这个问题。
特别是,如果你想直接通过这个模型改变这些关联。如果是这样,我建议查看 Sequelize 的 transactions.
我有 2 个模型,公司和用户。从数据库的角度来看,一家公司有很多用户。创建单个用户时,我想通过返回与用户关联的公司来利用 graphQL 的强大功能。但是,这仅在进行查询时有效。尝试突变时,对象发生突变,但请求的关系数据始终 returns null
在模型中,我们声明了一个 -> 多关系,并将公司模型模式包含在我们的用户模型模式中以访问数据
用户模型架构
type User {
clients: [Client!]
company: Company <------- Company Relation
companyId: UUID
confirmed: Boolean
defaultPortfolioSize: Int
email: String!
firstName: String!
lastLogin: String!
lastName: String!
id: UUID!
isActive: Boolean
isStaff: Boolean
isSuperuser: Boolean
password: String
phoneNumber: String
priceNotification: Boolean
priceThreshold: Float
sentimentNotification: Boolean
sentimentThreshold: Float
token: String
clientCount: Int
notificationCount: Int
portfolioCount: Int
stockAverageCount: Float
totalValue: Float
stockList: [PortfolioStock!]
}
在用户突变中,我们传递一个公司 ID,我们用它来将用户连接到关联的公司对象
用户突变
user(
companyId: UUID <---- Company ID for relation
confirmed: Boolean
defaultPortfolioSize: Int
delete: Boolean
email: String
firstName: String
lastName: String
id: UUID
isActive: Boolean
isStaff: Boolean
isSuperuser: Boolean
password: String
phoneNumber: String
priceNotification: Boolean
priceThreshold: Float
sentimentNotification: Boolean
sentimentThreshold: Float
username: String
): User!
解析器非常简单。我们验证授权,然后继续请求。
用户突变解析器
user: async (_, params, { user }) => {
if (params.id) {
await authorize(user, Permission.MODIFY_USER, { userId: params.id });
} else {
// Anyone can register
}
return await userDataLoader.upsertUser(user, params);
},
数据加载器是魔法发生的地方。我们调用 upsertUser 来创建、更新和删除任何对象。在这里我们成功创建了一个用户,并且可以在数据库中验证创建。
用户数据加载器
upsertUser: async (user, params) => {
...
/* Register */
if (!params.companyId) {
throw new UserInputError("Missing 'companyId' parameter");
}
if (!params.password) {
throw new UserInputError("Missing 'password' parameter");
}
let newUser = new User({
billingAddressId: 0,
dateJoined: new Date(),
defaultPortfolioSize: 45,
isActive: true,
isStaff: false,
isSuperuser: false,
lastLogin: new Date(),
phoneNumber: '',
priceNotification: false,
priceThreshold: 0,
sentimentNotification: false,
sentimentThreshold: 0,
subscriptionStatus: false,
...params,
});
newUser = await newUser.save();
newUser.token = getJWT(newUser.email, newUser.id);
EmailManager(
EmailTemplate.CONFIRM_ACCOUNT,
`${config.emailBaseUrl}authentication/account-confirmation/?key=${
newUser.token
}`,
newUser.email
);
return newUser;
},
// Including the users query dataloader for reference
users: async params => {
return await User.findAll(get({ ...defaultParams(), ...params }));
},
这是一个示例突变,我们在其中创建一个用户对象并请求具有嵌套公司关系的响应。
示例突变
mutation {
user(
companyId: "16a94e71-d023-4332-8263-3feacf1ad4dc",
firstName: "Test"
lastName: "User"
email: "test@gmail.com"
password: "PleaseWork"
) {
id
company {
id
name
}
email
firstName
lastName
}
}
但是,当请求包含在响应对象中的关系时,api returns null 而不是对象。
示例响应
ACTUAL:
{
"data": {
"user": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"company": null,
"email": "test@gmail.com",
"firstName": "Test",
"lastName": "User"
}
}
}
EXPECTED:
{
"data": {
"user": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"company": {
"id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
"name": "Test Company",
},
"email": "test@gmail.com",
"firstName": "Test",
"lastName": "User"
}
}
}
我想我有点困惑为什么 graphQL 不能在突变期间绘制我的嵌套对象,但可以通过查询来绘制。
问题出在 Sequelize 上。由于 table 的突变不与其关联共享,因此突变对象不包含典型查询可能具有的所述关联。因此,从变异对象请求的任何关联都将 return 为空,因为该对象不直接存在于模型中。
也就是说,有几种方法可以补充这个问题...
- 创建 - 插入新行时,您可以专门包括与 Sequelize 的创建或构建方法之一的关联。像这样:
let client = new Client(
{
...params
},
{ include: [ClientGroup] }
);
return client.save()
使用方法中的选项参数,我们可以将包含参数与关联模型一起传递。这将return与协会
- 更新 - 这个有点棘手,因为关联不在正在变异的模型中。因此 returned 的对象将不包含这些关联。此外,Sequelize 的更新方法不提供像我们第一次创建对象时那样包含模型关联的选项。这是一个快速解决方案:
await Client.update(params, {
// @ts-ignore: Unknown property 'plain'
plain: true,
returning: true,
where: { id: params.id },
});
return await Client.findOne({
include: [ClientGroup],
where: { id: params.id },
});
首先,我们使用更新方法来改变对象。更新后,我们使用 findOne 方法获取具有关联的变异对象。
虽然这解决了问题,但肯定还有其他方法可以解决这个问题。 特别是,如果你想直接通过这个模型改变这些关联。如果是这样,我建议查看 Sequelize 的 transactions.