Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates ADR with conventions for zod schemas #8824

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions common_knowledge/ADRs/003-ModelSchemas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# ADR 003: Conventions for Defining Zod Schemas for Sequelize Models

## Status

Proposed: 240812 by Roger Torres (@rotorsoft)

## Context

In our application, we are using Sequelize as the ORM to interact with our database and Zod to define schemas for model validation. To maintain consistency and ensure reliable data handling, we are adopting specific conventions for defining Zod schemas for Sequelize models. These conventions address how we handle Sequelize-managed timestamps, model associations, and nullable fields.

## Decision

We have decided to adopt the following conventions for defining Zod schemas for Sequelize models:

1. **Primary Key ID Columns:**
- **Schema Representation:** Primary key ID columns that are generated by Sequelize will be defined as optional but not nullable, similar to timestamps.
- **Reasoning:** Primary key IDs are auto-generated by Sequelize, so they are not required when creating a record but will be present afterward. Ensuring these IDs are correctly typed as integers or strings is crucial for consistency.
- **Example:** We'll use the `PG_INT` schema util in `libs/schemas` to keep integers inside PG range:

```typescript
export const PG_INT = z.number().int().min(MIN_SCHEMA_INT).max(MAX_SCHEMA_INT);

const Comment = z.object({
id: PG_INT.optional(),
...
})
```

2. **Sequelize-Managed Timestamps (`created_at` and `updated_at`):**
- **Schema Representation:** These fields will be defined as optional but not nullable in the Zod schema.
- **Reasoning:** Since these timestamps are automatically managed by Sequelize, they are not required when creating a record but will always be present once the record is created.
- **Coercion:** We will use Zod's coercion feature to convert ISO string representations of these timestamps to JavaScript `Date` objects for consistency in how dates are handled within our application.
- **Example:**:

```typescript
const Comment = z.object({
...
created_at: z.coerce.date().optional(),
updated_at: z.coerce.date().optional()
})
```

3. **Model Associations Represented as Foreign Key Constraints:**
- **Schema Representation:** Associations that are represented as foreign key constraints in the data model will be defined using Zod's nullish rules.
- **Reasoning:** In queries that use outer joins, associated records might not exist, resulting in null references. Using Zod's nullish rule allows us to accommodate these cases without validation errors.
- **Zod Example:**

```typescript
z.object({
foreignKeyId: z.string().nullish(),
})
```

4. **Nullable Fields (`allowNull: true` in Sequelize Model):**
- **Schema Representation:** Fields that are allowed to be null in the Sequelize model (`allowNull: true`) will also be represented as nullish in the Zod schema.
- **Reasoning:** This ensures that the Zod schema accurately reflects the database schema, allowing null values where they are permitted by the database.
- **Zod Example:**

```typescript
z.object({
nullableField: z.string().nullish(),
})
```

## Consequences

- **Consistency:** These conventions will lead to consistent handling of timestamps, associations, and nullable fields across all models in our application.
- **Validation:** By accurately representing nullable fields and associations, we reduce the risk of validation errors during data processing.
- **Type Safety:** Coercing timestamp fields to `Date` objects ensures that date manipulations are type-safe and consistent throughout the codebase.

## References

- [Sequelize Documentation](https://sequelize.org/)
- [Zod Documentation](https://zod.dev/)
Loading