29
loading...
This website collects cookies to deliver better user experience
yup
(in combination with other third party libraries though). yup
schemas and in the NestJS Backend we can use nestjs-yup
which is quite handy and straight forward to use as well. Btw: This works for both, GraphQL- as well as Rest-APIs built with Nest. 👌IPerson.ts
export const PersonSchema = yup.object({
firstName: yup
.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
lastName: yup
.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
email: yup.string().email("Invalid email").required("Required"),
});
export const UpdatePersonSchema = BaseSchema.concat(
yup.object({
firstName: yup.string().notRequired(),
lastName: yup.string().notRequired(),
email: yup.string().email("Invalid email").notRequired(),
})
);
export interface IPerson {
firstName: string;
lastName: string;
email: string;
}
export interface IUpdatePerson extends IUpdateBase, Partial<IPerson> {}
yup
generate the types automatically is the following:type PersonType = yup.InferType<typeof PersonSchema>;
?
won’t work at all when implementing the interfaces in e.g. entities.nestjs-yup
which will provide the necessary Decorators for easy usage.typeorm
). The important part here is that we can use the interfaces defined in the shared type so our Entity is forced to implement the fields defined in IPerson
(hence requiring adjustments once something changed in the interface declaration). person.entity.ts
@Entity()
@ObjectType()
export class Person extends Base implements IPerson {
@Field()
@Column("text")
firstName: string;
@Field()
@Column("text")
lastName: string;
@Field()
@Column("text")
email: string;
}
password
as well as a username
). The Decorator @UseSchema(Schema)
will register the Schema internally to be used by the YupValidationPipe
later on automatically.create-person.input.ts
@InputType()
@UseSchema(PersonSchema)
export class CreatePersonInput implements IPerson {
@Field()
firstName: string;
@Field()
lastName: string;
@Field()
email: string;
}
nullable
and register the UseSchema
for this Input-Type.update-person.input.ts
@InputType()
export class UpdatePersonInput
extends PartialType(CreatePersonInput)
implements IUpdatePerson
{
@Field(() => ID)
id: string;
}
YupValidationPipe
globally so each and every Endpoints using any of the Classes decorated with @UseSchema(Entity)
will be validated automatically using the schema that was given to the decorator.main.ts
// …
const app = await NestFactory.create(AppModule);
…
app.useGlobalPipes(new YupValidationPipe());
…
@UsePipes(new YupValidationPipe())
person.tsx
const initialPerson = {
firstName: "",
lastName: "",
email: "",
} as IPerson;
export const Person = () => (
<div>
<h1>Person</h1>
<Formik
initialValues={initialPerson}
validationSchema={PersonSchema}
onSubmit={(values) => {
console.log("submitting: ", { values });
}}
>
{({ errors, touched }) => (
<Form>
<div className={`${styles.flex} ${styles.column}`}>
<Field name="firstName" placeholder="FirstName" />
{errors.firstName && touched.firstName ? (
<div>{errors.firstName}</div>
) : null}
<Field name="lastName" placeholder="LastName" />
{errors.lastName && touched.lastName ? (
<div>{errors.lastName}</div>
) : null}
<Field name="email" placeholder="E-Mail" />
{errors.email && touched.email ? <div>{errors.email}</div> : null}
<button type="submit">Submit</button>
</div>
</Form>
)}
</Formik>
</div>
);
@Column({nullable: true})
) still has to be added manually. BUT it makes dealing with the same types in the Frontend as well as the Backend much easier because all of them are based on the same shared interface. So if something changes there ts-compiler will complain when e.g. running the tests and you'll know which places will have to be adjusted accordingly.Field
as well as the Column
to nullable: true
once the attribute of the implemented interface is optional ?
.