Skip to content

G3F4/express-graphql-workshop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WELCOME TO EXPRESS GRAPHQL WORKSHOP!

This repository contains full walkthrough creating GraphQL Server.

Technologies used:

Proceed with steps below.

STEPS

Start work from particular step

You can clone repo and checkout to commit with particular step

$ git clone https://github.com/G3F4/express-graphql-workshop.git
$ cd express-graphql-workshop
$ git reset --hard $changeId

... or do everything step by step :)

Initial project set up

  1. Create project folder.

     $ touch express-graphql-workshop
     $ cd express-graphql-workshop
    
  2. Initialize npm.

     $ npm init
    
  3. Install babel-register and babel-preset-es2015 with npm saving dependencies(to use es2015+ features).

     $ npm i babel-register --save
     $ npm i babel-preset-es2015 --save
    
  4. Create the entry file index.js in root folder and within:

     $ touch index.js
    
  5. and within use require to import babel-register(creates hook):

     require('babel-register')({
       "presets": ["es2015"]
     });
    

commit

Create express server

  1. Install express with npm and save flag and create server.js file ...

     $ npm i express --save
     $ touch server.js
    
  2. ... and within:

    • import express

      import express from 'express';
    • define port number

      const PORT = 30001;
    • create express application and start to listening

      express()
        .listen(PORT, () => console.log(`Server listening on localhost:${PORT}`));
  3. In index.js require server.js:

     require('./server.js');
    
  4. Add script starting server to package.json.

     "dev": "node index.js"
    
  5. Start server.

     $ npm run dev
    

commit

Adding GraphQL

  1. Using npm install graphql and express-graphql(Lee Byron Express app setup for graphql server).

    $ npm i graphql express-graphql --save
  2. Create graphql folder and schema.js file in it.

    $ mkdir graphql && cd graphql
    $ touch schema.js
  3. In schema.js:

    • import basic types needed to create test schema
    import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql';
    • create new schema with single field test of string type, resolving static literal
    const schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'query',
        fields: {
          test: {
            type: GraphQLString,
            resolve: () => 'test'
          }
        }
      })
    });
    • export schema
    export default schema;
  4. In server.js:

    • import express-graphql and schema
    import graphqlHTTP from 'express-graphql';
    import schema from './graphql/schema';
    • use express-graphql to process query document on /graphql route
    express()
      .use('/graphql', graphqlHTTP({ schema, graphiql: true }))
      .listen(PORT, () => console.log(`Server listening on localhost:${PORT}`));
  5. Open browser on localhost:30001 and query for test field!

commit

Adding query to schema

  1. Create query.js file in graphql folder.

    $ cd graphql
    $ touch query.js
  2. In query.js:

    • import necessary
    import { GraphQLObjectType, GraphQLString } from 'graphql';
    • create schema with two fields: participant and event of string type resolving static string for now.
    const query = new GraphQLObjectType({
      name: 'Query',
      fields: {
        participant: {
          resolve: () => 'participant',
          type: GraphQLString,
        },
        event: {
          resolve: () => 'event',
          type: GraphQLString,
        }
      }
    });
    • export query
    export default query;
  3. In schema.js:

    • reduce imports from graphql and import query
    import { GraphQLSchema } from 'graphql';
    import query from './query';
    • change schema definition to use imported query
    const schema = new GraphQLSchema({ query });

commit

