Schema and Validation
Use Frontbacked.checkState(path, schema, check?) to validate browser state before calling APIs such as signUp, uploadPost, updatePost, or updateUserData.
Client validation is for fast feedback: it catches missing values, wrong file types, oversized files, and invalid form shapes before the user waits on a request. FRL schemas still protect saved data, so use both: FQL validation for friendly forms, FRL validation for the final data contract.
Basic Form Validation
<!-- {STATE}
signup: {
name: "",
email: "",
password: "",
confirmPassword: "",
terms: false
}
-->
<form id="signupForm">
<input f-model="signup.name" placeholder="Name">
<input f-model="signup.email" placeholder="Email">
<input f-model="signup.password" type="password" placeholder="Password">
<input f-model="signup.confirmPassword" type="password" placeholder="Confirm password">
<label><input f-model="signup.terms" type="checkbox"> I accept the terms</label>
<button>Create account</button>
</form>
const signupSchema = {
name: { type: "string", required: true, trim: true, min: 2, max: 80 },
email: { type: "email", required: true, trim: true },
password: { type: "string", required: true, min: 8, max: 128 },
confirmPassword: {
type: "string",
required: true,
sameAs: "signup.password",
messages: { sameAs: "Passwords must match." }
},
terms: {
type: "boolean",
accepted: true,
messages: { accepted: "Please accept the terms to continue." }
}
};
document.querySelector("#signupForm").addEventListener("submit", async (event) => {
event.preventDefault();
const result = Frontbacked.checkState("signup", signupSchema);
if (!result.ok) return;
await Frontbacked.signUp({
email: result.values.email,
password: result.values.password,
name: result.values.name
});
});
result.values contains normalized values. In the example above, name is trimmed and email is trimmed and lowercased.
Validate Post Data Before Upload
File rules work alongside normal string, number, object, and array rules.
<!-- {STATE}
articleForm: {
title: "",
category: "market",
body: "",
cover: null,
video: null,
tags: []
}
-->
<input f-model="articleForm.title">
<select f-model="articleForm.category">
<option value="market">Market</option>
<option value="education">Education</option>
<option value="news">News</option>
</select>
<textarea f-model="articleForm.body"></textarea>
<input f-model="articleForm.cover" type="file" accept="image/jpeg,image/png,image/webp">
<input f-model="articleForm.video" type="file" accept="video/mp4,video/webm">
const articleSchema = {
title: { type: "string", required: true, trim: true, min: 3, max: 140 },
category: { type: "string", enum: ["market", "education", "news"], required: true },
body: { type: "string", required: true, min: 20, max: 50000 },
cover: {
type: "file",
required: true,
maxSize: 7000000,
mimeTypes: ["image/jpeg", "image/png", "image/webp"]
},
video: {
type: "file",
maxSize: 500000000,
maxDurationSeconds: 300,
mimeTypes: ["video/mp4", "video/webm", "video/quicktime"]
},
tags: {
type: "array",
max: 10,
of: { type: "string", trim: true, min: 1, max: 40 }
}
};
async function publishArticle() {
const result = Frontbacked.checkState("articleForm", articleSchema);
if (!result.ok) return;
await Frontbacked.uploadPost({
type: "articles",
post: result.values,
onUploadProgress(progress) {
Frontbacked.setState(`uploads.${progress.sessionId}`, progress);
}
});
}
maxSize and mimeTypes can be checked immediately from the selected file. maxDurationSeconds keeps your client schema aligned with the FRL video playback cap. If an uploaded video is longer than the allowed duration, Frontbacked prepares playback from the beginning up to that limit.
Nested Objects
Use fields for nested objects:
const profileSchema = {
displayName: { type: "string", required: true, trim: true, max: 80 },
location: {
type: "object",
fields: {
city: { type: "string", trim: true, max: 80 },
country: { type: "string", trim: true, max: 80 }
}
},
socials: {
type: "object",
fields: {
website: { type: "string", trim: true, max: 200 },
x: { type: "string", trim: true, max: 80 }
}
}
};
const result = Frontbacked.checkState("profile", profileSchema);
if (result.ok) {
await Frontbacked.updateUserData(result.values);
}
Validate One Field
Use the optional check object when validating several fields separately, such as on blur:
const check = { hasError: false, errors: [] };
Frontbacked.checkState("signup.email", signupSchema.email, check);
Frontbacked.checkState("signup.password", signupSchema.password, check);
if (check.hasError) {
console.log(check.errors);
}
Custom Rules
Use validate when a field needs theme-specific logic:
const schema = {
coupon: {
type: "string",
trim: true,
validate(value) {
if (!value) return true;
return /^SAVE-[A-Z0-9]{6}$/.test(value) || "Coupon codes look like SAVE-ABC123.";
}
}
};
Feedback Options
By default, Frontbacked places messages near matching inputs when it can. You can override where a message goes:
const schema = {
avatar: {
type: "file",
maxSize: 3000000,
mimeTypes: ["image/jpeg", "image/png"],
feedback: { to: "toast", duration: 4000 },
messages: {
maxSize: "Use an image smaller than 3MB.",
mimeTypes: "Use a JPEG or PNG image."
}
}
};
Useful feedback.to values:
| Value | Behavior |
|---|---|
"alert" | Show a Frontbacked alert. |
"toast" | Show a temporary toast. |
"formErrors" | Write messages to the form error area. |
"customFunction()" | Call a global function with the message payload. |
Schema Keys
| Key | Purpose |
|---|---|
type | string, email, number, boolean, object, array, file, or datetime. |
required | Require a present value. Empty strings, empty arrays, and missing files fail. |
trim, lowercase, uppercase | Normalize string values before validation returns. email lowercases by default. |
min, max | String length, number value, or array length limit. |
pattern | Regular expression for string values. |
enum | Allowed values. |
sameAs | Require the value to equal another state path. |
accepted | Require a boolean value to be true, useful for terms checkboxes. |
maxSize | Maximum file size in bytes. |
mimeTypes | Allowed file MIME types. |
maxDurationSeconds | Maximum prepared video playback duration in seconds. |
fields | Nested object schema. |
of or items | Array item schema. |
messages | Optional custom messages by failed rule. |
feedback | Message destination options. |
validate | Custom validation function that receives (value, state). |
allowFalse | Treat boolean false as valid. |
allowEmptyString | Treat an empty string as valid. |
allowEmptyArray | Treat an empty array as valid. |