├── .circleci └── config.yml ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples └── graphql-js │ ├── package.json │ ├── src │ └── index.ts │ └── yarn.lock ├── package.json ├── src ├── FieldValidationErrorType.ts ├── MutationValidationErrorType.ts ├── __tests__ │ ├── __snapshots__ │ │ └── yupMiddleware.spec.ts.snap │ └── yupMiddleware.spec.ts ├── buildErrorObjectFromValidationError.ts ├── graphql.augmented.ts ├── index.ts ├── types.ts └── yupMiddleware.ts ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/app 5 | docker: 6 | - image: circleci/node:12 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | keys: 11 | - v0-user-cache-{{ .Branch }} 12 | - v0-user-cache 13 | - run: 14 | name: Install Dependencies 15 | command: yarn install --frozen-lockfile --non-interactive --cache-folder ~/.cache/yarn 16 | - save_cache: 17 | key: v0-user-cache-{{ .Branch }} 18 | paths: 19 | - ~/.cache 20 | - run: 21 | name: Run lint 22 | command: yarn lint 23 | - run: 24 | name: Run tests 25 | command: yarn test 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@jcm/eslint-config'], 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | 5 | .vscode 6 | .idea 7 | 8 | *.log* 9 | .DS_Store 10 | .nyc_output 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [v1.0.0] - 2021-01-24 9 | 10 | ### Breaking Change 11 | 12 | ### Fixed 13 | 14 | ### Added 15 | 16 | ### Changed 17 | 18 | ## [v1.0.0] - 2021-01-24 19 | 20 | ### Breaking Change 21 | 22 | - Requires GraphQL v15 23 | - Requires Node.js >= 12 24 | - Requires `graphql-middleware` >= 6 25 | - Dropped support for `graphql-yoga` - This library is not maintained anymore 26 | - Only supports usage with GraphQL `extensions`. Extension property name is `yupMiddleware`. 27 | 28 | ## [v0.0.1] - 2018-09-06 29 | 30 | ### First version 31 | 32 | [unreleased]: https://github.com/JCMais/graphql-yup-middleware/compare/v1.0.0...HEAD 33 | [v1.0.0]: https://github.com/JCMais/graphql-yup-middleware/compare/ce531ee317dccb95be201d356806f508ef47ab58...v1.0.0 34 | [v0.0.1]: https://github.com/JCMais/graphql-yup-middleware/tree/ce531ee317dccb95be201d356806f508ef47ab58 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Jonathan Cardoso Machado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GraphQL Mutations Validation Yup Middleware 2 | 3 | [![npm](https://img.shields.io/npm/v/graphql-yup-middleware.svg)](https://www.npmjs.com/package/graphql-yup-middleware) 4 | [![CircleCI (all branches)](https://img.shields.io/circleci/project/github/JCMais/graphql-yup-middleware.svg)](https://circleci.com/gh/JCMais/graphql-yup-middleware) 5 | 6 | 1. [What is this?](#what-is-this) 7 | 1. [Install](#install) 8 | 1. [Usage](#usage) 9 | 10 | > **I have no plans to add new features to this library - It's on mainteance-only mode. When building the schema for any new GraphQL server, my recommendation is to use [nexus](https://github.com/graphql-nexus/nexus), which has native support for plugins - I have a few plugins available in [JCMais/nexus-plugins](https://github.com/JCMais/nexus-plugins), including one for Yup validation.** 11 | 12 | ### What is this? 13 | 14 | It's a middleware to be used with [`graphql-middleware`][graphql-middleware] to add validations to mutations arguments using [`yup`][yup]. 15 | 16 | It originated from this post: https://medium.com/@jonathancardoso/graphql-mutation-arguments-validation-with-yup-using-graphql-middleware-645822fb748 17 | 18 | ### Install 19 | 20 | ``` 21 | yarn add graphql-yup-middleware 22 | ``` 23 | 24 | Keep in mind that you also need to have [`graphql`][graphql] (>= `15`), [`graphql-middleware`](graphql-middleware) (>= `6`) and [`yup`][yup] as dependencies of your project. 25 | 26 | ### Options 27 | 28 | The `yupMutationMiddleware` function exported by this package should always 29 | be called when adding it as middleware. Do not add it without calling first. 30 | 31 | It accepts the following options, all are optional: 32 | 33 | ```ts 34 | type YupMiddlewareOptions = { 35 | // In case of errors, this function is going to be used to build the response. More on this below. 36 | errorPayloadBuilder?: ( 37 | error: ValidationError, 38 | errorContext: YupMiddlewareErrorContext, 39 | ) => Object; 40 | // if the values returned by yup should be merged into the args passed to the mutation resolver 41 | shouldTransformArgs?: boolean; 42 | // any options that are accepted by yup validate method 43 | yupOptions?: ValidateOptions; 44 | }; 45 | ``` 46 | 47 | The defaults are: 48 | 49 | ```ts 50 | { 51 | shouldTransformArgs: true, 52 | yupOptions: { 53 | abortEarly: false, 54 | }, 55 | } 56 | ``` 57 | 58 | The default `errorPayloadBuilder` makes the following assumptions about your mutation response fields: 59 | 60 | 1. It's an object with nested fields, that is, your mutation does not return a scalar value 61 | 2. One of those is named `error`. 62 | 3. `error` field is of type `String` or `MutationValidationError`. 63 | 64 | And it's going to create a payload based on the `error` type: 65 | 66 | 1. `String`: return `error.message` on it. 67 | 2. `MutationValidationError`: return an error object matching the following definition: 68 | 69 | ```graphql 70 | type FieldValidationError { 71 | field: String! 72 | errors: [String!]! 73 | } 74 | 75 | type MutationValidationError { 76 | message: String! 77 | details: [FieldValidationError!]! 78 | } 79 | ``` 80 | 81 | `MutationValidationError` and `FieldValidationError` are both exported as SDL, so you can add them to your typeDefs: 82 | 83 | ```ts 84 | import { 85 | MutationValidationError, 86 | FieldValidationError, 87 | } from 'graphql-yup-middleware'; 88 | 89 | // ... 90 | 91 | const typeDefs = [ 92 | MutationValidationError, 93 | FieldValidationError, 94 | /* ...your other types */ 95 | , 96 | ]; 97 | 98 | // ... 99 | ``` 100 | 101 | And they are also exported as `GraphQLObjectType`, in case you are building your schema manually, just append `Type` to their name. 102 | 103 | ```ts 104 | import { 105 | MutationValidationErrorType, 106 | FieldValidationErrorType, 107 | } from 'graphql-yup-middleware'; 108 | ``` 109 | 110 | ### Usage 111 | 112 | For using it with other servers, like apollo, express, koa, etc, you are going to need to install `graphql-middleware` too: 113 | 114 | ```sh 115 | yarn add graphql-middleware 116 | ``` 117 | 118 | Then you can apply the middleware to your schema: 119 | 120 | ```ts 121 | import { applyMiddleware } from 'graphql-middleware'; 122 | import { yupMutationMiddleware } from 'graphql-yup-middleware'; 123 | 124 | // ... use makeExecutableSchema from apollo-tools, or build your schema yourself 125 | 126 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 127 | ``` 128 | 129 | ### Setting the Validation Schema of each Mutation 130 | 131 | For each mutation that you want to validate the args, you must define the validation schema on the definition of the mutation. This is done using the `extensions` field: 132 | 133 | ```ts 134 | const resolvers = { 135 | // ... 136 | Mutation: { 137 | AddUser: { 138 | extensions: { 139 | yupMiddleware: { 140 | validationSchema: yupSchemaHere, 141 | }, 142 | }, 143 | resolve: async (root, args, context, info) => { 144 | // ... 145 | }, 146 | }, 147 | }, 148 | }; 149 | ``` 150 | 151 | You can also pass another property named `validationOptions` to pass 152 | other [options](#options) that should only be used for this mutation. 153 | 154 | #### graphql-relay 155 | 156 | If using the helper `mutationWithClientMutationId` from `graphql-relay`, you need to store the resulting mutation configuration to a variable, since if you try to add the `validationSchema` directly, it's not going to work (`graphql-relay` does not forward extra properties). See this issue for more details: https://github.com/graphql/graphql-relay-js/issues/244 157 | 158 | This will not work: 159 | 160 | ```js 161 | export default mutationWithClientMutationId({ 162 | name: 'MyMutation', 163 | validationSchema: yup.object().shape({ 164 | input: yup.object().shape({ 165 | // ... 166 | }), 167 | }), 168 | mutateAndGetPayload: async (args) => { 169 | // ... 170 | }, 171 | outputFields: { 172 | // ... 173 | }, 174 | }); 175 | ``` 176 | 177 | This will: 178 | 179 | ```js 180 | const mutation = mutationWithClientMutationId({ 181 | name: 'MyMutation', 182 | mutateAndGetPayload: async (args) => { 183 | // ... 184 | }, 185 | outputFields: { 186 | // ... 187 | }, 188 | }); 189 | 190 | export default { 191 | ...mutation, 192 | extensions: { 193 | ...mutation.extensions, 194 | yupMiddleware: { 195 | validationSchema: yup.object().shape({ 196 | input: yup.object().shape({ 197 | // ... 198 | }), 199 | }), 200 | }, 201 | }, 202 | }; 203 | ``` 204 | 205 | [graphql]: https://github.com/graphql/graphql-js 206 | [graphql-middleware]: https://github.com/maticzav/graphql-middleware 207 | [graphql-yoga]: https://github.com/prisma/graphql-yoga 208 | [yup]: https://github.com/jquense/yup 209 | -------------------------------------------------------------------------------- /examples/graphql-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": "true", 3 | "dependencies": { 4 | "express": "^4.17.1", 5 | "express-graphql": "^0.12.0", 6 | "graphql": "^15.4.0", 7 | "graphql-middleware": "^6.0.2", 8 | "graphql-yup-middleware": "next", 9 | "yup": "^0.32.8" 10 | }, 11 | "devDependencies": { 12 | "@types/express": "^4.11.1", 13 | "ts-node": "^9.1.1", 14 | "typescript": "^4.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/graphql-js/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import { graphqlHTTP } from 'express-graphql'; 3 | import { 4 | GraphQLNonNull, 5 | GraphQLObjectType, 6 | GraphQLSchema, 7 | GraphQLString, 8 | } from 'graphql'; 9 | import { applyMiddleware } from 'graphql-middleware'; 10 | import * as yup from 'yup'; 11 | import { 12 | MutationValidationErrorType, 13 | yupMiddleware, 14 | } from 'graphql-yup-middleware'; 15 | 16 | // Construct a schema 17 | 18 | const QueryType = new GraphQLObjectType({ 19 | name: 'Query', 20 | fields: { 21 | hello: { 22 | type: GraphQLNonNull(GraphQLString), 23 | resolve: () => 'world', 24 | }, 25 | }, 26 | }); 27 | 28 | const MutationType = new GraphQLObjectType({ 29 | name: 'Mutation', 30 | fields: { 31 | addUser: { 32 | extensions: { 33 | yupMiddleware: { 34 | validationSchema: yup.object({ 35 | name: yup.string().required().min(12), 36 | }), 37 | }, 38 | }, 39 | args: { 40 | name: { 41 | type: GraphQLNonNull(GraphQLString), 42 | }, 43 | }, 44 | type: new GraphQLObjectType({ 45 | name: 'AddUserPayload', 46 | fields: { 47 | error: { 48 | type: MutationValidationErrorType, 49 | }, 50 | }, 51 | }), 52 | }, 53 | }, 54 | }); 55 | 56 | const schema = new GraphQLSchema({ 57 | query: QueryType, 58 | mutation: MutationType, 59 | }); 60 | 61 | const app = express(); 62 | app.use( 63 | '/graphql', 64 | graphqlHTTP({ 65 | schema: applyMiddleware(schema, yupMiddleware()), 66 | graphiql: true, 67 | }), 68 | ); 69 | app.listen(3000); 70 | console.log('Running a GraphQL API server at http://localhost:3000/graphql'); 71 | -------------------------------------------------------------------------------- /examples/graphql-js/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@ardatan/aggregate-error@0.0.6": 6 | version "0.0.6" 7 | resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" 8 | integrity sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ== 9 | dependencies: 10 | tslib "~2.0.1" 11 | 12 | "@babel/runtime@^7.10.5": 13 | version "7.12.5" 14 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" 15 | integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== 16 | dependencies: 17 | regenerator-runtime "^0.13.4" 18 | 19 | "@graphql-tools/batch-execute@^7.0.0": 20 | version "7.0.0" 21 | resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz#e79d11bd5b39f29172f6ec2eafa71103c6a6c85b" 22 | integrity sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q== 23 | dependencies: 24 | "@graphql-tools/utils" "^7.0.0" 25 | dataloader "2.0.0" 26 | is-promise "4.0.0" 27 | tslib "~2.0.1" 28 | 29 | "@graphql-tools/delegate@^7.0.9": 30 | version "7.0.9" 31 | resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-7.0.9.tgz#9afbffce378cdcbe72e9f4339a1c03b92c0e496e" 32 | integrity sha512-nlelOb354cyweV+SpTXkoGZHKgJ1PwPkHWq/0RXM5LllQsR3OrSQKKWMrd1g9dyawEZbadCF3fDJJWSQ4HOg2g== 33 | dependencies: 34 | "@ardatan/aggregate-error" "0.0.6" 35 | "@graphql-tools/batch-execute" "^7.0.0" 36 | "@graphql-tools/schema" "^7.0.0" 37 | "@graphql-tools/utils" "^7.1.6" 38 | dataloader "2.0.0" 39 | is-promise "4.0.0" 40 | tslib "~2.1.0" 41 | 42 | "@graphql-tools/schema@^7.0.0", "@graphql-tools/schema@^7.1.2": 43 | version "7.1.2" 44 | resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-7.1.2.tgz#5084eaef893719ad01329f77673d102e7710542e" 45 | integrity sha512-GabNT51ErVHE2riDH4EQdRusUsI+nMElT8LdFHyuP53v8gwtleAj+LePQ9jif4NYUe/JQVqO8V28vPcHrA7gfQ== 46 | dependencies: 47 | "@graphql-tools/utils" "^7.1.2" 48 | tslib "~2.0.1" 49 | 50 | "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.1.6": 51 | version "7.2.4" 52 | resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.2.4.tgz#1164cf268988254f281b4cfbbc0e8f7ca24a8a41" 53 | integrity sha512-EDSb98dTWX8FngvayWejip1DutOl0wGtNbXC7a3CZf5fiJS7bGHQ/8cSlMhe9XaHwpLJCbAk/Ijnp/dYbXk33w== 54 | dependencies: 55 | "@ardatan/aggregate-error" "0.0.6" 56 | camel-case "4.1.2" 57 | tslib "~2.1.0" 58 | 59 | "@types/body-parser@*": 60 | version "1.19.0" 61 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" 62 | integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== 63 | dependencies: 64 | "@types/connect" "*" 65 | "@types/node" "*" 66 | 67 | "@types/connect@*": 68 | version "3.4.34" 69 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" 70 | integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== 71 | dependencies: 72 | "@types/node" "*" 73 | 74 | "@types/express-serve-static-core@^4.17.18": 75 | version "4.17.18" 76 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz#8371e260f40e0e1ca0c116a9afcd9426fa094c40" 77 | integrity sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA== 78 | dependencies: 79 | "@types/node" "*" 80 | "@types/qs" "*" 81 | "@types/range-parser" "*" 82 | 83 | "@types/express@^4.11.1": 84 | version "4.17.11" 85 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545" 86 | integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg== 87 | dependencies: 88 | "@types/body-parser" "*" 89 | "@types/express-serve-static-core" "^4.17.18" 90 | "@types/qs" "*" 91 | "@types/serve-static" "*" 92 | 93 | "@types/lodash.groupby@^4.6.6": 94 | version "4.6.6" 95 | resolved "https://registry.yarnpkg.com/@types/lodash.groupby/-/lodash.groupby-4.6.6.tgz#4d9b61a4d8b0d83d384975cabfed4c1769d6792e" 96 | integrity sha512-kwg3T7Ia63KtDNoQQR8hKrLHCAgrH4I44l5uEMuA6JCbj7DiSccaV4tNV1vbjtAOpX990SolVthJCmBVtRVRgw== 97 | dependencies: 98 | "@types/lodash" "*" 99 | 100 | "@types/lodash@*", "@types/lodash@^4.14.165": 101 | version "4.14.168" 102 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" 103 | integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== 104 | 105 | "@types/mime@^1": 106 | version "1.3.2" 107 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" 108 | integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== 109 | 110 | "@types/node@*": 111 | version "14.14.22" 112 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" 113 | integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== 114 | 115 | "@types/qs@*": 116 | version "6.9.5" 117 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" 118 | integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== 119 | 120 | "@types/range-parser@*": 121 | version "1.2.3" 122 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" 123 | integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== 124 | 125 | "@types/serve-static@*": 126 | version "1.13.9" 127 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" 128 | integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== 129 | dependencies: 130 | "@types/mime" "^1" 131 | "@types/node" "*" 132 | 133 | "@types/yup@^0.29.11": 134 | version "0.29.11" 135 | resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.11.tgz#d654a112973f5e004bf8438122bd7e56a8e5cd7e" 136 | integrity sha512-9cwk3c87qQKZrT251EDoibiYRILjCmxBvvcb4meofCmx1vdnNcR9gyildy5vOHASpOKMsn42CugxUvcwK5eu1g== 137 | 138 | accepts@^1.3.7, accepts@~1.3.7: 139 | version "1.3.7" 140 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 141 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 142 | dependencies: 143 | mime-types "~2.1.24" 144 | negotiator "0.6.2" 145 | 146 | arg@^4.1.0: 147 | version "4.1.3" 148 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 149 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 150 | 151 | array-flatten@1.1.1: 152 | version "1.1.1" 153 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 154 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 155 | 156 | body-parser@1.19.0: 157 | version "1.19.0" 158 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" 159 | integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== 160 | dependencies: 161 | bytes "3.1.0" 162 | content-type "~1.0.4" 163 | debug "2.6.9" 164 | depd "~1.1.2" 165 | http-errors "1.7.2" 166 | iconv-lite "0.4.24" 167 | on-finished "~2.3.0" 168 | qs "6.7.0" 169 | raw-body "2.4.0" 170 | type-is "~1.6.17" 171 | 172 | buffer-from@^1.0.0: 173 | version "1.1.1" 174 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 175 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 176 | 177 | bytes@3.1.0: 178 | version "3.1.0" 179 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 180 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 181 | 182 | camel-case@4.1.2: 183 | version "4.1.2" 184 | resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" 185 | integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== 186 | dependencies: 187 | pascal-case "^3.1.2" 188 | tslib "^2.0.3" 189 | 190 | content-disposition@0.5.3: 191 | version "0.5.3" 192 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" 193 | integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== 194 | dependencies: 195 | safe-buffer "5.1.2" 196 | 197 | content-type@^1.0.4, content-type@~1.0.4: 198 | version "1.0.4" 199 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 200 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 201 | 202 | cookie-signature@1.0.6: 203 | version "1.0.6" 204 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 205 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 206 | 207 | cookie@0.4.0: 208 | version "0.4.0" 209 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" 210 | integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== 211 | 212 | create-require@^1.1.0: 213 | version "1.1.1" 214 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 215 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 216 | 217 | dataloader@2.0.0: 218 | version "2.0.0" 219 | resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" 220 | integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== 221 | 222 | debug@2.6.9: 223 | version "2.6.9" 224 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 225 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 226 | dependencies: 227 | ms "2.0.0" 228 | 229 | depd@~1.1.2: 230 | version "1.1.2" 231 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 232 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 233 | 234 | destroy@~1.0.4: 235 | version "1.0.4" 236 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 237 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 238 | 239 | diff@^4.0.1: 240 | version "4.0.2" 241 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 242 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 243 | 244 | ee-first@1.1.1: 245 | version "1.1.1" 246 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 247 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 248 | 249 | encodeurl@~1.0.2: 250 | version "1.0.2" 251 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 252 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 253 | 254 | escape-html@~1.0.3: 255 | version "1.0.3" 256 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 257 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 258 | 259 | etag@~1.8.1: 260 | version "1.8.1" 261 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 262 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 263 | 264 | express-graphql@^0.12.0: 265 | version "0.12.0" 266 | resolved "https://registry.yarnpkg.com/express-graphql/-/express-graphql-0.12.0.tgz#58deabc309909ca2c9fe2f83f5fbe94429aa23df" 267 | integrity sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg== 268 | dependencies: 269 | accepts "^1.3.7" 270 | content-type "^1.0.4" 271 | http-errors "1.8.0" 272 | raw-body "^2.4.1" 273 | 274 | express@^4.17.1: 275 | version "4.17.1" 276 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" 277 | integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== 278 | dependencies: 279 | accepts "~1.3.7" 280 | array-flatten "1.1.1" 281 | body-parser "1.19.0" 282 | content-disposition "0.5.3" 283 | content-type "~1.0.4" 284 | cookie "0.4.0" 285 | cookie-signature "1.0.6" 286 | debug "2.6.9" 287 | depd "~1.1.2" 288 | encodeurl "~1.0.2" 289 | escape-html "~1.0.3" 290 | etag "~1.8.1" 291 | finalhandler "~1.1.2" 292 | fresh "0.5.2" 293 | merge-descriptors "1.0.1" 294 | methods "~1.1.2" 295 | on-finished "~2.3.0" 296 | parseurl "~1.3.3" 297 | path-to-regexp "0.1.7" 298 | proxy-addr "~2.0.5" 299 | qs "6.7.0" 300 | range-parser "~1.2.1" 301 | safe-buffer "5.1.2" 302 | send "0.17.1" 303 | serve-static "1.14.1" 304 | setprototypeof "1.1.1" 305 | statuses "~1.5.0" 306 | type-is "~1.6.18" 307 | utils-merge "1.0.1" 308 | vary "~1.1.2" 309 | 310 | finalhandler@~1.1.2: 311 | version "1.1.2" 312 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 313 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 314 | dependencies: 315 | debug "2.6.9" 316 | encodeurl "~1.0.2" 317 | escape-html "~1.0.3" 318 | on-finished "~2.3.0" 319 | parseurl "~1.3.3" 320 | statuses "~1.5.0" 321 | unpipe "~1.0.0" 322 | 323 | forwarded@~0.1.2: 324 | version "0.1.2" 325 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 326 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= 327 | 328 | fresh@0.5.2: 329 | version "0.5.2" 330 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 331 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 332 | 333 | graphql-middleware@^6.0.2: 334 | version "6.0.2" 335 | resolved "https://registry.yarnpkg.com/graphql-middleware/-/graphql-middleware-6.0.2.tgz#9a95f05d8b17a04dcb7e119184cf9eca431a33d8" 336 | integrity sha512-lC310BIJ1vxcG3OM8nhlbZE7ykRDpqMv3LkH72VLvy5Vj2c8ngPISli78iRGbjIlceUw5KeNpXTgqPoOBimk0Q== 337 | dependencies: 338 | "@graphql-tools/delegate" "^7.0.9" 339 | "@graphql-tools/schema" "^7.1.2" 340 | 341 | graphql-yup-middleware@next: 342 | version "1.0.0-0" 343 | resolved "https://registry.yarnpkg.com/graphql-yup-middleware/-/graphql-yup-middleware-1.0.0-0.tgz#8d006a47d03d284bcc562b22d98bd2ae154d8802" 344 | integrity sha512-37Eq0jMYLVWNNBSE9o43VImgZvdubEWP3Pl/sC0/Fj3iTkFabRRMV331K//ydYyGXLH+1nYfosYSQ15OxLjOgA== 345 | dependencies: 346 | "@types/lodash.groupby" "^4.6.6" 347 | "@types/yup" "^0.29.11" 348 | lodash.groupby "^4.6.0" 349 | 350 | graphql@^15.4.0: 351 | version "15.4.0" 352 | resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.4.0.tgz#e459dea1150da5a106486ba7276518b5295a4347" 353 | integrity sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA== 354 | 355 | http-errors@1.7.2: 356 | version "1.7.2" 357 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" 358 | integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== 359 | dependencies: 360 | depd "~1.1.2" 361 | inherits "2.0.3" 362 | setprototypeof "1.1.1" 363 | statuses ">= 1.5.0 < 2" 364 | toidentifier "1.0.0" 365 | 366 | http-errors@1.7.3, http-errors@~1.7.2: 367 | version "1.7.3" 368 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 369 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== 370 | dependencies: 371 | depd "~1.1.2" 372 | inherits "2.0.4" 373 | setprototypeof "1.1.1" 374 | statuses ">= 1.5.0 < 2" 375 | toidentifier "1.0.0" 376 | 377 | http-errors@1.8.0: 378 | version "1.8.0" 379 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" 380 | integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== 381 | dependencies: 382 | depd "~1.1.2" 383 | inherits "2.0.4" 384 | setprototypeof "1.2.0" 385 | statuses ">= 1.5.0 < 2" 386 | toidentifier "1.0.0" 387 | 388 | iconv-lite@0.4.24: 389 | version "0.4.24" 390 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 391 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 392 | dependencies: 393 | safer-buffer ">= 2.1.2 < 3" 394 | 395 | inherits@2.0.3: 396 | version "2.0.3" 397 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 398 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 399 | 400 | inherits@2.0.4: 401 | version "2.0.4" 402 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 403 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 404 | 405 | ipaddr.js@1.9.1: 406 | version "1.9.1" 407 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 408 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 409 | 410 | is-promise@4.0.0: 411 | version "4.0.0" 412 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" 413 | integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== 414 | 415 | lodash-es@^4.17.11: 416 | version "4.17.20" 417 | resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7" 418 | integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA== 419 | 420 | lodash.groupby@^4.6.0: 421 | version "4.6.0" 422 | resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" 423 | integrity sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E= 424 | 425 | lodash@^4.17.20: 426 | version "4.17.20" 427 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 428 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 429 | 430 | lower-case@^2.0.2: 431 | version "2.0.2" 432 | resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" 433 | integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== 434 | dependencies: 435 | tslib "^2.0.3" 436 | 437 | make-error@^1.1.1: 438 | version "1.3.6" 439 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 440 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 441 | 442 | media-typer@0.3.0: 443 | version "0.3.0" 444 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 445 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 446 | 447 | merge-descriptors@1.0.1: 448 | version "1.0.1" 449 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 450 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 451 | 452 | methods@~1.1.2: 453 | version "1.1.2" 454 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 455 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 456 | 457 | mime-db@1.45.0: 458 | version "1.45.0" 459 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" 460 | integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== 461 | 462 | mime-types@~2.1.24: 463 | version "2.1.28" 464 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" 465 | integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== 466 | dependencies: 467 | mime-db "1.45.0" 468 | 469 | mime@1.6.0: 470 | version "1.6.0" 471 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 472 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 473 | 474 | ms@2.0.0: 475 | version "2.0.0" 476 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 477 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 478 | 479 | ms@2.1.1: 480 | version "2.1.1" 481 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 482 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 483 | 484 | nanoclone@^0.2.1: 485 | version "0.2.1" 486 | resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" 487 | integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== 488 | 489 | negotiator@0.6.2: 490 | version "0.6.2" 491 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 492 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 493 | 494 | no-case@^3.0.4: 495 | version "3.0.4" 496 | resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" 497 | integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== 498 | dependencies: 499 | lower-case "^2.0.2" 500 | tslib "^2.0.3" 501 | 502 | on-finished@~2.3.0: 503 | version "2.3.0" 504 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 505 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 506 | dependencies: 507 | ee-first "1.1.1" 508 | 509 | parseurl@~1.3.3: 510 | version "1.3.3" 511 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 512 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 513 | 514 | pascal-case@^3.1.2: 515 | version "3.1.2" 516 | resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" 517 | integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== 518 | dependencies: 519 | no-case "^3.0.4" 520 | tslib "^2.0.3" 521 | 522 | path-to-regexp@0.1.7: 523 | version "0.1.7" 524 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 525 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 526 | 527 | property-expr@^2.0.4: 528 | version "2.0.4" 529 | resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" 530 | integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== 531 | 532 | proxy-addr@~2.0.5: 533 | version "2.0.6" 534 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" 535 | integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== 536 | dependencies: 537 | forwarded "~0.1.2" 538 | ipaddr.js "1.9.1" 539 | 540 | qs@6.7.0: 541 | version "6.7.0" 542 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 543 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== 544 | 545 | range-parser@~1.2.1: 546 | version "1.2.1" 547 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 548 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 549 | 550 | raw-body@2.4.0: 551 | version "2.4.0" 552 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" 553 | integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== 554 | dependencies: 555 | bytes "3.1.0" 556 | http-errors "1.7.2" 557 | iconv-lite "0.4.24" 558 | unpipe "1.0.0" 559 | 560 | raw-body@^2.4.1: 561 | version "2.4.1" 562 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" 563 | integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== 564 | dependencies: 565 | bytes "3.1.0" 566 | http-errors "1.7.3" 567 | iconv-lite "0.4.24" 568 | unpipe "1.0.0" 569 | 570 | regenerator-runtime@^0.13.4: 571 | version "0.13.7" 572 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" 573 | integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== 574 | 575 | safe-buffer@5.1.2: 576 | version "5.1.2" 577 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 578 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 579 | 580 | "safer-buffer@>= 2.1.2 < 3": 581 | version "2.1.2" 582 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 583 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 584 | 585 | send@0.17.1: 586 | version "0.17.1" 587 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 588 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== 589 | dependencies: 590 | debug "2.6.9" 591 | depd "~1.1.2" 592 | destroy "~1.0.4" 593 | encodeurl "~1.0.2" 594 | escape-html "~1.0.3" 595 | etag "~1.8.1" 596 | fresh "0.5.2" 597 | http-errors "~1.7.2" 598 | mime "1.6.0" 599 | ms "2.1.1" 600 | on-finished "~2.3.0" 601 | range-parser "~1.2.1" 602 | statuses "~1.5.0" 603 | 604 | serve-static@1.14.1: 605 | version "1.14.1" 606 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 607 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== 608 | dependencies: 609 | encodeurl "~1.0.2" 610 | escape-html "~1.0.3" 611 | parseurl "~1.3.3" 612 | send "0.17.1" 613 | 614 | setprototypeof@1.1.1: 615 | version "1.1.1" 616 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 617 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== 618 | 619 | setprototypeof@1.2.0: 620 | version "1.2.0" 621 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 622 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 623 | 624 | source-map-support@^0.5.17: 625 | version "0.5.19" 626 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" 627 | integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== 628 | dependencies: 629 | buffer-from "^1.0.0" 630 | source-map "^0.6.0" 631 | 632 | source-map@^0.6.0: 633 | version "0.6.1" 634 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 635 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 636 | 637 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 638 | version "1.5.0" 639 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 640 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 641 | 642 | toidentifier@1.0.0: 643 | version "1.0.0" 644 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 645 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== 646 | 647 | toposort@^2.0.2: 648 | version "2.0.2" 649 | resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" 650 | integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= 651 | 652 | ts-node@^9.1.1: 653 | version "9.1.1" 654 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" 655 | integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== 656 | dependencies: 657 | arg "^4.1.0" 658 | create-require "^1.1.0" 659 | diff "^4.0.1" 660 | make-error "^1.1.1" 661 | source-map-support "^0.5.17" 662 | yn "3.1.1" 663 | 664 | tslib@^2.0.3, tslib@~2.1.0: 665 | version "2.1.0" 666 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" 667 | integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== 668 | 669 | tslib@~2.0.1: 670 | version "2.0.3" 671 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" 672 | integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== 673 | 674 | type-is@~1.6.17, type-is@~1.6.18: 675 | version "1.6.18" 676 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 677 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 678 | dependencies: 679 | media-typer "0.3.0" 680 | mime-types "~2.1.24" 681 | 682 | typescript@^4.1.3: 683 | version "4.1.3" 684 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" 685 | integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== 686 | 687 | unpipe@1.0.0, unpipe@~1.0.0: 688 | version "1.0.0" 689 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 690 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 691 | 692 | utils-merge@1.0.1: 693 | version "1.0.1" 694 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 695 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 696 | 697 | vary@~1.1.2: 698 | version "1.1.2" 699 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 700 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 701 | 702 | yn@3.1.1: 703 | version "3.1.1" 704 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 705 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 706 | 707 | yup@^0.32.8: 708 | version "0.32.8" 709 | resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.8.tgz#16e4a949a86a69505abf99fd0941305ac9adfc39" 710 | integrity sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA== 711 | dependencies: 712 | "@babel/runtime" "^7.10.5" 713 | "@types/lodash" "^4.14.165" 714 | lodash "^4.17.20" 715 | lodash-es "^4.17.11" 716 | nanoclone "^0.2.1" 717 | property-expr "^2.0.4" 718 | toposort "^2.0.2" 719 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-yup-middleware", 3 | "version": "1.0.0", 4 | "description": "GraphQL middleware to validate mutations arguments using yup", 5 | "repository": "https://github.com/JCMais/graphql-yup-middleware", 6 | "license": "MIT", 7 | "author": "Jonathan Cardoso Machado", 8 | "main": "./dist/index.js", 9 | "types": "./dist/index.d.ts", 10 | "files": [ 11 | "dist" 12 | ], 13 | "scripts": { 14 | "build": "yarn clean && tsc", 15 | "clean": "rimraf dist", 16 | "lint": "eslint src/**/*.ts", 17 | "prepublishOnly": "yarn lint && yarn test && yarn build", 18 | "prettier:all": "prettier --write src/**/*.ts", 19 | "test": "jest", 20 | "test:watch": "jest --watchAll" 21 | }, 22 | "husky": { 23 | "hooks": { 24 | "pre-commit": "lint-staged" 25 | } 26 | }, 27 | "lint-staged": { 28 | "**/*.{ts,js,tsx,jsx}": [ 29 | "prettier --write", 30 | "eslint --fix" 31 | ], 32 | "**/*.{json,css,md,yaml,yml,html,graphql}": [ 33 | "prettier --write" 34 | ], 35 | "**/package.json": [ 36 | "sort-package-json" 37 | ] 38 | }, 39 | "jest": { 40 | "globals": { 41 | "ts-jest": { 42 | "babelConfig": false 43 | } 44 | }, 45 | "moduleFileExtensions": [ 46 | "ts", 47 | "tsx", 48 | "js", 49 | "jsx", 50 | "json", 51 | "node" 52 | ], 53 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 54 | "transform": { 55 | "^.+\\.tsx?$": "ts-jest" 56 | } 57 | }, 58 | "dependencies": { 59 | "@types/lodash.groupby": "^4.6.6", 60 | "@types/yup": "^0.29.11", 61 | "lodash.groupby": "^4.6.0" 62 | }, 63 | "devDependencies": { 64 | "@jcm/eslint-config": "^0.0.2", 65 | "@types/jest": "^26.0.20", 66 | "@typescript-eslint/eslint-plugin": "^4.14.0", 67 | "@typescript-eslint/parser": "^4.14.0", 68 | "eslint": "^7.18.0", 69 | "eslint-config-prettier": "^7.2.0", 70 | "eslint-plugin-prettier": "^3.3.1", 71 | "graphql": "^15.4.0", 72 | "graphql-middleware": "^6.0.2", 73 | "graphql-tools": "^7.0.2", 74 | "husky": "^4.3.8", 75 | "jest": "^26.6.3", 76 | "lint-staged": "^10.5.3", 77 | "np": "^7.2.0", 78 | "prettier": "^2.2.1", 79 | "rimraf": "^3.0.2", 80 | "sort-package-json": "^1.48.1", 81 | "ts-jest": "^26.4.4", 82 | "typescript": "^4.1.3", 83 | "yup": "^0.32.8" 84 | }, 85 | "peerDependencies": { 86 | "graphql": "^15.0.0", 87 | "graphql-middleware": ">= 6.0.2", 88 | "yup": ">= 0.25 < 1" 89 | }, 90 | "engines": { 91 | "node": ">= 12" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/FieldValidationErrorType.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType, 3 | GraphQLString, 4 | GraphQLNonNull, 5 | GraphQLList, 6 | } from 'graphql'; 7 | 8 | export const FieldValidationErrorType = new GraphQLObjectType({ 9 | name: 'FieldValidationError', 10 | fields: { 11 | field: { type: new GraphQLNonNull(GraphQLString) }, 12 | errors: { 13 | type: new GraphQLNonNull( 14 | new GraphQLList(new GraphQLNonNull(GraphQLString)), 15 | ), 16 | }, 17 | }, 18 | }); 19 | 20 | export const FieldValidationError = ` 21 | type FieldValidationError { 22 | field: String! 23 | errors: [String!]! 24 | } 25 | `; 26 | -------------------------------------------------------------------------------- /src/MutationValidationErrorType.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLString, 3 | GraphQLList, 4 | GraphQLObjectType, 5 | GraphQLNonNull, 6 | } from 'graphql'; 7 | 8 | import { FieldValidationErrorType } from './FieldValidationErrorType'; 9 | 10 | export const MutationValidationErrorType = new GraphQLObjectType({ 11 | name: 'MutationValidationError', 12 | fields: { 13 | message: { type: new GraphQLNonNull(GraphQLString) }, 14 | details: { 15 | type: new GraphQLNonNull( 16 | new GraphQLList(new GraphQLNonNull(FieldValidationErrorType)), 17 | ), 18 | }, 19 | }, 20 | }); 21 | 22 | export const MutationValidationError = ` 23 | type MutationValidationError { 24 | message: String! 25 | details: [FieldValidationError!]! 26 | } 27 | `; 28 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/yupMiddleware.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Options should forward yup options correctly 1`] = ` 4 | Object { 5 | "data": Object { 6 | "AddUser": Object { 7 | "error": "age must be greater than or equal to 18", 8 | "user": null, 9 | }, 10 | }, 11 | } 12 | `; 13 | 14 | exports[`Options should transform args only when specified 1`] = ` 15 | Object { 16 | "data": Object { 17 | "AddUser": Object { 18 | "error": null, 19 | "user": Object { 20 | "age": 18, 21 | "firstName": " Jon ", 22 | "id": 3, 23 | "lastName": " Doe ", 24 | }, 25 | }, 26 | }, 27 | } 28 | `; 29 | 30 | exports[`Options should transform args only when specified 2`] = ` 31 | Object { 32 | "data": Object { 33 | "AddUser": Object { 34 | "error": null, 35 | "user": Object { 36 | "age": 18, 37 | "firstName": "Jon", 38 | "id": 3, 39 | "lastName": "Doe", 40 | }, 41 | }, 42 | }, 43 | } 44 | `; 45 | 46 | exports[`Options should use validationOptions from mutation definition if present 1`] = ` 47 | Object { 48 | "data": Object { 49 | "AddUserWithOptions": Object { 50 | "error": "age must be greater than or equal to 18", 51 | "user": null, 52 | }, 53 | }, 54 | } 55 | `; 56 | 57 | exports[`should give error if not using middleware correctly 1`] = ` 58 | Array [ 59 | [GraphQLError: You have to call the yupMutationMiddleware before adding it to the middlewares: yupMutationMiddleware()], 60 | ] 61 | `; 62 | 63 | exports[`should validate correctly - custom error - error 1`] = ` 64 | Object { 65 | "data": Object { 66 | "AddUserErrorCustom": Object { 67 | "errors": Array [ 68 | "3 errors occurred", 69 | "firstName must be at least 1 characters", 70 | "lastName must be at least 1 characters", 71 | "age must be greater than or equal to 18", 72 | ], 73 | "user": null, 74 | }, 75 | }, 76 | } 77 | `; 78 | 79 | exports[`should validate correctly - custom error - pass 1`] = ` 80 | Object { 81 | "data": Object { 82 | "AddUserErrorCustom": Object { 83 | "errors": null, 84 | "user": Object { 85 | "age": 18, 86 | "firstName": "Jon", 87 | "id": 3, 88 | "lastName": "Doe", 89 | }, 90 | }, 91 | }, 92 | } 93 | `; 94 | 95 | exports[`should validate correctly - object error - error 1`] = ` 96 | Object { 97 | "data": Object { 98 | "AddUserErrorObject": Object { 99 | "error": Object { 100 | "details": Array [ 101 | Object { 102 | "errors": Array [ 103 | "firstName must be at least 1 characters", 104 | ], 105 | "field": "firstName", 106 | }, 107 | Object { 108 | "errors": Array [ 109 | "lastName must be at least 1 characters", 110 | ], 111 | "field": "lastName", 112 | }, 113 | Object { 114 | "errors": Array [ 115 | "age must be greater than or equal to 18", 116 | ], 117 | "field": "age", 118 | }, 119 | ], 120 | "message": "3 errors occurred", 121 | }, 122 | "user": null, 123 | }, 124 | }, 125 | } 126 | `; 127 | 128 | exports[`should validate correctly - object error - pass 1`] = ` 129 | Object { 130 | "data": Object { 131 | "AddUserErrorObject": Object { 132 | "error": null, 133 | "user": Object { 134 | "age": 18, 135 | "firstName": "Jon", 136 | "id": 3, 137 | "lastName": "Doe", 138 | }, 139 | }, 140 | }, 141 | } 142 | `; 143 | 144 | exports[`should validate correctly - string error - error 1`] = ` 145 | Object { 146 | "data": Object { 147 | "AddUser": Object { 148 | "error": "3 errors occurred", 149 | "user": null, 150 | }, 151 | }, 152 | } 153 | `; 154 | 155 | exports[`should validate correctly - string error - pass 1`] = ` 156 | Object { 157 | "data": Object { 158 | "AddUser": Object { 159 | "error": null, 160 | "user": Object { 161 | "age": 18, 162 | "firstName": "Jon", 163 | "id": 3, 164 | "lastName": "Doe", 165 | }, 166 | }, 167 | }, 168 | } 169 | `; 170 | -------------------------------------------------------------------------------- /src/__tests__/yupMiddleware.spec.ts: -------------------------------------------------------------------------------- 1 | import { graphql } from 'graphql'; 2 | import { applyMiddleware } from 'graphql-middleware'; 3 | import { 4 | IExecutableSchemaDefinition, 5 | IFieldResolverOptions, 6 | makeExecutableSchema, 7 | } from 'graphql-tools'; 8 | import * as yup from 'yup'; 9 | 10 | import yupMiddleware from '../yupMiddleware'; 11 | import { FieldValidationError } from '../FieldValidationErrorType'; 12 | import { MutationValidationError } from '../MutationValidationErrorType'; 13 | import { YupMiddlewareErrorContext } from '../types'; 14 | 15 | // got it throwing a dice 🎲 16 | const randomId = 3; 17 | 18 | const getDefaultSchema = >( 19 | validationSchema?: yup.AnyObjectSchema, 20 | ) => { 21 | const typeDefs = ` 22 | type User { 23 | id: Int! 24 | firstName: String! 25 | lastName: String! 26 | age: Int! 27 | } 28 | type AddUserPayload { 29 | error: String 30 | user: User 31 | } 32 | type AddUserPayloadWithErrorObject { 33 | error: MutationValidationError 34 | user: User 35 | } 36 | type AddUserPayloadWithErrorCustom { 37 | errors: [String!] 38 | user: User 39 | } 40 | type Mutation { 41 | AddUser(firstName: String!, lastName: String!, age: Int!): AddUserPayload! 42 | AddUserErrorObject(firstName: String!, lastName: String!, age: Int!): AddUserPayloadWithErrorObject! 43 | AddUserErrorCustom(firstName: String!, lastName: String!, age: Int!): AddUserPayloadWithErrorCustom! 44 | AddUserWithOptions(firstName: String!, lastName: String!, age: Int!): AddUserPayload! 45 | } 46 | type Query { 47 | hello: String! 48 | } 49 | `; 50 | 51 | const AddUser: IFieldResolverOptions = { 52 | extensions: validationSchema 53 | ? { 54 | yupMiddleware: { 55 | validationSchema, 56 | }, 57 | } 58 | : undefined, 59 | resolve: (_: TSource, args: TArgs) => { 60 | return { 61 | user: { 62 | ...args, 63 | id: randomId, 64 | }, 65 | }; 66 | }, 67 | }; 68 | 69 | const resolvers: IExecutableSchemaDefinition['resolvers'] = { 70 | Mutation: { 71 | AddUser, 72 | AddUserErrorObject: AddUser, 73 | AddUserErrorCustom: AddUser, 74 | AddUserWithOptions: { 75 | ...AddUser, 76 | extensions: AddUser.extensions && { 77 | yupMiddleware: { 78 | ...AddUser.extensions!.yupMiddleware, 79 | validationOptions: { 80 | yupOptions: { 81 | abortEarly: true, 82 | }, 83 | }, 84 | }, 85 | }, 86 | }, 87 | }, 88 | Query: { 89 | hello: () => 'world', 90 | }, 91 | }; 92 | 93 | return makeExecutableSchema({ 94 | resolvers, 95 | typeDefs: [typeDefs, MutationValidationError, FieldValidationError], 96 | }); 97 | }; 98 | 99 | const defaultQuery = ` 100 | mutation AddUser($firstName: String!, $lastName: String!, $age: Int!) { 101 | AddUser(firstName: $firstName, lastName: $lastName, age: $age) { 102 | error 103 | user { 104 | id 105 | firstName 106 | lastName 107 | age 108 | } 109 | } 110 | } 111 | `; 112 | 113 | const defaultQueryErrorObject = ` 114 | mutation AddUser($firstName: String!, $lastName: String!, $age: Int!) { 115 | AddUserErrorObject(firstName: $firstName, lastName: $lastName, age: $age) { 116 | error { 117 | message 118 | details { 119 | field 120 | errors 121 | } 122 | } 123 | user { 124 | id 125 | firstName 126 | lastName 127 | age 128 | } 129 | } 130 | } 131 | `; 132 | 133 | const defaultQueryErrorCustom = ` 134 | mutation AddUser($firstName: String!, $lastName: String!, $age: Int!) { 135 | AddUserErrorCustom(firstName: $firstName, lastName: $lastName, age: $age) { 136 | errors 137 | user { 138 | id 139 | firstName 140 | lastName 141 | age 142 | } 143 | } 144 | } 145 | `; 146 | 147 | const defaultQueryWithOptions = ` 148 | mutation AddUser($firstName: String!, $lastName: String!, $age: Int!) { 149 | AddUserWithOptions(firstName: $firstName, lastName: $lastName, age: $age) { 150 | error 151 | user { 152 | id 153 | firstName 154 | lastName 155 | age 156 | } 157 | } 158 | } 159 | `; 160 | 161 | const customErrorPayloadBuilder = ( 162 | error: yup.ValidationError, 163 | _errorContext: YupMiddlewareErrorContext, 164 | ) => { 165 | const reduceErrors = (error: yup.ValidationError): string[] => 166 | error.inner.reduce( 167 | (acc, innerError) => [...acc, ...reduceErrors(innerError)], 168 | [error.message], 169 | ); 170 | return { 171 | errors: reduceErrors(error), 172 | }; 173 | }; 174 | 175 | it('should validate correctly - string error - error', async () => { 176 | const schema = getDefaultSchema( 177 | yup.object().shape({ 178 | firstName: yup.string().trim().min(1), 179 | lastName: yup.string().trim().min(1), 180 | age: yup.number().min(18).max(100), 181 | }), 182 | ); 183 | 184 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 185 | 186 | const res = await graphql(schemaWithMiddleware, defaultQuery, null, null, { 187 | firstName: '', 188 | lastName: '', 189 | age: 10, 190 | }); 191 | 192 | expect(res).toMatchSnapshot(); 193 | }); 194 | 195 | it('should validate correctly - string error - pass', async () => { 196 | const schema = getDefaultSchema( 197 | yup.object().shape({ 198 | firstName: yup.string().trim().min(1), 199 | lastName: yup.string().trim().min(1), 200 | age: yup.number().min(18).max(100), 201 | }), 202 | ); 203 | 204 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 205 | 206 | const res = await graphql(schemaWithMiddleware, defaultQuery, null, null, { 207 | firstName: 'Jon', 208 | lastName: 'Doe', 209 | age: 18, 210 | }); 211 | 212 | expect(res).toMatchSnapshot(); 213 | }); 214 | 215 | it('should validate correctly - object error - error', async () => { 216 | const schema = getDefaultSchema( 217 | yup.object().shape({ 218 | firstName: yup.string().trim().min(1), 219 | lastName: yup.string().trim().min(1), 220 | age: yup.number().min(18).max(100), 221 | }), 222 | ); 223 | 224 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 225 | 226 | const res = await graphql( 227 | schemaWithMiddleware, 228 | defaultQueryErrorObject, 229 | null, 230 | null, 231 | { 232 | firstName: '', 233 | lastName: '', 234 | age: 10, 235 | }, 236 | ); 237 | 238 | expect(res).toMatchSnapshot(); 239 | }); 240 | 241 | it('should validate correctly - object error - pass', async () => { 242 | const schema = getDefaultSchema( 243 | yup.object().shape({ 244 | firstName: yup.string().trim().min(1), 245 | lastName: yup.string().trim().min(1), 246 | age: yup.number().min(18).max(100), 247 | }), 248 | ); 249 | 250 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 251 | 252 | const res = await graphql( 253 | schemaWithMiddleware, 254 | defaultQueryErrorObject, 255 | null, 256 | null, 257 | { 258 | firstName: 'Jon', 259 | lastName: 'Doe', 260 | age: 18, 261 | }, 262 | ); 263 | 264 | expect(res).toMatchSnapshot(); 265 | }); 266 | 267 | it('should validate correctly - custom error - error', async () => { 268 | const schema = getDefaultSchema( 269 | yup.object().shape({ 270 | firstName: yup.string().trim().min(1), 271 | lastName: yup.string().trim().min(1), 272 | age: yup.number().min(18).max(100), 273 | }), 274 | ); 275 | 276 | const schemaWithMiddleware = applyMiddleware( 277 | schema, 278 | yupMiddleware({ 279 | errorPayloadBuilder: customErrorPayloadBuilder, 280 | }), 281 | ); 282 | 283 | const res = await graphql( 284 | schemaWithMiddleware, 285 | defaultQueryErrorCustom, 286 | null, 287 | null, 288 | { 289 | firstName: '', 290 | lastName: '', 291 | age: 10, 292 | }, 293 | ); 294 | 295 | expect(res).toMatchSnapshot(); 296 | }); 297 | 298 | it('should validate correctly - custom error - pass', async () => { 299 | const schema = getDefaultSchema( 300 | yup.object().shape({ 301 | firstName: yup.string().trim().min(1), 302 | lastName: yup.string().trim().min(1), 303 | age: yup.number().min(18).max(100), 304 | }), 305 | ); 306 | 307 | const schemaWithMiddleware = applyMiddleware( 308 | schema, 309 | yupMiddleware({ 310 | errorPayloadBuilder: customErrorPayloadBuilder, 311 | }), 312 | ); 313 | 314 | const res = await graphql( 315 | schemaWithMiddleware, 316 | defaultQueryErrorCustom, 317 | null, 318 | null, 319 | { 320 | firstName: 'Jon', 321 | lastName: 'Doe', 322 | age: 18, 323 | }, 324 | ); 325 | 326 | expect(res).toMatchSnapshot(); 327 | }); 328 | 329 | it('should do nothing if there are no validation schema', async () => { 330 | const schema = getDefaultSchema(); 331 | 332 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 333 | 334 | const res = await graphql(schemaWithMiddleware, defaultQuery, null, null, { 335 | firstName: 'J', 336 | lastName: 'D', 337 | age: 10, 338 | }); 339 | 340 | expect(res.data).toBeDefined(); 341 | expect(res.data!.AddUser.error).toBeNull(); 342 | expect(res.data!.AddUser.user.id).toBe(randomId); 343 | }); 344 | 345 | it('should give error if not using middleware correctly', async () => { 346 | const schema = getDefaultSchema(); 347 | 348 | // @ts-ignore 349 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware); 350 | 351 | const res = await graphql(schemaWithMiddleware, defaultQuery, null, null, { 352 | firstName: '', 353 | lastName: '', 354 | age: 10, 355 | }); 356 | 357 | expect(res.errors).toMatchSnapshot(); 358 | }); 359 | 360 | describe('Options', () => { 361 | it('should transform args only when specified', async () => { 362 | const schema = () => 363 | getDefaultSchema( 364 | yup.object().shape({ 365 | firstName: yup.string().trim().min(1), 366 | lastName: yup.string().trim().min(1), 367 | age: yup.number().min(18).max(100), 368 | }), 369 | ); 370 | 371 | const schemaWithMiddleware1 = applyMiddleware( 372 | schema(), 373 | yupMiddleware({ 374 | shouldTransformArgs: false, 375 | }), 376 | ); 377 | 378 | const schemaWithMiddleware2 = applyMiddleware( 379 | schema(), 380 | yupMiddleware({ 381 | shouldTransformArgs: true, 382 | }), 383 | ); 384 | 385 | const variables = { 386 | firstName: ' Jon ', 387 | lastName: ' Doe ', 388 | age: 18, 389 | }; 390 | 391 | const res1 = await graphql( 392 | schemaWithMiddleware1, 393 | defaultQuery, 394 | null, 395 | null, 396 | variables, 397 | ); 398 | expect(res1).toMatchSnapshot(); 399 | 400 | const res2 = await graphql( 401 | schemaWithMiddleware2, 402 | defaultQuery, 403 | null, 404 | null, 405 | variables, 406 | ); 407 | expect(res2).toMatchSnapshot(); 408 | }); 409 | 410 | it('should forward yup options correctly', async () => { 411 | const schema = getDefaultSchema( 412 | yup.object().shape({ 413 | firstName: yup.string().trim().min(1), 414 | lastName: yup.string().trim().min(1), 415 | age: yup.number().min(18).max(100), 416 | }), 417 | ); 418 | 419 | const schemaWithMiddleware = applyMiddleware( 420 | schema, 421 | yupMiddleware({ 422 | yupOptions: { 423 | abortEarly: true, 424 | }, 425 | }), 426 | ); 427 | 428 | const res = await graphql(schemaWithMiddleware, defaultQuery, null, null, { 429 | firstName: '', 430 | lastName: '', 431 | age: 10, 432 | }); 433 | 434 | expect(res).toMatchSnapshot(); 435 | }); 436 | 437 | it('should use validationOptions from mutation definition if present', async () => { 438 | const schema = getDefaultSchema( 439 | yup.object().shape({ 440 | firstName: yup.string().trim().min(1), 441 | lastName: yup.string().trim().min(1), 442 | age: yup.number().min(18).max(100), 443 | }), 444 | ); 445 | 446 | const schemaWithMiddleware = applyMiddleware(schema, yupMiddleware()); 447 | 448 | const res = await graphql( 449 | schemaWithMiddleware, 450 | defaultQueryWithOptions, 451 | null, 452 | null, 453 | { 454 | firstName: '', 455 | lastName: '', 456 | age: 10, 457 | }, 458 | ); 459 | 460 | expect(res).toMatchSnapshot(); 461 | }); 462 | }); 463 | -------------------------------------------------------------------------------- /src/buildErrorObjectFromValidationError.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from 'yup'; 2 | 3 | import groupBy = require('lodash.groupby'); 4 | 5 | import { 6 | YupMiddlewareErrorContext, 7 | YupMiddlewareDefaultError, 8 | YupMiddlewareFieldValidationError, 9 | } from './types'; 10 | 11 | export default function buildErrorObjectFromValidationError( 12 | error: ValidationError, 13 | _errorContext: YupMiddlewareErrorContext, 14 | ): { error: YupMiddlewareDefaultError } { 15 | let rootError: YupMiddlewareDefaultError = { 16 | message: error.message, 17 | details: [], 18 | }; 19 | 20 | if (error.inner.length) { 21 | const errorsGrouped = groupBy(error.inner, 'path'); 22 | 23 | const details = Object.keys(errorsGrouped).reduce( 24 | (acc: YupMiddlewareFieldValidationError[], key) => [ 25 | ...acc, 26 | { 27 | field: key, 28 | errors: errorsGrouped[key].map((fieldError) => fieldError.message), 29 | }, 30 | ], 31 | [], 32 | ); 33 | 34 | rootError = { 35 | ...rootError, 36 | details, 37 | }; 38 | } 39 | 40 | return { 41 | error: rootError, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/graphql.augmented.ts: -------------------------------------------------------------------------------- 1 | import 'graphql/type/definition'; 2 | 3 | import { GraphQLExtensionsYupMiddleware } from './types'; 4 | 5 | // We need to be merged to improve the types here https://github.com/graphql/graphql-js/pull/2465 6 | 7 | declare module 'graphql/type/definition' { 8 | export interface GraphQLFieldExtensions< 9 | _TSource, 10 | _TContext, 11 | _TArgs = { [argName: string]: any } 12 | > { 13 | /** 14 | * Options to be passed to the yup middleware. This is only used if this field is a Mutation 15 | */ 16 | yupMiddleware?: GraphQLExtensionsYupMiddleware<_TSource, _TContext, _TArgs>; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as yupMiddleware } from './yupMiddleware'; 2 | 3 | export { 4 | YupMiddlewareErrorContext, 5 | YupMiddlewareOptions, 6 | YupMiddlewareFieldValidationError, 7 | YupMiddlewareDefaultError, 8 | } from './types'; 9 | 10 | export { 11 | FieldValidationError, 12 | FieldValidationErrorType, 13 | } from './FieldValidationErrorType'; 14 | export { 15 | MutationValidationError, 16 | MutationValidationErrorType, 17 | } from './MutationValidationErrorType'; 18 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { GraphQLResolveInfo } from 'graphql'; 3 | 4 | export type YupMiddlewareErrorContext = { 5 | root: any; 6 | args: TArgs; 7 | context: TContext; 8 | info: GraphQLResolveInfo; 9 | }; 10 | 11 | export type YupMiddlewareOptions = { 12 | errorPayloadBuilder?: ( 13 | error: Yup.ValidationError, 14 | errorContext: YupMiddlewareErrorContext, 15 | ) => any; 16 | shouldTransformArgs?: boolean; 17 | yupOptions?: Parameters[1]; 18 | }; 19 | 20 | export type YupMiddlewareFieldValidationError = { 21 | field: string; 22 | errors: string[]; 23 | }; 24 | 25 | export type YupMiddlewareDefaultError = { 26 | message: string; 27 | details: YupMiddlewareFieldValidationError[]; 28 | }; 29 | 30 | export interface GraphQLExtensionsYupMiddleware< 31 | _TSource, 32 | _TContext, 33 | _TArgs extends Record = Record 34 | > { 35 | validationOptions?: YupMiddlewareOptions; 36 | validationSchema: 37 | | Yup.ObjectSchema<_TArgs> 38 | | (( 39 | root: _TSource, 40 | args: _TArgs, 41 | context: _TContext, 42 | info: GraphQLResolveInfo, 43 | ) => Yup.ObjectSchema<_TArgs>); 44 | } 45 | -------------------------------------------------------------------------------- /src/yupMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from 'yup'; 2 | import { 3 | GraphQLString, 4 | GraphQLObjectType, 5 | isObjectType, 6 | GraphQLNonNull, 7 | GraphQLResolveInfo, 8 | GraphQLFieldResolver, 9 | } from 'graphql'; 10 | import { IMiddleware } from 'graphql-middleware'; 11 | 12 | import './graphql.augmented'; 13 | import buildErrorObjectFromValidationError from './buildErrorObjectFromValidationError'; 14 | import { YupMiddlewareOptions } from './types'; 15 | import { MutationValidationErrorType } from './MutationValidationErrorType'; 16 | 17 | const defaultOptions = { 18 | errorPayloadBuilder: buildErrorObjectFromValidationError, 19 | shouldTransformArgs: true, 20 | yupOptions: { 21 | abortEarly: false, 22 | }, 23 | }; 24 | 25 | export default function yupMutationMiddleware< 26 | TSource = any, 27 | TContext = any, 28 | TArgs extends Record = Record 29 | >(options: YupMiddlewareOptions = {}): IMiddleware { 30 | const hasSuppliedErrorPayloadBuilder = !!options.errorPayloadBuilder; 31 | const mergedOptions = { 32 | ...defaultOptions, 33 | ...options, 34 | }; 35 | 36 | if (typeof options === 'function') { 37 | throw new TypeError( 38 | 'You have to call the yupMutationMiddleware before adding it to the middlewares: yupMutationMiddleware()', 39 | ); 40 | } 41 | 42 | return { 43 | async Mutation( 44 | resolve: GraphQLFieldResolver, 45 | root: TSource, 46 | args: TArgs, 47 | context: TContext, 48 | info: GraphQLResolveInfo, 49 | ) { 50 | const mutationField = info.schema.getMutationType(); 51 | 52 | // this should not really happen 53 | if (!mutationField) { 54 | return; 55 | } 56 | 57 | const mutationDefinition = mutationField.getFields()[info.fieldName]; 58 | 59 | if ( 60 | !mutationDefinition.extensions || 61 | !mutationDefinition.extensions.yupMiddleware 62 | ) { 63 | return resolve(root, args, context, info); 64 | } 65 | 66 | const mutationValidationSchema = 67 | mutationDefinition.extensions.yupMiddleware.validationSchema; 68 | const mutationValidationOptions = mutationDefinition.extensions! 69 | .yupMiddleware.validationOptions; 70 | 71 | if (!mutationValidationSchema) { 72 | throw new Error( 73 | `You must set extensions.yupMiddleware.validationSchema to a valid Yup schema - Error found on Mutation ${mutationDefinition.name}`, 74 | ); 75 | } 76 | 77 | const finalOptions = { 78 | ...mergedOptions, 79 | ...mutationValidationOptions, 80 | }; 81 | 82 | const schema = 83 | typeof mutationValidationSchema === 'function' 84 | ? mutationValidationSchema(root, args, context, info) 85 | : mutationValidationSchema; 86 | 87 | try { 88 | const values = await schema.validate(args, { 89 | abortEarly: false, 90 | ...finalOptions.yupOptions, 91 | }); 92 | 93 | return resolve( 94 | root, 95 | // @ts-expect-error 'TArgs' could be instantiated with a different subtype of constraint 'Record' 96 | finalOptions.shouldTransformArgs ? values : args, 97 | context, 98 | info, 99 | ); 100 | } catch (error) { 101 | if (error instanceof ValidationError) { 102 | const errorResult = finalOptions.errorPayloadBuilder(error, { 103 | root, 104 | args, 105 | context, 106 | info, 107 | }); 108 | 109 | if (!hasSuppliedErrorPayloadBuilder) { 110 | let returnType = info.returnType; 111 | 112 | // non nullable 113 | if (info.returnType instanceof GraphQLNonNull) { 114 | returnType = info.returnType.ofType; 115 | } 116 | 117 | const isObjReturnType = isObjectType(returnType); 118 | 119 | if (!isObjReturnType) { 120 | throw new Error( 121 | 'Only mutations with object return type are supported', 122 | ); 123 | } 124 | 125 | // returnType cannot be anything else at this point 126 | const fields = (returnType as GraphQLObjectType).getFields(); 127 | 128 | if (!fields.error) { 129 | throw new Error( 130 | 'You must have an error field on the payload of your mutation when using default error builder', 131 | ); 132 | } 133 | 134 | if (fields.error && fields.error.type === GraphQLString) { 135 | return { 136 | error: error.message, 137 | }; 138 | } 139 | 140 | if (!isObjectType(fields.error.type)) { 141 | throw new Error( 142 | 'Mutation payload error field must be of type GraphQLString or GraphQLObjectType', 143 | ); 144 | } 145 | 146 | // @TODO Improve this validation 147 | if (fields.error.type.name !== MutationValidationErrorType.name) { 148 | throw new Error( 149 | 'Mutation payload error must be of type MutationValidationErrorType if using default options', 150 | ); 151 | } 152 | } 153 | 154 | return errorResult; 155 | } else { 156 | throw error; 157 | } 158 | } 159 | }, 160 | }; 161 | } 162 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | // Node.js >= 12 supports 100% of ES2019 (https://node.green/) 5 | "target": "es2019", 6 | "lib": [ 7 | "es2019", 8 | "es2020.bigint", 9 | "es2020.string", 10 | "es2020.symbol.wellknown", 11 | "esnext.asynciterable" 12 | ], 13 | "declaration": true, 14 | "rootDir": "src", 15 | "outDir": "dist", 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strict": true, 19 | "strictFunctionTypes": false 20 | }, 21 | "include": ["src/**/*"], 22 | "exclude": ["node_modules", "**/*.spec.ts"] 23 | } 24 | --------------------------------------------------------------------------------