Giter VIP home page Giter VIP logo

graphql-constraint-directive's Introduction

graphql-constraint-directive

Build Status Coverage Status Known Vulnerabilities

Allows using @constraint as a directive to validate input data. Inspired by Constraints Directives RFC and OpenAPI

Install

npm install graphql-constraint-directive

For GraphQL v15 and below, use v2 of this package

npm install graphql-constraint-directive@v2

Usage

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.

Schema wrapper

Implementation based on schema wrappers - basic scalars are wrapped as custom scalars with validations.

Benefits

  • based on graphql library, works everywhere
  • posibility to also validate GraphQL response data

Caveats

  • modifies GraphQL schema, basic scalars (Int, Float, String) are replaced by custom scalars
const { constraintDirective, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const { makeExecutableSchema } = require('@graphql-tools/schema')
const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
})
schema = constraintDirective()(schema)

const app = express()
const server = new ApolloServer({ schema })

await server.start()

server.applyMiddleware({ app })

Server plugin

Implementation based on server plugin. Common server plugins are implemented, function validateQuery(schema, query, variables, operationName) can be used to implement additional plugins.

Benefits

  • schema stays unmodified

Caveats

  • runs only in supported servers
  • validates only GraphQL query, not response data

Envelop

Use as an Envelop plugin in supported frameworks, e.g. GraphQL Yoga. Functionality is plugged in execute phase

This plugin requires the following dependencies installed in your project:

  • @envelop/core - ^2.0.0
const { createEnvelopQueryValidationPlugin, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
const express = require('express')
const { createServer } = require('@graphql-yoga/node')
const { makeExecutableSchema } = require('@graphql-tools/schema')

const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
})

const app = express()

const yoga = createServer({
    schema,
    plugins: [createEnvelopQueryValidationPlugin()],
    graphiql: false
})

app.use('/', yoga)

app.listen(4000);

Apollo 3 Server

As an Apollo 3 Server plugin

This plugin requires the following dependencies installed in your project:

  • dependencies required for your selected Apollo Server 3 variant
const { createApolloQueryValidationPlugin, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const { makeExecutableSchema } = require('@graphql-tools/schema')

const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
})

const plugins = [
  createApolloQueryValidationPlugin({
    schema
  })
]

const app = express()
const server = new ApolloServer({
  schema,
  plugins
})

await server.start()

server.applyMiddleware({ app })

Apollo 4 Server

As an Apollo 4 Server plugin

This plugin requires the following dependencies installed in your project:

  • @apollo/server - ^4.0.0
  • graphql-tag - ^2.0.0
const { createApollo4QueryValidationPlugin, constraintDirectiveTypeDefs } = require('graphql-constraint-directive/apollo4')
const { ApolloServer } = require('@apollo/server')
const { startStandaloneServer } = require('@apollo/server/standalone');
const { makeExecutableSchema } = require('@graphql-tools/schema')

const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
})

const plugins = [
  createApollo4QueryValidationPlugin()
]

const server = new ApolloServer({
  schema,
  plugins
})

await startStandaloneServer(server);

Apollo 4 Subgraph server

There is a small change required to make the Apollo Server quickstart work when trying to build an Apollo Subgraph Server. We must use the buildSubgraphSchema function to build a schema that can be passed to an Apollo Gateway/supergraph, instead of makeExecuteableSchema. This uses makeExecutableSchema under the hood.

This plugin requires the following dependencies installed in your project:

  • @apollo/server - ^4.0.0
  • graphql-tag - ^2.0.0
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { createApollo4QueryValidationPlugin, constraintDirectiveTypeDefsGql } from 'graphql-constraint-directive/apollo4';

const typeDefs = gql`
  extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])

  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }
`;

const schema = buildSubgraphSchema({
  typeDefs: [constraintDirectiveTypeDefsGql, typeDefs]
});

const plugins = [
  createApollo4QueryValidationPlugin()
]

