home

Best GraphQL Stack for Node.js

Mar 29, 2020

If you’re looking to start a GraphQL project using Node.js, I would recommend using Hasura, PostGraphile, or writing your own GraphQL schema + resolvers.

Which one you choose is going to come down to how you want to structure your project and personal preference.

If your a total noob that wants to learn backend development/GraphQL, start with “Build your Own” then come back and try Hasura/PostGraphile to appreciate/better understand what they automate.

Hasura and PostGraphile

Both take a PostgreSQL database as input and generate a GraphQL API on top of it. Instantly creating a GraphQL interface that allows you to do CRUD operations on all your database tables. This can save a ton of time and also solve things like the N+1 problem for you.

While you can get up and running very quickly with this approach, the more business logic you need to add across resolvers the less useful auto-generated CRUD becomes.

I would recommend as an exercise to try out both frameworks and add some custom business logic to a query or mutation to see how it works for each.

For example try adding validation + checking permissions:

editNote = (id, newNote) => {
  if (newNote.title.includes("corona")) {
    throw new Error("no controversial topics")
  }

  if (!canEditNote(id)) {
    throw new Error("not authorized")
  }

  return dbUpdateNote(id, newNote)
}

Doing that will give you a good feel for which one you like better.

If you need more flexibility or you don’t think you’ll be benefiting from what Hasura/PostGraphile offers or you don’t want to use PostgreSQL (that’s the only database they support) then do the next approach.

Build your Own

This consists of picking a server, a tool to create a GraphQL schema, and any number of data sources/databases. You’ll have the most flexibility with this approach, but will have to write your own schema/resolvers.

An example stack:

  • apollo-server-express (server)
  • type-graphql (creates a GraphQL schema)
  • typeorm (database ORM for PostgreSQL)

Note: you can also combine this API with the API generated by Hasura/PostGraphile.

Now let’s get into some of the options for each piece of the stack.

Pick a Server

If you’re new and not sure which one to go with, I would start with Apollo Server.

Note: you can use any server with GraphQL, you mainly just need some kind of adapter that understands how to handle GraphQL requests.

Personally, I’m experimenting with a middleware which sits on top of express: https://github.com/benawad/express-gql.

Pick a Tool for Creating a GraphQL Schema

If your using JavaScript go with this approach where you write a string to define the types:

const typeDefs = gql`
  type Book {
    title: String
    author: String
  }
  type Query {
    books: [Book]
  }
`

const resolvers = {
  Query: {
    books: () => [
      {
        title: "Harry Potter and the Chamber of Secrets",
        author: "J.K. Rowling",
      },
      {
        title: "Jurassic Park",
        author: "Michael Crichton",
      },
    ],
  },
}

const executableSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

This is built in with Apollo Server or you can use it with https://github.com/apollographql/graphql-tools.

If your using TypeScript go with TypeGraphQL or GraphQL Nexus.

In general, I would go with GraphQL Nexus because it has better type inference (aka you don’t have to explicitly write as many types and gives you more autocompletion), but there is 2 reasons I still think TypeGraphQL can be a good choice:

  1. You like the decorator syntax better
import { ObjectType, Field } from "type-graphql"

@ObjectType()
export class Book {
  @Field(() => String, { nullable: true })
  title: string
  @Field(() => String, { nullable: true })
  author: string
}

Compared to GraphQL Nexus’ syntax:

import { objectType, scalarType } from "nexus"

const Book = objectType({
  name: "Book",
  definition(t) {
    t.string("title", { nullable: true })
    t.string("author", { nullable: true })
  },
})
  1. You’re using TypeORM

It integrates nicely with TypeGraphQL, so you can use a single class for both your database and GraphQL types:

@ObjectType()
@Entity()
export class Movie extends BaseEntity {
  @Field(() => Int)
  @PrimaryGeneratedColumn()
  id: number

  @Field()
  @Column()
  title: string

  @Field(() => Int)
  @Column("int", { default: 60 })
  minutes: number
}

Here’s an introduction to this technique: https://www.youtube.com/watch?v=WhzIjYQmWvs

Pick a Database

Doesn’t matter what you choose here, all of them can be used with GraphQL.

Personally, I like to use PostgreSQL with TypeORM, but you could go with MongoDB, MySQL, DynamoDB, Redis, etc…

PS: Prisma 2 is pretty good, but still in beta: https://www.isprisma2ready.com/

Less Good Options

Nothing wrong with using one of the following, but for one reason or another I find them less optimal.

AWS AppSync

Great for instantly creating an entire backend for simple examples, but difficult to figure out how to do more complex operations and dealing with AWS services can be a pain.

Looks interesting for offline apps though.

Graph Databases that have GraphQL extensions

Including Neo4j, Dgraph, and ArangoDB just to name a few.

I wouldn’t pick one of these databases because I wanted to use GraphQL.

If my project would benefit from a graph database then it’s a nice plus that they integrate with GraphQL.

Conclusion

Try Hasura/PostGraphile and if you don’t like it, build your own GraphQL stack.

Currently, I’m using the following stack for http://mysaffronapp.com/ and would be happy to build another project with it.

If you have any questions or suggestions feel free to hit me up on Twitter.