Check the primary identity
The key must identify a user and contain a primary key such as u-42.
Validation protects the boundaries of the connected domain. It checks that Alex is really a User, that an Address belongs at the right level in the hierarchy, and that queries and operation parameters have safe shapes before the rest of Fjell processes them.
Use the same example from the Items page: Alex Morgan is a User, Acme Systems is a Company, Alex has many Addresses, and a Team membership carries a role. Validation does not decide your business rules; it verifies the structural contracts that let those relationships travel safely through the system.
The key must identify a user and contain a primary key such as u-42.
A contained Address key must preserve its parent User location and the expected key-type order.
Fjell distinguishes a primary key from a composite key. That distinction makes the User graph explicit in code.
import { validatePK, validateKeys } from "@fjell/validation";
// Alex is a primary User Item.
validatePK(alex, "user");
// Alex's home Address is contained by a User.
validateKeys(homeAddress, ["address", "user"]);If an Address arrives with the wrong key type or the wrong parent location, validation fails before the operation continues. This is where a PItem-to-many-CItem relationship gets a structural guardrail.
Keys tell Fjell where an Item lives. Your application still owns the domain properties. Validate those properties at the boundary before creating or updating an Item.
type UserInput = {
firstName: string;
lastName: string;
birthDate?: string;
companyId: string;
};
const input: UserInput = request.body;
if (!input.firstName.trim() || !input.lastName.trim()) {
throw new Error("User must have a first and last name");
}
// The validated payload can now reach User operations.For an Address, the equivalent boundary might require street, city, and postalCode. For a Team membership, it might restrict role to manager or member. The package supplies Item/key validation; application validators express domain policy.
Queries are also part of the contract. A request for Alex's active team memberships should be an object with the expected shape—not an array, string, or accidental null.
import { validateQuery, validateOperationParams } from "@fjell/validation";
validateQuery(
{ filter: { role: "manager" }, limit: 20 },
"team-memberships"
);
validateOperationParams(
{ userId: "u-42", teamId: "team-9" },
"team-memberships"
);Bad shapes fail early, with operation-specific errors, instead of becoming confusing storage or router failures.
Validation is an early foundation layer. After it passes, core and lib can apply Item operations, adapters can persist them, and routers can expose the graph over HTTP.
Previous: types →Next: core →Return to the object graph →