GraphQL is a new way of building APIs through a strongly typed query language. Released by Facebook in 2015, GraphQL is quickly gaining traction and is being adopted by other huge companies such as Twitter and Github. In this article we’ll walk through how to set up an API with Express and Apollo Server, and how we can structure a GraphQL schema to keep it manageable. As a bonus we’ll add hot reloading to our API as well. Check out the repo here; https://github.com/mhaagens/express_graphql_hmr_article_boilerplate High level overview of GraphQL on the server GraphQL is actually quite simple to get started with once you get familiar with all the moving parts. I’ll try to keep this section short, but if you just want to get to the code you can skip ahead. A GraphQL server is defined through a schema which works roughly like this; GraphQL schema overview. Types are a strongly typed representation of your data model.Here’s an example of a post type defined using from Apollo, which is what we’ll be using to define our schema in this tutorial; Types graphql-tools import User from "./user_type"; const Post = `type Post {id: Int!title: String!body: String!author_id: Int!author: User}`; export default () => [Post, User]; **Queries**Queries are how you define what queries can be run against the schema. Here’s an example of some queries in the RootQuery of a schema; const RootQuery = `type RootQuery {posts: [Post]post(id:Int!): Postusers: [User]user(id:Int!): User}`; **Mutations**Mutations are like POST-requests (though they are really just a synchronous version of queries), they allow you to send data to the server to perform inserts, updates and do other work. Here’s an example of defining a mutation for a new blog post, it takes an input type of PostInput and returns the created post as a Post type. const RootMutation = `type RootMutation {createPost(input: PostInput!): Post}`; **Subscriptions**Subscriptions allow you to publish realtime events through a GraphQL subscriptions server. Here’s an example of how to define a subscription; const RootSubscription = `type RootSubscription {postAdded(title: String): Post}`; Now you can publish events to those subscribed by running this in your createPost mutation resolver; pubsub.publish(‘postAdded’, { postAdded: post }); Resolvers is where you perform work in response to either a query, mutation or subscription. This is where you would go to your database layer to do CRUD operations and return an appropriate response. Resolvers Here’s an example of some resolver functions; ...resolvers: {RootQuery: {posts: () => posts,post: async (_, { id }) =>await Post.query()},RootMutations: {createPost: async (_, { input }) =>await Post.query.insert(input)},RootSubscriptions: {postAdded: {subscribe: () =>pubsub.asyncIterator('postAdded')},Post: {author: async post =>await User.query().where("id", "=", post.author_id)}}... The schema is what connects all the moving parts together.Let’s get started building our API so we can learn how to make one! Schema Getting started If you want to grab the code to look at, there’s a repo here; https://github.com/mhaagens/express_graphql_hmr_article_boilerplate Installing dependencies Run then yarn init yarn add express graphql-server-express graphql-tools graphql body-parser Then we need to install some development dependencies, we don’t want to restart our server every time we make a change and that’s where Webpack can help us out. Run yarn add webpack webpack-node-externals start-server-webpack-plugin babel-loader babel-core babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime babel-plugin-transform-regenerator --dev Setting up our webpack configuration In the root of your folder, create a file called then paste this in; webpack.config.js const webpack = require('webpack');const path = require('path');const nodeExternals = require('webpack-node-externals');const StartServerPlugin = require('start-server-webpack-plugin'); module.exports = {entry: ['webpack/hot/poll?1000', './src/index'],watch: true,target: 'node',node: {__filename: true,__dirname: true},externals: [nodeExternals({ whitelist: ['webpack/hot/poll?1000'] })],module: {rules: [{test: /\.js?$/,use: [{loader: 'babel-loader',options: {babelrc: false,presets: [['env', { modules: false }], 'stage-0'],plugins: ['transform-regenerator', 'transform-runtime']}}],exclude: /node_modules/}]},plugins: [new StartServerPlugin('server.js'),new webpack.NamedModulesPlugin(),new webpack.HotModuleReplacementPlugin(),new webpack.NoEmitOnErrorsPlugin(),new webpack.DefinePlugin({'process.env': { BUILD_TARGET: JSON.stringify('server') }})],output: { path: path.join(__dirname, 'dist'), filename: 'server.js' }}; Then add this to your package.json scripts; "scripts": {"start": "webpack --config webpack.config.js"}, Let’s create some files we’ll need to get up and running Create a folder called and create three files within it; , and src index.js server.js schema.js Setting up our schema with graphql-tools Inside paste this in; schema.js import { makeExecutableSchema } from 'graphql-tools'; const RootQuery = `type RootQuery {hello_world: String!}`; const SchemaDefinition = `schema {query: RootQuery}`; export default makeExecutableSchema({typeDefs: [SchemaDefinition, RootQuery],resolvers: {RootQuery: {hello_world: () => "Hi from GraphQL!"}}}); Here we’re defining a RootQuery which will return a query called hello_world. It returns a required type String (required is denoted by the ! after the String type definition). String is a built-in type from GraphQL, eventually we’ll be making our own types. After defining our RootQuery we create a schema definition which will be used to hold our queries, mutations, subscriptions and so on, before we export it as an executable schema ready to be used by graphql-server-express. Creating our Webpack entry point In paste this in; index.js import http from 'http';import { execute, subscribe } from 'graphql';import { createServer } from 'http'; import app from './server';import schema from './schema'; const server = http.createServer(app);let currentApp = app; server.listen(3000, () => {console.log(`GraphQL-server listening on port 3000.`)}); if (module.hot) {module.hot.accept(['./server', './schema'], () => {server.removeListener('request', currentApp);server.on('request', app);currentApp = app;});} This file serves as the entry point for webpack to import our Express server and hot reload our server on changes. Setting up our Express GraphQL server Finally, in we paste this in; server.js import express from 'express';import bodyParser from 'body-parser';import { graphqlExpress, graphiqlExpress } from 'graphql-server-express'; import schema from './schema'; const app = express(); app.use('/graphiql',graphiqlExpress({endpointURL: '/graphql'}));app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: schema })); export default app; Here we attach GraphQL to our /graphql endpoint, and GraphiQL — a GUI for running queries to /graphiql. Run and open your browser and go to npm start [http://localhost:3000/graphiql](http://localhost:3000/graphiql) Apollo Server ( ) comes with a GUI tool to test queries called GraphiQL, which is an awesome tool also created by the nice people at Facebook (the creators of GraphQL). apollo-server In the left hand pane of GraphiQL, paste in the following and click the “run”-button (the button that looks like a play-button); {hello_world} You should now see an output on the right hand pane saying “Hi from GraphQL!”. Running our hello_world query. Try changing the value of the hello_world resolver and re-run the query,it should return your new value immediately thanks to hot reloading through Webpack! Getting ready for the next part GraphQL can quickly become unwieldy and it’s important to figure out a good structure and modularizing your schema.Let’s lay the basis for our structure by adding a few folders inside ; src , , , and controllers/ services/ lib/ models/ types/ That’s it for part one. You now have the basics set up for a hot-reloadable GraphQL API with Express and Apollo Server. In the next part we’ll start structuring our project and adding in our resolvers, mutations, data layer, error handling and so on. Stay tuned!
Share Your Thoughts