sql >> Base de Datos >  >> RDS >> PostgreSQL

Cómo conectar GraphQL y PostgreSQL

GraphQL es independiente de la base de datos, por lo que puede usar lo que normalmente usa para interactuar con la base de datos y usar la consulta o la mutación resolve método para llamar a una función que ha definido que obtendrá/agregará algo a la base de datos.

Sin relé

Aquí hay un ejemplo de una mutación que usa el generador de consultas Knex SQL basado en promesas, primero sin Relay para tener una idea del concepto. Asumiré que ha creado un tipo de usuario en su esquema de GraphQL que tiene tres campos:id , username y created :todo lo requerido, y que tiene un getUser función ya definida que consulta la base de datos y devuelve un objeto de usuario. En la base de datos también tengo una password columna, pero como no quiero que se consulte eso, lo dejo fuera de mi 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,
        })
      ),
    },
  }),
});

Dado que Postgres crea el id para mí y calculo el created marca de tiempo, no los necesito en mi consulta de mutación.

El camino del relevo

Usando los ayudantes en graphql-relay y ceñirme bastante al kit de inicio de relevo me ayudó, porque era mucho para asimilar todo a la vez. Relay requiere que configure su esquema de una manera específica para que pueda funcionar correctamente, pero la idea es la misma:use sus funciones para buscar o agregar a la base de datos en los métodos de resolución.

Una advertencia importante es que la forma de retransmisión espera que el objeto devuelto por getUser es una instancia de una clase User , por lo que tendrás que modificar getUser para acomodar eso.

El último ejemplo usando Relay (fromGlobalId , globalIdField , mutationWithClientMutationId y nodeDefinitions son todos de 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),
    },
  }),
});