【问题标题】:GraphQL resolver with DataLoader and nested endpoints带有 DataLoader 和嵌套端点的 GraphQL 解析器
【发布时间】:2018-05-16 02:35:42
【问题描述】:

改编由 Apollo 团队 (https://github.com/apollographql/GitHunt-API/tree/master/api) 创建的 GraphQL 最佳实践示例,我很难想出一个解析器,它会导致使用 DataLoaders 的 Person 列表.

这里是一个api示例(数据来自:https://github.com/steveluscher/zero-to-graphql/tree/master/zero-node

鉴于 /people/ 端点的输出,例如:

{
  "people": [
    {
      "username": "steveluscher",
      "id": "1",
    },
    {
      "username": "aholovaty",
      "id": "2",
    },
    {
      "username": "swillison",
      "id": "3",
    },
    {
      "username": "gvr",
      "id": "4",
    }
  ]
}

还有一个来自端点/people/1/

{
  "person": {
    "last_name": "Luscher",
    "username": "steveluscher",
    "friends": [
      "/people/2/",
      "/people/3/"
    ],
    "id": "1",
    "email": "steveluscher@fb.com",
    "first_name": "Steven"
}

我想要一个解析器,它会给我一个 Person 列表,例如:

[
  {
    "person": {
      "last_name": "Luscher",
      "username": "steveluscher",
      "friends": [
        "/people/2/",
        "/people/3/"
      ],
      "id": "1",
      "email": "steveluscher@fb.com",
      "first_name": "Steven"
    }
  },
  {
    "person": {
      "last_name": "Holovaty",
      "username": "aholovaty",
      "friends": [
        "/people/1/",
        "/people/4/"
      ],
      "id": "2",
      "email": "a.holovaty@django.com",
      "first_name": "Adrian"
    }
  },
  ...
]

这是我目前得到的:

server.js

import { ApiConnector } from './api/connector';
import { People } from './api/models';

import schema from './schema';

export function run() {
    const PORT = 3000;

    const app = express();

    app.use(bodyParser.json());

    app.use('/graphql', graphqlExpress((req) => {
        const query = req.query.query || req.body.query;

        if (query && query.length > 2000) {
            throw new Error('Query too large.');
        }

        const apiConnector = new ApiConnector();

        return {
            schema,
            context: {
                People: new People({ connector: apiConnector }),
            },
        };
    }));

    app.use('/graphiql', graphiqlExpress({
        endpointURL: '/graphql',
    }));

    const server = createServer(app);

    server.listen(PORT, () => {
        console.log(`API Server is now running on http://localhost:${PORT}`);
    });

    return server;
}

models.js

export class People {
    constructor({ connector }) {
        this.connector = connector;
    }

    getPeople() {
        return this.connector.get(`/people/`);
    }

    getPerson(id) {
        return this.connector.get(`/people/${id}/`);
    }
}

connector.js

const API_ROOT = 'http://localhost:8080';

export class ApiConnector {

    constructor() {
        this.rp = rp;

        this.loader = new DataLoader(this.fetch.bind(this));
    }

    fetch(urls) {
        const options = {
            json: true,
            resolveWithFullResponse: true,
            headers: {
                'user-agent': 'Request-Promise',
            },
        };
        return Promise.all(urls.map((url) => {
            return new Promise((resolve) => {
                this.rp({
                    uri: url,
                    ...options,
                }).then((response) => {
                    const body = response.body;
                    resolve(body);
                }).catch((err) => {
                    console.error(err);
                    resolve(null);
                });
            });
        }));
    }

    get(path) {
        return this.loader.load(API_ROOT + path);
}

架构中的解析器将具有以下内容:

const rootResolvers = {
    Query: {
        people(root, args, context) {
            return context.People.getPeople();
        },
        person(root, { id }, context) {
            return context.People.getPerson(id)
        }
    },
};

到目前为止,我可以从 /people/id/ 获取第一个端点 /people/ 和一个人。但是如何将其更改为拥有人员列表?我不太确定这段代码应该如何/在哪里。

非常感谢!

【问题讨论】:

    标签: graphql graphql-js apollo-server


    【解决方案1】:

    您可以将人员解析器更改为如下代码:

    const rootResolvers = {
        Query: {
            people(root, args, context) {        
                const list = context.People.getPeople();
    
                if (list && list.length > 0) {
                  return list.map(item => context.People.getPerson(item.id))
                }
            },
            ...
        },
    };
    

    Ps:您说您使用的是dataLoader,所以我认为您的API调用只是被缓存,但如果不是这样,您需要实现一些缓存以避免多次调用相同的端点。

    【讨论】:

      猜你喜欢
      • 2018-08-25
      • 2019-10-14
      • 2021-07-28
      • 2019-10-05
      • 2018-08-27
      • 2021-07-20
      • 2023-04-01
      • 2019-05-17
      • 2018-09-02
      相关资源
      最近更新 更多