2.21.2 • Published 2 years ago

warthog v2.21.2

Weekly downloads
118
License
MIT
Repository
github
Last release
2 years ago

Summary

Warthog is a Node.js GraphQL API framework for quickly building consistent GraphQL APIs that have sorting, filtering and pagination out of the box. It is written in TypeScript and makes heavy use of decorators for concise, declarative code.

Philosophy

This library is intentionally opinionated and generates as much code as possible. When teams build products quickly, even if they have strong conventions and good linters, the GraphQL can quickly become inconsistent, making it difficult for clients to consume the APIs in a reusable way.

To do this, Warthog automatically generates the following:

  • Database schema - generated by TypeORM
  • Your entire GraphQL Schema including:
    • types to match your entities - generated by TypeGraphQL
    • GraphQL inputs for consistent creates, updates, filtering, and pagination inspired by Prisma's conventions
  • A graphql-binding for type-safe programmatic access to your APIs.
  • TypeScript classes for the generated GraphQL schema for type-safety while developing.

Further, it covers the following concerns by hooking into best-in-class open source libraries:

  • Validation: Automatic validation before data is saved using any of the decorators available in the class-validator library.

Prerequisites

You must have Postgresql installed to use Warthog. If you already have it installed, you can skip this step, otherwise there are 3 options:

Docker

See the warthog-starter project for how to use Docker to run Postgres.

Homebrew

If you're on OSX and have Homebrew installed, you can simply run:

brew install postgresql
`brew --prefix`/opt/postgres/bin/createuser -s postgres

Postgres.app

Otherwise, you can install Postgres.app or use the Google machine to figure out how to install on your OS.

Usage

The easiest way to start using Warthog for a fresh project is to clone the warthog-starter repo. This has a simple example in place to get you started. There are also a bunch of examples in the examples folder for more advanced use cases.

Note that the examples in the examples folder use relative import paths to call into Warthog. In your projects, you won't need to set this config value as it's only set to deal with the fact that it's using the Warthog core files without consuming the package from NPM. In your projects, you can omit this as I do in warthog-starter.

Installing in Existing Project

yarn add warthog

1. Create a Model

The model will auto-generate your database table and graphql types. Warthog will find all models that match the following glob - '/**/*.model.ts'. So for this file, you would name it user.model.ts

import { BaseModel, Model, StringField } from 'warthog';

@Model()
export class User extends BaseModel {
  @StringField()
  name?: string;
}

2. Create a Resolver

The resolver auto-generates queries and mutations in your GraphQL schema. Warthog will find all resolvers that match the following glob - '/**/*.resolver.ts'. So for this file, you would name it user.resolver.ts

import { User } from './user.model';
import { UserService } from './user.service';

@Resolver(User)
export class UserResolver {
  constructor(@Inject('UserService') readonly service: UserService) {}

  @Query(() => [User])
  async users(@Args() { where, orderBy, limit, offset }: UserWhereArgs): Promise<User[]> {
    return this.service.find<UserWhereInput>(where, orderBy, limit, offset);
  }

  @Mutation(() => User)
  async createUser(@Arg('data') data: UserCreateInput, @Ctx() ctx: BaseContext): Promise<User> {
    return this.service.create(data, ctx.user.id);
  }
}

3. Create a Service

import { User } from './user.model';

@Service('UserService')
export class UserService extends BaseService<User> {
  constructor(@InjectRepository(User) protected readonly repository: Repository<User>) {
    super(User, repository);
  }
}

4. Add config to .env file

WARTHOG_APP_HOST=localhost
WARTHOG_APP_PORT=4100
WARTHOG_DB_DATABASE=warthog
WARTHOG_DB_USERNAME=postgres
WARTHOG_DB_PASSWORD=

5. Run your server

import 'reflect-metadata';
import { Server } from 'warthog';

async function bootstrap() {
  const server = new Server();
  return server.start();
}

bootstrap();

When you start your server, there will be a new generated folder that has your GraphQL schema in schema.graphql. This contains:

