##Initial project set up
- Create project folder.
- Initialize npm.
- Install babel-register with npm saving dependencies(to use es2015+ features).
- Create the entry file "index.js" in root folder and within:
- use require to import babel-register(creates hook)
require('babel-register')({ "presets": ["es2015"] });
-
Install express.js with npm and save flag.
npm -i express --save
-
Create "server.js" file in project root folder and within:
- import express
import express from 'express';
- define port number
const PORT = 3000;
- create express application and start to listening
express() .listen(PORT, () => console.log(`Server listening on localhost:${PORT}`));
-
In "index.js" require server.js:
require('./server.js');
-
Add script starting server to package.json.
"dev": "node index.js"
-
Start server.
npm run dev
-
Using npm install graphql and express-graphql(Lee Byron Express app setup for graphql server).
npm i graphql express-graphql --save
-
Create "graphql" folder and "schema.js" file in it.
mkdir graphql && cd graphql touch schema.js
-
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;
-
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}`));
-
Create "query.js" file in "graphql" folder.
cd graphql touch query.js
-
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;
-
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 });
-
Create "types" folder in "graphql" folder. Then create "EventType.js" and "ParticipantType.js" files in it.
cd graphql mkdir types && cd touch touch EventType.js ParticipantType.js
-
In "EventType.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 './ParticipantType';
- 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;
-
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;
-
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, } } });
-
In "query.js":
- 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"
args: { id: { type: new GraphQLNonNull(GraphQLID) }, name: { type: GraphQLString } }
-
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] }
-
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] }
-
Create new file in root directory "fakeApi.js".
touch fakeApi.js
-
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 }
-
Inside "fakeApi.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 }
-
Use fakeApi in "query.js":
- import fakeApi functions
import { getEvent, getParticipant } from '../fakeApi';
- use them to resolve value for "event" and "participant" field, passing id from "args" argument
participant: { ... resolve: (root, args) => getParticipant(args.id), ... }, event: { ... resolve: (root, args) => getEvent(args.id), ... }
-
In "EventType.js":
- import getParticipant
import { getParticipant } from '../../fakeApi';
- in "participant" field resolve method use getParticipant while mapping through "participantIds" from root value
resolve: (root) => root.participantsIds.map((id) => getParticipant(id))
-
Add new functions to "fakeApi.js" and update export
const addEvent = ({ id, name }) => { events.push({ id, name, participantsIds: [] }); return getEvent(id); }; const addParticipant = ({ id, name }) => { 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 }
-
Create "mutation.js" file in "graphql" folder.
cd graphql touch mutation.js
-
Inside:
- import necessary GraphQL types, functions from fakeApi, 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 }) } } });
-
Import and use mutation in "schema.js".
import mutation from './mutation'; const schema = new GraphQLSchema({ query, mutation });