Skip to content

Commit d09e1f7

Browse files
committed
Add examples, partial docs and support for unsupported JSON schema fields using the schema arg
1 parent 66ab1f0 commit d09e1f7

17 files changed

+1068
-105
lines changed

README.md

+95-7
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,111 @@
44

55
Mercurius Validation is a plugin for [Mercurius](https://mercurius.dev) that adds configurable validation support.
66

7+
Features:
8+
9+
- Supports JSON Schema.
10+
- Supports JTD.
11+
- Supports Custom Function validation on field arguments.
12+
- Supports validation through `constraint` directives in your schema. This plugin will apply JSON Schema validation policies based on the matching directives.
13+
- Provides SDL type definitions for the GraphQL directive.
14+
- Works in both normal and gateway mode.
15+
- In addition to the argument metadata and value, custom functions have access to the same GraphQL information that any GraphQL resolver has access to.
16+
- Define custom errors.
17+
- GraphQL spec compliant.
18+
719
## Docs
820

921
- [Install](#install)
10-
<!-- TODO: -->
1122
- [Quick Start](#quick-start)
12-
<!-- TODO: -->
1323
- [Examples](#examples)
1424
- [Benchmarks](#benchmarks)
15-
<!-- TODO -->
1625
- [API](docs/api/options.md)
17-
<!-- TODO -->
18-
- [Errors](docs/errors.md)
19-
<!-- TODO -->
20-
- [Federation](docs/federation.md)
2126

2227
## Install
2328

2429
```bash
2530
npm i fastify mercurius mercurius-validation
2631
```
2732

33+
## Quick Start
34+
35+
```js
36+
'use strict'
37+
38+
const Fastify = require('fastify')
39+
const mercurius = require('mercurius')
40+
const mercuriusValidation = require('mercurius-validation')
41+
42+
const schema = `
43+
type Message {
44+
id: ID!
45+
text: String
46+
}
47+
48+
input Filters {
49+
id: ID
50+
text: String
51+
}
52+
53+
type Query {
54+
message(id: ID): Message
55+
messages(filters: Filters): [Message]
56+
}
57+
`
58+
59+
const messages = [
60+
{
61+
id: 0,
62+
text: 'Some system message.'
63+
},
64+
{
65+
id: 1,
66+
text: 'Hello there'
67+
},
68+
{
69+
id: 2,
70+
text: 'Give me a place to stand, a lever long enough and a fulcrum. And I can move the Earth.'
71+
},
72+
{
73+
id: 3,
74+
text: ''
75+
}
76+
]
77+
78+
const resolvers = {
79+
Query: {
80+
message: async (_, { id }) => {
81+
return messages.find(message => message.id === Number(id))
82+
},
83+
messages: async () => {
84+
return messages
85+
}
86+
}
87+
}
88+
89+
const app = Fastify()
90+
91+
app.register(mercurius, {
92+
schema,
93+
resolvers
94+
})
95+
96+
app.register(mercuriusValidation, {
97+
schema: {
98+
Filters: {
99+
text: { type: 'string', minLength: 1 }
100+
},
101+
Query: {
102+
message: {
103+
id: { type: 'string', minLength: 1 }
104+
}
105+
}
106+
}
107+
})
108+
109+
app.listen(3000)
110+
```
111+
28112
## Benchmarks
29113

30114
### Normal GraphQL Server Mode | Without Validation
@@ -95,6 +179,10 @@ Last run: `2021-09-18`
95179
96180
```
97181

182+
## Examples
183+
184+
Check [GitHub repo](https://github.com/mercurius-js/validation/tree/master/examples) for more examples.
185+
98186
## License
99187

100188
MIT

bench/gateway-with-validation.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const Fastify = require('fastify')
44
const mercurius = require('mercurius')
5-
const mercuriusAuth = require('..')
5+
const mercuriusValidation = require('..')
66

77
const app = Fastify()
88

@@ -20,7 +20,7 @@ app.register(mercurius, {
2020
jit: 1
2121
})
2222

23-
app.register(mercuriusAuth, {
23+
app.register(mercuriusValidation, {
2424
schema: {
2525
User: {
2626
topPosts: {

bench/normal-with-validation.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const Fastify = require('fastify')
44
const mercurius = require('mercurius')
5-
const mercuriusAuth = require('..')
5+
const mercuriusValidation = require('..')
66
const { schema, resolvers } = require('./normal-setup')
77

88
const app = Fastify()
@@ -14,7 +14,7 @@ app.register(mercurius, {
1414
jit: 1
1515
})
1616

17-
app.register(mercuriusAuth, {
17+
app.register(mercuriusValidation, {
1818
schema: {
1919
Filters: {
2020
text: { type: 'string', minLength: 1 }

docs/api/options.md

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# mercurius-validation
2+
3+
- [Plugin options](#plugin-options)
4+
- [Registration](#registration)
5+
6+
## Plugin options
7+
8+
<!-- TODO -->
9+
**mercurius-validation** supports the following options:
10+
11+
Extends: [`Options`](https://ajv.js.org/options.html)
12+
13+
* **mode** `"JSONSchema" | "JTD"` (optional, default: `JSONSchema`) - the validation mode of the plugin. This is used to specify the type of schema that needs to be compiled.
14+
* **schema** `MercuriusValidationSchema` (optional) - the validation schema definition that the plugin with run. One can define JSON Schema or JTD definitions for GraphQL types, fields and arguments or functions for GraphQL arguments.
15+
16+
It extends the [AJV options](https://ajv.js.org/options.html). These can be used to register additional `formats` and provide further customization to the AJV validation behavior for example.
17+
18+
### Parameter: `MercuriusValidationSchema`
19+
20+
Extends: `Record<string, MercuriusValidationSchemaType>`
21+
22+
Each key within the `MercuriusValidationSchema` type corresponds with the GraphQL type name. For example, if we wanted validation on input type:
23+
24+
```gql
25+
input Filters {
26+
...
27+
}
28+
```
29+
30+
We would use the key: `Filters`:
31+
32+
```js
33+
{
34+
Filters: { ... }
35+
}
36+
```
37+
38+
### Parameter: `MercuriusValidationSchemaType`
39+
40+
Extends: `Record<string, MercuriusValidationSchemaField>`
41+
42+
* **__typeValidation** `JSONSchema | JTD` (optional) - The [JSON Schema](https://json-schema.org/understanding-json-schema/) or [JTD](https://jsontypedef.com/docs/) schema definitions for the type. This is only applicable to GraphQL Input object types, so only schema definitions for `object` are applicable here.
43+
44+
Each key within the `MercuriusValidationSchemaType` type corresponds with the GraphQL field name on a type. For example, if we wanted validation on type field `text`:
45+
46+
```gql
47+
input Filters {
48+
id: ID
49+
text: String
50+
}
51+
```
52+
53+
We would use the key: `text`:
54+
55+
```js
56+
{
57+
Filters: {
58+
text: { ... }
59+
}
60+
}
61+
```
62+
63+
### Parameter: `MercuriusValidationSchemaField`
64+
65+
The field definition is different from GraphQL Input Object types and GraphQL Object types.
66+
67+
#### GraphQL Input Object Types
68+
69+
Union: `JSONSchema | JTD`
70+
71+
#### GraphQL Object Types
72+
73+
Extends: `Record<string, MercuriusValidationSchemaArgument>`
74+
75+
Each key within the `MercuriusValidationSchemaField` type corresponds with the GraphQL argument name on a field. For example, if we wanted validation on field argument `id`:
76+
77+
```gql
78+
type Query {
79+
message(id: ID): String
80+
}
81+
```
82+
83+
We would use the key: `id`:
84+
85+
```js
86+
{
87+
Query: {
88+
message: {
89+
id: {... }
90+
}
91+
}
92+
}
93+
```
94+
95+
### Parameter: `MercuriusValidationSchemaArgument`
96+
97+
Union: `JSONSchema | JTD` | `MercuriusValidationFunction`
98+
99+
### Parameter: `MercuriusValidationFunction(metadata, value, parent, arguments, context, info)`
100+
101+
Arguments:
102+
103+
* **metadata** `MercuriusValidationFunctionMetadata` - the GraphQL argument metadata associated with the function definition.
104+
* **value** `any` - the value of the argument.
105+
* **parent** `object` - the parent data associated with the GraphQL field.
106+
* **arguments** `object` - the key value object of the GraphQL field arguments.
107+
* **context** `MercuriusContext` - the [Mercurius context](https://mercurius.dev/#/docs/context).
108+
* **info** `GraphQLResolveInfo` - the [GraphQL Resolve info](https://graphql.org/graphql-js/type/#graphqlobjecttype) of the object type.
109+
110+
#### Parameter: `MercuriusValidationFunctionMetadata`
111+
112+
* **type** `string` - the name of the GraphQL type.
113+
* **field** `string` - the name of the GraphQL field.
114+
* **argument** `string` - the name of the GraphQL argument.
115+
116+
Returns: `void`
117+
118+
### Parameter: `JSONSchema`
119+
120+
The [JSON Schema](https://json-schema.org/understanding-json-schema/) schema definition for the input object type.
121+
122+
### Parameter: `JTD`
123+
124+
The [JTD](https://jsontypedef.com/docs/) schema definition for the input object type field.
125+
126+
## Registration
127+
128+
The plugin must be registered **after** Mercurius is registered.
129+
130+
```js
131+
'use strict'
132+
133+
const Fastify = require('fastify')
134+
const mercurius = require('mercurius')
135+
const mercuriusValidation = require('mercurius-validation')
136+
137+
const app = Fastify()
138+
139+
const schema = `
140+
directive @validation(
141+
requires: Role = ADMIN,
142+
) on OBJECT | FIELD_DEFINITION
143+
144+
enum Role {
145+
ADMIN
146+
REVIEWER
147+
USER
148+
UNKNOWN
149+
}
150+
151+
type Query {
152+
add(x: Int, y: Int): Int @validation(requires: USER)
153+
}
154+
`
155+
156+
const resolvers = {
157+
Query: {
158+
add: async (_, { x, y }) => x + y
159+
}
160+
}
161+
162+
app.register(mercurius, {
163+
schema,
164+
resolvers
165+
})
166+
167+
app.register(mercuriusValidation, {
168+
validationContext (context) {
169+
return {
170+
identity: context.reply.request.headers['x-user']
171+
}
172+
},
173+
async applyPolicy (validationDirectiveAST, parent, args, context, info) {
174+
return context.validation.identity === 'admin'
175+
},
176+
validationDirective: 'validation'
177+
})
178+
179+
app.listen(3000)
180+
```

0 commit comments

Comments
 (0)