type User implements BaseGraphQLObject {
  id: String!
  createdAt: DateTime!
  createdById: String!
  updatedAt: DateTime
  updatedById: String
  deletedAt: DateTime
  deletedById: String
  version: Int!
  name: String!
}

type Mutation {
  createUser(data: UserCreateInput!): User!
}

type Query {
  users(offset: Int, limit: Int = 50, where: UserWhereInput, orderBy: UserOrderByInput): [User!]!
}

input UserCreateInput {
  name: String!
}

enum UserOrderByInput {
  createdAt_ASC
  createdAt_DESC
  updatedAt_ASC
  updatedAt_DESC
  deletedAt_ASC
  deletedAt_DESC
  name_ASC
  name_DESC
}

input UserUpdateInput {
  name: String
}

input UserWhereInput {
  id_eq: String
  id_in: [String!]
  createdAt_eq: String
  createdAt_lt: String
  createdAt_lte: String
  createdAt_gt: String
  createdAt_gte: String
  createdById_eq: String
  updatedAt_eq: String
  updatedAt_lt: String
  updatedAt_lte: String
  updatedAt_gt: String
  updatedAt_gte: String
  updatedById_eq: String
  deletedAt_all: Boolean
  deletedAt_eq: String
  deletedAt_lt: String
  deletedAt_lte: String
  deletedAt_gt: String
  deletedAt_gte: String
  deletedById_eq: String
  name_eq: String
  name_contains: String
  name_startsWith: String
  name_endsWith: String
  name_in: [String!]
}

input UserWhereUniqueInput {
  id: String!
}

# ...

Notice how we've only added a single field on the model and you get pagination, filtering and tracking of who created, updated and deleted records automatically.

Server API (appOptions)

Most of the config in Warthog is done via environment variables (see Config - Environment Variables below). However, more complex/dynamic objects should be passed via the server config.

attributedescriptiondefault
containerTypeDI container. Warthog uses dependency injection under the hood.empty container
authCheckerAn instance of an AuthChecker to secure your resolvers.
contextContext getter of form (request: Request) => objectempty
loggerLoggerdebug
middlewaresTypeGraphQL middlewares to add to your servernone
onBeforeGraphQLMiddlewareCallback executed just before the Graphql server is started. The Express app is passed.none
onAfterGraphQLMiddlewareCallback executed just after the Graphql server is started. The Express app is passed.none

Config - Environment Variables

Almost all config in Warthog is driven by environment variables. The following items are available:

variablevaluedefault
WARTHOG_APP_HOSTApp server hostnone
WARTHOG_APP_PORTApp server port4000
WARTHOG_APP_PROTOCOLApp server protocolhttp
WARTHOG_AUTO_GENERATE_FILESAuto-generate filesfalse (true in development)
WARTHOG_AUTO_OPEN_PLAYGROUNDOpen playground on server startfalse (true in development)
WARTHOG_CLI_GENERATE_PATHWhere should CLI generate files./src
WARTHOG_DB_CONNECTIONDB connection typepostgres
WARTHOG_DB_DATABASEDB namenone
WARTHOG_DB_ENTITIESWhere should warthog look for modelssrc\/**\/*.model.ts
WARTHOG_DB_MIGRATIONSWhat DB migrations should TypeORM rundb/migrations/**\/*.ts
WARTHOG_DB_MIGRATIONS_DIRWhere should generated migrations be placeddb/migrations
WARTHOG_DB_PORTDB port5432
WARTHOG_DB_USERNAMEDB usernamenone
WARTHOG_DB_LOGGERTypeORM loggeradvanced-console
WARTHOG_DB_PASSWORDDB passwordnone
WARTHOG_DB_SYNCHRONIZEDB automatically migratedfalse
WARTHOG_GENERATED_FOLDERWhere should generated code be placed./generated
WARTHOG_INTROSPECTIONAllow server to be introspectedtrue
WARTHOG_MOCK_DATABASEShould we use mock sqlite DB?false
WARTHOG_RESOLVERS_PATHWhere should Warthog look for resolverssrc/**\/*.resolver.ts

Field/Column Decorators

All of the auto-generation magic comes from the decorators added to the attributes on your models. Warthog decorators are convenient wrappers around TypeORM decorators (to create DB schema) and TypeGraphQL (to create GraphQL schema). You can find a list of decorators available in the src/decorators folder. Most of these are also used in the examples folder in this project.

Complex use cases/ejecting

Warthog makes building simple CRUD endpoints incredibly easy. However, since it is built on top of TypeORM and TypeGraphQL it is flexible enough to handle complex use cases as well.

DB-only

If you need to add a column to the DB that does not need to be exposed via the API, you should just use the TypeORM decorators

API-only

If you need to add a field that is only exposed via the API that is not DB-backed, you should just use the TypeGraphQL Field Decorator

Custom Query

See the feature-flag example for an example of where we'd want to build something beyond the standard CRUD actions. In this example we want to add a custom query that makes a complex DB call.

  • First add the query to the resolver - link to code
  • Then add the custom query input in the resolver - link to code
  • Then add the custom service method that fetches the data link to code

Warthog will generate the correct GraphQL query and InputType automatically.

CLI

Warthog ships with the following commands that can be accessed by running yarn warthog <command>.

See the warthog-starter project's package.json for example usage.

CommandArgsDescription
codegennoneautogenerates code from decorated models and resolvers and places in generated folder
db:createnonecreates DB based on DB specified in config file
db:dropnonedrops DB based on DB specified in config file
generateSee belowgenerates a model, service and resolver
db:migratenonemigrates DB (proxies through TypeORM CLI)
db:migrate:createnoneauto-generates DB migration based on new code additions (proxies through TypeORM CLI)

generate Command in depth

The generate command will create a new resolver, service and model for a given resource. Let's start with a complex example and we'll break it down:

yarn warthog generate user name! nickname numLogins:int! verified:bool! registeredAt:date balance:float! --folder my_generated_folder
  • user - this is the name of the new resource (required)
  • ...args - each of the remaining args until --folder is a field on the resource
  • name! - the name field is of type string by default since no data type is specified. The ! states that it's required
  • numLogins:int! - numLogins states that it is of type int - also required
  • registeredAt:date - registeredAt is of type date (which correlates to an ISO8601 datetime). The absence of the ! means it is optional.
  • ...the rest of the args are self-explanatory. Possible types are bool, date, int, float and string. If you need to use other types, just add them as strings and update the models manually.
  • --folder - this allows you to explicitly set the folder where the generated files should go. This is not recommended and instead you should use the .rc file (see below)

warthogrc config file

Warthog uses cosmiconfig for config that shouldn't change between environments (so typically file paths). This means you can put any of the following config files in your project root:

  • .warthogrc.json
  • .warthogrc.yaml
  • .warthogrc.js
  • warthog.config.js file (exporting a JS object)

The following config options are currently available:

Config KeyDescriptionEquivalent Environment Variable
generatedFolderRelative location to generated folder (relative path from the config file)WARTHOG_GENERATED_FOLDER
cliGeneratePathWhere should CLI place generated files? (relative path from the config file)WARTHOG_CLI_GENERATE_PATH
resolversPathWhere should Warthog look for resolvers? (comma-delimited list of regexes)WARTHOG_RESOLVERS_PATH

Note that for cliGeneratePath, you can interpolate in the following strings to generate dynamic paths:

  • className (pascal case)
  • camelName (camel case)
  • kebabName (kebab case)

Example:

{
  "cliGeneratePath": "./src/${kebabName}"
}

Running yarn warthog generate featureFlag would create 3 files in the ./src/feature-flag/ folder. See feature-flag example for a live example.

Usage in Production

It is recommended that you not run Warthog's TypeScript files via ts-node in Production as we do in development as ts-node has been known to cause issues in some smaller AWS instances. Instead, compile down to JS and run in node. For a full project example (using dotenvi for config management), see warthog-starter

Intentionally Opinionated

Warthog is intentionally opinionated to accelerate development and make use of technology-specific features:

  • Postgres - currently the only database supported. This could be changed, but choosing Postgres allows adding a docker container and other goodies easily.
  • Jest - other harnesses will work, but if you use Jest, we will not open the GraphQL playground when the server starts, for example.
  • Soft deletes - no records are ever deleted, only "soft deleted". The base service used in resolvers filters out the deleted records by default.

Thanks

Special thanks to:

Warthog is essentially a really opinionated composition of TypeORM and TypeGraphQL that uses similar GraphQL conventions to the Prisma project.

Contribute

PRs accepted, fire away! Or add issues if you have use cases Warthog doesn't cover.

License

MIT © Dan Caddigan

3.0.0-beta.25

2 years ago

3.0.0-beta.26

2 years ago

3.0.0-beta.27

2 years ago

3.0.0-beta.23

3 years ago

3.0.0-beta.24

3 years ago

3.0.0-beta.21

3 years ago

3.0.0-beta.22

3 years ago

3.0.0-beta.20

3 years ago

3.0.0-beta.19

3 years ago

3.0.0-beta.18

3 years ago

3.0.0-beta.17

3 years ago

3.0.0-beta.16

3 years ago

2.21.2

3 years ago

2.21.1

3 years ago

3.0.0-beta.12

3 years ago

3.0.0-beta.13

3 years ago

3.0.0-beta.14

3 years ago

3.0.0-beta.15

3 years ago

3.0.0-beta.7

3 years ago

3.0.0-beta.9

3 years ago

3.0.0-beta.8

3 years ago

2.21.0

3 years ago

2.20.1

3 years ago

3.0.0-beta.10

3 years ago

3.0.0-beta.11

3 years ago

2.20.0

3 years ago

3.0.0-beta.6

4 years ago

3.0.0-beta.5

4 years ago

3.0.0-beta.4

4 years ago

2.19.0

4 years ago

2.18.1

4 years ago

3.0.0-beta.3

4 years ago

3.0.0-beta.2

4 years ago

3.0.0-beta.1

4 years ago

2.18.0

4 years ago

2.17.0

4 years ago

2.16.0

4 years ago

2.15.2

4 years ago

2.15.3

4 years ago

2.15.1

4 years ago

2.15.0

4 years ago

2.14.0

4 years ago

2.11.0

4 years ago

2.13.0

4 years ago

2.12.0

4 years ago

2.12.3

4 years ago

2.12.1

4 years ago

2.12.2

4 years ago

2.10.0

4 years ago

2.9.2

4 years ago

2.9.1

4 years ago

2.9.0

4 years ago

2.8.0

4 years ago

2.7.0

4 years ago

2.6.0

4 years ago

2.5.0

4 years ago

2.4.1

4 years ago

2.4.0

4 years ago

2.3.0

4 years ago

2.2.0

4 years ago

2.1.8

4 years ago

2.1.7

4 years ago

2.1.6

4 years ago

2.1.5

4 years ago

2.1.4

4 years ago

2.1.3

4 years ago

2.1.2

4 years ago

2.1.1

4 years ago

2.1.0

4 years ago

2.0.0

4 years ago

1.51.1

4 years ago

2.0.0-beta.16

4 years ago

2.0.0-beta.15

4 years ago

2.0.0-beta.14

4 years ago

1.51.0

4 years ago

2.0.0-beta.13

4 years ago

2.0.0-beta.12

4 years ago

1.50.3

4 years ago

2.0.0-beta.11

4 years ago

1.50.2

4 years ago

1.50.1

4 years ago

1.50.0

4 years ago

2.0.0-beta.10

4 years ago

1.49.4

4 years ago

1.49.3

4 years ago

1.49.2

4 years ago

1.49.1

4 years ago

2.0.0-beta.9

4 years ago

2.0.0-beta.8

4 years ago

1.49.0

4 years ago

2.0.0-beta.7

4 years ago

2.0.0-beta.6

4 years ago

1.48.1

4 years ago

1.48.0

4 years ago

2.0.0-beta.5

4 years ago

1.47.1

4 years ago

2.0.0-beta.4

4 years ago

1.47.0

4 years ago

2.0.0-beta.3

4 years ago

2.0.0-beta.2

4 years ago

2.0.0-beta.1

4 years ago

1.47.0-alpha.2

4 years ago

1.47.0-alpha.1

4 years ago

1.47.0-beta.2

4 years ago

1.47.0-beta.1

4 years ago

1.46.0

5 years ago

1.45.1

5 years ago

1.45.0

5 years ago

1.44.0

5 years ago

1.43.1

5 years ago

1.43.0

5 years ago

1.42.4

5 years ago

1.42.3

5 years ago

1.42.2

5 years ago

1.42.1

5 years ago

1.42.0

5 years ago

1.41.0

5 years ago

1.40.2

5 years ago

1.40.1

5 years ago

1.40.0

5 years ago

1.39.2

5 years ago

1.39.1

5 years ago

1.39.0

5 years ago

1.38.0

5 years ago

1.37.3

5 years ago

1.37.2

5 years ago

1.37.1

5 years ago

1.37.0

5 years ago

1.36.4

5 years ago

1.36.3

5 years ago

1.36.2

5 years ago

1.36.1

5 years ago

1.36.0

5 years ago

1.35.0

5 years ago

1.34.2

5 years ago

1.34.1

5 years ago

1.34.0

5 years ago

1.33.1

5 years ago

1.33.0

5 years ago

1.32.16

5 years ago

1.32.15

5 years ago

1.32.14

5 years ago

1.32.13

5 years ago

1.32.12

5 years ago

1.32.11

5 years ago

1.32.10

5 years ago

1.32.9

5 years ago

1.32.8

5 years ago

1.32.7

5 years ago

1.32.6

5 years ago

1.32.5

5 years ago

1.32.4

5 years ago

1.32.3

5 years ago

1.32.2

5 years ago

1.32.1

5 years ago

1.32.0

5 years ago

1.31.0

5 years ago

1.30.0

5 years ago

1.29.0

5 years ago

1.28.1

5 years ago

1.28.0

5 years ago

1.27.1

5 years ago

1.27.0

5 years ago

1.26.0

5 years ago

1.25.0

5 years ago

1.24.3

5 years ago

1.24.2

5 years ago

1.24.1

5 years ago

1.24.0

5 years ago

1.23.10

5 years ago

1.23.9

5 years ago

1.23.8

5 years ago

1.23.7

5 years ago

1.23.6

5 years ago

1.23.5

5 years ago

1.23.4

5 years ago

1.23.3

5 years ago

1.23.2

5 years ago

1.23.1

5 years ago

1.23.0

5 years ago

1.22.0

5 years ago

1.21.1

5 years ago

1.21.0

5 years ago

1.20.0

5 years ago

1.19.2

5 years ago

1.19.1

5 years ago

1.19.0

5 years ago

1.18.0

5 years ago

1.17.5

5 years ago

1.17.4

5 years ago

1.17.3

5 years ago

1.17.2

5 years ago

1.17.1

5 years ago

1.17.0

5 years ago

1.16.2

5 years ago

1.16.1

5 years ago

1.16.0

5 years ago

1.15.0

5 years ago

1.14.1

5 years ago

1.14.0

5 years ago

1.13.0

5 years ago

1.12.0

5 years ago

1.11.0

5 years ago

1.10.1

5 years ago

1.10.0

5 years ago

1.9.4

5 years ago

1.9.3

5 years ago

1.9.2

5 years ago

1.9.1

5 years ago

1.9.0

5 years ago

1.8.2

5 years ago

1.8.1

5 years ago

1.8.0

5 years ago

1.7.0

5 years ago

1.6.0

5 years ago

1.5.2

5 years ago

1.5.1

5 years ago

1.5.0

5 years ago

1.4.1

5 years ago

1.4.0

5 years ago

1.3.1

5 years ago

1.3.0

5 years ago

1.1.0

5 years ago

1.0.0

6 years ago