How to Setup and Structure Your GraphQL Project by Cules Coding

How to Setup and Structure Your GraphQL Project

In this blog, you will learn how to set up a graphql project for production. Also, you will learn how to split your type definitions and resolvers into different file types.

Note

I will assume you have a basic idea of graphql. We won't build any serious projects. I will teach you with some dummy examples. But if you know the basics, you will be able to apply them in your project.

Video Tutorial

Please check the video to get a better understating.

Setup

Install packages.

1npm install @apollo/server graphql lodash
2npm install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env nodemon

Explanation:

  1. @apollo/server, graphql: For graphql
  2. Lodash: Utils library
  3. @babel/core: Babel is a transcompiler that compiles js code based on the given configuration. The babel does not do anything. Plugins do.
  4. @babel/preset-env: Preset is a combination of a bunch of plugins. This preset will allow us to use next-generation javascript code. Then it will be compiled down to browser and nodejs compatible code. It also adds many features.
  5. nodemon: nodemon is a tool to restart the server automatically whenever we change our source code so that we don't have to do it manually.

Setup babel and scripts

  1. Create a .babelrc file and add the config
1{
2 "presets": ["@babel/preset-env"]
3}
  1. Add the following scripts to package.json
1{
2 "scripts": {
3 "dev": "nodemon --exec babel-node src/index.js",
4 "build": "babel src -d build",
5 "start": "npm run build && node build/index.js"
6 }
7}

Let's explore the scripts:

  • dev: This script will be used to start the development server. nodemon will automatically start the server when our code changes. But even before that nodemon will execute babel-node and will compile the src/index.js file.
  • build: This script will be used for production. babel will compile our code from the src folder to the build directory. babel will automatically create a build directory for you if it does not exist.
  • start: I will first run our build script. Then it will start our node server from the build directory.

Setup graphql

Let's set up a simple graphql server. We will put the following code in the src/index.js file.

1import { ApolloServer } from '@apollo/server'
2import { startStandaloneServer } from '@apollo/server/standalone'
3
4const books = [
5 {
6 title: 'The Awakening',
7 author: 'Kate Chopin',
8 },
9 {
10 title: 'City of Glass',
11 author: 'Paul Auster',
12 },
13]
14
15const typeDefs = `#graphql
16 type Book {
17 title: String
18 author: String
19 }
20
21 type Query {
22 books: [Book]
23 }
24`
25
26const resolvers = {
27 Query: {
28 books: () => books,
29 },
30}
31
32;(async () => {
33 const server = new ApolloServer({
34 typeDefs,
35 resolvers,
36 })
37
38 const { url } = await startStandaloneServer(server, {
39 listen: {
40 port: 5000,
41 },
42 })
43
44 console.log(`🚀 Server ready at: ${url}`)
45})()

Explanation:

  1. We have the type definition in typeDefs and resolvers in resolvers.
  2. The book query type will return the array of books.
  3. In the iife, we have created a new instance of ApolloServer
  4. Then we run the server instance with the startStandaloneServer method.

Test

  1. Now run the server using npm run dev. You should not have any errors.
  2. Then go to http://localhost:5000/ and you should see the graphql playground.

This is how you can set up a graphql project. Now let me show you how you can structure your GraphQL codebase into different files and directories.

Structuring graphql project

Note: There is no standard way of structuring a graphql project. You don't have to strictly follow it. You can get the basic idea from here and then you can use that in your project.

  • Let's create a directory inside src/
  • Create two more files inside the graphql directory
    • resolvers.js: It will store all the resolvers inside a single object
    • typeDefs.js: It will store all the type definitions inside an array
1mkdir graphql
2touch resolvers.js typeDefs.js

Suppose we are building a social media application.

So, we will create more directory-based features or services. For example, A social media app has users. Users can log in, log out, register, etc. So, we will create a directory for users. Then you can create directories for profiles, posts, and so on. We will just use the user/ directory.

Inside the user/ directory will create few more files. There we will put all user related typedefs and resolvers.

1touch userResolvers.js userTypeDefs.js
  • userTypeDefs.js
1const type = `#graphql
2 type User {
3 name: String
4 country: String
5 }
6
7 extend type Mutation {
8 registerUser(name: String): User
9 loginUser(name: String): User
10 logOutUser(name: String): User
11 }
12`
13
14export default type

Explanation:

  1. We have user type and some Mutations which will return User type.
  2. Note that we are extending the Mutation type. There can be only one Query and Mutation type.
  3. Then we export that type

Now, we will create another file authenticateUser.js Where we will put auth related mutation resolvers.

  • authenticateUser.js
1const registerUser = (_, { name }) => {
2 // action
3 return { name }
4}
5
6const loginUser = (_, { name }) => {
7 // action
8 return { name }
9}
10
11const logOutUser = (_, { name }) => {
12 // action
13 return { name }
14}
15
16const resolvers = {
17 Mutation: {
18 registerUser,
19 loginUser,
20 logOutUser,
21 },
22}
23
24export default resolvers

Explanation:

  1. We first create resolvers object where we will have a Mutation(or Query) property.
  2. Inside that we will put all of our resolvers function. Make sure the property names match with the mutation types.
  • userResolvers.js
1import merge from 'lodash/merge'
2
3import authenticateUser from './authenticateUser'
4
5const userResolvers = merge(authenticateUser)
6
7export default userResolvers

Explanation:

  1. First we import all the user resolvers
  2. Then we merge them inside an single object and export that.
  • Let's go to src/graphql/typedefs.js file.
1import userTypeDefs from './user/userTypeDefs'
2
3const initialTypeDefs = `#graphql
4 type Query {
5 _empty: String
6 }
7
8 type Mutation {
9 _empty: String
10 }
11`
12
13const typeDefs = [initialTypeDefs, userTypeDefs]
14
15export default typeDefs

Explanation:

  1. We are creating the initial Query and Mutation type.
  2. Like I have mentioned before, there can be only one Query and Mutation type and we always extend them.
  3. We create a new array typedefs. We add all the typedefs to the to that array.
  • src/index.js
1import { ApolloServer } from '@apollo/server'
2import { startStandaloneServer } from '@apollo/server/standalone'
3
4import typeDefs from './graphql/typeDefs'
5import resolvers from './graphql/resolvers'
6;(async () => {
7 const server = new ApolloServer({
8 typeDefs,
9 resolvers,
10 })
11
12 const { url } = await startStandaloneServer(server, {
13 listen: {
14 port: 5000,
15 },
16 })
17
18 console.log(`🚀 Server ready at: ${url}`)
19})()

Explanation:

  1. Now, we just import that typedefs and resolvers and add them to the ApolloServer instance.

Final Code structure

Again you don't have to follow the exact same structure.

1├── package.json
2├── package-lock.json
3└── src
4 ├── graphql
5 │   ├── resolvers.js
6 │   ├── typeDefs.js
7 │   └── user
8 │   ├── authenticateUser.js
9 │   ├── userResolvers.js
10 │   └── userTypeDefs.js
11 └── index.js

Shameless Plug

I have made an Xbox landing page clone with React and Styled components. I hope you will enjoy it. Please consider like this video and subscribe to my channel.

That's it for this blog. I have tried to explain things simply. If you get stuck, you can ask me questions.

Contacts

Blogs you might want to read:

Videos might you might want to watch:

Next PostHow to send emails from Node.js with SendInBlue