Validation in FQL
There is not a best practice on how to do validation in FQL yet.
It’s quite verbose atm but you could of course create a few JavaScript (or whatever your host language is, assuming you are not writing this in the dashboard) to abstract that verbosity away:
function CheckString(attribute, newData) {
return q.IsString(q.Select(['data', attribute], newData, false))
}
So that you can write:
q.And(
CheckString('title', q.Var('newData')),
...
)
Which you can do for everything you want to check. FQL is easy to compose, use that feature wherever you can
Stopping users from adding extra fields or disallowing specific fields
You could even write a role that ranges over the fields of oldData and newData and makes sure no fields are added. A function such as ObjectKeys as provided by Eigils faunadb lib would come in handy there (thanks Eigil! )
Validation in ABAC roles or in User Defined Functions (UDFs)?
Although it’s possible, it’s not what ABAC was made for. ABAC is meant to write security roles initially yet we see many add validation in there. Another approach that is probably easier to debug is to use User Defined Functions and write the logic there and abort in case the validation is wrong. For example, you could also do something like this:
function RegisterAccount(email, password) {
const ValidateEmail = FqlStatement =>
If(
ContainsStrRegex(
email,
"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
),
// If it's valid, we continue with the original statement
FqlStatement,
// Else we Abort!
Abort('Invalid e-mail provided')
)
const ValidatePassword = FqlStatement =>
If(
GTE(Length(password), 8),
// If it's valid, we continue with the original statement
FqlStatement,
// Else we Abort!
Abort('Invalid password, please provided at least 8 chars')
)
const Query = Create(Collection('accounts'), {
// credentials is a special field, the contents will never be returned
// and will be encrypted. { password: ... } is the only format it currently accepts.
credentials: { password: password },
// everything you want to store in the document should be scoped under 'data'
data: {
email: email
}
})
// Compose, each Validate function will Abort if something is wrong, else it will just call the FQL
// that was passed.
return ValidateEmail(ValidatePassword(Query))
}
and then register it as a UDF
const RegisterUDF = CreateOrUpdateFunction({
name: 'register',
body: Query(Lambda(['email', 'password'], RegisterAccount(Var('email'), Var('password')))),
role: Role('functionrole_register')
})
that way you have more control over the error messages and can give feedback to the client.