带有 DataLoader 和嵌套端点的 GraphQL 解析器

GraphQL resolver with DataLoader and nested endpoints

采用 Apollo 团队 (https://github.com/apollographql/GitHunt-API/tree/master/api) 创建的 GraphQL 最佳实践示例,我很难想出一个解析器来生成 Person 列表 使用 DataLoader。

这是 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/ 和一个人。但是如何将其更改为具有人员列表?我不太确定 how/where 应该是这段代码。

非常感谢!

您可以将您的人员解析器更改为类似于以下代码的内容:

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调用只是被缓存了,但如果不是这样,你需要实现一些缓存以避免多次调用相同的端点。