I.6
Async functions: calculations and validation
You need this: When a value or a rule depends on something only your server knows.
Register a function that asks your server a question, then use it in any expression: a calculated value, a validator, visibleIf. With isAsync: true the function can return a promise; the expression updates when it resolves.
Try this
Code that just ran
These regions are read from the files that served this page, the same lines the Server Integration page shows.
shared/client/async-functions.js · sjs:I.6.client
View on GitHub
import { registerFunction } from "survey-core";
// Validation: does a customer with this email already exist?
registerFunction({
name: "emailExists",
isAsync: true,
func: async ([email]) => {
if (!email) return false;
const res = await fetch(`/api/customers/exists?email=${encodeURIComponent(email)}`);
return (await res.json()).exists;
}
});
// Calculation and validation: the shipping price for a postcode, null where you don't deliver
const prices = new Map(); // one request per postcode serves both the calculated value and the validator
registerFunction({
name: "shippingCost",
isAsync: true,
func: async ([postcode]) => {
if (!postcode) return 0;
if (!prices.has(postcode)) prices.set(postcode, fetch(`/api/shipping?postcode=${encodeURIComponent(postcode)}`).then(r => r.json()));
return (await prices.get(postcode)).price;
}
});
routes/examples/async-functions.php · sjs:I.6.server
View on GitHub
// GET /api/customers/exists?email= — answers emailExists()
Route::get('/api/customers/exists', function (Request $request) {
$email = Str::lower(trim((string) $request->query('email')));
return ['exists' => DB::table('customers')->where('email', $email)->exists()];
});
// GET /api/shipping?postcode= — answers shippingCost(): the longest matching postcode prefix wins,
// { "price": null } when none matches (the form shows "We don't deliver to this postcode yet")
Route::get('/api/shipping', function (Request $request) {
$postcode = Str::upper(str_replace(' ', '', (string) $request->query('postcode')));
$rate = DB::table('shipping_rates')->get()
->filter(fn ($rate) => str_starts_with($postcode, $rate->postcode_prefix))
->sortByDesc(fn ($rate) => strlen($rate->postcode_prefix))
->first();
return ['price' => $rate ? (float) $rate->price : null];
});
Definition: shared/definitions/async-functions.json