Skip to content

Commit 744b10d

Browse files
authored
feat: validation by server side plugin (#95)
fixes #92
1 parent 2bc0a32 commit 744b10d

33 files changed

+5693
-3068
lines changed

.eslintrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"extends": [
33
"standard"
4-
]
4+
],
5+
"env": {
6+
"mocha": true
7+
}
58
}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ npm-debug.log
66
typings*
77
.sync
88
.vscode
9-
.idea
9+
.idea
10+
/.project

README.md

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ npm install graphql-constraint-directive@v2
1818
```
1919

2020
## Usage
21+
There are multiple ways to make use of the constraint directive in your project. Below outlines the benefits and caveats. Please choose the most appropriate to your use case.
22+
23+
### Schema wrapper
24+
25+
Implementation based on schema wrappers - basic scalars are wrapped as custom scalars with validations.
26+
27+
#### Benefits
28+
* based on `graphql` library, works everywhere
29+
* posibility to also validate GraphQL response data
30+
31+
#### Caveats
32+
* modifies GraphQL schema, basic scalars (Int, Float, String) are replaced by custom scalars
33+
2134
```js
2235
const { constraintDirective, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
2336
const express = require('express')
@@ -51,6 +64,150 @@ server.applyMiddleware({ app })
5164

5265
```
5366

67+
### Server plugin
68+
69+
Implementation based on server plugin. Common server plugins are implemented,
70+
function `validateQuery(schema, query, variables, operationName)` can be used to implement additional plugins.
71+
72+
#### Benefits
73+
* schema stays unmodified
74+
75+
#### Caveats
76+
* runs only in supported servers
77+
* validates only GraphQL query, not response data
78+
79+
#### Envelop
80+
81+
Use as an [Envelop plugin](https://www.envelop.dev) in supported frameworks, e.g. [GraphQL Yoga](https://www.graphql-yoga.com/).
82+
Functionality is plugged in `execute` phase
83+
84+
```js
85+
const { createEnvelopQueryValidationPlugin, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
86+
const express = require('express')
87+
const { createServer } = require('@graphql-yoga/node')
88+
const { makeExecutableSchema } = require('@graphql-tools/schema')
89+
90+
const typeDefs = `
91+
type Query {
92+
books: [Book]
93+
}
94+
type Book {
95+
title: String
96+
}
97+
type Mutation {
98+
createBook(input: BookInput): Book
99+
}
100+
input BookInput {
101+
title: String! @constraint(minLength: 5, format: "email")
102+
}`
103+
104+
let schema = makeExecutableSchema({
105+
typeDefs: [constraintDirectiveTypeDefs, typeDefs],
106+
})
107+
108+
const app = express()
109+
110+
const yoga = createServer({
111+
schema,
112+
plugins: [createEnvelopQueryValidationPlugin()],
113+
graphiql: false
114+
})
115+
116+
app.use('/', yoga)
117+
118+
app.listen(4000);
119+
```
120+
121+
#### Apollo Server
122+
123+
As an [Apollo Server](https://www.apollographql.com/docs/apollo-server/) plugin
124+
125+
```js
126+
const { createApolloQueryValidationPlugin, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
127+
const express = require('express')
128+
const { ApolloServer } = require('apollo-server-express')
129+
const { makeExecutableSchema } = require('@graphql-tools/schema')
130+
131+
const typeDefs = `
132+
type Query {
133+
books: [Book]
134+
}
135+
type Book {
136+
title: String
137+
}
138+
type Mutation {
139+
createBook(input: BookInput): Book
140+
}
141+
input BookInput {
142+
title: String! @constraint(minLength: 5, format: "email")
143+
}`
144+
145+
let schema = makeExecutableSchema({
146+
typeDefs: [constraintDirectiveTypeDefs, typeDefs],
147+
})
148+
149+
const plugins = [
150+
createApolloQueryValidationPlugin({
151+
schema
152+
})
153+
]
154+
155+
const app = express()
156+
const server = new ApolloServer({
157+
schema,
158+
plugins
159+
})
160+
161+
await server.start()
162+
163+
server.applyMiddleware({ app })
164+
```
165+
166+
#### Express
167+
168+
As a [Validation rule](https://graphql.org/graphql-js/validation/) when query `variables` are available
169+
170+
```js
171+
const { createQueryValidationRule, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
172+
const express = require('express')
173+
const { graphqlHTTP } = require('express-graphql')
174+
const { makeExecutableSchema } = require('@graphql-tools/schema')
175+
176+
const typeDefs = `
177+
type Query {
178+
books: [Book]
179+
}
180+
type Book {
181+
title: String
182+
}
183+
type Mutation {
184+
createBook(input: BookInput): Book
185+
}
186+
input BookInput {
187+
title: String! @constraint(minLength: 5, format: "email")
188+
}`
189+
190+
let schema = makeExecutableSchema({
191+
typeDefs: [constraintDirectiveTypeDefs, typeDefs],
192+
})
193+
194+
const app = express()
195+
196+
app.use(
197+
'/api',
198+
graphqlHTTP(async (request, response, { variables }) => ({
199+
schema,
200+
validationRules: [
201+
createQueryValidationRule({
202+
variables
203+
})
204+
]
205+
}))
206+
)
207+
app.listen(4000);
208+
209+
```
210+
54211
## API
55212
### String
56213
#### minLength
@@ -140,6 +297,14 @@ const formatError = function (error) {
140297
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema, formatError }))
141298

142299
```
300+
301+
#### Apollo Server
302+
Throws a [`UserInputError`](https://www.apollographql.com/docs/apollo-server/data/errors/#bad_user_input) for each validation error
303+
304+
#### Envelop
305+
The Envelop plugin throws a prefilled `GraphQLError` for each validation error
306+
143307
### uniqueTypeName
144308
```@constraint(uniqueTypeName: "Unique_Type_Name")```
145-
Override the unique type name generate by the library to the one passed as an argument
309+
Override the unique type name generate by the library to the one passed as an argument.
310+
Has meaning only for `Schema wrapper` implementation.

index.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import {GraphQLSchema} from "graphql";
1+
import {GraphQLSchema, GraphQLError, DocumentNode, ValidationContext} from "graphql";
2+
import QueryValidationVisitor from "./lib/query-validation-visitor";
23

34
export function constraintDirective () : (schema: GraphQLSchema) => GraphQLSchema;
45

56
export const constraintDirectiveTypeDefs: string
7+
8+
export function validateQuery () : (schema: GraphQLSchema, query: DocumentNode, variables: Record<string, any>, operationName?: string) => Array<GraphQLError>;
9+
10+
export function createApolloQueryValidationPlugin () : (schema: GraphQLSchema) => function;
11+
12+
export function createQueryValidationRule( options: { [key: string]: any }) : (context: ValidationContext) => QueryValidationVisitor;
13+
14+
export function createEnvelopQueryValidationPlugin() : object;

0 commit comments

Comments
 (0)