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

时间:2017-12-01 23:00:50

标签: graphql graphql-js apollo-server

调整Apollo团队(https://github.com/apollographql/GitHunt-API/tree/master/api)创建的GraphQL最佳实践示例,我很难想出一个能够生成人员的解析器的解析器使用DataLoaders。

以下是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 /和一个人。但是如何改变它以获得一个人的名单?我不太确定这段代码应该在何处/何处。

非常感谢!

1 个答案:

答案 0 :(得分:0)

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

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