const server = new ApolloServer({
  schema,
  plugins
});

await startStandaloneServer(server);

Express

This implementation is untested now, as express-graphql module is not maintained anymore.

As a Validation rule when query variables are available

const { createQueryValidationRule, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
const express = require('express')
const { graphqlHTTP } = require('express-graphql')
const { makeExecutableSchema } = require('@graphql-tools/schema')

const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
})

const app = express()

app.use(
  '/api',
  graphqlHTTP(async (request, response, { variables }) => ({
    schema,
    validationRules: [
      createQueryValidationRule({
        variables
      })
    ]
  }))
)
app.listen(4000);

Schema documentation

You can use the provided schema transformation to automatically add @constraint documentation into fields and arguments descriptions. By default directives are not typically present in the exposed introspected schema

const { constraintDirectiveTypeDefs, constraintDirectiveDocumentation } = require('graphql-constraint-directive')
const { makeExecutableSchema } = require('@graphql-tools/schema')

const typeDefs = ...

let schema = makeExecutableSchema({
      typeDefs: [constraintDirectiveTypeDefs, typeDefs]
})

schema = constraintDirectiveDocumentation()(schema);

// any constraint directive handler implementation

This transformation appends constraint documentation header, and then a list of constraint conditions descriptions to the description of each field and argument where the @constraint directive is used.

Original schema:

"""
Existing field or argument description.
"""
fieldOrArgument: String @constraint(minLength: 10, maxLength: 50)

Transformed schema:

"""
Existing field or argument description.

*Constraints:*
* Minimum length: `10`
* Maximum length: `50`
"""
fieldOrArgument: String @constraint(minLength: 10, maxLength: 50)

CommonMark is used in the desccription for better readability.

If constraint documentation header already exists in the field or argument description, then constraint documentation is not appended. This allows you to override constraint description when necessary, or use this in a chain of subgraph/supergraph schemes.

Both constraint documentation header and constraint conditions descriptions can be customized during the transformation creation, eg. to localize them.

schema = constraintDirectiveDocumentation(
  {
    header: '*Changed header:*',
    descriptionsMap: {
      minLength: 'Changed Minimum length',
      maxLength: 'Changed Maximum length',
      startsWith: 'Changed Starts with',
      endsWith: 'Changed Ends with',
      contains: 'Changed Contains',
      notContains: 'Changed Doesn\'t contain',
      pattern: 'Changed Must match RegEx pattern',
      format: 'Changed Must match format',
      min: 'Changed Minimum value',
      max: 'Changed Maximum value',
      exclusiveMin: 'Changed Grater than',
      exclusiveMax: 'Changed Less than',
      multipleOf: 'Changed Must be a multiple of',
      minItems: 'Changed Minimum number of items',
      maxItems: 'Changed Maximum number of items'
    }
  }
)(schema);

API

String

minLength

@constraint(minLength: 5) Restrict to a minimum length

maxLength

@constraint(maxLength: 5) Restrict to a maximum length

startsWith

@constraint(startsWith: "foo") Ensure value starts with foo

endsWith

@constraint(endsWith: "foo") Ensure value ends with foo

contains

@constraint(contains: "foo") Ensure value contains foo

notContains

@constraint(notContains: "foo") Ensure value does not contain foo

pattern

@constraint(pattern: "^[0-9a-zA-Z]*$") Ensure value matches regex, e.g. alphanumeric

format

@constraint(format: "email") Ensure value is in a particular format

Supported formats:

  • byte: Base64
  • date-time: RFC 3339
  • date: ISO 8601
  • email
  • ipv4
  • ipv6
  • uri
  • uuid

Custom Format

You can add your own custom formats by passing a formats object to the plugin options. See example below.

@constraint(format: "my-custom-format")

const formats = {
  'my-custom-format': (value) => {
    if (value === 'foo') {
      return true
    }

    throw new GraphQLError('Value must be foo')
  }
};

