Graphql input mandatory property - validation

Is there a possibility to create a GraphQL input with a mandatory input or another.
For example :
input ClientInput
{
name: String!
token: String!
email: String!
}
I want one of the properties token or email to be mandatory.
If one of these is provided, the other becomes optional.
Is it possible ?
If not, is there a kind of "workaround" ?

No, this is not possible. One workaround would be to make the two fields aptional, then do manual validation, upon which you can return custom errors.
Another way would be to combine the fields into something like
input ClientInput
{
name: String!
tokenOrEmail: String!
}
By doing this you can make the field mandatory, then do some parsing to determine whether the provided value is token or email
An even better way would be to add another field to provide the authMethod, like so:
input ClientInput
{
name: String!
authData: String!
authMethod: AuthMethod!
}
enum AuthMethod {
EMAIL
TOKEN
}

Related

How to update single data when using Fauna and GraphQL

I have some problems when using Fauna and GraphQL
When I use GraphQL to update data, all parameters are required
For example
// Schema
type User {
username: String!
password: String!
phone: String!
}
// Mutation
mutation {
updateUser(id: "xxxxx", {"phone": "+886 110220330"}){
username,
phone
}
}
// Error
But I only want to update the phone data this time, how can I skip other parameters?
Although it can solve the problem when I delete the Not null of all attributes in User type,
But it doesn't look like a good way and safe
// after
// Schema
type User {
username: String
password: String
phone: String
}
There is a preview feature that you can use by adding a header to your GraphQL query.
X-Schema-Preview: partial-update-mutation
Once you do this, you will have access to a new mutation called partialUpdateUser which will make all of the input values optional.
See the docs for more information: https://docs.fauna.com/fauna/current/api/graphql/previews/

GraphQL variable combination validation

Imagine the following (simplified) GraphQL schema:
type User {
username: String
email: String
}
type Query {
user(username: String, email: String): User
}
If I would only want to allow querying user by giving a username, I would of course change it to user(username: String!), making the username required using the exclamation mark. Same thing vice versa with the email.
Is it possible though to have a GraphQL native solution where I validate for the existence of only either one (logical XOR) or at least one (logical OR) of the two input parameters?
Of course I could do it in the query resolver manually, but a #constraint directive like it is being used in Apollo GraphQL spreading across variables would be nice.
Directly ... not possible:
check fields existence [within args] on resolver;
use #constraint to check each field shape;
Indirectly:
you can try to use union of input types:
.
type UserNameInput {
username: String!
}
type UserEmailInput {
email: String!
}
type UserInput = UserNameInput | UserEmailInput
type Query {
user(input: UserInput): User
}

What is the proper way to validate user password confirmed input using Laravel lighthouse

