Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js 在ApolloServer中支持异议“$formatJson”_Node.js_Graphql_Apollo_Apollo Server_Objection.js - Fatal编程技术网

Node.js 在ApolloServer中支持异议“$formatJson”

Node.js 在ApolloServer中支持异议“$formatJson”,node.js,graphql,apollo,apollo-server,objection.js,Node.js,Graphql,Apollo,Apollo Server,Objection.js,我是这两者的忠实粉丝,我正试图让他们在我的公司里为一个新的API一起工作。不幸的是,我遇到了一些困难 Objective最好的地方之一是,它允许您在模型类内部指定如何在各种格式之间转换其属性。您可以以一种方式将其存储到数据库中,以另一种方式在代码中使用它,并在以另一种方式将其发送到客户机之前对其进行序列化 例如,可用于更改日期属性,这些属性在处理模型时是JS日期对象,但在响应中发送时会转换为ISO字符串: $formatJson(json: Pojo): Pojo { json = su

我是这两者的忠实粉丝,我正试图让他们在我的公司里为一个新的API一起工作。不幸的是,我遇到了一些困难

Objective最好的地方之一是,它允许您在模型类内部指定如何在各种格式之间转换其属性。您可以以一种方式将其存储到数据库中,以另一种方式在代码中使用它,并在以另一种方式将其发送到客户机之前对其进行序列化

例如,可用于更改日期属性,这些属性在处理模型时是JS日期对象,但在响应中发送时会转换为ISO字符串:

$formatJson(json: Pojo): Pojo {
    json = super.$formatJson(json);
    if (json.lastActive) json.lastActive = json.lastActive.toISOString();
    return json;
}
此方法在实例的
#toJSON
方法中调用,该方法通常在按照所述进行字符串化时调用

然而,ApolloServer(特别是我正在使用的)并没有直接序列化这些模型实例。它似乎(合理地)将属性子集复制到新对象,将数据与其实例方法分离。因此,
#$formatJson
将永远不会被调用,我的日期作为时间戳返回,因为默认情况下JS日期就是这样字符串化的

似乎需要以某种方式在解析器函数和从返回值复制属性之间插入一些
#toJSON
调用。我查看了
formatResponse
,但它看起来像是在数据已经从模型类中分离出来之后才接收数据的

熟悉阿波罗服务器的人能给我指出正确的方向吗?有什么插件API我需要研究吗

我发现这非常酷,但它似乎通过在顶级解析器中处理整个查询并在解析器返回之前递归调用
#toJSON
来处理这个问题。默认的解析程序从急切加载的结构中提取所有其他内容。超级酷,但它看起来不够灵活,无法满足我的需要。实际上,我只想解决这个特定的问题,而不是用魔法破坏我的整个api\


编辑:

我在这方面做了更多的研究,现在我对GraphQL的实际实现有了更好的理解,我想更清楚地说明这个问题

GraphQL通过调用解析器并按属性组装响应属性来工作。首先调用顶级解析程序,在它们返回后,将调用下一级的每个属性的解析程序,依此类推,直到只剩下剩余的部分。每个叶的返回值最终分配到一个对象结构中,该对象结构被字符串化为JSON。这意味着这些属性最初来自的对象上的任何实例方法此时都将完全丢失。因此,如果您希望使用这些实例方法对整个结果进行某种“最终”转换(在本例中,是Objective的
$formatJSON
),您将遇到一些困难

困难

可以使用截取任何解析器的结果并递归调用其内容上的转换,方法如下:

import { isArray, isObjectLike, map, mapValues } from 'lodash';
import { resolvers, typeDefs } from './api';
import { ApolloServer } from 'apollo-server-koa';
import Koa from 'koa';
import { Model } from 'objection';
import { applyMiddleware } from 'graphql-middleware';

function toResponse(value: any): any {
    // If this isn't an object or an array, just return it.
    if (!isObjectLike(value)) return value;

    // If this is an instance of Model, convert it using #$toJson
    if (value instanceof Model) return value.$toJson();

    // Create a recursive mapping function.
    const mapFn = (item: any) => toResponse(item);

    /*
     * If this an array, convert each one of its items with the mapping
     * function.
     */
    if (isArray(value)) return map(value, mapFn);

    /*
     * Otherwise, this must be an object. Convert its values with the mapping
     * function.
     */
    return mapValues(value, mapFn);
}

const schema = applyMiddleware(
    makeExecutableSchema({ typeDefs, resolvers }),
    async(
        resolve,
        root,
        args,
        info,
    ) => toResponse(await resolve(root, args, ctx, info)),
);

const app = new Koa();
const server = new ApolloServer({ schema });
server.applyMiddleware({ app });

app.listen(3000);
type Query {
    person(id: Int!): Person
}

type Person {
    id: Int!
    name: String!
    children: [Person!]
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: (person: Person) => person.$relatedQuery('children'),
    },
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: entityResolver(
            (person: Person) => person.$relatedQuery('children'),
        ),
    },
}
{
    Query: {
        ...
    },
    Person: {
        children: (person: Person) => Person.fromJson(person).$relatedQuery('children'),
    },
}
这里的问题是,转换将在调用较低级别的解析程序深入到图形之前发生。因此,这些解析器的第一个参数将接收这些转换后的模型实例,而这些实例将不再具有您可能想要实现解析器的原始模型类(如
$relatedQuery
)的任何有用实例方法