// Envelop
createEnvelopQueryValidationPlugin({ formats })

// Apollo 3 Server
createApolloQueryValidationPlugin({ formats })

// Apollo 4 Server
createApollo4QueryValidationPlugin({ formats })

Int/Float

min

@constraint(min: 3) Ensure value is greater than or equal to

max

@constraint(max: 3) Ensure value is less than or equal to

exclusiveMin

@constraint(exclusiveMin: 3) Ensure value is greater than

exclusiveMax

@constraint(exclusiveMax: 3) Ensure value is less than

multipleOf

@constraint(multipleOf: 10) Ensure value is a multiple

Array/List

minItems

@constraint(minItems: 3) Restrict array/List to a minimum length

maxItems

@constraint(maxItems: 3) Restrict array/List to a maximum length

ConstraintDirectiveError

Each validation error throws a ConstraintDirectiveError. Combined with a formatError function, this can be used to customise error messages.

{
  code: 'ERR_GRAPHQL_CONSTRAINT_VALIDATION',
  fieldName: 'theFieldName',
  context: [ { arg: 'argument name which failed', value: 'value of argument' } ]
}
const formatError = function (error) {
  const code = error?.originalError?.originalError?.code || error?.originalError?.code || error?.code
  if (code === 'ERR_GRAPHQL_CONSTRAINT_VALIDATION') {
    // return a custom object
  }

  return error
}

app.use('/graphql', bodyParser.json(), graphqlExpress({ schema, formatError }))

Apollo Server 3

Throws a UserInputError for each validation error.

Apollo Server 4

Throws a prefilled GraphQLError with extensions.code set to BAD_USER_INPUT and http status code 400. In case of more validation errors, top level error is generic with Query is invalid, for details see extensions.validationErrors message, detailed errors are stored in extensions.validationErrors of this error.

Envelop

The Envelop plugin throws a prefilled GraphQLError for each validation error.

uniqueTypeName

@constraint(uniqueTypeName: "Unique_Type_Name") Override the unique type name generate by the library to the one passed as an argument. Has meaning only for Schema wrapper implementation.

graphql-constraint-directive's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

graphql-constraint-directive's Issues

Feature request: add the constraint details to field description

