Skip to content

fix: serialize Date objects before Standard Schema validation#1690

Closed
raunak-rpm wants to merge 4 commits intoelysiajs:mainfrom
raunak-rpm:fix/standard-schema-date-serialization
Closed

fix: serialize Date objects before Standard Schema validation#1690
raunak-rpm wants to merge 4 commits intoelysiajs:mainfrom
raunak-rpm:fix/standard-schema-date-serialization

Conversation

@raunak-rpm
Copy link
Copy Markdown

@raunak-rpm raunak-rpm commented Jan 20, 2026

Summary

This PR fixes an issue where response validation fails when using Standard Schema validators (Zod, Effect, Valibot, etc.) with Date types.

Fixes #1670

Problem

When returning Date objects from handlers with Standard Schema response validation, the validation fails with a 422 error:

const app = new Elysia().get(
  '/date',
  () => new Date(),
  {
    response: {
      200: Schema.standardSchemaV1(Schema.Date) // Effect Schema
    }
  }
)
// Returns: 422 Validation Error
// "Expected string, actual 2026-01-10T20:07:52.545Z"

The root cause is:

  1. Response validation (Check) happens before encoding
  2. The schema expects a string (JSON representation of dates)
  3. But the Date object hasn't been serialized to ISO string yet
  4. JSON.stringify would convert Date to ISO string, but validation fails first

This was already fixed for TypeBox's t.Date() in PR #1327, but not for Standard Schema validators.

Solution

Added a serializeDates helper function that recursively converts Date objects to ISO strings (matching JSON.stringify behavior). This function is now called before validation in all three Standard Schema validator code paths:

  • Dynamic async validators
  • Non-async validators (with sub-validators)
  • Non-async validators (without sub-validators)

The Encode function also uses serializeDates to ensure proper transformation.

Changes

  • src/schema.ts: Added serializeDates helper and updated all Standard Schema validators to serialize dates before validation
  • test/standard-schema/date-serialization.test.ts: Added comprehensive tests for the fix

Testing

All 1455 tests pass, including 8 new tests for date serialization:

✓ Date Serialization > serializeDates helper > should convert Date to ISO string
✓ Date Serialization > serializeDates helper > should handle null and undefined
✓ Date Serialization > serializeDates helper > should pass through primitives unchanged
✓ Date Serialization > serializeDates helper > should serialize nested Date in object
✓ Date Serialization > serializeDates helper > should serialize Date in array
✓ Date Serialization > serializeDates helper > should handle deeply nested objects with dates
✓ Date Serialization > Standard Schema with Date response > should serialize Date to ISO string before validation
✓ Date Serialization > Standard Schema with Date response > should serialize Date in object response

Summary by CodeRabbit

  • New Features

    • Validation, encoding, and error messages now consistently convert Date objects to ISO strings so inputs/outputs match JSON behavior.
    • Added a public helper to perform Date → ISO serialization used during validation and encoding.
  • Tests

    • Added comprehensive tests for date serialization across primitives, nested objects/arrays, and end-to-end validation/response flows.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 20, 2026

Warning

Rate limit exceeded

@raunak-rpm has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ca6b74d and 378cc13.

📒 Files selected for processing (2)
  • src/schema.ts
  • test/standard-schema/date-serialization.test.ts

Walkthrough

Added a public serializeDates(value: unknown): unknown helper that recursively converts Date instances to ISO strings and integrated it into schema validation, check, decode, and encode paths so values are serialized before validation and emitted as JSON-friendly strings.

Changes

Cohort / File(s) Summary
Date Serialization Core
src/schema.ts
Added exported serializeDates() to recursively convert Date → ISO strings; wired serializeDates(...) into validation, check, decode, and encode flows across dynamic, standard, and non-dynamic schema branches. Added comments linking behavior to related issue.
Tests
test/standard-schema/date-serialization.test.ts
New test suite covering serializeDates() (Date → ISO, null/undefined, primitives, nested objects/arrays, toJSON) and verifying Dates are serialized before validation in response scenarios.
Manifest / Package
manifest_file, package.json
Manifest/test additions and package manifest updates referenced in the diff.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Handler
  participant Serializer as Serializer\n(serializeDates)
  participant Validator
  participant Encoder as Encoder/Response

  Client->>Handler: send/return value (may contain Date)
  Handler->>Serializer: serializeDates(value)
  Serializer-->>Handler: value with Dates as ISO strings
  Handler->>Validator: validate(serializedValue)
  Validator-->>Handler: validation result
  Handler->>Encoder: encode(serializedValue)
  Encoder-->>Client: HTTP response (ISO strings)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hop through fields where timestamps play,
