validate-directive

A GraphQL directive for input validation

Usage no npm install needed!

<script type="module">
  import validateDirective from 'https://cdn.skypack.dev/validate-directive';
</script>

README

validate-directive

Current Version Dependencies belly-button-style

A GraphQL directive for input validation. View the complete directive as GraphQL SDL here.

Description

GraphQL's type system provides a degree of input validation by default. However, it does not provide more fine grained validation out of the box. validate-directive provides dozens of battle-tested validation functions via a @validate directive.

@validate is a wrapper for the joi validation module. The @validate directive exposes the joi API where it makes sense. For example, joi provides validation for functions, symbols, and many other things that don't make sense in the context of a GraphQL directive.

Some joi API names are duplicated across data types. For example, in joi, min() and max() validators exist for arrays, dates, numbers, objects, etc. @validate exposes these with unique names such as arrayMax, dateMax, etc.

For questions regarding how specific joi validation functions work, see the joi documentation.

Features

  • Input validation for GraphQL primitives, enums, lists, and input objects.
  • Validation provided on input objects is inherited, and can be overridden in other locations within the schema. This allows validation to be centralized with the input type and reused.
  • The name of the directive and related input types is configurable.

Usage

enum TestEnum { A B C }

# Input objects can have schemas.
input TestInput {
  even: Int @validate(multiple: 2)

  nestedObject: [NestedTestInput!]! @validate(arrayLength: 3)
}

input NestedTestInput {
  # Enums are validated as strings.
  nestedEnum: TestEnum @validate(case: UPPER, length: { limit: 1 })
}

type Query {
  queries (
    # Boolean validation - does nothing.
    someBoolean: Boolean @validate

    # Strings and integers can be converted to dates and validated.
    dateAsString: String @validate(type: DATE, dateGreater: "1-1-1974")
    dateAsInteger: Int @validate(type: DATE, dateLess: "12-31-1973")
    isoDateString: String @validate(type: DATE, iso: TRUE)
    timestamp: Int @validate(type: DATE, timestamp: UNIX)

    # Number validation.
    greater: Float @validate(greater: 5)
    integer: Float @validate(integer: TRUE)
    less: Float @validate(less: 10)
    max: Float @validate(max: 3)
    min: Float @validate(min: -5)
    multiple: Float @validate(multiple: 2)
    negative: Float @validate(negative: TRUE)
    port: Float @validate(port: TRUE)
    positive: Float @validate(positive: TRUE)
    precision: Float @validate(precision: 2)
    sign: Float @validate(sign: NEGATIVE)
    unsafeAllowed: Float @validate(unsafe: true)
    unsafeNotAllowed: Float @validate(unsafe: false)
    doubleInteger: Int @validate(integer: TRUE)

    # String validation.
    alphanum: String @validate(alphanum: TRUE)
    base64: String @validate(base64: { paddingRequired: false })
    case: String @validate(case: UPPER)
    length: String @validate(length: { limit: 4, encoding: "utf8" })
    maxLength: String @validate(maxLength: { limit: 3, encoding: "utf8" })
    minLength: String @validate(minLength: { limit: 4, encoding: "utf8" })

    # Object validation.
    objectMaxSize: TestInput @validate(objectMax: 20)
    andRelationship: TestInput @validate(and: ["even", "nestedObject"])
    nand: TestInput @validate(nand: ["even", "nestedObject"])
    or: TestInput @validate(or: ["even", "nestedObject"])
    with: TestInput @validate(with: { key: "nestedObject", peers: ["even"] })
    without: TestInput @validate(
      without: { key: "nestedObject", peers: ["even"] }
    )
    oxor: TestInput @validate(oxor: ["even", "nestedObject"])
    xor: TestInput @validate(xor: ["even", "nestedObject"])

    # List validation.
    arrayLength: [String] @validate(case: LOWER, arrayLength: 3)
    arrayMax: [Int] @validate(arrayMax: 3)
    arrayMin: [Int] @validate(arrayMin: 1)
    sort: [TestInput] @validate(sort: { by: "even" })
    sortDescending: [TestInput] @validate(
      sort: { order: DESCENDING, by: "nestedObject.nestedEnum" }
    )
    uniqueBoolean: [Boolean] @validate(unique: {})
    uniqueObject: [TestInput] @validate(
      unique: { comparator: "nestedObject.nestedEnum" }
    )
  ): Boolean
}

type Mutation {
  validate (
    port: Int @validate(port: TRUE)
  ): Int
}