Here is my input field declaration (I'm using SDL files):

    """
    Account number. Only digits are allowed.
    """
    accountNo: String @constraint(minLength: 6, maxLength: 64, pattern: "^[0-9]*$")

Here is my declaration of the ConstraintString type:

"""
Usual string but with some additional contraints
"""
scalar ConstraintString

Here is how it looks in a schema browser:
image

Please note this bit:

Account number. Only digits are allowed.

Feature

Is it possible to add info about the actual constraints (min/max length and RegEx string)?

So that the description would be this:

Account number. Only digits are allowed.
Constraints:
Minimum string length: 6
Maximum string length: 64
String should match RegEx: ^[0-9]*$

Reminder. The GraphQL field description can use CommonMark: http://commonmark.org/

Using "uniqueTypeName: Int" behalves unexpected

When using limit: Int @constraint(uniqueTypeName: "Int", min: 1, max: 200),
I would expect the validation to still run. However no errors are generated.

Is this expected behavior? Is this something this library should support?

I get that conceptually there could be two types of "Int" (one with a constraint and one without) - which goes a little against the graphql schema logic. Consider throwing an assert maybe?

I'm honestly not sure what the correct path of action is here

ConstraintDirectiveError typescript definition

I have a use case where I have to translate the error message, so I have to go over the error structure and map it.

Here is my partial type I'm currently using

type StringConstraint<T extends string, V = string> = {
  arg: T
  value: V
}

type NumberConstraint<T extends string> = {
  arg: T
  value: number
}

type ConstraintDirectiveError = {
  code: 'ERR_GRAPHQL_CONSTRAINT_VALIDATION'
  fieldName: string
  context: (
    | StringConstraint<'minLength'>
    | StringConstraint<'maxLength'>
    | StringConstraint<'format', 'email'>
    | NumberConstraint<'min'>
    | NumberConstraint<'max'>
  )[] // TODO: rest of constrains
}

I would be handy to have it (or something similar) in the library, so I can easily define a translations for the constrains

Error thrown in query with `@skip` directive

Using this package v4.0.0 (as an Envelop plugin) is throwing errors for queries containing the @skip directive.

This query:

query Test($skipTest: Boolean!) {
  user {
    id @skip(if: $skipTest)
  }
}

Throws the following error:

TypeError: Cannot read properties of undefined (reading 'type')
    at QueryValidationVisitor.onArgumentEnter (/app/src/node_modules/graphql-constraint-directive/lib/query-validation-visitor.js:120:54)
    at Object.enter (/app/src/node_modules/graphql/utilities/TypeInfo.js:391:27)
    at visit (/app/src/node_modules/graphql/language/visitor.js:197:21)
    at validateQuery (/app/src/node_modules/graphql-constraint-directive/index.js:112:3)
    at onExecute (/app/src/node_modules/graphql-constraint-directive/index.js:148:24)
    at /app/src/node_modules/@envelop/core/cjs/orchestrator.js:348:37
    at /app/src/node_modules/@envelop/core/cjs/utils.js:116:54
    at /app/src/node_modules/graphql-helix/dist/process-request.js:182:42
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at processRequest (/app/src/node_modules/graphql-helix/dist/process-request.js:59:20)

originalError not available in formatError

Hello,

I am using graphql-constraint-directive with apollo-server-express
But in formatError, when I call error.originalError it always return undefined.
Here my code:

const { constraintDirective, constraintDirectiveTypeDefs } = require('graphql-constraint-directive')
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const { makeExecutableSchema } = require('graphql-tools')
const typeDefs = `
  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`
const schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, typeDefs],
  schemaTransforms: [constraintDirective()]
})

const formatError = function (error) {
  if (error.originalError && error.originalError.code === 'ERR_GRAPHQL_CONSTRAINT_VALIDATION') {
    // return a custom object
  }

  console.log(error);

  return error
}

const app = express()
const server = new ApolloServer({ schema, formatError })

server.applyMiddleware({ app })

app.listen({ port: 3000 }, () =>
  console.log(`๐Ÿš€ Server ready at http://localhost:3000${server.graphqlPath}`)
);

Please help me how can I fix it.

Plugin not compabible with @vercel/ncc

This plugin will currently make the output for @vercel/ncc fail when compiled with esm target.

I've adjusted the requires as appropriate in this PR

Ready for review. This is a very simple PR, and I am hoping we can get this merged and released in a timely manner! Cheers!

Is there a way to have nullable field with constraint?

input UpdatePost {
  # ...
  content: String
  excerpt: String @constraint(maxLength: 1000)
}

with the schema in the example above, I'm unable to send null as a value for excerpt because constraint asserts string type. Basically, the field became mandatory.

originalError is undefined

When trying to customise the error, i seem to be unable to access the originalError property.

Using the following function as error formatter

const formatError = (error: GraphQLError) => {
  console.log('original error', error.originalError)
  return error
}

Simply logs original error {} to the console.

I was able to access the original error when using node-graphql-constraint-lambda, but they don't seem to throw custom errors, so it might just be a problem with apollo/graphql.

I'm using [email protected] and [email protected]

Edit: here is a minimal example:

const ConstraintDirective = require('graphql-constraint-directive')
const express = require('express')
const { ApolloServer } = require('apollo-server-express')

const formatError = (error) => {
  console.log('original error', error.originalError)
  return error
}


