Skip to content

Commit

Permalink
feat(handler): Add validationRules option for extending or replacin…
Browse files Browse the repository at this point in the history
…g the GraphQL validation rule set (#51)
  • Loading branch information
enisdenjo authored Feb 9, 2023
1 parent d54b4fe commit 46c5309
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 1 deletion.
18 changes: 18 additions & 0 deletions docs/interfaces/handler.HandlerOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [rootValue](handler.HandlerOptions.md#rootvalue)
- [schema](handler.HandlerOptions.md#schema)
- [validate](handler.HandlerOptions.md#validate)
- [validationRules](handler.HandlerOptions.md#validationrules)

## Properties

Expand Down Expand Up @@ -249,3 +250,20 @@ Will not be used when implementing a custom `onSubscribe`.
##### Returns

`ReadonlyArray`<`GraphQLError`\>

___

### validationRules

`Optional` **validationRules**: readonly `ValidationRule`[] \| (`req`: [`Request`](handler.Request.md)<`RequestRaw`, `RequestContext`\>, `args`: [`OperationArgs`](../modules/handler.md#operationargs)<`Context`\>, `specifiedRules`: readonly `ValidationRule`[]) => readonly `ValidationRule`[] \| `Promise`<readonly `ValidationRule`[]\>

The validation rules for running GraphQL validate.

When providing an array, the rules will be APPENDED to the default
`specifiedRules` array provided by the graphql-js module.

Alternatively, providing a function instead will OVERWRITE the defaults
and use exclusively the rules returned by the function. The third (last)
argument of the function are the default `specifiedRules` array provided
by the graphql-js module, you're free to prepend/append the defaults to
your rule set, or omit them altogether.
55 changes: 55 additions & 0 deletions src/__tests__/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,58 @@ it('should correctly serialise execution result errors', async () => {
}
`);
});

it('should append the provided validation rules array', async () => {
const server = startTServer({
validationRules: [
(ctx) => {
ctx.reportError(new GraphQLError('Woah!'));
return {};
},
],
});
const url = new URL(server.url);
url.searchParams.set('query', '{ idontexist }');
const result = await fetch(url.toString());
await expect(result.json()).resolves.toMatchInlineSnapshot(`
{
"errors": [
{
"message": "Woah!",
},
{
"locations": [
{
"column": 3,
"line": 1,
},
],
"message": "Cannot query field "idontexist" on type "Query".",
},
],
}
`);
});

it('should replace the validation rules when providing a function', async () => {
const server = startTServer({
validationRules: () => [
(ctx) => {
ctx.reportError(new GraphQLError('Woah!'));
return {};
},
],
});
const url = new URL(server.url);
url.searchParams.set('query', '{ idontexist }');
const result = await fetch(url.toString());
await expect(result.json()).resolves.toMatchInlineSnapshot(`
{
"errors": [
{
"message": "Woah!",
},
],
}
`);
});
30 changes: 29 additions & 1 deletion src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
ExecutionResult,
GraphQLSchema,
validate as graphqlValidate,
ValidationRule,
specifiedRules,
execute as graphqlExecute,
parse as graphqlParse,
DocumentNode,
Expand Down Expand Up @@ -203,6 +205,25 @@ export interface HandlerOptions<
* Will not be used when implementing a custom `onSubscribe`.
*/
validate?: typeof graphqlValidate;
/**
* The validation rules for running GraphQL validate.
*
* When providing an array, the rules will be APPENDED to the default
* `specifiedRules` array provided by the graphql-js module.
*
* Alternatively, providing a function instead will OVERWRITE the defaults
* and use exclusively the rules returned by the function. The third (last)
* argument of the function are the default `specifiedRules` array provided
* by the graphql-js module, you're free to prepend/append the defaults to
* your rule set, or omit them altogether.
*/
validationRules?:
| readonly ValidationRule[]
| ((
req: Request<RequestRaw, RequestContext>,
args: OperationArgs<Context>,
specifiedRules: readonly ValidationRule[],
) => Promise<readonly ValidationRule[]> | readonly ValidationRule[]);
/**
* Is the `execute` function from GraphQL which is
* used to execute the query and mutation operations.
Expand Down Expand Up @@ -373,6 +394,7 @@ export function createHandler<
schema,
context,
validate = graphqlValidate,
validationRules = [],
execute = graphqlExecute,
parse = graphqlParse,
getOperationAST = graphqlGetOperationAST,
Expand Down Expand Up @@ -545,7 +567,13 @@ export function createHandler<
};
}

const validationErrs = validate(args.schema, args.document);
let rules = specifiedRules;
if (typeof validationRules === 'function') {
rules = await validationRules(req, args, specifiedRules);
} else {
rules = [...rules, ...validationRules];
}
const validationErrs = validate(args.schema, args.document, rules);
if (validationErrs.length) {
return makeResponse(validationErrs, acceptedMediaType);
}
Expand Down

0 comments on commit 46c5309

Please sign in to comment.