【问题标题】:How to connect GraphQL and PostgreSQL如何连接 GraphQL 和 PostgreSQL
【发布时间】:2016-06-26 17:11:41
【问题描述】:

GraphQL 有突变,Postgres 有 INSERT; GraphQL 有查询,Postgres 有 SELECT;等等,等等。我还没有找到一个示例来说明如何在项目中同时使用这两种方法,例如在 GraphQL 中传递来自前端(React、Relay)的所有查询,但实际上将数据存储在 Postgres 中。

有谁知道 Facebook 使用什么作为数据库以及它如何与 GraphQL 连接?

现在是否只有在 Postgres 中存储数据以构建自定义“适配器”来接受 GraphQL 查询并将其转换为 SQL?

【问题讨论】:

标签: postgresql graphql


【解决方案1】:

GraphQL 与数据库无关,因此您可以使用通常用于与数据库交互的任何内容,并使用查询或突变的 resolve 方法调用您定义的函数,该函数将获取/添加某些内容到数据库。

无中继

这是一个使用基于 promise 的 Knex SQL query builder 的突变示例,首先没有 Relay 来感受一下这个概念。我将假设您已经在 GraphQL 架构中创建了一个 userType,它具有三个字段:idusernamecreated:都是必需的,并且您已经定义了一个 getUser 函数用于查询数据库并返回一个用户对象。在数据库中,我也有一个 password 列,但由于我不想被查询,所以我将其从 userType 中删除。

// db.js
// take a user object and use knex to add it to the database, then return the newly
// created user from the db.
const addUser = (user) => (
  knex('users')
  .returning('id') // returns [id]
  .insert({
    username: user.username,
    password: yourPasswordHashFunction(user.password),
    created: Math.floor(Date.now() / 1000), // Unix time in seconds
  })
  .then((id) => (getUser(id[0])))
  .catch((error) => (
    console.log(error)
  ))
);

// schema.js
// the resolve function receives the query inputs as args, then you can call
// your addUser function using them
const mutationType = new GraphQLObjectType({
  name: 'Mutation',
  description: 'Functions to add things to the database.',
  fields: () => ({
    addUser: {
      type: userType,
      args: {
        username: {
          type: new GraphQLNonNull(GraphQLString),
        },
        password: {
          type: new GraphQLNonNull(GraphQLString),
        },
      },
      resolve: (_, args) => (
        addUser({
          username: args.username,
          password: args.password,
        })
      ),
    },
  }),
});

由于 Postgres 为我创建了 id 并且我计算了 created 时间戳,所以我的变异查询中不需要它们。

中继方式

使用graphql-relay 中的助手并非常接近Relay Starter Kit 对我很有帮助,因为一次可以吸收很多东西。 Relay 要求您以特定方式设置架构,以便它能够正常工作,但想法是相同的:在解析方法中使用您的函数从数据库中获取或添加到数据库。

一个重要的警告是,中继方式期望从getUser 返回的对象是User 类的实例,因此您必须修改getUser 以适应这种情况。

最后一个使用 Relay 的例子(fromGlobalIdglobalIdFieldmutationWithClientMutationIdnodeDefinitions 都来自graphql-relay):

/**
 * We get the node interface and field from the Relay library.
 *
 * The first method defines the way we resolve an ID to its object.
 * The second defines the way we resolve an object to its GraphQL type.
 *
 * All your types will implement this nodeInterface
 */
const { nodeInterface, nodeField } = nodeDefinitions(
  (globalId) => {
    const { type, id } = fromGlobalId(globalId);
    if (type === 'User') {
      return getUser(id);
    }
    return null;
  },
  (obj) => {
    if (obj instanceof User) {
      return userType;
    }
    return null;
  }
);

// a globalId is just a base64 encoding of the database id and the type
const userType = new GraphQLObjectType({
  name: 'User',
  description: 'A user.',
  fields: () => ({
    id: globalIdField('User'),
    username: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'The username the user has selected.',
    },
    created: {
      type: GraphQLInt,
      description: 'The Unix timestamp in seconds of when the user was created.',
    },
  }),
  interfaces: [nodeInterface],
});

// The "payload" is the data that will be returned from the mutation
const userMutation = mutationWithClientMutationId({
  name: 'AddUser',
  inputFields: {
    username: {
      type: GraphQLString,
    },
    password: {
      type: new GraphQLNonNull(GraphQLString),
    },
  },
  outputFields: {
    user: {
      type: userType,
      resolve: (payload) => getUser(payload.userId),
    },
  },
  mutateAndGetPayload: ({ username, password }) =>
    addUser(
      { username, password }
    ).then((user) => ({ userId: user.id })), // passed to resolve in outputFields
});

