1.6.12 • Published 10 months ago
ivo v1.6.12
Foreword
ivo is a user story focused event-driven schema validator which provides an interface for you to clearly define the behaviour of your entities at creation and during updates
Installation
$ npm i ivoImporting
// CJS
const { Schema } = require("ivo");
// ESM
import { Schema } from "ivo";Defining a schema
import { Schema, type MutableSummary } from "ivo";
type UserInput = {
email: string;
username: string;
phoneNumber: string;
};
type User = {
id: string;
createdAt: Date;
email: string | null;
username: string;
phoneNumber: string | null;
updatedAt: Date;
usernameLastUpdatedAt: Date | null;
};
const userSchema = new Schema<UserInput, User>(
{
id: { constant: true, value: generateUserID },
email: {
default: null,
required: isEmailOrPhoneRequired,
validator: [validateEmail, makeSureEmailIsUnique],
},
phoneNumber: {
default: null,
required: isEmailOrPhoneRequired,
validator: validatePhoneNumber,
},
username: {
required: true,
validator: [validateUsername, makeSureUsernameIsUnique],
shouldUpdate({ usernameLastUpdatedAt }) {
if (!usernameLastUpdatedAt) return true;
const timeDifferenceInMillisecs =
new Date().getTime() - usernameUpdatableFrom.getTime();
const thirtyDaysInMillisecs = 2_592_000_000;
return timeDifferenceInMillisecs >= thirtyDaysInMillisecs;
},
},
usernameLastUpdatedAt: {
default: null,
dependsOn: "username",
resolver: ({ isUpdate }) => (isUpdate ? new Date() : null),
},
},
{ timestamps: true },
);
function isEmailOrPhoneRequired({
context: { email, phoneNumber },
}: MutableSummary<UserInput, User>) {
return [!email && !phoneNumber, 'Provide "email" or "phone" number'] as const;
}
async function makeSureEmailIsUnique(email: string) {
const userWithEmail = await usersDb.findByEmail(email);
return userWithEmail ? { valid: false, reason: "Email already taken" } : true;
}
async function makeSureUsernameIsUnique(username: string) {
const userWithUsername = await usersDb.findByUsername(username);
return userWithUsername
? { valid: false, reason: "Username already taken" }
: true;
}
// get the model
const UserModel = userSchema.getModel();Creating an entity
const { data, error } = await UserModel.create({
email: "john.doe@mail.com",
id: 5, // will be ignored because it is a constant property
name: "John Doe", // will be ignored because it is not on schema
username: "john_doe",
usernameLastUpdatedAt: new Date(), // will be ignored because it is a dependent property
});
if (error) return handleError(error);
console.log(data);
// {
// createdAt: new Date(),
// email: 'john.doe@mail.com',
// id: 101,
// phoneNumber: null,
// updatedAt: new Date(),
// username: 'john_doe',
// usernameLastUpdatedAt: null
// }
// data is safe to dump in db
await usersDb.insertOne(data);Updating an entity
const user = await usersDb.findByID(101);
if (!user) return handleError({ message: "User not found" });
const { data, error } = await UserModel.update(user, {
usernameLastUpdatedAt: new Date(), // dependent property -> will be ignored
id: 75, // constant property -> will be ignored
age: 34, // not on schema -> will be ignored
username: "johndoe",
});
if (error) return handleError(error);
console.log(data);
// {
// updatedAt: new Date(),
// username: 'johndoe',
// usernameLastUpdatedAt: Date // value returned from resolver -> 30days from now
// }
await usersDb.updateByID(user.id, data);// updating 'username' again will not work
const { error } = await UserModel.update(user, {
username: "john-doe", // will be ignored because shouldUpdate rule will return false
});
console.log(error);
// {
// message: 'NOTHING_TO_UPDATE',
// payload: {}
// }Docs
1.6.4
12 months ago
1.6.3
1 year ago
1.6.2
1 year ago
1.6.1
1 year ago
1.6.9
12 months ago
1.6.11
10 months ago
1.6.8
12 months ago
1.6.10
12 months ago
1.6.7
12 months ago
1.6.6
12 months ago
1.6.12
10 months ago
1.6.5
12 months ago
1.6.0
1 year ago
1.5.2
1 year ago
1.4.5
1 year ago
1.4.4
2 years ago
1.4.3
2 years ago
1.5.1
1 year ago
1.4.2
2 years ago
1.5.0
1 year ago
1.4.1
2 years ago
1.4.0
2 years ago
1.3.5
2 years ago
1.3.4
2 years ago
1.3.3
2 years ago
1.3.2
2 years ago
1.3.1
2 years ago
1.3.0
2 years ago
1.2.3
2 years ago
1.2.2
2 years ago
1.2.0
2 years ago
1.2.1
2 years ago
1.1.0
2 years ago
1.0.2
2 years ago
1.0.1
2 years ago
0.0.1
2 years ago
1.0.0
7 years ago