const typeDefs = `
  directive @constraint(
    # String constraints
    minLength: Int
    maxLength: Int
    startsWith: String
    endsWith: String
    notContains: String
    pattern: String
    format: String

    # Number constraints
    min: Int
    max: Int
    exclusiveMin: Int
    exclusiveMax: Int
    multipleOf: Int
  ) on INPUT_FIELD_DEFINITION

  type Query {
    books: [Book]
  }
  type Book {
    title: String
  }
  type Mutation {
    createBook(input: BookInput): Book
  }
  input BookInput {
    title: String! @constraint(minLength: 5, format: "email")
  }`


const resolvers = {
  Mutation: {
    createBook: () => ({title:'Hello world!'}),
  },
};
const server = new ApolloServer({ typeDefs, resolvers, schemaDirectives: { constraint: ConstraintDirective }, formatError });
const app = express()
server.applyMiddleware({ app });

app.listen({ port: 8080 }, () =>
  console.log(`๐Ÿš€ Server ready at http://localhost:8080${server.graphqlPath}`)
);

Standardize rule names

This package is currently in fact dealing with two things:

  • the value of field
  • the length of field

The names of rules for these things can be improved (standardized).

What's wrong with current names? They are not intuitive. E.g. min and minLength are greater than or greater than or equal to?

I really like how Prisma auto-generates names by postfixes:

<thing>_lt
<thing>_lte
<thing>_gt
<thing>_gte

<thing>_contains
<thing>_not_contains
<thing>_starts_with
<thing>_not_starts_with
<thing>_ends_with
<thing>_not_ends_with

<thing>_in
<thing>_not_in

<thing>_every
<thing>_some
<thing>_none

etc.

In other words, I propose to rename min to value_gte, minLength to length_gte, notContains to value_not_contains, etc.

variable used in position expecting type value_String_NotNull_maxLength_255

image

