After learning to work with Github's GraphQL explorer, we now want to try and learn how it's done. Our implementation will be much more simpler but it will eventually help us understand how to wrap every REST Api with GraphQL endpoint.
- Clone from tag #step-2
mkdir step-2 && cd step-2
git clone https://github.com/davidyaha/graphql-workshop.git ./
git checkout tags/step-2- Install all
npm i
(cd server && npm i)- Make sure you are running on node@^v6.0.0. If not run:
nvm install 6- Create a new file named schema.js. We would write our schema inside a single ES6 template string.
const typeDefs = `
... our schema goes here ....
`;- Every great schema starts with a
schemadeclaration. It will have two fields,queryof typeQueryandmutationof typeMutation.
schema {
query: Query
mutation: Mutation
}- Next we define our
Querytype. We will add only one fieldmeof typeUser, because this is the only thing the interests us at the moment.
type Query {
me: User
}- Now define our Mutation type. Add a field called
follow. This field will get a mandatory argument calleduserIdof the typeID. It will return the typeUseras well.
type Mutation {
follow(userId: ID!): User
}-
Note how we used the exclamation mark (
!) to define a mandatory type. -
All we have left is to define our
Usertype. A user will haveid,login,name,followerCountand a list offollowers.followerscan accept optionalskipandlimitarguments to control the returned items on the followers list. We will giveskipa default value of 0 andlimitdefault value of 10.
type User {
id: ID!
login: String!
name: String
followerCount: Int
followers(skip: Int = 0, limit: Int = 10): [User]
}- Now we use apollo to parse that schema and make an executable schema out of it.
- Import the
makeExecutableSchemafromgraphql-toolspackage.
const {makeExecutableSchema} = require('graphql-tools');- Call it with the
typeDefsstring we just defined.
const Schema = makeExecutableSchema({typeDefs});- And lastly, export the executable schema.
module.exports = {
Schema,
};- Create a new file named index.js. This of course, will be our server's entry point.
- Import
express,body-parserand ourgraphqlExpressmiddleware.
const express = require('express');
const bodyParser = require('body-parser');
const {graphqlExpress} = require('graphql-server-express');- Import our
Schemaobject.
const {Schema} = require('./schema');- Now we create our
expressapp, and add ourbodyParserandgraphqlExpressmiddleware on/graphqlpath.
const app = express();
app.use('/graphql', bodyParser.json(), graphqlExpress({
schema: Schema,
}));- Lastly we need to tell
expressto start listening on some port.
app.listen(3001);-
Now we can go ahead and start the app by typing
npm startin our project directory. -
If it works without errors, close it using Ctrl + C and run
npm run watchto make our server restart when we change our files. -
So now we have a GrpahQL endpoint but we don't know how to explore the API. To do just that, we will add
graphiqlExpressmiddleware.
// Import graphiqlExpress from the same package
const {graphqlExpress, graphiqlExpress} = require('graphql-server-express');
// ... Just before calling the listen method
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql', // This will tell the graphiql interface where to run queries
}));-
Now open your browser on http://localhost:3001/graphiql and start exploring the schema we've just wrote!
-
Try to run the following
mequery.
query {
me {
login
name
followerCount
followers(limit: 5) {
login
name
}
}
}- You will get the following response:
{
"data": {
"me": null
}
}Our schema does not know how to resolve me or any other field for that matter.
We need to provide it with proper resolvers but until we get to do that,
there is one more very cool feature to apollo which is generating mock resolvers.
- Import from
graphql-toolsthe functionaddMockFunctionsToSchemaon our schema.js file.
const {makeExecutableSchema, addMockFunctionsToSchema} = require('graphql-tools');- Now call this function right before the export of our
Schemaobject.
addMockFunctionsToSchema({schema: Schema});-
Go back to our grahpiql tool on http://localhost:3001/graphiql and run the
mequery again. -
This time you will receive a response of the following structure:
{
"data": {
"me": {
"login": "Hello World",
"name": "Hello World",
"followerCount": -96,
"followers": [
{
"login": "Hello World",
"name": "Hello World"
},
{
"login": "Hello World",
"name": "Hello World"
}
]
}
}
}So we can see that our schema now knows how to return data. We can also see that the data is quite genric and sometimes doesn't make sense.
In order to change that, we use a package called casual to tweak some of the mocked data.
- Import
casualto our schema.js file and create amocksobject. Pass thatmocksobject to theaddMockFunctionsToSchemafunction.
const casual = require('casual');
// ....
const mocks = {};
addMockFunctionsToSchema({schema: Schema, mocks});- First let's make
followerCountto be a positive number.
const mocks = {
User: () => ({
followerCount: () => casual.integer(0), // start from 0
}),
};- Now let's get
nameandloginfields return fitting strings.
const mocks = {
User: () => ({
login: () => casual.username,
name: () => casual.name,
followerCount: () => casual.integer(0),
}),
};- Lastly, we will use
MockedListfromgraphql-tools, to make followers return a list of users that it's length corresponds to the givenlimitargument.
const {makeExecutableSchema, addMockFunctionsToSchema, MockList} = require('graphql-tools');
// ....
const mocks = {
User: () => ({
login: () => casual.username,
name: () => casual.name,
followerCount: () => casual.integer(0),
followers: (_, args) => new MockList(args.limit),
}),
};- Now run the
mequery again. You should get a more sensible result.
{
"data": {
"follow": {
"login": "Haleigh.Kutch",
"name": "Dr. Marlen Smith",
"followerCount": 182,
"followers": [
{
"login": "Solon_Hirthe",
"name": "Mrs. Jamie Roberts"
},
{
"login": "Wyman_Arnold",
"name": "Dr. Alisa Price"
},
{
"login": "Mabelle_Donnelly",
"name": "Ms. Monica Bosco"
},
{
"login": "Wiegand_Keira",
"name": "Miss Emilia McDermott"
},
{
"login": "Duncan.Hickle",
"name": "Mrs. Jacinto Reinger"
}
]
}
}
}