Bloggin'

GraphQL Yoga - The Basics

GraphQL Yoga - The Basics

Tuesday, April 30th 2019

We are going to be using GraphQL Yoga because it provides the most advanced feature set and has a very easy setup process.

import { GraphQLServer } from "graphql-yoga";

That's all we have to do to get our server running. Next, we need our Type Definitions and Resolvers.

Type Definitions (schema) Defines all of the operations in our API and what our custom data types looks like.

// Type definitions (schema)const typeDefs = ` type Query { hello: String! }`

String is the type that gets returned when hello is called. The exclamation mark means that we will always get a string back, as opposed to getting null back.

Resolvers

Set of functions for each of the operations that can be performed on our API.

// Resolvers

const resolvers = { Query: { hello() { return 'hello' } }}

Hello can be called with arguments, this query returns a static string 'hello'.

const server = new GraphQlServer({ typeDefs: typeDefs, resolvers: resolvers}) server.start(() => { console.log('The server is up.')})

To get the server set up, we just call server.start with our typeDefinitions and Resolvers as an object. If you visit localhost:4000 you can call your hello function from the GraphQL playground. Adding more API calls looks like this.

import { GraphQLServer } from "graphql-yoga"; // Type definitions

(schema)const typeDefs = ` type Query { hello: String! name: String! }` // Resolversconst resolvers = { Query: { hello() { return 'hello' }, name() { return 'Nuwan ' } }} const server = new GraphQLServer({ typeDefs: typeDefs, resolvers: resolvers}) server.start(() => { console.log('The server is up.')})

Scalar Types5 Scalar Types in GraphQL -> String, Boolean, Int, Float, ID

// Type definitions (schema)const typeDefs = ` type Query { id: ID! name: String! age: Int! employed: Boolean! gpa: Float }` // Resolvers

const resolvers = { Query: { id() { return 'abc123' }, name() { return 'Nugoo' }, age() { return 26 }, employed() { return false }, gpa() { return null } }}

Custom TypesCustom types must start with an uppercase and looks exactly like a Query definition.

// Type definitions (schema)

const typeDefs = ` type Query { me: User! } type User { id: ID! name: String! email: String! age: Int }`// Resolversconst resolvers = { Query: { me() { return { id: '123098', name: 'Mike', email: 'mike@email.com' } } }}

Passing in Arguments to the Query

The Type Definition is quite straightforward. You must define the arguments that can be passed in, separated by commas.

const typeDefs = ` type Query { greeting(name: String, position: String): String! }

The query is a little different.

query {    greeting(name: "Jess", position: "Teacher")}

The resolver looks very different. All resolvers can accept four arguments.Parent - useful for relational data -> Figuring out users' posts.Args - operation arguments supplied (this is what we'll be starting with).Context - contextual data like user logged in ID.Info - great information about actual operations sent to the server.

const resolvers = { Query: { greeting(parent, args, ctx, info) { if (args.name && args.position) { return `Hello ${args.name}!. You are my favorite ${args.position}.` } else { return 'Hello!' } } }}

Another example taking in two floats and returning the sum of the two:

type Query { add(a: Float!, b:Float!): Float! } add(parent, args, ctx, info) { return args.a + args.b }

Working with Arrays

Sending arrays to and from the server. First we'll start with scalar arrays, then we'll send arrays of custom types like Posts.

type Query { grades: [Int!]! // Can return null but if it returns an array, each item must be an Integer, it cannot be null }

So what about passing in Arrays as arguments to the server?

type Query { add(numbers: [Float!]!): Float! // Always pass in an array with floats } // Resolver

add(parent, args, ctx, info) { if (args.numbers.length === 0) { return 0 } return args.numbers.reduce((accumulator, currentValue) => { return accumulator + currentValue }) }

Now we've sent arrays in both directions.

Working with Custom Arrays

The users query returns an array every time (not null), and each array item must be a User type.

// Type definitions (schema)

const typeDefs = ` type Query { users: [User!]! } type User { id: ID! name: String! email: String! age: Int }

In the GraphQL query, we can run something like this:

query {    users{ id name email }} //Returns{ "data": { "users": [ { "id": "1", "name": "Nuwan", "email": "nuwan@example.com" }, { "id": "2", "name": "Sarah", "email": "sarah@example.com" }, { "id": "3", "name": "Mike", "email": "mike@example.com" } ] }

Filtering the Array Data

Now lets look at options that from the client side such as filtering the array of data that is returned. Notice the optional String query.

const typeDefs = ` type Query { users(query: String): [User!]! // Array definition and define individual elements (user) me: User! post: Post! } type User { id: ID! name: String! email: String! age: Int } type Post { id: ID! title: String! body: String! published: Boolean! }` // Resolversconst resolvers = { Query: { users(parent, args, ctx, info) { if (!args.query) { return users; // returns all users if no query is provided } return users.filter(user => { return user.name.toLowerCase().includes(args.query.toLowerCase()); // filters through the array items and checks if the string provided matches the user's name }); }, me() { return { id: '123098', name: 'Mike', email: 'mike@email.com' } }, post() { return { id: '092', title: 'GraphQL 101', body: '', published: false } } }}