阿波罗服务器+续集错误不可为空的字段
apollo server + sequelize error non-nullable field
我是 运行 一个带有 sequelize 作为 orm 到 postgres 数据库的 apollo 服务器。这是架构:
type Tag {
id: ID!
name: String!
}
type Service {
id: ID!
name: String!
slug: String!
tags: [Tag!]!
}
解析器:
findServicesByTag: async(_, { tag }, { models }) => {
const res = await models.Service.findAll({
where: {
'$Tags.name$': tag
}
,include: [
{
model: models.Tag,
as: 'Tags'
}
]
})
console.log(res)
return res
}
但是当执行这个查询时
query {
findServicesByTag(tag: "restaurant")
{
id name slug
tags {
name
}
}
}
我收到消息“不能 return 非空字段 Service.id 为空。”
console.log 指令打印此数据:
[ Service {
dataValues: { id: 1, name: 'Restaurant X', Tags: [Array] },
_previousDataValues: { id: 1, name: 'Restaurant X', Tags: [Array] },
_changed: Set {},
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true },
isNewRecord: false,
Tags: [ [Tag] ] },
Service {
dataValues: { id: 2, name: 'Restaurant Y', Tags: [Array] },
_previousDataValues: { id: 2, name: 'Restaurant Y', Tags: [Array] },
_changed: Set {},
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true },
isNewRecord: false,
Tags: [ [Tag] ] } ]
apollo 似乎无法处理这些数据,它不会查询后续标签实体。
你应该使用instance.get方法得到dataValues
。
Model instances operate with the concept of a dataValues
property, which stores the actual values represented by the instance.
然后,您需要为模型关联定义 alias: 'tags'
,因为 graphql 类型对象 - service
具有 tags
字段。确保它们映射正确。
使用 "sequelize": "^5.21.3"
和 "apollo-server": "^2.19.0"
的工作示例。
model.ts
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
class Service extends Model {}
Service.init(
{
id: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
},
name: DataTypes.STRING,
slug: DataTypes.STRING,
},
{ sequelize },
);
class Tag extends Model {}
Tag.init(
{
id: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
},
name: DataTypes.STRING,
},
{ sequelize },
);
Service.belongsToMany(Tag, { through: 'Service_Tag', as: 'tags' });
Tag.belongsToMany(Service, { through: 'Service_Tag', as: 'services' });
(async function test() {
try {
await sequelize.sync({ force: true });
//seed
await Service.bulkCreate(
[
{ name: 'Restaurant X', slug: 'a', tags: [{ name: 'restaurant' }, { name: 'b' }] },
{ name: 'Restaurant Y', slug: 'b', tags: [{ name: 'c' }] },
{ name: 'Restaurant Z', slug: 'c', tags: [{ name: 'restaurant' }] },
],
{ include: [{ model: Tag, as: 'tags' }] },
);
} catch (error) {
console.log(error);
}
})();
export { Service, Tag };
app.ts
:
import { ApolloServer, gql } from 'apollo-server';
import * as models from './model';
const typeDefs = gql`
type Tag {
id: ID!
name: String!
}
type Service {
id: ID!
name: String!
slug: String!
tags: [Tag!]!
}
type Query {
findServicesByTag(tag: String!): [Service]!
}
`;
const resolvers = {
Query: {
async findServicesByTag(_, { tag }, { models }) {
const res = await models.Service.findAll({
where: {
'$tags.name$': tag,
},
include: [
{
model: models.Tag,
as: 'tags',
},
],
});
const data = res.map((v) => v.get({ plain: true }));
return data;
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: {
models,
},
});
server.listen().then(({ url }) => {
console.log(` Server ready at ${url}`);
});
使用 curl
:
测试 GraphQl 查询
⚡ curl 'http://localhost:4000/graphql' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'Origin: chrome-extension://flnheeellpciglgpaodhkhmapeljopja' --data-binary '{"query":"\nquery{\n findServicesByTag(tag: \"restaurant\"){\n id\n name\n slug\n tags {\n \tname\n\t\t}\n }\n}","variables":{}}' --compressed
{"data":{"findServicesByTag":[{"id":"1","name":"Restaurant X","slug":"a","tags":[{"name":"restaurant"}]},{"id":"3","name":"Restaurant Z","slug":"c","tags":[{"name":"restaurant"}]}]}}
数据库中的数据记录:
node-sequelize-examples=# select * from "Service";
id | name | slug
----+--------------+------
1 | Restaurant X | a
2 | Restaurant Y | b
3 | Restaurant Z | c
(3 rows)
node-sequelize-examples=# select * from "Tag";
id | name
----+------------
1 | restaurant
2 | b
3 | c
4 | restaurant
(4 rows)
node-sequelize-examples=# select * from "Service_Tag";
ServiceId | TagId
-----------+-------
1 | 1
1 | 2
2 | 3
3 | 4
(4 rows)
我是 运行 一个带有 sequelize 作为 orm 到 postgres 数据库的 apollo 服务器。这是架构:
type Tag {
id: ID!
name: String!
}
type Service {
id: ID!
name: String!
slug: String!
tags: [Tag!]!
}
解析器:
findServicesByTag: async(_, { tag }, { models }) => {
const res = await models.Service.findAll({
where: {
'$Tags.name$': tag
}
,include: [
{
model: models.Tag,
as: 'Tags'
}
]
})
console.log(res)
return res
}
但是当执行这个查询时
query {
findServicesByTag(tag: "restaurant")
{
id name slug
tags {
name
}
}
}
我收到消息“不能 return 非空字段 Service.id 为空。”
console.log 指令打印此数据:
[ Service {
dataValues: { id: 1, name: 'Restaurant X', Tags: [Array] },
_previousDataValues: { id: 1, name: 'Restaurant X', Tags: [Array] },
_changed: Set {},
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true },
isNewRecord: false,
Tags: [ [Tag] ] },
Service {
dataValues: { id: 2, name: 'Restaurant Y', Tags: [Array] },
_previousDataValues: { id: 2, name: 'Restaurant Y', Tags: [Array] },
_changed: Set {},
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true },
isNewRecord: false,
Tags: [ [Tag] ] } ]
apollo 似乎无法处理这些数据,它不会查询后续标签实体。
你应该使用instance.get方法得到dataValues
。
Model instances operate with the concept of a
dataValues
property, which stores the actual values represented by the instance.
然后,您需要为模型关联定义 alias: 'tags'
,因为 graphql 类型对象 - service
具有 tags
字段。确保它们映射正确。
使用 "sequelize": "^5.21.3"
和 "apollo-server": "^2.19.0"
的工作示例。
model.ts
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
class Service extends Model {}
Service.init(
{
id: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
},
name: DataTypes.STRING,
slug: DataTypes.STRING,
},
{ sequelize },
);
class Tag extends Model {}
Tag.init(
{
id: {
primaryKey: true,
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
},
name: DataTypes.STRING,
},
{ sequelize },
);
Service.belongsToMany(Tag, { through: 'Service_Tag', as: 'tags' });
Tag.belongsToMany(Service, { through: 'Service_Tag', as: 'services' });
(async function test() {
try {
await sequelize.sync({ force: true });
//seed
await Service.bulkCreate(
[
{ name: 'Restaurant X', slug: 'a', tags: [{ name: 'restaurant' }, { name: 'b' }] },
{ name: 'Restaurant Y', slug: 'b', tags: [{ name: 'c' }] },
{ name: 'Restaurant Z', slug: 'c', tags: [{ name: 'restaurant' }] },
],
{ include: [{ model: Tag, as: 'tags' }] },
);
} catch (error) {
console.log(error);
}
})();
export { Service, Tag };
app.ts
:
import { ApolloServer, gql } from 'apollo-server';
import * as models from './model';
const typeDefs = gql`
type Tag {
id: ID!
name: String!
}
type Service {
id: ID!
name: String!
slug: String!
tags: [Tag!]!
}
type Query {
findServicesByTag(tag: String!): [Service]!
}
`;
const resolvers = {
Query: {
async findServicesByTag(_, { tag }, { models }) {
const res = await models.Service.findAll({
where: {
'$tags.name$': tag,
},
include: [
{
model: models.Tag,
as: 'tags',
},
],
});
const data = res.map((v) => v.get({ plain: true }));
return data;
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: {
models,
},
});
server.listen().then(({ url }) => {
console.log(` Server ready at ${url}`);
});
使用 curl
:
⚡ curl 'http://localhost:4000/graphql' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'Origin: chrome-extension://flnheeellpciglgpaodhkhmapeljopja' --data-binary '{"query":"\nquery{\n findServicesByTag(tag: \"restaurant\"){\n id\n name\n slug\n tags {\n \tname\n\t\t}\n }\n}","variables":{}}' --compressed
{"data":{"findServicesByTag":[{"id":"1","name":"Restaurant X","slug":"a","tags":[{"name":"restaurant"}]},{"id":"3","name":"Restaurant Z","slug":"c","tags":[{"name":"restaurant"}]}]}}
数据库中的数据记录:
node-sequelize-examples=# select * from "Service";
id | name | slug
----+--------------+------
1 | Restaurant X | a
2 | Restaurant Y | b
3 | Restaurant Z | c
(3 rows)
node-sequelize-examples=# select * from "Tag";
id | name
----+------------
1 | restaurant
2 | b
3 | c
4 | restaurant
(4 rows)
node-sequelize-examples=# select * from "Service_Tag";
ServiceId | TagId
-----------+-------
1 | 1
1 | 2
2 | 3
3 | 4
(4 rows)