@loydjs/async
v1.1.0
Published
Loyd async — two-pass async pipeline
Maintainers
Readme
Overview
@loydjs/async adds async validation to Loyd schemas using a two-pass pipeline. Synchronous rules execute first - if any fail, async rules are skipped entirely. This avoids unnecessary database queries or network calls when the input is structurally invalid.
Multiple async rules on the same schema run in parallel via Promise.all, minimizing total latency.
Installation
npm install @loydjs/asyncRequires
@loydjs/core·@loydjs/schema· Node.js ≥ 20 · TypeScript ≥ 5.4
API
parseAsync(schema, input, options?)
import { parseAsync } from "@loydjs/async";
import { string } from "@loydjs/schema";
import { refineAsync } from "@loydjs/schema";
const UniqueEmail = string()
.email()
.pipe(
refineAsync(async (email) => {
const exists = await db.users.exists({ email });
return !exists;
}, { code: "ERR_EMAIL_TAKEN" })
);
// Sync check (.email()) runs first.
// If it fails, the async DB query is never made.
const result = await parseAsync(UniqueEmail, formData.email);
if (result.success) {
console.log(result.data); // "[email protected]"
} else {
console.log(result.issues[0].code); // "ERR_EMAIL_TAKEN"
}safeParseAsync(schema, input, options?)
Alias for parseAsync — same behavior, different name for symmetry with safeParse.
import { safeParseAsync } from "@loydjs/async";
const result = await safeParseAsync(schema, input);With AbortSignal
Cancel an in-flight validation when a request is aborted.
const controller = new AbortController();
// Cancel after 2 seconds
setTimeout(() => controller.abort(), 2000);
const result = await parseAsync(HeavySchema, input, {
signal: controller.signal,
});
if (!result.success && result.issues[0]?.code === "ERR_ABORTED") {
console.log("Validation was cancelled");
}refineAsync(predicate, options)
Adds an async refinement to any schema.
import { object, string, number } from "@loydjs/schema";
import { refineAsync } from "@loydjs/schema";
const CreatePostSchema = object({
title: string().minLength(3).maxLength(200),
authorId: number().int().pipe(
refineAsync(async (id) => {
const exists = await db.users.exists({ id });
return exists;
}, { code: "ERR_AUTHOR_NOT_FOUND" })
),
categoryId: number().int().pipe(
refineAsync(async (id) => {
const exists = await db.categories.exists({ id });
return exists;
}, { code: "ERR_CATEGORY_NOT_FOUND" })
),
});
// authorId and categoryId async checks run in parallel
const result = await parseAsync(CreatePostSchema, req.body);Pipeline behavior
Input
│
[Sync rules] -> fail -> return immediately, skip async
│
pass
│
[Async rules] -> all run in parallel via Promise.all
│
ResultDependencies
| Package | Role |
|:---|:---|
| @loydjs/core | LoydSchema, LoydResult types |
| @loydjs/schema | refineAsync schema modifier |
Documentation
License
MIT © b3nito404
