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.
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true
})
);
app.listen(4201, err => {
if (err) {
return console.log(err);
}
return console.log('My Express App listening on port 4201');
});
The graphiql
provides you with a nice user interface on which you can test queries to the server.
To start the server run:
node index.js
Then open your browser and navigate to http://localhost:4201/graphql
. You will see an interactive test-bed for GraphQL queries.
Add Your Angular 7 Client
What is a web application without a client? In this section, I will walk you through the implementation of a single page application using Angular 7. To start off, create a new Angular application. If you haven’t already done so, install the newest version of the angular command line tool on your system.
npm install -g @angular/cli@7.1.0
You might have to run this command using sudo
, depending on your operating system. Now you can create a new angular application. In a new directory run:
ng new AngularGraphQLClient
This will create a new directory and install all the necessary packages for an Angular application into it. You will be prompted with two questions. Answer yes to include routing in the application. The style sheets I will be using in this tutorial will be simple CSS.
The application will contain three component associated with the main app
module. You can generate them by navigating into the directory that was just created and running the following three commands.
ng generate component Home
ng generate component Players
ng generate component Ranking
This will create three directories in src/app
and add the component .ts
code file, the .html
template and the .css
stylesheet for each component. In order to use GraphQL in Angular, I will be making use of the Apollo library. Setting up Apollo in angular is a simple command.
ng add apollo-angular
This command will install a number of Node modules. It will also create an Angular module in a file graphql.module.ts
in the /src/app/
folder and import it into the main app
module. Inside this file, you will see the line
const uri = ''; // <-- add the URL of the GraphQL server here
Change it to
const uri = 'http://localhost:4201/graphql';
This specifies the URI at which the GraphQL service can be found.
Note: If you want to generate any components after installing Apollo Angular, you will need to specify the module to which the component belongs. So generating the Home component above would change to
ng generate component Home --module app
I will be using the Forms Module in order to bind values to input elements in the HTML. Open up src/app/app.module.ts
and add
import { FormsModule } from '@angular/forms';
to the top of the file. Then add FormsModule
to the imports
array in the @NgModule
declaration.
Create Your Layout and Routing in Angular
Now open src/index.html
. This file contains the HTML container in which your Angular app will live. You will need some external CSS and JavaScript resources to spruce up the design of your application. Add the following lines inside the <head>
tag. This will include some minimal Material Design styling.
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<link
rel="stylesheet"
href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css"
/>
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
Next, open src/app.component.html
and replace the content with the following.
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<div class="mdl-layout__header mdl-layout__header--waterfall">
<div class="mdl-layout__header-row">
<span class="mdl-layout-title" routerLink="/">
<i class="material-icons">home</i> Angular with GraphQL
</span>
<!-- Add spacer, to align navigation to the right in desktop -->
<div class="mdl-layout-spacer"></div>
<!-- Navigation -->
<ul class="mdl-navigation">
<li class="mdl-navigation__link" routerLink="/">Home</li>
<li class="mdl-navigation__link" routerLink="/players">Players</li>
<li class="mdl-navigation__link" routerLink="/ranking">Rankings</li>
<li
class="mdl-navigation__link"
*ngIf="!isAuthenticated"
(click)="login()"
>
Login
</li>
<li
class="mdl-navigation__link"
*ngIf="isAuthenticated"
(click)="logout()"
>
Logout
</li>
</ul>
</div>
</div>
<div class="mdl-layout__drawer">
<ul class="mdl-navigation">
<li class="mdl-navigation__link" routerLink="/">Home</li>
<li class="mdl-navigation__link" routerLink="/players">Players</li>
<li class="mdl-navigation__link" routerLink="/ranking">Rankings</li>
<li
class="mdl-navigation__link"
*ngIf="!isAuthenticated"
(click)="login()"
>
Login
</li>
<li
class="mdl-navigation__link"
*ngIf="isAuthenticated"
(click)="logout()"
>
Logout
</li>
</ul>
</div>
<div class="mdl-layout__content content"><router-outlet></router-outlet></div>
</div>
This creates a basic layout with a top-bar and a few links which will load different components into the router-outlet
. In order to load make the routes available to the application you should modify the app-routing.module.ts
. At the top you will see the declaration of the routes
array.
const routes: Routes = [];
Replace this line with the following.
import { PlayersComponent } from './players/players.component';
import { HomeComponent } from './home/home.component';
import { RankingComponent } from './ranking/ranking.component';
const routes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'players',
component: PlayersComponent
},
{
path: 'ranking',
component: RankingComponent
}
];
Now the router knows which components to place into the outlet when a specific route is selected. At this point, your application already shows the three pages and the links in the top-bar will load them into the content area of your application.
Finally, let’s give the page some styling. In app.component.css
paste the following content.
.content {
padding: 1rem;
display: flex;
justify-content: center;
}
Add Components in Angular
You are ready to implement the components. Let’s start with the component that lets the user page through all the tennis players in the database. Copy the following into the file src/app/players/players.component.ts
. I will walk you through the meaning of each part of this file next.
import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';
const PLAYERS_QUERY = gql`
query players($offset: Int) {
players(offset: $offset, limit: 10) {
id
first_name
last_name
hand
birthday
country
}
}
`;
@Component({
selector: 'app-players',
templateUrl: './players.component.html',
styleUrls: ['./players.component.css']
})
export class PlayersComponent implements OnInit {
page = 1;
players: any[] = [];
private query: QueryRef<any>;
constructor(private apollo: Apollo) {}
ngOnInit() {
this.query = this.apollo.watchQuery({
query: PLAYERS_QUERY,
variables: { offset: 10 * this.page }
});
this.query.valueChanges.subscribe(result => {
this.players = result.data && result.data.players;
});
}
update() {
this.query.refetch({ offset: 10 * this.page });
}
nextPage() {
this.page++;
this.update();
}
prevPage() {
if (this.page > 0) this.page--;
this.update();
}
}
The top three lines of this file contain the imports needed to drive the component.
import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';
Apart from the core Angular imports, this makes available Apollo
and QueryRef
from apollo-angular
, and gql
from graphql-tag
. The latter of these to is used straight away to create a GraphQL query.
const PLAYERS_QUERY = gql`
query players($offset: Int) {
players(offset: $offset, limit: 10) {
id
first_name
last_name
hand
birthday
country
}
}
`;
The gql
tag takes the template string and turns it into a query object. The query defined here will ask the server to return an array of players, populated with all the player’s fields. The limit
parameter will cause the server to return at most 10 records. The offset parameter can be specified as a parameter to the query. This will allow paging through the players.
@Component({
selector: 'app-players',
templateUrl: './players.component.html',
styleUrls: ['./players.component.css']
})
export class PlayersComponent implements OnInit {
page = 0;
players: any[] = [];
private query: QueryRef<any>;
constructor(private apollo: Apollo) {}
}
The properties of PlayersComponent
specify the state of the component. The property page
stores the current page in the list of players. players
will contain the array of players that will be displayed in a table. There is also a query
variable which stores the query. This is needed to be able to re-fetch data, whenever the user navigates to another page. The constructor will inject the apollo
property so that you can access the GraphQL interface.
ngOnInit() {
this.query = this.apollo
.watchQuery({
query: PLAYERS_QUERY,
variables: {offset : 10*this.page}
});
this.query.valueChanges.subscribe(result => {
this.players = result.data && result.data.players;
});
}
During the initialization phase of the component’s life-cycle the ngOnInit
method will be called. This is the place where the Players Component will initiate the loading of the data. This is achieved by this.apollo.watchQuery
. By passing the PLAYERS_QUERY
together with a value for the offset
parameter. You can now subscribe to any data changes using valueChanges.subscribe
. This method takes a callback which will set the players
array with the data obtained from the server.
update() {
this.query.refetch({offset : 10*this.page});
}
nextPage() {
this.page++;
this.update();
}
prevPage() {
if (this.page>0) this.page--;
this.update();
}
To round things off, nextPage
and prevPage
will increment or decrement the page
property. By calling refetch
on query
with the new parameters a server request is issued. When the data is received the subscription callback will be called automatically.
The HTML template that goes with this component is stored in players.component.html
. Paste the following content into it.
<table
class="mdl-data-table mdl-js-data-table mdl-data-table--selectable mdl-shadow--2dp"
>
<tr>
<th class="mdl-data-table__cell--non-numeric">First Name</th>
<th class="mdl-data-table__cell--non-numeric">Last Name</th>
<th class="mdl-data-table__cell--non-numeric">Hand</th>
<th>Birthday</th>
<th class="mdl-data-table__cell--non-numeric">Country</th>
</tr>
<tr *ngFor="let player of players">
<td class="mdl-data-table__cell--non-numeric">{{player.first_name}}</td>
<td class="mdl-data-table__cell--non-numeric">{{player.last_name}}</td>
<td class="mdl-data-table__cell--non-numeric">{{player.hand}}</td>
<td>{{player.birthday}}</td>
<td class="mdl-data-table__cell--non-numeric">{{player.country}}</td>
</tr>
</table>
<div class="paging">
<button
class="mdl-button mdl-js-button mdl-button--colored"
(click)="prevPage()"
>
<i class="material-icons">arrow_back</i>
</button>
Page {{page+1}}
<button
class="mdl-button mdl-js-button mdl-button--colored"
(click)="nextPage()"
>
<i class="material-icons">arrow_forward</i>
</button>
</div>
This displays a list of players in a table. Below the table, I have added paging links.
The Ranking component pretty much follows the same pattern. The src/app/ranking.component.ts
looks like this.
import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';
const RANKINGS_QUERY = gql`
query rankings($rank: Int!) {
rankings(rank: $rank) {
date
rank
points
player {
first_name
last_name
}
}
}
`;
@Component({
selector: 'app-ranking',
templateUrl: './ranking.component.html',
styleUrls: ['./ranking.component.css']
})
export class RankingComponent implements OnInit {
rank: number = 1;
rankings: any[];
private query: QueryRef<any>;
constructor(private apollo: Apollo) {}
ngOnInit() {
this.query = this.apollo.watchQuery({
query: RANKINGS_QUERY,
variables: { rank: Math.round(this.rank) }
});
this.query.valueChanges.subscribe(result => {
this.rankings = result.data && result.data.rankings;
});
}
update() {
return this.query.refetch({ rank: Math.round(this.rank) });
}
}
As you can see, most of the code is very similar to that in players.component.ts
. The definition of RANKINGS_QUERY
queries the players over time which held a particular rank. Note that the query is only requesting the first_name
and last_name
of the player. This means that the server will not be sending any additional player data back which the client hasn’t asked for.
The template for the rankings component contains a text field and button in which the user can enter a rank and reload the page. Below that is the table of players. This is the content of ranking.component.html
.
<h1>Rankings</h1>
<input class="mdl-textfield__input" type="text" id="rank" [(ngModel)]="rank" />
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
(click)="update()"
>
Update
</button>
<table
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp"
*ngIf="rankings"
>
<tr>
<th>Rank</th>
<th>Date</th>
<th>Points</th>
<th class="mdl-data-table__cell--non-numeric">First Name</th>
<th class="mdl-data-table__cell--non-numeric">Last Name</th>
</tr>
<tr *ngFor="let ranking of rankings">
<td>{{ranking.rank}}</td>
<td>{{ranking.date}}</td>
<td>{{ranking.points}}</td>
<td class="mdl-data-table__cell--non-numeric">
{{ranking.player.first_name}}
</td>
<td class="mdl-data-table__cell--non-numeric">
{{ranking.player.last_name}}
</td>
</tr>
</table>
To start the client, run:
ng serve
Make sure that the server is also running, so that the client can successfully request data.
Add Access Control to your Express + Angular GraphQL App
One of the most important features of every web application is user authentication and access control. In this section, I will guide you through the steps needed to add authentication to both the server and the client part of your Angular application. This is often the most daunting part of writing an application. Using Okta greatly simplifies this task and makes secure authentication available to every developer. If you haven’t already done so, create a developer account with Okta. Visit https://developer.okta.com/ and select Create Free Account.
Fill out the form and register yourself. After your registration is complete you can see your developer dashboard.
From the top menu of your dashboard, select Applications and then add an application by clicking the green Add Application button.
You will see a choice of different types of application. You are registering a Single Page App. On the next page, you will see the settings for your application. Here the port number is pre-filled to 8080. Angular uses the port 4200 by default. So you will have to change the port number to 4200.
Once completed, you will be given a ClientId. You will need this in both your client and server applications. You will also need your Okta developer domain. This is the URL that you see at the top of the page when you are logged in to your Okta developer dashboard.
Secure Your Angular Client
In order to use Okta authentication with the Angular client, you will have to install the okta-angular
library. In the base directory of your client application run the following command.
npm install @okta/okta-angular@1.0.7 apollo-link-context@1.0.10 --save
Now open src/app/app.module.ts
. At the top of the file add the import statement.
import { OktaAuthModule } from '@okta/okta-angular';
Now add the module to the list of imports
of the app
module.
OktaAuthModule.initAuth({
issuer: 'https://{yourOktaDomain}/oauth2/default',
redirectUri: 'http://localhost:4200/implicit/callback',
clientId: '{yourClientId}'
});
You will need to replace yourOktaDomain
development domain you see in your browser when you navigate to your Okta dashboard. Also, replace yourClientId
with the client ID that you obtained when registering your application. Now you are ready to use Okta authentication throughout your application. Next, you will implement logging in and logging out from the application. Open app.component.ts
and import OktaAuthService
from okta-angular
. Paste the following code into the file.
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { OktaAuthService } from '@okta/okta-angular';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
public title = 'My Angular App';
public isAuthenticated: boolean;
constructor(public oktaAuth: OktaAuthService) {
this.oktaAuth.$authenticationState.subscribe(
(isAuthenticated: boolean) => (this.isAuthenticated = isAuthenticated)
);
}
async ngOnInit() {
this.isAuthenticated = await this.oktaAuth.isAuthenticated();
}
login() {
this.oktaAuth.loginRedirect();
}
logout() {
this.oktaAuth.logout('/');
}
}
The OktaAuthService
service is injected through the constructor. It is then used to set the isAuthenticated
flag. The subscribe
method subscribes a callback function that is triggered whenever the log-in status changes. The isAuthenticated
is initialized during the ngOnInit
phase to reflect the log-in status when the application is first loaded. login
and logout
handle the process of logging in and out. In order to make authentication work, okta-angular
uses a special route called implicit/callback
. In the file app-routing.module.ts
add the following import.
import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';
The implicit/callback
route is now linked to the OktaCallbackComponent
by adding the following to the routes
array.
{
path: 'implicit/callback',
component: OktaCallbackComponent
}
This is all that is needed to log in and out. But the application is not protected yet. For any route that you want to access-control, you will have to add an Authorization Guard. Luckily this is easy. In each of the routes that you want to protect add the canActivate
property. Add the following to the players
and the ranking
routes.
canActivate: [OktaAuthGuard];
That’s all there is to it. Now, when a user tries to access the Players view, he will be redirected to the Okta login page. Once logged on, the user will be redirected back to the Products view.
You have secured the client pages, but before you can move on to securing the back end let’s take a moment and think about how the server will authenticate the user. Okta uses a bearer token that identifies the user. The bearer token must be sent to the server with every request. To achieve this, the client has to make sure that the bearer token is added to the HTTP headers. All you need to do is add a few lines of code to the graphql.module.ts
. At the top of the file import the following.
import { OktaAuthService } from '@okta/okta-angular';
import { setContext } from 'apollo-link-context';
Then modify the createApollo
function to add the bearer token.
export function createApollo(httpLink: HttpLink, oktaAuth: OktaAuthService) {
const http = httpLink.create({ uri });
const auth = setContext((_, { headers }) => {
return oktaAuth.getAccessToken().then(token => {
return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
});
});
return {
link: auth.concat(http),
cache: new InMemoryCache()
};
}
Secure Your Express GraphQL Server
Securing the server is done by adding an express middleware function to the server application. To do this you will need a few additional libraries. Change into your server directory and run the command
npm install @okta/jwt-verifier@0.0.13 body-parser@1.18.3 express-bearer-token@2.2.0
Next, let’s create that function in a separate file called auth.js
in the root folder of the server.
const OktaJwtVerifier = require('@okta/jwt-verifier');
const oktaJwtVerifier = new OktaJwtVerifier({
clientId: '{yourClientId}',
issuer: 'https://{yourOktaDomain}/oauth2/default'
});
module.exports = async function oktaAuth(req, res, next) {
try {
const token = req.token;
if (!token) {
return res.status(401).send('Not Authorized');
}
const jwt = await oktaJwtVerifier.verifyAccessToken(token);
req.user = {
uid: jwt.claims.uid,
email: jwt.claims.sub
};
next();
} catch (err) {
return res.status(401).send(err.message);
}
};
Again, you have to replace yourOktaDomain
and yourClientId
with the development domain and the client id. The purpose of this function is simple. It checks the presence of a token field in the request. If present, oktaJwtVerifier
checks the validity of the token. If everything is in order, calling next()
signals success. Otherwise, a 401
error is returned. All you have to do now is to make sure that the function is used in the application. Add the following require statements to the index.js
file.
const bodyParser = require('body-parser');
const bearerToken = require('express-bearer-token');
const oktaAuth = require('./auth');
Then modify the declaration of app
in the following way.
const app = express()
.use(cors())
.use(bodyParser.json())
.use(bearerToken())
.use(oktaAuth);
The bearerToken
middleware will look for a bearer token and add it to the request for oktaAuth
to find it. With this simple addition, your server will only allow requests that provide a valid authentication.
Learn More about Express, Angular, and GraphQL
In this simple tutorial, I have shown you how to create a single page application with Angular using GraphQL. User authentication was implemented with minimal effort using the Okta service.
I have not talked about how to use GraphQL to add or modify the data in the database. In GraphQL language this is called mutations. To learn more about mutations using Apollo, check out the manual pages.
The complete code for this project can be found at https://github.com/oktadeveloper/okta-graphql-angular-example.
If you’re interested in learning more about Express, Angular, GraphQL, or secure user management, I’d encourage you to check out any of these resources:
- Build a Simple API Service with Express and GraphQL
- Build a Secure API with Spring Boot and GraphQL
- Build and Understand Express Middleware
- Angular 6: What’s New and Why Upgrade?
- Build a Basic CRUD App with Angular and Node
Like what you learned today? We’d love to have you follow us on Twitter and subscribe to our YouTube channel!