rodriggj / graphql_by_example

GraphQL by example coursework
0 stars 0 forks source link

GraphQL

OBJECTIVES The objective of this readme file is to provide a high-level starter GraphQL discovery. The readme file will provide an explanation of:

  1. What is GraphQL
  2. How does it compare to alterantive API implmentation options -- specifically REST?
  3. What are the benefits of GraphQL?
  4. What is the process of getting a GraphQL API created?

What is GraphQL

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.

Why do you need GraphQL vs some alternative?

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:

  1. ensure to use RESTful conventions (noun/verb convention, use of query params, etc)
  2. ensure the solution supports developer consumption (well documented, discoverable, versioned, etc)
  3. ensure the solution maximizes operational ownership as the API producer (easy to maintain, efficient change management, sufficient metadata, etc)

Observations...

  1. Further the nesting goes ... the harder the convention is to follow ...

  1. Quantity of requests increases ... IMPACT: COST

  1. Customized endpoints ... IMPACT: Consumption / Documentation / Versioning / Maintanence

  2. Break RESTful conventions ... IMPACT: Why are you using REST?

  3. Overfetching data ... IMPACT: Network, Security, Compliance, ETL, etc.

So why do you need GraphQL

  1. 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.

  2. 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.

  3. 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.

  4. Powerful Developer Tools

    Build/use tools like Graphiql to provide insight into the success of queries before implmented into code.

  5. 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.

  6. 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 an Happy-Path GraphQL API

Creating a GraphQL API is relatively straight-foward and follows a repeatable process.

  1. Ensure that your environment has any needed dependencies installed.
  2. Build your API Schema
  3. Build your Resolver Functions
  4. Configure the Server
  5. Execute Queries

1. Account for Dependencies

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...

  1. Here we will create a project called 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
  1. Initiallize the following package dependencies with the following command:
npm install apollo-server graphql --save
  1. Now the the directory structure is in place, a package.json file is created to manage dependencies, and our dependencies have been installed -- we will start our GraphQL process by defining a Schema. To do this, create a new file called server.js and open this file in your code editor.
code server.js
  1. In the server.js file, we will create a variable called 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.

  1. The schema that we just defined needs to be parsed by the GraphQL server. The GraphQL server doesn't intrinsically know how to do this, so we need to import function gql from the 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 a DocumentNode. You can validate this by adding a console.log(typeDefs), and running the server.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.

3. Defining a Resolver

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.

  1. Within the server.js file, lets create our resolver function. Start by defining an Object literal called resolvers
const resolvers = {

}
  1. This 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.

4. Creating a Server

  1. Now that we've defined a schema and created an implementation method, our resolver we now need some service to handle running this code, our server.

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')
  1. We now need to utilize this ApolloServer class to create our server. First, lets assign our new Class to a variable, server. Our ApolloServer class utilizes a constructor, where in the parameters it is expecting an Object, where you can pass configuration properties. Here we will pass our Object, our typeDefs & our resolvers.
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 })
  1. Finally, we can call the listen() method on our server variable, and explicitly set the listening port to 9000. The listen method will return a Promise, so we can chain onto our Promise a .then() function which will take serverInfo as an arguement and log a message to the console, via a callback method.
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}`))
  1. Finally we can run our server using Nodejs on our server.js file. In the console you should see the url that will allow you to access your content in a browser. If you go to this url, you should see a view similiar to the one presented below.
node server.js

5. Execute Queries on your GraphQL API