I'm working on a GraphQL schema for my Laravel project using the lighthouse library. But I'm running into a problem when trying to validate the user confirmed their password. The issue occurs when I try to register a user. Consider the following type for my user
type User {
id: ID
first_name: String!
last_name: String!
email: String!
password: String!
phone_number: String
avatar: String
email_verified_at: DateTime
}
I try to register my user using the following mutation
extend type Mutation {
createUser(input: CreateUser #spread): User! #create
}
Where my CreateUser input looks like this
input CreateUser {
first_name: String!
last_name: String!
email: String! #rules(apply: ["email"])
password: String! #rules(apply: ["confirmed"])
phone_number: String
}
I get the expected error for my failed validation: "Validation failed for the field [createUser]" so that is fine. But when I try to add a password_confirmation within my request the following error is encountered: "Field \"password_confirmation\" is not defined by type CreateUser.". Thats when I thought I should add the password_confirmation field to the CreateUser input. But when I try that the validation passes but I get a database error that the password_confirmation is an Undefined column. Which makes sense because it isn't in my migration and I feel this shouldn't be required to be able to validate passwords.
tldr; How can I use the laravel confirmed validation within the lighthouse #rules directive
It is possible to override the fill function for the User model and manually unset the password_confirmation attribute:
public function fill(array $attributes)
{
unset($attributes['password_confirmation']);
return parent::fill($attributes);
}
The validation will run and the password_confirmation field will be unset before saving the model.

GraphQL data modelling - extended types (Prisma)

In my Prisma Data Model I started out with a basic User type like this:
type User {
name: String!
email: String! #unique
password: String!
}
Now a User can have two roles: either as a candidate or as a user associated with an employer. If a candidate, the user should also have a set of applications and a set of qualifications, if associated with an employer it should have an access level and a reference to the employer.
First off, is there any way to extend basic types in GraphQL data modelling? If so, how would I go about doing it?
If there is not, I can see three different methods used, and I'm curious what are the pros and cons of each approach:
Having two separate types CandidateUser and EmployerUser, each with the fields name, email, password. I see two problems with this approach: The #unique tag on email is not reliable, and I would have to write a custom verification to make sure the field is unique across both types; and having a single login-function that takes email and fetches the users corresponding data is no longer trivial: it needs to do a lookup in both tables.
Like this:
type CandidateUser {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
name: String!
email: String! #unique
password: String!
employer: Employer!
accessRight: AccessRight!
}
Again two separate types, but with a RootUser containing name, email and password, and with CandidateUser and EmployerUser each having a one-to-one reference to a RootUser. This would enforce the #unique tag on the email field, but lookup would still be nontrivial.
type RootUser{
name: String!
email: String! #unique
password: String!
}
type CandidateUser {
rootUser: RootUser!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
rootUser: RootUser!
employer: Employer!
accessRight: AccessRight!
}
Extending User to have the fields within EmployerUser and CandidateUser as optional parameters. This is a pretty simple approach, but I would need custom handling to enforce requiring fields (as in, I can not mark for instance employer as required as that field would not exist for a Candidate).
type User{
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
employer: Employer
accessRight: AccessRight
}
I really want to ask if there is a better way of solving this. I'm still pretty new to GraphQL and not the best data modeler to begin with, but I'd greatly appraciate any nudge in the right direction :)
And if I do not have any other choice but the three I listed, which one would make the most sense?
What you're trying to do is implementing an interface type:
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
interface User {
name: String!
email: String! #unique
password: String!
}
This means that any type that implements User needs to have these exact fields, with these arguments and return types. So now your Candidate type can implement User:
type Candidate implements User {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
Interfaces are useful when you want to return an object or set of objects, but those might be of several different types. Have a look at the interface abstract type documentation for more information.
Update:
Since this is a Prisma GraphQL question now, you should be aware that Prisma does not support Interfaces or Union Types as yet. Issue #83 and issue #165 discuss both respectively as feature requests.
However, there is this great article that discuss the workarounds for such approach:
GraphQL Interfaces (and Union Types) with Prisma and Yoga
Which boils down to 2 options:
Storing all data with optional type-specific fields under one type (the interface) in Prisma, and then splitting the data back between the primitive types in the app server.
Storing the data in each primitive type on Prisma, and stitching things for queries on the app server.

How to reference a list of types representing existing records?

Suppose I have two types, User and Permission, e.g.
type User {
id: ID!
firstName: String!
lastName: String!
email: String!
permissions: [Permission!]
}
type Permission {
id: ID!
name: String!
}
User has permissions field, which contains a list of associated Permission type records.
How do I structure a mutation query that updates which permissions user has?
I have simply used a list of ID type for the permissions parameter in the mutation
I have set permissions parameter to a list of GraphQLID.
type Mutation {
updateUser (
id: ID!
email: String
password: String
firstName: String
lastName: String
# List of Permission IDs
permissions: [ID!]
): User
}
The problem with this approach is that this does not define relation between Permission type and ID. Therefore, I have added "List of Permission IDs" comment.
Another approach would be to create a dedicated input type, e.g.
input PermissionInput {
id: ID!
}
And use that in instead of ID in the updateUser permissions parameter. However, this does not solve the reference issue.
Unfortunately, I can't add just a comment, but we solve the problem in the same way - but to ensure clarity and consistency of the shape of the data, we use specific id fields. I.e. we would define the input like this:
type Mutation {
updateUser (
id: ID!
email: String
password: String
firstName: String
lastName: String
permissionIds: [ID!]
): User
}
Pretty basic difference, but it clearly differentiates the input from the output, each field name is intuitive and there isn't much overhead.

Resources