async-graphql/docs/en/src/input_value_validators.md

3.3 KiB

Input value validators

Arguments to a query (InputObject) are called Input Objects in GraphQL. If the provided input type does not match for a query, the query will return a type mismatch error. But, sometimes we want to provide more restrictions on specific types of values. For example, we might want to require that an argument is a valid email address. Async-graphql provides an input validators to solve this problem.

An input validator can be combined via and and or operators.

The following is an input validator which checks that a String is a valid Email or MAC address:

use async_graphql::*;
use async_graphql::validators::{Email, MAC};

struct Query;

#[Object]
impl Query {
    async fn input(#[graphql(validator(or(Email, MAC(colon = "false"))))] a: String) {
    }
}

The following example verifies that the i32 parameter a is greater than 10 and less than 100, or else equal to 0:

use async_graphql::*;
use async_graphql::validators::{IntGreaterThan, IntLessThan, IntEqual};

struct Query;

#[Object]
impl Query {
    async fn input(#[graphql(validator(
        or(
            and(IntGreaterThan(value = "10"), IntLessThan(value = "100")),
            IntEqual(value = "0")
        )))] a: String) {
    }
}

Validate the elements of the list.

You can use the list operator to indicate that the internal validator is used for all elements in a list:

use async_graphql::*;
use async_graphql::validators::Email;

struct Query;

#[Object]
impl Query {
    async fn input(#[graphql(validator(list(Email)))] emails: Vec<String>) {
    }
}

Custom validator

Here is an example of a custom validator:

struct MustBeZero {}

impl InputValueValidator for MustBeZero {
    fn is_valid(&self, value: &Value) -> Result<(), String> {
        if let Value::Int(n) = value {
            if n.as_i64().unwrap() != 0 {
                // Validation failed
                Err(format!(
                    "the value is {}, but must be zero",
                    n.as_i64().unwrap(),
                ))
            } else {
                // Validation succeeded
                Ok(())
            }
        } else {
            // If the type does not match we can return None and built-in validations
            // will pick up on the error
            Ok(())
        }
    }
}

Here is an example of a custom validator with extensions (return async_graphql::Error):

pub struct Email;

impl InputValueValidator for Email {
    fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
        if let Value::String(s) = value {
            if &s.to_lowercase() != s {
                return Err(Error::new("Validation Error").extend_with(|_, e| {
                    e.set("key", "email_must_lowercase")
                }));
            }

            if !validate_non_control_character(s) {
                return Err(Error::new("Validation Error").extend_with(|_, e| {
                    e.set("key", "email_must_no_non_control_character")
                }));
            }

            if !validate_email(s) {
                return Err(Error::new("Validation Error").extend_with(|_, e| {
                    e.set("key", "invalid_email_format")
                }));
            }
        }

        Ok(())
    }
}