Adding types to schema

  1. Create types subfolder in graphql folder. Then create event-type.js and participant-type.js files in it.

    $ cd graphql
    $ mkdir types && cd touch
    $ touch event-type.js participant-type.js
  2. In event-type.js"

    • import necessary GraphQL types and ParticipantType(we will use it to resolve event participants)
    import { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList } from 'graphql';
    import ParticipantType from './participant-type';
    • create new type with 3 fields:
      • id of ID type, resolving static literal for now
      • name of string type, resolving static literal for now
      • participants type of list of ParticipantType, resolving array with empty string for now
    const EventType = new GraphQLObjectType({
      name: 'event',
      fields: () => ({
        id: {
          type: GraphQLID,
          resolve: () => 'id'
        },
        name: {
          type: GraphQLString,
          resolve: () => 'name'
        },
        participants: {
          type: new GraphQLList(ParticipantType),
          resolve: () => ['']
        }
      })
    });
    • export EventType
    export default EventType;
  3. Do the same for ParticipantType with fields(import EventType instead of ParticipantType):

    • id of ID type, resolving static literal for now
    • name of string type, resolving static literal for now
    • friends type of list of ParticipantType, resolving array with empty string for now
    • events type of list of EventType, resolving array with empty string for now
    import { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList } from 'graphql';
    import EventType from './EventType';
    
    const ParticipantType = new GraphQLObjectType({
      name: 'participant',
      fields: () => ({
        id: {
          type: GraphQLID,
          resolve: () => 'id'
        },
        name: {
          type: GraphQLString,
          resolve: () => 'name'
        },
        friends: {
          type: new GraphQLList(ParticipantType),
          resolve: () => ['']
        },
        events: {
          type: new GraphQLList(EventType),
          resolve: () => ['']
        }
      })
    });
    
    export default `ParticipantType`;
  4. In query.js:

    • import created types
    import EventType from './types/EventType';
    import ParticipantType from './types/ParticipantType';
    • use them in query as fields types
    const query = new GraphQLObjectType({
      name: 'Query',
      fields: {
        participant: {
          resolve: () => 'participant',
          type: ParticipantType,
        },
        event: {
          resolve: () => 'event',
          type: EventType,
        }
      }
    });

commit

Understanding fields resolve method (root and arguments)

  1. In query.js:

    • import neseccary types from graphql
    import { GraphQLObjectType, GraphQLNonNull, GraphQLID, GraphQLString } from 'graphql';
    • add arguments to fields(the same for both participant and event)
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) },
      name: { type: GraphQLString }
    }
    • add root and args arguments to resolve methods and return args
    resolve: (root, args) => args,
  2. Next in EventType.js:

    • add "root" and "args" arguments to resolve methods
    • the "root" argument has value returned from parent in schema in this case the "event" field which is returning it's args for scalar(primitive) fields return corresponding value from root for list return table with root value
    id: {
      type: GraphQLID,
      resolve: (root) => root.id
    },
    name: {
      type: GraphQLString,
      resolve: (root) => root.name
    },
    participants: {
      type: new GraphQLList(ParticipantType),
      resolve: (root) => [root]
    }
  3. Do the same thing for ParticipantType.

    id: {
      type: GraphQLID,
      resolve: (root) => root.id
    },
    name: {
      type: GraphQLString,
      resolve: (root) => root.name
    },
    friends: {
      type: new GraphQLList(ParticipantType),
      resolve: (root) => [root]
    },
    events: {
      type: new GraphQLList(EventType),
      resolve: (root) => [root]
    }

commit

Adding fakeApi to serve dummy data

  1. Create new file in root directory fake-api.js.

    $ touch fake-api.js
  2. We need two lists: one for events and one for participants.

    • event item in events list should be structured like below:
    {
      id: String // event id
      name: String // event name
      participantsIds: List(String) // list of participants ids
    }
    • participant item in participants list should be structured like below:
    {
      id: String // event id
      name: String // event name
      eventsIds: List(String) // list of events ids
      friendsIds: List(String) // list of friends ids
    }
  3. Inside fake-api.js:

    • declaring dummy data
    const DATA = {
      participants: [{
        id: '1',
        name: 'Bolek',
        friendsIds: ['2', '3'],
        eventsIds: ['1']
      }, {
        id: '2',
        name: 'Franek',
        friendsIds: ['1'],
        eventsIds: ['1']
      }, {
        id: '3',
        name: 'Zenek',
        friendsIds: [],
        eventsIds: ['1', '2']
      }],
      events: [{
        id: '1',
        name: 'WarsawJS',
        participantsIds: ['1', '2', '3']
      }, {
        id: '2',
        name: 'ReactWarsaw',
        participantsIds: ['2']
      }, {
        id: '3',
        name: 'AngularWarsaw',
        participantsIds: []
      }]
    };
    • declare functions returning proper event and participant by id
    const getEvent = (id) => DATA.events.find(participant => participant.id === id);
    const getParticipant = (id) => DATA.participants.find(event => event.id === id);
    • export functions
    export {
      getEvent,
      getParticipant
    }
  4. Use fake-api in query.js:

    • import fake-api functions
    import { getEvent, getParticipant } from '../fake-api';
    • use them to resolve value for event and participant fields, passing id from args argument
    participant: {
      ...
      resolve: (root, args) => getParticipant(args.id),
      ...
    },
    event: {
      ...
      resolve: (root, args) => getEvent(args.id),
      ...
    }
  5. In event-type.js:

    • import getParticipant
    import { getParticipant } from '../../fake-api';
    • in participants field resolve method use getParticipant while mapping through participantIds from root value
    resolve: (root) => root.participantsIds.map((id) => getParticipant(id))
  6. In participant-type.js:

    • import getParticipant and getEvent
    import { getParticipant, getEvent } from '../../fake-api';
    • in event field resolve method use getEvent while mapping through eventIds from root value
    resolve: (root) => root.eventsIds.map(id => getEvent(id))
    • in friends field resolve method use getParticipant while mapping through friendsIds from root value
    resolve: (root) => root.friendsIds.map(id => getParticipant(id))

