Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to connect GraphQL and PostgreSQL

GraphQL has mutations, Postgres has INSERT; GraphQL has queries, Postgres has SELECT's; etc., etc.. I haven't found an example showing how you could use both in a project, for example passing all the queries from front end (React, Relay) in GraphQL, but to a actually store the data in Postgres.

Does anyone know what Facebook is using as DB and how it's connected with GraphQL?

Is the only option of storing data in Postgres right now to build custom "adapters" that take the GraphQL query and convert it into SQL?

like image 695
Ska Avatar asked Mar 11 '16 12:03

Ska


People also ask

Can GraphQL connect to database?

Your GraphQL API can interact with any combination of data sources. Apollo provides a DataSource class that we can extend to handle interaction logic for a particular type of data source. In this section, we'll extend DataSource to connect both a REST API and a SQL database to Apollo Server.

How do I connect to PostgreSQL connection?

The default username for postgres is postgres. (If you are using Advanced Server it is enterprisedb.) On a Mac or Windows, you are able to connect to the default instance by simply hitting enter at the shell or command prompt when trying to run psql and keying in the password.

What DB to use with GraphQL?

Dgraph - The Native-GraphQL Database and Cloud Platform Dgraph offers a solution that was built specifically as a GraphQL database. This is unlike other GraphQL database offerings that have added GraphQL to existing databases. Dgraph was engineered around the kinds of data and queries that GraphQL apps need.

How do I connect GraphQL TO REST API?

In three steps, you write the schema that describes your GraphQL endpoint. Define an interface and queries for a type that the front-end application will query. This could be a customer type, an order type, a ticket, whatever. Connect backends by creating one or more concrete types in support of the interface type.


2 Answers

We address this problem in Join Monster, a library we recently open-sourced to automatically translate GraphQL queries to SQL based on your schema definitions.

like image 44
Andy Carlson Avatar answered Oct 25 '22 05:10

Andy Carlson


GraphQL is database agnostic, so you can use whatever you normally use to interact with the database, and use the query or mutation's resolve method to call a function you've defined that will get/add something to the database.

Without Relay

Here is an example of a mutation using the promise-based Knex SQL query builder, first without Relay to get a feel for the concept. I'm going to assume that you have created a userType in your GraphQL schema that has three fields: id, username, and created: all required, and that you have a getUser function already defined which queries the database and returns a user object. In the database I also have a password column, but since I don't want that queried I leave it out of my 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,         })       ),     },   }), }); 

Since Postgres creates the id for me and I calculate the created timestamp, I don't need them in my mutation query.

The Relay Way

Using the helpers in graphql-relay and sticking pretty close to the Relay Starter Kit helped me, because it was a lot to take in all at once. Relay requires you to set up your schema in a specific way so that it can work properly, but the idea is the same: use your functions to fetch from or add to the database in the resolve methods.

One important caveat is that the Relay way expects that the object returned from getUser is an instance of a class User, so you'll have to modify getUser to accommodate that.

The final example using Relay (fromGlobalId, globalIdField, mutationWithClientMutationId, and nodeDefinitions are all from 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),     },   }), }); 
like image 185
Eric Streeper Avatar answered Oct 25 '22 06:10

Eric Streeper