Getting Started with GraphQL, Express, and Mongoose
For a while, I’ve often struggled figuring out how best to design my backends. It’s usually something I evolve as I figure out how to build my projects, but more often than not leaves me with a very unsatisfied mess of an API.
Luckily there’s GraphQL, which is an entire API specification that makes it simple for any front-end to understand and consume. I’ve been reading up on it here and there, and I have to say: all the excitement and hype around it gets to me too. Who wouldn’t want to deal with a clean, self-documenting, get only what ask for API?
Plus, the added prize of using network abstractions like Apollo and Relay makes me cry tears of joy to the developers. All the painstaking go arounds to make sure the app’s state and data are up-to-the-minute can be used with just a few added lines.
Of course, this is only if you have the knowledge or help to set up the connections and infrastructure to do this. And thankfully, there are some wonderful initiatives out there which are trying to make it easier on everyone. Graphcool, in particular, makes it dirt simple to create serverless GraphQL API’s.
Though it can be quite the task to read through all the documentation and figure out how to make your own, I decided to experiment a little and see how far I could get trying to stand up my own server.
So I started out with the documentation on graphql.org and howtographql.com. Though there’s a bunch of great information on the graphql specifications and some small examples around, I found that only the basics could be covered in the tutorials and docs. In order to get a good grasp of how the nuts and bolts work, I had to get my hands dirty and try things out.
I ended up with a very basic Create and Read app using Express, Mongo, and GraphQL. And now I think I have a slight handle on how to handle at least the basic ideas of types, schemas, queries, and mutations.
First I created a Node app and added my dependencies
$ yarn add express express-graphql graphql mongoose nodemon
Then I set up my main server.js file like so:
I will get to the schema in a bit. But essentially here I have the ability to set up the graphql server using HTTP, and can interface using the GraphiQL tool.
I originally started with a copy of the express server from here, but it ended up being a little too simple and I couldn’t really understand how to connect it to my database very simply. So I switched to using GraphQLSchema rather than buildSchema, since it gave me more power to define my own resolvers.
The schema itself also isn’t too hard. You really only need to define the root Query and the root Mutation, at least for this app. All other types will be defined elsewhere and used. For the purpose of this app, I only have two other types defined, Player and PlayerInput, which I will discuss.
Now for the next part, I needed to set up the Mongoose schema so I could persist data in Mongo. So I made a very simple Player Schema like so:
And this will give me the power to user all the Mongoose helper functions associated with this collection in my database. I will need to use those when I define my resolver methods for the GraphQL API.
Now, I’ll get more into the meat of it, and since I need to have a create mutation for adding players to the database, I’ll start with the MutationType first.
So here I’ve created a new GraphQLObjectType, calling it MutationType, which will eventually become my Root Mutation. Since I only have one mutation, there is only one entry for fields: createPlayer. The return type iof this particular mutation is of PlayerType, and it has an args field in addition to the resolve. This is pretty much the bare minimum needed for a mutation.
The PlayerType is a very basic GraphQL type. It has a first_name, last_name, and id value. And it is resolved by taking advantage of the Mongoose/Mongo API. When I have an object of this type, I know I can grab it’s data the way I would a Mongoose object defined by the playerSchema way above. In the end, this and the Mongoose schema are very similar, and I seem to end up doing the schema work twice. Defining once with Mongoose and once with GraphQL. This might be different if I had chosen to use the Mongo ODM, but I won’t explore those too deeply here.
The argument I’ve specified in the mutation is of PlayerInputType. From what I’ve found, Input Types are pretty self-explanatory. For any input, it has to be a GraphQL input type. This might not be true for more basic inputs, but since mine is slightly complex, it will crash if it’s not defined as such here.
Still , it’s very straight forward. I require it to have non-null string values in first_name, and last_name. This way I know a player can’t be created without a first or last name.
Finally, in the above resolve function, I once again take advantage of the Mongoose API, creating a new player with the given arguments, saving it to my database, and returning the saved entity. In the resolve function, be sure your second parameter is your args. The first will be the source by default. In this case the source is actually root, since it is a root mutation, but in other types, it may be the type or object which calls the resolver.
We can finally see this mutation in action on your GraphiQL API.
Here I’ve used my createPlayer mutation to create the player Phillipe Coutinho in my database, and I’m able to specify that I want to see the id and last name of the entity.
Now, last but not least of all, I can set up a query to find allPlayers. This is much simpler than the mutation, since I’m not trying to enter arguments or do anything smart. I’m just going to grab all instances of player in my database and return them.
[Funnily enough, I actually have a second query in here, but all it does it return the string “Root”. ]
So, here I’ve defined my QueryType, which will become my root Query on my schema. It has the fields helloRoot, which isn’t very interesting, and allPlayers, which is slightly less trivial. the allPlayers query has a type of GraphQLList, and I’ve constructed one which holds objects of the PlayerType. It resolves by returning all the entities found using the Mongoose/Mongo API find for the player collection.
And that’s all for basic Create and Read on express-graphql! One thing which put me off with an express implementation is I’m fairly certain subscriptions won’t work out of the box. Express being an HTTP server, there would need to be some kind of manual websockets action fleshed for the server to push information to the client.
It also took me a bit of time to fiddle around since there aren’t too many examples using Mongoose and tons different ways to set up GraphQL servers, but I eventually dug out the basics and can probably take it a step further in the future. I hope it was intelligible and some people out there will find this simple example helpful. Happy programming :)
P.S. I noticed I had a lot of extra mongoose imports in my code. If you try to follow, you’ll probably figure out I didn’t actually use it anywhere besides the server entry point and Mongoose schema.