When I wrote a little over a year ago, I was pretty early in adopting to replace my REST endpoints, my ORM, and my imperative client-side data requests. Shortly after, when I was hired to build an from the ground up, I jumped at the chance to use GraphQL both on the server and as the basis for my own client cache. A year later, I learned that building a GraphQL app for production is a lot different than one of those demo apps you see on GitHub. Go figure. After all the mistakes I made, here are my lessons learned. Meatier GraphQL open-source realtime app GraphQL is flexible. When starting out, you often wonder if you’re doing it the “right” way. Should GraphQL talk to an ORM like mongoose, or does it go straight to the database? Do you create a library of custom scalars for passwords, paragraphs, and titles, or do you use external validation? Do you favor many, smaller mutations (eg ) or larger, more powerful ones (eg )? Should you use it for Auth? (Yes) How about as an intermediary for an external API? (Yes!) Can it do subscriptions yet? (Kinda). Let’s dig in. updateName, updateTitle updateUserDoc 1. Folder Structure Yawn fest. But seriously, keeping your GraphQL schema modular is critical. I keep a discrete folder for each DB entity ( , , etc.) and inside is a discrete file for every operation ( ). Explaining folder structure in words sucks. . Now on to the interesting stuff… Post Comment PostQuery, PostMutation, PostSchema Check it out here 2. Connecting to a DB with Node.js In production, you use a driver to connect to your database. demos import their DB driver at the top of file. This is crap for one major reason: it isn’t lazy! When you start your server, your GraphQL endpoint is going to resolve your GraphQL schema file, which in turn will resolve your db driver and start that connection. By eagerly instantiating the DB connection, you increase the time it takes to start up your server. More importantly, your connection is now stateful so any webpack magic that uses your schema (think creating a webpack bundle for server-side rendering) will need to manage that connection and end it when necessary. Imagine building a universal app that concurrently builds the client and server bundles & then maybe starts up the server. It’s like asking the last person out to close the door, but you can’t see or hear anyone. Spaghetti code becomes inevitable. To avoid the headache, I only establish the connection when necessary: All // getDBDriver.jslet driver;export default function getDBDriver() {if (!driver) driver = startDriver();return driver;}; Now, instead of importing the driver, I import my wrapper & just call that in the method: resolve // in userSchema.jsresolve(source, {id}) {const db = getDBDriver();return db.get(id);} Now my imports don’t have side effects. Yay closures! 3. Structuring Resolve Functions In every GraphQL demo ever, the function does nothing but resolves. Must be nice. In production, you have attackers calling your GraphQL endpoints trying to query documents that don’t belong to them. Even if someone owns the document, they may try to pass in values that shouldn’t be allowed (eg ). So how can you protect yourself while keeping your code readable? My answer is something I call the Auth/Validation/Resolution pattern. To describe it, let’s assume we have an app with a form allowing a person to change their team : resolve userCredits = $1000000 name async resolve( , { }, { }) {const db = getDBDriver(); source updatedTeam authToken // AUTHrequireUserOnTeam( , .id); authToken updatedTeam // VALIDATIONconst validate = makeTeamSchema();const {errors, data: {id, name}} = validate( );handleSchemaErrors(errors); updatedTeam // RESOLUTIONreturn await db.table('Team').get(id).update({name});} In 5 lines of code, I’ve established authentication and authorization. I’ve validated and normalized the arguments, and I’ve given back the result. Let’s break it down. Auth In an entire enterprise SaaS, I found that each mutation only requires 1 of about 5 unique auth checks. (Are they signed in? Are they editing something that belongs to them or their team? Do they have an active websocket connection?). These are inexpensive checks to make sure the user can do what they want to do. Ideally, they’re synchronous. To make them synchronous, I sometimes cheat & sneak in extra info in the primary key. For example, if a user has a to-do item, and that to-do item can’t change users, I’ll make the id . When a user tries to update it, we can compare the to-do’s with the id from their authToken/cookie. This type of thing becomes critical when you have a realtime app and are editing the same document in sub-second intervals. I don’t care if you can hack an election, you still can’t change the primary key in a DB. user123::xYz8yUo id Validation Now that we know the is good, we need to see if the is good. Sure, you probably already validated this on the client, but rule #1 of a secure web app is to never trust the client. To do this, I use a function that both validates and normalizes (eg ) the data. There are a bunch of popular libraries out there like the bloated and the async , but I roll my own that plugs directly into so both client and server can use the same validation schema. user data String.trim() Joi Yup 100 LOC solution redux-form Why no GraphQL custom scalars? , but I was wrong. Unfortunately, they only flirt with being useful. They are long on verbosity and short on power. For example, if I want to validate a new user’s email address, I’ll need a regex, but I’ll also need to make sure it doesn’t already exist in my DB. If I need to validate that in the function anyways, I might as well do the regex in there, too (and return a client error message far prettier than the ugly mess GraphQL gives me). I thought that was the way to go resolve 4. Writing your Schemas Some folks like using the GraphQL shorthand to write a big text blob schema. I don’t do that for the same reason I don’t write all my in a text file & call on it. It’s harder to write and I like the colorful error squiggles my IDE gives me. Sure, in the future an editor might detect GraphQL bits of code & lint them on the fly, but that day isn’t today. JavaScript eval() As for descriptions, there are 2 important things to include: An asterisk to denote a field that’s indexed in the DB (so those without direct DB access can still write efficient queries) A descriptor of how any IDs were generated. This could be: etc. Just something to let you know what to expect and any non-random information you may be able to extract from it (extra useful for many-to-many relationships) shortid, UUID, teamId::userId, 5. Writing your Mutations The age old question: would you rather fight 1 horse-sized duck, or 100 duck-sized horses? Honestly, I’d take the horses. I’m from the country. I’ve had to run from angry ducks. It applies to GraphQL, too. Should you write 1 mutation to handle data from 100 different forms, or write 100 different mutations for every different way a document might be edited? While the correct answer is always “it depends”, I like to default to larger, more powerful mutations because it lets you iterate faster. If your form gets a new field, you add it to the front-end, you add it to your GraphQL schema, your validation schema, and you’re good to go. You’ll know it’s time to break it apart when you start seeing multiple massive conditional blocks in your function and a growing number of arguments. resolve Final Remarks That’s it! Your intermediary course in GraphQL is complete. If you want a deep dive into auth and error handling in GraphQL, you can always check out my . Am I full of crap? Let me know in the comments. GraphQL Field Guide to Auth