如何连接 GraphQL 和 PostgreSQL

How to connect GraphQL and PostgreSQL

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

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

现在唯一的选择是在 Postgres 中存储数据以构建自定义 "adapters" 以获取 GraphQL 查询并将其转换为 SQL 吗?

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

例如,对于突变 (create/update/delete),您可以 look at the examples in the relay repo

GraphQL 与数据库无关,因此您可以使用您通常使用的任何方式与数据库交互,并使用查询或突变的 resolve 方法来调用您定义的函数 get/add 一些东西到数据库。

无中继

这里是一个使用基于 promise 的突变的例子 Knex SQL query builder,首先不使用 Relay 来感受一下这个概念。我将假设您已经在 GraphQL 模式中创建了一个 userType,它具有三个字段:idusernamecreated:都是必需的,并且您有一个 getUser 函数已经定义,它查询数据库和 returns 用户对象。在数据库中,我还有一个 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 要求您以特定方式设置您的模式,以便它可以正常工作,但想法是相同的:使用您的函数在 resolve 方法中从数据库中获取或添加到数据库。

一个重要的警告是中继方式期望从 getUser 返回的对象是 class 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),
    },
  }),
});

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

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

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

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

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

(免责声明:我是作者)

看看 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());
  });

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

Rapidly build highly customisable, lightning-fast GraphQL APIs

PostGraphile is an open-source tool to help you rapidly design and serve a high-performance, secure, client-facing GraphQL API backed primarily by your PostgreSQL database. Delight your customers with incredible performance whilst maintaining full control over your data and your database. Use our powerful plugin system to customise every facet of your GraphQL API to your liking.

如果您使用 Javascript,则可以使用 sequelize 之类的 ORM,如果您使用 Typescript,则可以使用 Typeorm