PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

So verbinden Sie GraphQL und PostgreSQL

GraphQL ist datenbankunabhängig, sodass Sie alles verwenden können, was Sie normalerweise verwenden, um mit der Datenbank zu interagieren, und die resolve der Abfrage oder Mutation verwenden Methode zum Aufrufen einer von Ihnen definierten Funktion, die der Datenbank etwas abruft/hinzufügt.

Ohne Relais

Hier ist ein Beispiel für eine Mutation unter Verwendung des Promise-basierten Knex-SQL-Abfragegenerators, zunächst ohne Relay, um ein Gefühl für das Konzept zu bekommen. Ich gehe davon aus, dass Sie in Ihrem GraphQL-Schema einen userType mit drei Feldern erstellt haben:id , username , und created :alle erforderlich, und dass Sie einen getUser haben bereits definierte Funktion, die die Datenbank abfragt und ein Benutzerobjekt zurückgibt. In der Datenbank habe ich auch ein password Spalte, aber da ich das nicht abgefragt haben möchte, lasse ich es aus meinem userType weg .

// 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,
        })
      ),
    },
  }),
});

Da Postgres die id erstellt für mich und ich berechne den created timestamp, ich brauche sie nicht in meiner Mutationsabfrage.

Der Relaisweg

Verwenden der Helfer in graphql-relay und es hat mir geholfen, mich ziemlich eng an das Staffel-Starter-Kit zu halten, weil es eine Menge auf einmal zu verarbeiten war. Relay erfordert, dass Sie Ihr Schema auf eine bestimmte Weise einrichten, damit es ordnungsgemäß funktioniert, aber die Idee ist die gleiche:Verwenden Sie Ihre Funktionen, um in den Auflösungsmethoden Daten aus der Datenbank abzurufen oder zu ihr hinzuzufügen.

Ein wichtiger Vorbehalt ist, dass der Relay-Weg erwartet, dass das Objekt von getUser zurückgegeben wird ist eine Instanz einer Klasse User , also müssen Sie getUser ändern um dem Rechnung zu tragen.

Das letzte Beispiel mit Relay (fromGlobalId , globalIdField , mutationWithClientMutationId und nodeDefinitions sind alle von 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),
    },
  }),
});