Tuesday, January 15, 2019

Build a Simple Web App with Express, Angular, and GraphQL

This article was originally published on the Okta developer blog. Thank you for supporting the partners who make SitePoint possible.

During the past 10 years or so, the concept of REST APIs for web services has become the bread and butter for most web developers. Recently a new concept has emerged, GraphQL. GraphQL is a query language that was invented by Facebook and released to the public in 2015. During the last three years, it has created quite a stir. Some regard it as a new revolutionary way of creating web APIs. The main difference between traditional REST and GraphQL is the way queries are sent to the server. In REST APIs you will have a different endpoint for each type of resource and the response to the request is determined by the server. Using GraphQL you will typically have only a single endpoint, and the client can explicitly state which data should be returned. A single request in GraphQL can contain multiple queries to the underlying model.

In this tutorial, I will be showing you how to develop a simple GraphQL web application. The server will run using Node and Express and the client will be based on Angular 7. You will see how easy it is to prepare the server for responding to different queries. This removes much of the work needed compared to implementing REST-style APIs. To provide an example I will create a service in which users can browse through the ATP Tennis players and rankings.

Build Your Express Server using GraphQL

I will start by implementing the server. I will assume that you have Node installed on your system and that the npm command is available. I will also be using SQLite to store the data. In order to create the database tables and import the data, I will be making use of the sqlite3 command line tool. If you haven’t got sqlite3 installed, head over to the SQLite download page and install the package that contains the command-line shell.

To start off, create a directory that will contain the server code. I have simply called mine server/. Inside the directory run

npm init -y

Next, you will have to initialize the project with all the packages that we will be needing for the basic server.

npm install --save express@4.16.4 cors@2.8.4 express-graphql@0.6.12 graphql@14.0.2 sqlite3@4.0.2

Import Data to Your Express Server

Next, let’s create the database tables and import some data into them. I will be making use of the freely available ATP Tennis Rankings by Jeff Sackmann. In some directory on your system clone the GitHub repository.

git clone https://github.com/JeffSackmann/tennis_atp.git

In this tutorial, I will only be using two of the files from this repository, atp_players.csv and atp_rankings_current.csv. In your server/ directory start SQLite.

sqlite3 tennis.db

This will create a file tennis.db that will contain the data and will give you a command line prompt in which you can type SQL commands. Let’s create our database tables. Paste and run the following in the SQLite3 shell.

CREATE TABLE players(
  "id" INTEGER,
  "first_name" TEXT,
  "last_name" TEXT,
  "hand" TEXT,
  "birthday" INTEGER,
  "country" TEXT
);

CREATE TABLE rankings(
  "date" INTEGER,
  "rank" INTEGER,
  "player" INTEGER,
  "points" INTEGER
);

SQLite allows you to quickly import CSV data into your tables. Simply run the following command in the SQLite3 shell.

.mode csv
.import {PATH_TO_TENNIS_DATA}/atp_players.csv players
.import {PATH_TO_TENNIS_DATA}/atp_rankings_current.csv rankings

In the above, replace {PATH_TO_TENNIS_DATA} with the path in which you have downloaded the tennis data repository. You have now created a database that contains all ATP ranked tennis players ever, and the rankings of all active players during the current year. You are ready to leave SQLite3.

.quit

Implement the Express Server

Let’s now implement the server. Open up a new file index.js, the main entry point of your server application. Start with the Express and CORS basics.

const express = require('express');
const cors = require('cors');

const app = express().use(cors());

Now import SQLite and open up the tennis database in tennis.db.

const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('tennis.db');

This creates a variable db on which you can issue SQL queries and obtain results.

Now you are ready to dive into the magic of GraphQL. Add the following code to your index.js file.

const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    players(offset:Int = 0, limit:Int = 10): [Player]
    player(id:ID!): Player
    rankings(rank:Int!): [Ranking]
  }

  type Player {
    id: ID
    first_name: String
    last_name: String
    hand: String
    birthday: Int
    country: String
  }

  type Ranking {
    date: Int
    rank: Int
    player: Player
    points: Int
  }
`);

The first two lines import graphqlHTTP and buildSchema. The function graphqlHTTP plugs into Express and is able to understand and respond to GraphQL requests. The buildSchema is used to create a GraphQL schema from a string. Let’s look at the schema definition in a little more detail.

The two types Player and Ranking reflect the contents of the database tables. These will be used as the return types to the GraphQL queries. If you look closely, you can see that the definition of Ranking contains a player field that has the Player type. At this point, the database only has an INTEGER that refers to a row in the players table. The GraphQL data structure should replace this integer with the player it refers to.

The type Query defines the queries a client is allowed to make. In this example, there are three queries. players returns an array of Player structures. The list can be restricted by an offset and a limit. This will allow paging through the table of players. The player query returns a single player by its ID. The rankings query will return an array of Ranking objects for a given player rank.

To make your life a little easier, create a utility function that issues an SQL query and returns a Promise that resolves when the query returns. This is helpful because the sqlite3 interface is based on callbacks but GraphQL works better with Promises. In index.js add the following function.

function query(sql, single) {
  return new Promise((resolve, reject) => {
    var callback = (err, result) => {
      if (err) {
        return reject(err);
      }
      resolve(result);
    };

    if (single) db.get(sql, callback);
    else db.all(sql, callback);
  });
}

Now it’s time to implement the database queries that power the GraphQL queries. GraphQL uses something called rootValue to define the functions corresponding to the GraphQL queries.

const root = {
  players: args => {
    return query(
      `SELECT * FROM players LIMIT ${args.offset}, ${args.limit}`,
      false
    );
  },
  player: args => {
    return query(`SELECT * FROM players WHERE id='${args.id}'`, true);
  },
  rankings: args => {
    return query(
      `SELECT r.date, r.rank, r.points,
              p.id, p.first_name, p.last_name, p.hand, p.birthday, p.country
      FROM players AS p
      LEFT JOIN rankings AS r
      ON p.id=r.player
      WHERE r.rank=${args.rank}`,
      false
    ).then(rows =>
      rows.map(result => {
        return {
          date: result.date,
          points: result.points,
          rank: result.rank,
          player: {
            id: result.id,
            first_name: result.first_name,
            last_name: result.last_name,
            hand: result.hand,
            birthday: result.birthday,
            country: result.country
          }
        };
      })
    );
  }
};

The first two queries are pretty straightforward. They consist of simple SELECT statements. The result is passed straight back. The rankings query is a little more complicated because a LEFT JOIN statement is needed to combine the two database tables. Afterward, the result is cast into the correct data structure for the GraphQL query. Note in all these queries how args contains the arguments passed in from the client. You do not need to worry in any way about checking missing values, assigning defaults, or checking the correct type. This is all done for you by the GraphQL server.

All that is left to do is create a route and link the graphqlHTTP function into it.

The post Build a Simple Web App with Express, Angular, and GraphQL appeared first on SitePoint.


by Holger Schmitz via SitePoint

No comments:

Post a Comment