Graphql-go Has No Built-In Validation For Argument/Variables Passed On?

54 Views Asked by At

I am building golang graphql endpoint with graphql-go
https://github.com/graphql-go/graphql

Here is chunk of my resolver.go

func GetRootMutation(db *gorm.DB, req *http.Request) *graphql.Object {
    rootMutation := graphql.NewObject(graphql.ObjectConfig{
        Name: "RootMutation",
        Fields: graphql.Fields{
            "send_otp_email": SendOtpEmail(db, req),
        },
    })

    return rootMutation
}

func SendOtpEmail(db *gorm.DB, req *http.Request) *graphql.Field {
    return &graphql.Field{
        Type:        graphqlGlobal.ResultType,
        Description: "Send OTP to user's email.",
        Args: graphql.FieldConfigArgument{
            "email": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
            // I HAVE SET IT TO STRING (NON-NULLABLE)
        },
        Resolve: func(params graphql.ResolveParams) (interface{}, error) {
            success := true
            messages := []string{}
            email, isOK := params.Args["email"].(string)
            if !isOK {
                success = false

                // ISN'T IT SUPPOSED TO BE STOPPED HERE BECAUSE VALUE IS NOT STRING?
                return nil, errors.New(language.GetErrValueInvalid())
            }

            if success {
                userImpl := repository.NewAuthRepo(db, req)
                err := userImpl.SendOtpEmail(email)
                if err != nil {
                    success = false
                    messages = []string{err.Error()}
                }
            }

            message := ""
            if len(messages) > 0 {
                message = messages[0]
            }
            result := map[string]interface{}{"success": success, "messages": messages}
            var errResult error
            if !success {
                errResult = errors.New(message)
            }
            return result, errResult // IT PASS THROUG THIS!!!
        },
    }
}

Query Operation

mutation RootMutation($email: String!) {
  send_otp_email(email: $email) {
    success
    messages
  }
}

argument / variable

{
  "email": 123
}

result (unexpected)

{
  "data": {
    "send_otp_email": {
      "messages": [],
      "success": true
    }
  }
}

Even in other operations, when I fill arguments with integer while it's supposed to be array of integer, there is no error from invalid data type.

Does graphql-go really have no built-in validation to validate argument passed on on the operation?

1

There are 1 best solutions below

1
On

Apparently graphql-go has no built-in for its args validation, so to solve this issue this is the workaround, using:
https://github.com/mitchellh/mapstructure
https://github.com/go-playground/validator

// resolver.go

func SendOtpEmail(db *gorm.DB, req *http.Request) *graphql.Field {
    return &graphql.Field{
        Type:        graphqlGlobal.ResultType,
        Description: "Send OTP to user's email.",
        Args: graphql.FieldConfigArgument{
            "email": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
        },
        Resolve: func(params graphql.ResolveParams) (interface{}, error) {
            return resolver(params, db, req)
        },
    }
}

type Args struct {
    Email string `json:"email" validate:"required,email"`
}

func resolver(params graphql.ResolveParams, db *gorm.DB, req *http.Request) (interface{}, error) {
    args := params.Args

    success := true
    messages := []string{}

    var argsDecoded Args

    err := mapstructure.Decode(args, &argsDecoded)
    if err != nil {
        success = false
        messages = []string{"Decoding map data type failed."}
    }

    if success {
        validate := validator.New()
        errValidate := validate.Struct(argsDecoded)

        if errValidate != nil {
            success = false
            messages = strings.Split(errValidate.Error(), "\n")
        }
    }

    if success {
        userImpl := repository.NewAuthRepo(db, req)
        email := argsDecoded.Email
        err := userImpl.SendOtpEmail(email)
        if err != nil {
            success = false
            messages = []string{err.Error()}
        }
    }

    response := fiber.Map{
        "success":  success,
        "messages": messages,
    }

    return response, nil
}