1.6.12 • Published 4 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 ivo
Importing
// 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
6 months ago
1.6.3
7 months ago
1.6.2
7 months ago
1.6.1
7 months ago
1.6.9
6 months ago
1.6.11
4 months ago
1.6.8
6 months ago
1.6.10
6 months ago
1.6.7
6 months ago
1.6.6
6 months ago
1.6.12
4 months ago
1.6.5
6 months ago
1.6.0
8 months ago
1.5.2
8 months ago
1.4.5
1 year ago
1.4.4
1 year ago
1.4.3
1 year ago
1.5.1
12 months ago
1.4.2
1 year ago
1.5.0
12 months ago
1.4.1
1 year ago
1.4.0
1 year ago
1.3.5
1 year ago
1.3.4
1 year ago
1.3.3
1 year ago
1.3.2
1 year ago
1.3.1
1 year ago
1.3.0
1 year ago
1.2.3
1 year ago
1.2.2
1 year ago
1.2.0
1 year ago
1.2.1
1 year ago
1.1.0
1 year ago
1.0.2
1 year ago
1.0.1
1 year ago
0.0.1
2 years ago
1.0.0
6 years ago