Skip to content

API Development

Muhammad Fawwaz Orabi edited this page Apr 7, 2018 · 25 revisions

The Hollowverse website is implemented as a client-side web app that consumes a publicly-accessible GraphQL API.

To get started with the development of the API, you should familiarize yourself with GraphQL. The official GraphQL website has an excellent, easy to understand introduction to GraphQL. You should at least have a grasp of the basic components of the GraphQL specification:

After getting familiar with the basics, head over to the GraphQL interactive playground for the Hollowverse API. Try performing a couple of queries and check the results. The playground will help you with automatic completion, comments and documentation.

Making changes to the API code

First off, make sure you have the following tools installed:

  • Node.js: runs JavaScript code.
  • yarn: package manager for dependencies used in the codebase.
  • Docker: we will use Docker to launch a development database.

To start work on the API code, fork hollowverse/api:

mkdir -p ./hollowverse
git clone <url to your fork> ./hollowverse/api
cd ./hollowverse/api

Install the dependnecies:

yarn

Create a database instance using Docker:

docker run --network=host -p 3306 --name=hollowverse-db -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=hollowverse mysql

The database connection is exposed on the standard MySQL port (3306) so that it can be inspected with MySQL Workbench or similar applications.

Run the following command to start the API server in development mode:

yarn dev

This may take a few minutes. You can monitor the logs in the terminal window to see when it is finished. The API server will run in watch mode, so any changes to the database or server code will restart the server and any changes to the GraphQL schema files will regenerate the corresponding TypeScript type definitions.

For the best development experience, we recommend using a code editor with support for language server protocol and a compatible extension for GraphQL. One such editor is Visual Studio Code with the GraphQL for VS Code extension. The repository is already configured for this extension. Once installed, you will get syntax highlighting, comments, auto completion, jump to definition, validation and linting inside GraphQL files.

Note: unlike the TypeScript language server, you will need to save your changes to GraphQL files before the language server is informed of the changes.

If you'd like to customize the server port, you can set PORT environment variable when running the command:

PORT=8888 API_DEBUG_PORT=9999 yarn dev

To fill the database with randomly generated data, execute the following command:

yarn database/mock

Working with the database

The database layer resides in the src/database directory. We use TypeORM to communicate with the database. The models (or entities as they are called in TypeORM terminology) are defined in the entities folder.

Input validation and sanitization

It's important that the validation rules are implemented at the database layer level, not at the query layer level. This ensures that changes to the GraphQL schema do not cause malformed or invalid input to leak into the database.

Although the GraphQL layer performs some initial input validation, like checking if URLs and email addresses are valid, this is only intended for displaying friendly error messages when querying the API. The actual validation process should be performed at the database layer.

Two great libraries that integrate very well with TypeORM are class-validator and class-sanitizer which export many decorators to help with validation and sanitization of user input in a declarative way:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { IsEmail, IsUrl, ValidateIf } from 'class-validator';
import { Trim } from 'class-sanitizer';
import { BaseEntity } from './base';

/**
 * A Hollowverse user
 */
@Entity()
export class User extends BaseEntity {
  @PrimaryGeneratedColumn('uuid') id: string;

  @ValidateIf((_, v) => typeof v === 'string')
  @IsEmail({
    allow_display_name: false,
    require_tld: true,
  })
  @Column({ type: 'varchar', unique: true, nullable: true })
  email: string | null;

  @Trim()
  @Column({ type: 'varchar', nullable: false })
  name: string;

  @ValidateIf((_, v) => typeof v === 'string')
  @IsUrl({
    require_protocol: true,
    require_valid_protocol: true,
    protocols: ['https', 'http'],
  })
  @Column({ type: 'text', nullable: true })
  photoUrl: string | null;
}

Note that all entities must extend the BaseEntity class defined in the entities/base.ts file to ensure that the validation and sanitization are applied when inserting or updating an entity.

TypeORM, class-validator and class-sanitizer are all written in TypeScript so you get excellent type checking and auto-completion when working with the database code.

Making changes to the GraphQL schema

The schema.graphql file serves as the source of truth for the API schema. TypeScript definitions are automatically generated from this file using a CLI tool.

To generate TypeScript types from the GraphQL schema, run the following command:

yarn generate-schema-types

Working with API queries that require authorization

Some of the API queries expect that the user is authenticated. One such query is the viewer query:

{
  viewer {
    name
    email
  }
}

This query returns the name and email of the currently authenticated user. As recommended by the GraphQL documentation, the authentication and authorization logic of the Hollowverse API is implemented at the business logic layer.

For the Hollowverse API, the standard HTTP Authorization header is used to handle user authentication. For queries that require authorization, we expect that header to be of the form:

Authorization: Basic <access_token>

Where <access_token> is a valid Facebook access token issued for a particular user of the Hollowverse Facebook application after the user grants permission to the Hollowverse application on Facebook.

This process is automatically handled by the Facebook JavaScript SDK, which is used by our frontend website.

If you are a member of the core Hollowverse team and have access to the Hollowverse application on Facebook, you can use the Facebook Graph API Explorer tool to issue an access token for the Hollowverse app and use it for authenticated queries while working on the API.

If you do not have access to the Hollowverse application on Facebook, you can create your own Facebook application and use it for development, you can also create test users scoped to your Facebook app for development purposes.

There must be a file named facebookApp.json in the secrets folder during development which contains the Facebook app credentials. The structure of this file is described in src/typings/facebookApp.json.d.ts.

After obtaining the access token, you can create your account on the development instance of the database by running the following mutation in your local GraphQL Playground:

mutation {
  createUser(data: {fbAccessToken: "<access_token>"}) {
    name
  }
}

Now that you have an account, you can use it to perform queries that require a registered user.

You can test that authenticated requests work by setting the Authorization in GraphQL Playground and performing the following query to view your account details:

{
  viewer {
    name
    email
  }
}