I turn each Date to ISO light and bright,
In objects, arrays, or hidden nooks they stay,
I tidy them up for validators' sight,
Off they go—neat strings into the night ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: serialize Date objects before Standard Schema validation' accurately captures the main change—adding automatic date serialization before validation to fix response validation failures with Standard Schema validators.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #1670: it adds automatic Date-to-ISO-string serialization before validation across all Standard Schema validator code paths and in the Encode function, directly fixing the validation failure when returning Date objects.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing the date serialization issue: the new serializeDates helper, its integration into validation paths, and comprehensive tests. No unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Jan 20, 2026

Open in StackBlitz

npm i https://pkg.pr.new/elysiajs/elysia@1690

commit: 378cc13

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/schema.ts`:
- Around line 387-405: serializeDates currently calls
Date.prototype.toISOString() which throws RangeError for invalid Dates; instead
detect invalid Date values inside serializeDates (the Date branch) by checking
Number.isNaN(date.getTime()) or similar and return null for invalid Dates to
match JSON.stringify behavior, leaving valid Dates serialized toISOString();
apply the same check when traversing nested structures via serializeDates so
invalid Date instances anywhere in arrays/objects produce null rather than
throwing.

This commit fixes an issue where response validation fails when using
Standard Schema validators (Zod, Effect, etc.) with Date types.

## Problem

When returning Date objects from handlers with Standard Schema response
validation, the validation fails because:
1. Response validation (Check) happens BEFORE encoding
2. The schema expects a string (JSON representation)
3. But the Date object hasn't been serialized yet
4. JSON.stringify would convert Date to ISO string, but validation failed first

## Solution

Added `serializeDates` helper function that recursively converts Date objects
to ISO strings (matching JSON.stringify behavior). This function is now called
BEFORE validation in all three Standard Schema validator code paths:
- Dynamic async validators
- Non-async validators (with sub-validators)
- Non-async validators (without sub-validators)

The Encode function also uses serializeDates to ensure proper transformation.

Fixes elysiajs#1670
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/schema.ts`:
- Around line 387-403: serializeDates currently skips invoking toJSON(), causing
mismatches with JSON.stringify; update serializeDates (function name:
serializeDates) so that after handling Date instances you check whether the
value has a toJSON function (e.g. value && typeof (value as any).toJSON ===
'function') and, if so, call that toJSON() and pass its result back into
serializeDates (to handle nested serialization and maintain JSON.stringify
semantics) before proceeding to array/object traversal; ensure the recursive
call replaces the original value to avoid duplicating logic or missing
transformed primitives.

The serializeDates function now checks for toJSON method on objects before
array/object traversal. This ensures proper JSON.stringify semantics where
custom toJSON methods are called and their results are recursively processed.

This fixes cases where objects with custom toJSON methods would be traversed
as regular objects instead of having their toJSON result serialized.

Changes:
- Added toJSON check in serializeDates after Date handling
- toJSON result is recursively passed through serializeDates
- Added tests for toJSON handling with various return types
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/schema.ts`:
- Around line 656-669: Errors currently returns a Promise; change it to return
an iterable immediately by calling Check(serializeDates(value)) and returning
its .issues synchronously: implement Errors as something like const res =
Check(serializeDates(value)); if (res && typeof (res as any).then ===
'function') return []; return (res as { issues?: unknown[] }).issues ?? []; this
keeps the signature expected by callers (use of spread and .First()) while using
Check and serializeDates from the surrounding code.
🧹 Nitpick comments (1)
src/schema.ts (1)

892-902: Clarify: Decode does not serialize dates, unlike Check/Encode.

Decode validates the raw value without calling serializeDates, while Check and Errors serialize dates before validation. This appears intentional (Decode is for input parsing, Check/Encode for output), but the asymmetry could cause confusion. Consider adding a brief comment explaining why Decode operates on the raw value.

📝 Suggested comment
 			// `@ts-ignore`
 			Decode(value) {
+				// Decode operates on raw input values, not serialized output
 				// `@ts-ignore`
 				const response = schema['~standard'].validate(value)

@SaltyAom
Copy link
Copy Markdown
Member

This user has mass usage of fully automatic AI-generated pull requests without human interaction (also known as "AI slop”) to abuse issues and pull requests for multiple times for "karma farming" purpose

For example:

This is against CONTRIBUTING.md as stated

  • AI generated pull request without human interaction, review and supervision may result in close without further notice or ban from future contribution to Elysia

We have blocked this users to prevent them abusing the system in the future

Thank you for your understanding

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Elysia Response Validation Fails to Serialize Dates from Effect Schema, Zod (and Potentially Other Libraries)

3 participants