Skip to content

This repository holds several examples of GraphQL performance tests with different solutions.

Notifications You must be signed in to change notification settings

lifelynl/graphql-performance-tests

Repository files navigation

GraphQL example

Installing

Prerequisites

  1. Install node (latest).
  2. Install yarn
  3. Install docker
  4. Install TSlint plugin for your editor (vscode).
  5. Install editorconfig plugin for your editor (vscode).

Quick start

Run yarn dev-server and yarn dev-client from the project's root to quickly start the development enviroment. Or run yarn dev to start both concurrently.

Client

cd client Commands here are relative to the client directory.

  1. yarn - Install dependencies.
  2. yarn start - Starts the development environment.
  3. Navigate to http://localhost:3000 to see the client in the browser.

Optional: Install vscode's "Project Snippets" extension (search for in the extension sidebar) to have access to some react project snippets.

# Snippets Tab completions
rc: React component
rcr: Routed React Component ("view")
rcs: Statless React Component
rcp: Pure React Component
  • To generate the translation typings file run yarn translation.
  • To run a production build run yarn build.
  • To test the serviceworker run a http 2 server with simplehttp2server on the build/ folder.
  • To analyze the build package dependencies run yarn build && yarn analyze.

Important note: Require lodash depencies by specifing the function you will use. For example. Use:

import get from 'lodash-es/get' 

Don't use:

import { get } from 'lodash-es' 

Tree shaking with lodash does not work (yet). Not using this style of import will bundle all of lodash (100kb) into the build package.

Server

Initial setup

cd server All commands are relative to the server directory.

  1. yarn - Install dependencies.
  2. cp .env.example .env - Copy over developent .env settings.
  3. yarn start-db - Starts a postgres docker image (connection settings are in .env file).
  4. yarn dev - Start a development server on http://localhost:5000
  5. (optional) Set the node version in the package.json and .babelrc to lock the node version.

Daily use

  1. Start docker
  2. Start the database: yarn start-db
  3. Start the dev server: yarn dev
  4. Typeorm docs: http://typeorm.io/
  5. Yup (input validation) docs: https://github.com/jquense/yup

Migrations & Seeders

Database tables and columns are defined in the entities with typeorm's decorators. See TypeORM - Entities.

Migrations

  • Note I: Always run make sure the development server is running or run yarn build before using the typeorm cli, typeorm needs the compiled javascript files to run.
  • Note II: Foreign keys, cascades, and indexes are not always properly generated by typeorm migrations:generate. Always make sure these things are included in your migrations.
  • Note III: Always check if migrations can be reverted by running typeorm migrations:revert after creating one.
# Typeorm CLI commands:
typeorm migrations:run                                    # Runs migrations
typeorm migrations:revert                                 # Rollbacks the last migration
typeorm migrations:generate -n migrationFileName          # Generates a migrations based on schema changes
typeorm migrations:create -n migrationFileName            # Creates a new migration

Seeders

Run seeders with yarn seeders. To clear the database and run seeders run yarn seeders --fresh. The repo includes faker to generate fake values for the database (See seeders/seedUsersTable.ts) for an example.

Application Structure

dist/ -> compiled javascript files.
app/
  commands/
  config/
  entities/ 
  http/ -> api layer
    graphql/
    middleware/
    routes/
  repository
  routes/
  services/
  ssr/ -> email templates etc.
  utils/
  index.ts -> main access point of the app
database/
  migrations/
  seeders/
# CLI commands:
yarn start-db                     # Start the database
yarn start-server                 # Start the server
yarn seeders                      # Run database seeders
yarn build                        # Compiles typescript and run babel to resolve paths.
yarn watch                        # Compile typescript and watch for changes
yarn dev                          # Compile typescript, start the server and watch for changes
yarn start                        # Starts the server (without babel)
yarn typeorm                      # Typeorm cli for creating entities, migrations and running migrations
yarn typeorm entity:create        # Generates a new entity
yarn typeorm migrations:create    # Creates a new migration file.

Typescript compiles to the latest version of ECMASCRIPT ("esnext"). Then using babel, compiles to the latest version of node. Babel provides more a fine grained control with babel-preset-env over what gets transpiled for the version of node we are using.

Entities, Repositories and graphql

  • Entities hold business logic and the database schema
  • Repositories deal with persisting (caching) and retrieving entities.
  • Graphql resolve function act as entry points (controllers) for the app.

Autentication

Authentication is done via Json Web Token (can be changed). Include a JWT token in a AUTHORIZATION Bearer xxxxx.yyyyy.zzzzz Header. To authenticate users the JWT is included in the graphql context.

Logging

Use logging! Use the logger included in services/. Use correct log levels. Log info for general actions. Log debugs for debugging. Log errors when needed (errors logged need to be sent to sentry).

GraphQL Query/Mutation structure.

interface Response extends User {}

interface InputArgs {
    username: string
    email: string
    fullname: string
}

export default makeGraphQLField<InputArgs, Response>({
    type: UserType, // The response tyoe
    args: {
        username: { type: GraphQLString },
        email: { type: GraphQLString },
        fullname: { type: GraphQLString },
        // ... additional arguments
    },
    resolve: async (root, args, context) => {
        // AUTHENTICATION GUARD (throws AuthenticationError)
        const user = await authGraphQLViewer(context)

        // AUTHORIZATION GUARDS
        // Checks if the user has permission to execute this action
        if (!user.isAdmin) {
            throw new AuthorizationError('User is no admin')
        }

        // VALIDATION GUARD (throws ValidationError)
        await validateArgs(schema => ({
            username: schema.string().trim().required(),
            email: schema.string().trim().email()
                .test('unique', '${path} already exists', async email => ! await getUserRepository().findOne({ email }))
                .required(),
            fullname: schema.string().trim().required()
        }), args, { abortEarly: false })
                
        // Do some business logic
        const user = User.createNewUser(args)

        // Persist the user.
        return await getUserRepository().save(user)
    }
})

Todo's

  • Use a better docker setup (bryan).
  • File storage and mailing.
  • Add event (pub/sub).
  • Add a transaction id (session id) to generated logs.
  • Log errors to sentry.
  • Add validation typings (yup).
  • Log to papertrail.
  • Add redis (for caching/events).

Much love, Lifely team <3

https://i.imgur.com/tQr3eO3.gif

About

This repository holds several examples of GraphQL performance tests with different solutions.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published