commit

Adding mutations

  1. Add new functions to fake-api.js and update export

    const addEvent = ({ id, name }) => {
      DATA.events.push({ id, name, participantsIds: [] });
    
      return getEvent(id);
    };
    const addParticipant = ({ id, name }) => {
      DATA.participants.push({ id, name, eventsIds: [], friendsIds: [] });
    
      return getParticipant(id);
    };
    const addParticipantToEvent = ({ participantId, eventId }) => {
      const participant = getParticipant(participantId);
      participant.eventsIds.push(eventId);
      getEvent(eventId).participantsIds.push(participantId);
    
      return participant;
    };
    const addParticipantFriend = ({ participantId, friendId }) => {
      const participant = getParticipant(participantId);
      participant.friendsIds.push(friendId);
      getParticipant(friendId).friendsIds.push(participantId);
    
      return participant;
    };
    
    export {
      addEvent,
      addParticipant,
      addParticipantToEvent,
      addParticipantFriend,
      getEvent,
      getParticipant
    }
  2. Create mutation.js file in graphql folder.

    $ cd graphql
    $ touch mutation.js
  3. Inside:

    • import necessary graphql types, functions from fake-api, EventType and ParticipantType
    import { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLNonNull } from 'graphql';
    import { addEvent, addParticipant, addParticipantToEvent, addParticipantFriend } from '../fakeApi';
    import EventType from './types/EventType';
    import ParticipantType from './types/ParticipantType';
    • declaring mutation is similar to declaring query. Mutation have fields with types after mutating data, GrapqhQL will return mutated data, that's why mutation fields have similar types to query fields we will add 4 mutations: addEvent, addParticipant, addParticipantToEvent, addParticipantFriend
    const mutation = new GraphQLObjectType({
      name: 'Mutation',
      description: 'Mutate data',
      fields: {
        addEvent: {
          type: EventType,
          args: {
            id: { type: new GraphQLNonNull(GraphQLID) },
            name: { type: new GraphQLNonNull(GraphQLString) }
          },
          resolve: (root, { id, name }) => addEvent({ id, name })
        },
        addParticipant: {
          type: ParticipantType,
          args: {
            id: { type: new GraphQLNonNull(GraphQLID) },
            name: { type: new GraphQLNonNull(GraphQLString) }
          },
          resolve: (root, { id, name }) => addParticipant({ id, name })
        },
        addParticipantToEvent: {
          type: ParticipantType,
          args: {
            participantId: { type: new GraphQLNonNull(GraphQLID) },
            eventId: { type: new GraphQLNonNull(GraphQLID) }
          },
          resolve: (root, { participantId, eventId }) => addParticipantToEvent({ participantId, eventId })
        },
        addParticipantFriend: {
          type: ParticipantType,
          args: {
            participantId: { type: new GraphQLNonNull(GraphQLID) },
            friendId: { type: new GraphQLNonNull(GraphQLID) }
          },
          resolve: (root, { participantId, friendId }) => addParticipantFriend({ participantId, friendId })
        }
      }
    });
    
    export default mutation;
  4. Import and use mutation in schema.js.

    import mutation from './mutation';
    
    const schema = new GraphQLSchema({ query, mutation });

commit

About

Creating Express GraphQL server in Node.js

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors