GraphQL

GraphQL module built with typegraphql

Installation#

npm i @appolo/graphql

Options#

keyDescriptionTypeDefault
contextrequest custom context objectobjectnull
authrequest auth custom objectobjectnull
pathui pathobject/graphql
middlewarearray of custom middlewaresmiddleware[]
serverRegistrationoptions for custom serverobject{}
apolloServerConfigoptions for apolloobject{}
buildSchemaOptionsoptions for typegraphqlobject{}

in config/modules/all.ts

import {GraphqlModule} from '@appolo/graphql';
export = async function (app: App) {
app.module.use( GraphqlModule.for({
middleware: [AuthMiddleware],
auth: AuthChecker,
buildSchemaOptions: {validate: true}
}));
}

Usage#

Resolver#

import {define, singleton} from '@appolo/inject'
import {Resolver, Query, FieldResolver, Arg, Root, Mutation, UseMiddleware, Ctx, Authorized,Register} from "@appolo/graphql";
@Resolver(of => Recipe)
@Register()
@singleton()
export class RecipeResolver {
@inject() recipeService: RecipeService;
@Query(returns => Recipe, {nullable: true})
@UseMiddleware(LogAccess)
async recipe(@Arg("recipeId") recipeId: string) {
return this.recipeService.getOne(recipeId);
}
@Query(returns => Recipe, {nullable: true})
@UseMiddleware(LogAccess)
@Authorized("ADMIN", "MODERATOR")
async recipeWithAuth(@Arg("recipeId") recipeId: string) {
return this.recipeService.getOne(recipeId);
}
@Query(returns => [Recipe])
async recipes(): Promise<Recipe[]> {
return this.recipeService.getAll();
}
@Mutation(returns => Recipe)
async addRecipe(@Arg("recipe") recipe: RecipeInput, @Ctx() context): Promise<Recipe> {
return this.recipeService.add(recipe);
}
@FieldResolver()
async numberInCollection(@Root() recipe: Recipe): Promise<number> {
const index = await this.recipeService.findIndex(recipe);
return index + 1;
}
@FieldResolver()
contextParam(@Root() recipe: Recipe, @Ctx() context) {
return context.test || ""
}
}

ObjectType#

import { Field, ID, ObjectType, Int } from "type-graphql";
@ObjectType()
export class Recipe {
@Field(type => ID)
id: string;
@Field()
title: string;
@Field({ nullable: true })
description?: string;
@Field(type => [String])
ingredients: string[];
@Field(type => Int)
protected numberInCollection: number;
@Field(type => String)
protected contextParam: string;
@Field(type => Int)
protected get ingredientsLength(): number {
return this.ingredients.length;
}
}

InputType#

import { InputType, Field } from "type-graphql";
import { string,array } from "@appolo/validator";
@InputType()
export class RecipeInput implements Partial<Recipe> {
@Field({ nullable: true })
@string().optional()
description: string;
@Field(type => [String])
@array().items(string())
ingredients: string[];
@Field()
@string().required()
title: string;
}

Auth#

import {Auth} from "@appolo/graphql";
@define()
export class AuthChecker extends Auth<any> {
async check() {
return this.resolverData.context.req.user.name === "test" && this.roles.includes("ADMIN");
}
}

Middleware#

@define()
@singleton()
export class LogAccess implements MiddlewareInterface<any> {
@inject() env: any
@inject() logger: ILogger
async use({context, info}: ResolverData<any>, next: NextFn) {
context.test = "aaaa";
return next();
}
}