had to comment out all constraints after updating config according to new readme :(

i want to use String type in request but not an arbitrary value_String_maxLength_255

### graphql-constraint-directive
directive @constraint(
	uniqueTypeName: String
	# string
	contains: String
	endsWith: String
	format: String
	maxLength: Int
	minLength: Int
	notContains: String
	pattern: String
	startsWith: String
	# number
	exclusiveMax: Float
	exclusiveMin: Float
	max: Float
	min: Float
	multipleOf: Float
) on INPUT_FIELD_DEFINITION

input SomeInput {
	value: String @constraint(maxLength: 255)
}

config below works improperly

import {GraphQLFileLoader} from '@graphql-tools/graphql-file-loader'
import {constraintDirective} from 'graphql-constraint-directive'
import {loadSchemaSync} from '@graphql-tools/load'
import {makeExecutableSchema} from '@graphql-tools/schema'

import resolvers from '@/resolvers'

const loaders = [
		new GraphQLFileLoader(),
]

const typeDefs = loadSchemaSync('source/schema/**/*.graphql', {
	loaders,
})

const schemaTransforms = [
	constraintDirective(),
]

const schema = makeExecutableSchema({
	resolvers,
	schemaTransforms,
	typeDefs,
} as any)

export default schema

config below does not work

import {GraphQLFileLoader} from '@graphql-tools/graphql-file-loader'
import {constraintDirective} from 'graphql-constraint-directive'
import {loadSchemaSync} from '@graphql-tools/load'
import {makeExecutableSchema} from '@graphql-tools/schema'

const loaders = [
		new GraphQLFileLoader(),
]

const typeDefs = loadSchemaSync('source/schema/**/*.graphql', {
	loaders,
})

const schema = makeExecutableSchema({
	resolvers,
	typeDefs,
})

export default constraintDirective()(schema)

Example doesn't work

The example in the README doesn't work with the error: Error: Unknown directive "constraint".

As far as I can tell the directive returns its own definition, it shouldn't need to be defined in the graphql schema, right?

I've created a file containing only the js from the readme with the various imports installed, still get this error.

No number validation on 0

I tried to add @constraint (min: 0) but number constraints like these checking on 0 don't work, because of if conditions like this:
if (args.min && value < args.min) {

args.min is false on 0, so no validation will happen. I think args.min !== undefined would do it.

Support Lists

It would be nice if there was support for list constraints in particulair:

minListLength and maxListLength

input Example {
  beep: [Int!]! @constraint(minListLength: 3)
  boop: [String!]! @constraint(maxListLength: 3)
}

Add support of logical operations

In current implementation rules inside @constraint directive are combined by logical (boolean) AND:

input BookInput {
  title: String! @constraint(minLength: 5, format: "email")
}

Custom directives in GraphQL are also combined by AND:

input BookInput {
  title: String! @constraint(minLength: 5) @constraint(format: "email")
}

What about adding support of logic operations (AND, OR and NOT) for nest rules (similar to https://github.com/maticzav/graphql-shield#and-or-not)? Something like (the syntax is subject to discussion):

@constraint("or(multipleOf: 7, multipleOf: 11)")

or

@constraint(OR: [multipleOf: 7, multipleOf: 11])

Custom scalars vs custom schema directives

At the moment, I'm working on a GraphQL API architecture for my app. And I'm interested in pros and cons in using custom scalars vs. custom schema directives for validation (in the limitations and potential flexibility of each of these approaches).

See, e.g. @okgrow/graphql-scalars for a library of custom GraphQL scalar types for creating precise type-safe GraphQL schemas.

@confuser Did I understand correctly that @constraint directive from graphql-constraint-directive package can only be applied to INPUT_FIELD_DEFINITION, while there are many places where directives (in general) can be applied (see the full list here: https://github.com/graphql/graphql-js/blob/master/src/language/directiveLocation.js)?

Validate a object?

I'm wondering if is possible validate a object against the schema

What I want to do is validate the input in the client before send it to graphql server.

formatError in the README isn't working with Apollo Stack 2

This if clause doesn't work with Apollo Stack 2. (Haven't checked v1. Sorry.)

  if (error.originalError && error.originalError.code === 'ERR_GRAPHQL_CONSTRAINT_VALIDATION') {
    // return a custom object
  }

But this one works:

if (error.originalError.errors.some(e => e.originalError.code === 'ERR_GRAPHQL_CONSTRAINT_VALIDATION')) {
    // return a custom object
}

This is a good thing, because if @constraint found several violations in your mutation then all of them will be returned, not just the first one.

apollo server version 2

hi, i just wanted to confirm that does this package works with apollo-server v2 because in lot of forums its written it does't but also those comments are fairly old.

If it does work with v2, its not working for me , i have done something like this ๐Ÿคทโ€โ™‚๏ธ :

import schema from './schema';
import resolvers from './resolvers';
import { ApolloServer, makeExecutableSchema } from 'apollo-server-express';
import { constraintDirective, constraintDirectiveTypeDefs } from 'graphql-constraint-directive';

const server = new ApolloServer({
	introspection: true,
	playground: true,
	schema: makeExecutableSchema({
		typeDefs: [constraintDirectiveTypeDefs, ...schema],
		resolvers,
		schemaTransforms: [constraintDirective()],
	}),
});

please help ๐Ÿ‘ฑโ€โ™‚๏ธ

Add TypeScript support

It would be really nice to have types added so that we can use this library with TypeScript.

I would be happy to help with this too.

ConstraintString type appeared in graphql schema on the client

ConstraintString leaked through introspection. Is it by design?
This makes GraphiQL unhappy.

Error: Invalid or incomplete schema, unknown type: ConstraintString. Ensure that a full introspection query is used in order to build a client schema.

ConstraintString can be revealed by the following query:

query Introspection { 
  __schema {
    types {
      name
      inputFields {
        type {
          name
        }
      }
    }
  }
}

GraphQLNotNull not supported

Use example in readme:
input BookInput { title: String! @constraint(minLength: 5, format: "email") }
The ! is gone after generate schema. I have cloned the repo and with the fix in my local testing but without permission to create the PR.

Constrainting GraphQL ID type

Do you think it's reasonable to use this directive for GraphQL ID type?

input OrderInput {
  fromFacilityId: ID @constraint(minLength: 24, maxLength: 24)
  toFacilityId: ID @constraint(minLength: 24, maxLength: 24)
}

The only caveat here - ID can be both string and number.

I'd be happy to PR this feature.

Add support to optional fields

Actually it's impossible to use constraints with optional values.

For example:
this doesn't work if birth_date is not present

input UserInput {
  name: String @constraint(minLength: 2),
  surname: String @constraint(minLength: 2),
  dni: String,
  birth_date: String @constraint(format: "date")
}

And this would be awesome!

input UserInput {
  name: String @constraint(minLength: 2, optional:true),
  surname: String @constraint(minLength: 2, optional:true),
  dni: String,
  birth_date: String @constraint(format: "date", optional:true)
}

I think this improve would increase the package capabilities .

Thanks!

min value validation throws errors

When I tried to give min value constraint value as 0.01 got the below error:

Argument "min" has invalid value 0.01.

Input looks like this:

input someInput {
someId: ID!
someValue: Float! @constraint(min: 0.01, max: 3)
}

Allow detecting null

I would like to able to assert that a field on an input is optional, but not nullable (so I can do a partial update of an entity). GraphQL has no such syntax so I was hoping I could use this dirctive for that.

Something like:

input UpdateUser {
  id: ID!
  username: String @contraint(notNull: true)
  password: String @contraint(notNull: true)
}

Is this possible?

Getting "Couldn't find type constraint in any of the schemas." error

I use graphql-tools to load up multiple files into one schema. Maybe i should set it up diffrently? Pls help :)

const loadSchema = loadSchemaSync(join(__dirname, "./graphql/*.graphql"), {
  loaders: [new GraphQLFileLoader()],
});

let schema = makeExecutableSchema({
  typeDefs: [constraintDirectiveTypeDefs, loadSchema],
});
schema = constraintDirective()(schema);

app.register(mercurius, {
  schema: schema,
  resolvers,
  loaders,
  context: buildContext,
  graphiql: true,
});

constraintDirective() wipes out __resolveReference() resolver

When passing this transform over a federated schema, the constraint directive mapSchema() function wipes out and destroys the __resolveReference() resolver on our type which is responsible for fulfilling entity resolution in a federated schema.

Before calling transform(schema) the type looks like this:

image

Directly after calling transform(schema):

image

This breaks all federated data type resolution.

Directive Doesn't Work When using `makeExecutableSchema` from "apollo-server-express"

The directive makes no effect when used with makeExecutableSchema from "apollo-server-express"
In the screenshot below you can see that we expect the email string of length >5 as a title and limit >0
As you can see, limit: -1 and title: "st" are allowed, although they shouldn't pass the validation.
image

When I switched to @graphql-tools/schema makeExecutableSchema, everything works
image

feat: "Custom Validator via Callback"

Hallo,

thanks for the great project. Please also provide a Validator, that allows to specify a callback/anonymous function that is used for validation, so one can easily create custom complex validation logic on the fly.

Thanks very much.

[Feature]: allow empty values

Hey thanks for this easy to use package, but I'm not sure how to deal with empty values.

For example, someone filling out an email field (constraint works fine), but then removing that value (empty string or null) and then the constraint will throw an error and there doesn't seem to be a way around this.

A practical use-case is someone updating their profile information where they have the ability to fill certain fields out (like Twitter handle, etc) that needs some constraints, but can also be empty.

Type name is too long with directive constraints: uniqueTypeName

Hi,

Can there be a way that the uniqueTypeName is referred from the directiveArgumentMap and pass something like "typeName" as an argument to the directive? If not passed, it could generate the uniqueTypeName as is today.

Reason being this ends up generating a pretty long type name if you've a couple of constraints on it and the type names become not so friendly/readable when you use something like a graphql code generator to generate Typescript Types

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.