const mutationType = new GraphQLObjectType({
  name: 'Mutation',
  description: 'Functions to add things to the database.',
  fields: () => ({
    addUser: userMutation,
  }),
});

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    node: nodeField,
    user: {
      type: userType,
      args: {
        id: {
          description: 'ID number of the user.',
          type: new GraphQLNonNull(GraphQLID),
        },
      },
      resolve: (root, args) => getUser(args.id),
    },
  }),
});

【讨论】:

  • 那不是必须异步/等待 addUser 函数吗?我说的是我更喜欢的非中继方式。
  • “中继方式”部分中的第一个链接指向已弃用的存储库。
【解决方案2】:

我们在Join Monster 中解决了这个问题,这是我们最近开源的一个库,可以根据您的架构定义自动将 GraphQL 查询转换为 SQL。

【讨论】:

  • 这也是在构建表还是在你已经拥有它们之后才查询它们 - 更像是 PostgraphQL?
  • 在你已经拥有它们之后才查询它们。不同之处在于您仍然编写自己的 API 层,而不是让 API 自动生成。
【解决方案3】:

这个GraphQL Starter Kit可以用来试验GraphQL.js和PostgreSQL:

https://github.com/kriasoft/graphql-starter-kit - Node.js、GraphQL.js、PostgreSQL、Babel、Flow

(免责声明:我是作者)

【讨论】:

    【解决方案4】:

    查看graphql-sequelize 了解如何使用 Postgres。

    例如,对于突变(创建/更新/删除),您可以look at the examples in the relay repo

    【讨论】:

    • 我写了这篇小文章,如果你想要一个教程风格的 graphql-sequelize 来进行探索。 medium.com/@leonyapkl/…
    【解决方案5】:

    Postgraphile https://www.graphile.org/postgraphile/ 是开源的

    快速构建高度可定制的闪电般快速的 GraphQL API

    PostGraphile 是一个开源工具,可帮助您快速设计和 提供高性能、安全、面向客户端的 GraphQL API 支持 主要由您的 PostgreSQL 数据库。取悦您的客户 令人难以置信的性能,同时保持对数据的完全控制 和你的数据库。使用我们强大的插件系统来定制每一个 您的 GraphQL API 的各个方面都可以满足您的喜好。

    【讨论】:

      【解决方案6】:

      如果您使用的是 Javascript,则可以使用像 sequelize 这样的 ORM,如果您使用的是 Typescript,则可以使用 Typeorm

      【讨论】:

      • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
      • @Irere Em12 我假设您指的是服务器端代码。您能否添加该详细信息,并可能添加指向示例 ORM 的官方文档的链接?
      • 您可以找到 Typeorm Docs 和续集 Docs 的文档,如果您需要有关如何设置它的资源,我在这里有 github repo Typeorm + Graphql
      【解决方案7】:

      看看SequelizeJS,这是一个基于承诺的ORM,可以与多种方言一起使用; PostgreSQL、MySQL、SQLite 和 MSSQL

      下面的代码是从它的例子中提取出来的

      const Sequelize = require('sequelize');
      const sequelize = new Sequelize('database', 'username', 'password', {
        host: 'localhost',
        dialect: 'mysql'|'sqlite'|'postgres'|'mssql',
      
        pool: {
          max: 5,
          min: 0,
          acquire: 30000,
          idle: 10000
        },
      
        // SQLite only
        storage: 'path/to/database.sqlite',
      
        // http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
        operatorsAliases: false
      });
      
      const User = sequelize.define('user', {
        username: Sequelize.STRING,
        birthday: Sequelize.DATE
      });
      
      sequelize.sync()
        .then(() => User.create({
          username: 'janedoe',
          birthday: new Date(1980, 6, 20)
        }))
        .then(jane => {
          console.log(jane.toJSON());
        });
      

      【讨论】:

      • 这个答案中的 GraphQL 部分在哪里?
      • 这与问题无关,只是带来混乱......
      【解决方案8】:

      可能 FB 在后端使用 mongodb 或 nosql。我最近阅读了一篇博客文章,其中解释了如何连接到 mongodb。基本上,您需要构建一个图形模型来匹配您在数据库中已有的数据。然后编写resolve,reject函数来告诉GQL在发布查询请求时如何表现。

      https://www.compose.io/articles/using-graphql-with-mongodb/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-20
        • 1970-01-01
        • 2015-06-20
        • 1970-01-01
        • 2019-11-22
        • 2021-08-28
        • 2017-08-11
        • 2018-11-03
        相关资源
        最近更新 更多