Skip to content

Schema

Description

Schema type builder.

One of the main goals of this library is to bring the data schema from the database to the user's form itself. The goal is ambitious but feasible.

Because most of the data tends to come from a limited schema (SQL/NoSQL), @dreamkit/schema does not aspire to be able to build very complex schemas, since these complex schemas, in addition to not existing in a database, are not easy to represent with secure typing in a form.

On the other hand, you may be tired of the number of libraries of this kind (e.g. zod, yup, valibot), but below I will explain some of the reasons for this decision.

In the initial stages of development, valibot was considered, but its user experience is diminished by its aggressive tree shaking system, and if there is one thing that DreamKit aims to stand out for, it is its UX.

Zod was another alternative, but its large size and performance deficiencies in TypeScript were important reasons for its discard.

Import

import { s } from "dreamkit";

Definition

import type {
BoolType,
NumberType,
ObjectType,
StringType,
FileType,
ArrayType,
Type,
} from "dreamkit";
declare const s: {
title(value: string): typeof s;
object(props: Record<string, Type>): ObjectType;
array(type: Type): ArrayType;
string(): StringType;
number(): NumberType;
bool(): BoolType;
file(): FileType;
};

Examples

String validation

import { $route, Input, s } from "dreamkit";
import { createSignal } from "solid-js";
const type = s.string().min(3).max(5);
export default $route.path("/").create(() => {
const [name, setName] = createSignal("");
return (
<>
<Input value={name} onChange={setName} />
<p>
{"errors: "}
{JSON.stringify(type.validate(name()), null, 2)}
</p>
</>
);
});

Object manipulation

import { $route, ObjectType, s } from "dreamkit";
import { createSignal, For } from "solid-js";
const object = s
.object({
id: s.number().integer().min(1),
})
.assign({
profile: { name: s.string() },
});
const schemas: Record<string, ObjectType<{}>> = {
// { id: number }
pickOnlyId: object.pick({ id: true }),
// { id?: number, profile?: { name?: string } }
deepPartial: object.deepPartial(),
// { id?: number | null, profile?: { name?: string | null } | null }
deepNullish: object.deepNullish(),
// { profile: { name: string, age?: number } }
assign: object
.assign({ profile: { age: s.number().optional() } })
.pick({ profile: true }),
// { id: number, profile: {}}
omitName: object.omit({ profile: { name: true } }),
};
export default $route.path("/").create(() => {
const [schema, setSchema] = createSignal("pickOnlyId");
const [json, setJson] = createSignal(JSON.stringify({ id: 1 }, null, 2));
const errors = () => {
let subject;
try {
subject = JSON.parse(json());
} catch {
subject = {};
}
return schemas[schema()].validate(subject);
};
return (
<>
Schema:{" "}
<select onInput={(event) => setSchema(event.target.value)}>
<For each={Object.keys(schemas)}>
{(value) => <option>{value}</option>}
</For>
</select>
<p>
JSON Schema: <br />
<textarea
style={{ height: "300px", width: "100%" }}
disabled
onInput={(event) => setJson(event.target.value)}
>
{JSON.stringify(schemas[schema()].toJsonSchema(), null, 2)}
</textarea>
</p>
<p>
Input:
<br />
<textarea
style={{ height: "300px", width: "100%" }}
onInput={(event) => setJson(event.target.value)}
>
{json()}
</textarea>
</p>
<p>Errors: {JSON.stringify(errors(), null, 2)}</p>
</>
);
});