OBJECTIVES The objective of this readme file is to provide a high-level starter GraphQL discovery. The readme file will provide an explanation of:
- What is GraphQL
- How does it compare to alterantive API implmentation options -- specifically REST?
- What are the benefits of GraphQL?
- What is the process of getting a GraphQL API created?
Graphql is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of your data in your API, gives the client the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables the power of developer tools.
RESTful Routing Given a collection of records on a server, there should be a uniform URL & HTTP request method used to utilize that collection of records.
Single RESTful resource
Nested RESTful resources
So what's the problem? The complexity of mapping/rendering relational data is increasing -
client-side JOINs
Consider the following example...
Challenge... Use the RESTful convention, and write a an endpoint that will fetch the data needed to mimic the
User / Friend / Company / Position
relationship required for my front-end. Requirements:
- ensure to use RESTful conventions (noun/verb convention, use of query params, etc)
- ensure the solution supports developer consumption (well documented, discoverable, versioned, etc)
- ensure the solution maximizes operational ownership as the API producer (easy to maintain, efficient change management, sufficient metadata, etc)
Observations...
IMPACT: COST
Customized endpoints ... IMPACT: Consumption / Documentation / Versioning / Maintanence
Break RESTful conventions ... IMPACT: Why are you using REST?
Overfetching data ... IMPACT: Network, Security, Compliance, ETL, etc.
So why do you need GraphQL
You can query for exactly what you need (no Overfetching).
The client (not the server) will dictate exactly what is needed in the API request.
Get many resources in a single request
GraphQL not only access the properties of a resource, but also the references between them, without referencing multiple Endpoints or Requests.
Describe what is possible with a Type System
GraphQL APIs are developee with a Schema first approach; organized in terms of types and fields not endpoints. Apps can use types to avoid writing manual parsing code.
Powerful Developer Tools
Build/use tools like
Graphiql
to provide insight into the success of queries before implmented into code.
Evolve your APIs witouth Versions
Add new fields and types to your GraphQL API without impacting existing queries. Aging fields can be deprecated and hidden from tools. By using a single evolving version of the GraphQL API, gives continous access to new features and maintainable code.
Bring your own code
GraphQL is a layer that sits on top of your existing code. All your data and business logic can stay the same within your application. You provide functions for each field in they type system and GraphQL executes the query. Language agnostic - GraphQL is an Open Specification with implmentations in several languages.
Creating a GraphQL API is relatively straight-foward and follows a repeatable process.
- Ensure that your environment has any needed dependencies installed.
- Build your API Schema
- Build your Resolver Functions
- Configure the Server
- Execute Queries
The examples from this point forward will be implemented using Nodejs. You can use whatever runtime you would like, you are not bound to Nodejs, but if you want to follow the examples, you'll need the following dependencies.
What you'll need to start...
Installation of NodeJs
Recognize and continue to remind yourself that there are only 2 actors from here forward: 1.
a Client
& 2.a GraphQL Server
. So as we define aSchema
realize that we are defining a preconditioned response from the server for any call coming from the client.
hello-world
. Open your code editor and in a terminal session initialize a NodeJs project with the following command: mkdir hello-world-server
cd hello-world-server
npm init
npm install apollo-server graphql --save
Schema
. To do this, create a new file called server.js
and open this file in your code editor. code server.js
typeDefs
, short for Type Definitions. Here we will use a special language called, GraphQL Schema Definition Language (SDL), to create our Type Definitions. It is much like defining classes, but instead of utilizing the class keyword, we are using type. So in this example we are defining a type of Query. Make note of the template literal back-tick marks used for the sake of creating a multi-line definition. Inside the curly brackets we define the fields that belong to this Type.
const typeDefs = `
type Query{
greeting: String
}
`;
This Code Reads Like ...: a client may issue a query to the GraphQL Server. When the GraphQL server receives this request it is to return a String, called "Greeting". This definition is known as a Schema.
apollo-server
module to do this. The gql is a tag function
which allows us to tag a template literal -- place the gql tag in front our our template literal quotes. const { gql } = require('apollo-server');
const typeDefs = gql`
type Query{
greeting: String
}
`;
Note that the
gql
tag, parses the template literal into the GraphQL, creating an Object known as aDocumentNode
. You can validate this by adding aconsole.log(typeDefs)
, and running theserver.js
file. Doing so will produce a result like the following in the console:
// Add a console.log() to the server.js file
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query{
greeting: String
}
`;
console.log(typeDefs)
# Run the server.js file
node server.js
{
kind: 'Document',
definitions: [
{
kind: 'ObjectTypeDefinition',
description: undefined,
name: [Object],
interfaces: [],
directives: [],
fields: [Array]
}
],
loc: { start: 0, end: 48 }
}
The above is an Abstract Syntax Tree of the GraphQL code we wrote. This shows that the template literal string has been parsed by the gql function and returned a ObjectTypeDefinition (aka Schema) that GraphQL Server will use to respond to query requests.
NOTE: You can now comment out or remove the
console.log()
from the schema definition.
So the schema will define What needs to be returned when a Query is made by a client to the GraphQL server, but what about the HOW (aka the implementation)? How will the query execute? This question is answered with a Resolver Function.
resolvers
const resolvers = {
}
resolvers
Object needs to match out Type Defiinition, therefore we need a property called Query. Query will be a nested Object, because it represents a Type. The Query Object will also have a property called greeting just like the field we declared in the typeDefs. Here greeting will be assigned to a function, that will be the implementation for any query request to the GraphQL server. In this case greeting returns a String literal.
const resolvers = {
Query: {
greeting: () => 'Hello from the GraphQL Server'
}
}
This Code Reads Like ...: Everytime a client makes a Query to the GraphQL server, a reference to the greeting schema will be implemented via a resolver function which returns a string.
To instantiate our server there are a few things we need to do. The first is to import, the ApolloServer class available to us from the apollo-server module we installed. We have already used the gql
tag function from this module, so we simply need to edit the existing require statement at the top of our file.
// Was
const { gql } = require('apollo-server')
// Update to ...
const { ApolloServer, gql } = require('apollo-server')
const server = new ApolloServer({ typeDefs: typeDefs, resolvers: resolvers })
// Because the property names match our value names, in javascript we can use short-hand and simply write the following:
const server = new ApolloServer({ typeDefs, resolvers })
server.listen({port:9000})
.then((serverInfo) => console.log(`Server Running at: ${serverInfo.url}`))
// Again using a short-hand notation we can use descructuring to simply pull back the url info
server.listen({port:9000})
.then(({url})=> console.log(`Server Running at: ${url}`))
node server.js