例如,我可能有这样一个简单的模式:

import { isArray, isObjectLike, map, mapValues } from 'lodash';
import { resolvers, typeDefs } from './api';
import { ApolloServer } from 'apollo-server-koa';
import Koa from 'koa';
import { Model } from 'objection';
import { applyMiddleware } from 'graphql-middleware';

function toResponse(value: any): any {
    // If this isn't an object or an array, just return it.
    if (!isObjectLike(value)) return value;

    // If this is an instance of Model, convert it using #$toJson
    if (value instanceof Model) return value.$toJson();

    // Create a recursive mapping function.
    const mapFn = (item: any) => toResponse(item);

    /*
     * If this an array, convert each one of its items with the mapping
     * function.
     */
    if (isArray(value)) return map(value, mapFn);

    /*
     * Otherwise, this must be an object. Convert its values with the mapping
     * function.
     */
    return mapValues(value, mapFn);
}

const schema = applyMiddleware(
    makeExecutableSchema({ typeDefs, resolvers }),
    async(
        resolve,
        root,
        args,
        info,
    ) => toResponse(await resolve(root, args, ctx, info)),
);

const app = new Koa();
const server = new ApolloServer({ schema });
server.applyMiddleware({ app });

app.listen(3000);
type Query {
    person(id: Int!): Person
}

type Person {
    id: Int!
    name: String!
    children: [Person!]
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: (person: Person) => person.$relatedQuery('children'),
    },
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: entityResolver(
            (person: Person) => person.$relatedQuery('children'),
        ),
    },
}
{
    Query: {
        ...
    },
    Person: {
        children: (person: Person) => Person.fromJson(person).$relatedQuery('children'),
    },
}
并实现如下解析程序:

import { isArray, isObjectLike, map, mapValues } from 'lodash';
import { resolvers, typeDefs } from './api';
import { ApolloServer } from 'apollo-server-koa';
import Koa from 'koa';
import { Model } from 'objection';
import { applyMiddleware } from 'graphql-middleware';

function toResponse(value: any): any {
    // If this isn't an object or an array, just return it.
    if (!isObjectLike(value)) return value;

    // If this is an instance of Model, convert it using #$toJson
    if (value instanceof Model) return value.$toJson();

    // Create a recursive mapping function.
    const mapFn = (item: any) => toResponse(item);

    /*
     * If this an array, convert each one of its items with the mapping
     * function.
     */
    if (isArray(value)) return map(value, mapFn);

    /*
     * Otherwise, this must be an object. Convert its values with the mapping
     * function.
     */
    return mapValues(value, mapFn);
}

const schema = applyMiddleware(
    makeExecutableSchema({ typeDefs, resolvers }),
    async(
        resolve,
        root,
        args,
        info,
    ) => toResponse(await resolve(root, args, ctx, info)),
);

const app = new Koa();
const server = new ApolloServer({ schema });
server.applyMiddleware({ app });

app.listen(3000);
type Query {
    person(id: Int!): Person
}

type Person {
    id: Int!
    name: String!
    children: [Person!]
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: (person: Person) => person.$relatedQuery('children'),
    },
}
{
    Query: {
        person: (
            root: undefined,
            args: { id: number },
        ) => Person.query().findById(args.id),
    },
    Person: {
        children: entityResolver(
            (person: Person) => person.$relatedQuery('children'),
        ),
    },
}
{
    Query: {
        ...
    },
    Person: {
        children: (person: Person) => Person.fromJson(person).$relatedQuery('children'),
    },
}
使用上面所示的中间件设置,Person.children的此解析器将无法工作,因为它的
Person
参数不会接收
模型的实例,因此它不会有
$relatedQuery
实例方法

原因:

为什么会有人想要这个?除了简单地希望使用Objective最好的特性之一(正如Herku正确地指出的,在本例中,使用GQL更具体的东西可能会更好地处理该特性),还有可能与其他库集成,这些库对您的API框架做出与Objective类似的假设

为了简单起见,我最初省略了这一点,但我还有一个(当前专有的)权限库,它能够过滤查询结果,以删除基于各种因素不允许用户看到的属性。我目前计划的集成方式还包括对需要原始模型实例的模型进行“后解析器”转换。我可能会也可能不会走这条路,我现在不想分享太多关于这个库是如何工作的,但希望这能让情况更清楚一些


我发现了一种变通方法,它似乎很管用,很快就会给出答案,除非有人想出更好的办法。

因此,在GraphQL中,对象不会直接转换为JSON。相反,只有少数地方将值转换为JSON值。这是叶类型的内部

叶类型是GraphQL中没有子选择的类型。具体来说,它们是标量和枚举。让我们看看如何在内部使用不同的值,但在外部使用不同的值:

对于枚举类型,我们可以在内部定义不同于外部的值。可以将任何需要的内容指定给value属性:

从'graphql'导入{GraphQLEnumType};
const MyEnum=新的GraphQLEnumType({
名称:“MyEnum”,
价值观:{
价值A:{
value:'value-a',
},
价值B:{
值:'value-b',
}
}
});
如果您使用Apollo和
graphql工具
,您必须这样做。现在来看更有趣的标量情况。在您的示例中,日期可以是ser