PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come collegare GraphQL e PostgreSQL

GraphQL è indipendente dal database, quindi puoi utilizzare tutto ciò che usi normalmente per interagire con il database e utilizzare la query o la resolve della mutazione metodo per chiamare una funzione che hai definito che otterrà/aggiungerà qualcosa al database.

Senza staffetta

Ecco un esempio di una mutazione che utilizza il generatore di query Knex SQL basato su promesse, prima senza Relay per avere un'idea del concetto. Suppongo che tu abbia creato un userType nel tuo schema GraphQL che ha tre campi:id , username e created :tutto richiesto e che tu abbia un getUser funzione già definita che interroga il database e restituisce un oggetto utente. Nel database ho anche una password colonna, ma poiché non voglio che venga interrogato, lo lascio fuori dal mio 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,
        })
      ),
    },
  }),
});

Poiché Postgres crea l'id per me e calcolo il created timestamp, non mi servono nella mia query di mutazione.

La staffetta

Utilizzo degli helper in graphql-relay e restare abbastanza vicino al Relay Starter Kit mi ha aiutato, perché era molto da assorbire tutto in una volta. L'inoltro richiede di impostare il tuo schema in un modo specifico in modo che possa funzionare correttamente, ma l'idea è la stessa:usa le tue funzioni per recuperare o aggiungere al database nei metodi di risoluzione.

Un avvertimento importante è che il modo Relay prevede che l'oggetto restituito da getUser è un'istanza di una classe User , quindi dovrai modificare getUser per accontentarlo.

L'ultimo esempio che utilizza Relay (fromGlobalId , globalIdField , mutationWithClientMutationId e nodeDefinitions provengono tutti da 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),
    },
  }),
});