Skip to content

Change formatResult to return string instead of Date object for DATE_TIME#17407

Merged
lucasbordeau merged 2 commits intomainfrom
feat/change-format-result-date-time-to-string
Jan 23, 2026
Merged

Change formatResult to return string instead of Date object for DATE_TIME#17407
lucasbordeau merged 2 commits intomainfrom
feat/change-format-result-date-time-to-string

Conversation

@lucasbordeau
Copy link
Copy Markdown
Contributor

This PR modifies our broadly used formatResult util to counter act TypeORM transforming any date time to a Date object.

Instead we return the ISO string for any DATE_TIME, this way we're not transporting Date object from one function to another in the backend.

We do this because there was problems working with events utils that take string date time in parameters and received Date objects.

As this is a recurring problem and because it's an opinionated choice from TypeORM, we chose to switch to string only in our codebase, from TypeORM's output to frontend.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Jan 23, 2026

Greptile Overview

Greptile Summary

Modified formatResult utility to convert DATE_TIME fields from Date objects to ISO strings, counteracting TypeORM's automatic transformation. This ensures consistent string-based datetime handling throughout the backend.

  • Added dedicated handling for DATE_TIME field types (lines 185-216)
  • Preserves strings as-is, converts Date objects to ISO strings via toISOString()
  • Throws descriptive error for invalid DATE_TIME values
  • Mirrors existing DATE field handling pattern (lines 168-183)
  • Used by all query builders (WorkspaceSelectQueryBuilder, WorkspaceInsertQueryBuilder, WorkspaceUpdateQueryBuilder) to format TypeORM results before returning to callers

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk - it normalizes DATE_TIME field handling to consistently return ISO strings
  • The change is straightforward and defensive: it handles both string and Date inputs for DATE_TIME fields, converting Date objects to ISO strings. This addresses TypeORM's automatic date transformation and establishes consistent string-based datetime handling throughout the backend. The implementation includes proper type checking and error handling for invalid values.
  • No files require special attention

Important Files Changed

Filename Overview
packages/twenty-server/src/engine/twenty-orm/utils/format-result.util.ts Adds DATE_TIME field handling to convert Date objects to ISO strings, addressing TypeORM's automatic date transformation and ensuring consistent string format throughout the backend

Sequence Diagram

sequenceDiagram
    participant Client
    participant QueryBuilder as WorkspaceSelectQueryBuilder
    participant TypeORM
    participant FormatResult as formatResult util
    participant Backend

    Client->>QueryBuilder: getOne() / getMany()
    QueryBuilder->>TypeORM: Execute SQL query
    TypeORM->>TypeORM: Transform DATE_TIME columns to Date objects
    TypeORM-->>QueryBuilder: Raw result with Date objects
    QueryBuilder->>FormatResult: formatResult(data, metadata)
    FormatResult->>FormatResult: Filter DATE_TIME fields
    FormatResult->>FormatResult: Check if value is Date object
    FormatResult->>FormatResult: Convert Date to ISO string via toISOString()
    FormatResult-->>QueryBuilder: Formatted result with ISO strings
    QueryBuilder-->>Backend: Return consistent string datetimes
    Backend-->>Client: String datetimes throughout
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

No files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Copy Markdown
Member

@Weiko Weiko left a comment

Choose a reason for hiding this comment

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

LGTM
Hopefully we are not expecting those being Date objects somewhere in the codebase 🤞 (I'm pretty sure we don't)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the formatResult utility so that DATE_TIME fields are consistently represented as ISO strings instead of Date objects, aligning backend behavior with consumers that expect string-based datetimes.

Changes:

  • Added a post-processing pass in formatResult that locates all DATE_TIME fields in the flat metadata and inspects corresponding values on the formatted result object.
  • Normalizes DATE_TIME field values: leaves strings unchanged, converts Date instances to ISO strings, and throws an error for any other non-null/undefined type.
  • Keeps existing DATE handling intact while extending the utility’s behavior in a backward-compatible pattern with the DATE-only logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +185 to +215
const fieldMetadataItemsOfTypeDateTimeOnly =
getFlatFieldsFromFlatObjectMetadata(
flatObjectMetadata,
flatFieldMetadataMaps,
).filter((field) => field.type === FieldMetadataType.DATE_TIME);

for (const dateTimeField of fieldMetadataItemsOfTypeDateTimeOnly) {
// @ts-expect-error legacy noImplicitAny
const rawUpdatedDateTime = newData[dateTimeField.name] as
| string
| Date
| null
| undefined;

if (!isDefined(rawUpdatedDateTime)) {
continue;
}

if (typeof rawUpdatedDateTime === 'string') {
// @ts-expect-error legacy noImplicitAny
newData[dateTimeField.name] = rawUpdatedDateTime;
} else if (rawUpdatedDateTime instanceof Date) {
const dateIsoString = rawUpdatedDateTime.toISOString();

// @ts-expect-error legacy noImplicitAny
newData[dateTimeField.name] = dateIsoString;
} else {
throw new Error(
`Invalid DATE_TIME field "${dateTimeField.name}", value: "${rawUpdatedDateTime}", it should be a string or Date instance, (current type : ${typeof rawUpdatedDateTime}).`,
);
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

This new DATE_TIME normalization logic introduces several new behaviors (preserving string values, converting Date instances to ISO strings, and throwing on unexpected types) in a core utility, but there are no dedicated unit tests covering these cases. Given the existing pattern of unit tests for other Twenty ORM utils in utils/__tests__, it would be safer to add tests that (1) verify DATE_TIME fields passed as Date objects are converted to ISO strings, (2) verify string DATE_TIME values are left unchanged, and (3) assert that passing an invalid type (e.g., a number) causes the expected error to be thrown.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 23, 2026

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:15119

This environment will automatically shut down when the PR is closed or after 5 hours.

@lucasbordeau lucasbordeau self-assigned this Jan 23, 2026
@lucasbordeau lucasbordeau added this pull request to the merge queue Jan 23, 2026
Merged via the queue into main with commit a07590e Jan 23, 2026
68 of 71 checks passed
@lucasbordeau lucasbordeau deleted the feat/change-format-result-date-time-to-string branch January 23, 2026 18:39
github-merge-queue bot pushed a commit that referenced this pull request Jan 27, 2026
This PR fixes a bug that arises in `formatResult` following up the
recent refactor for DATE_TIME :
#17407

In the case of workflows, we pass a plain object to `formatResult` : 

```ts
{
  before: null, 
  after: '2026-01-27T10:59:15.525Z'
}
```

So a case has been added to handle this. 

@Weiko are we ok with this more restrictive else-if part in this util ?
We could also just put a `continue` for unknown shapes.
camilo-agudelo-uma pushed a commit to innovation-grupo-uma/twenty-uma that referenced this pull request Feb 2, 2026
…DATE_TIME` (twentyhq#17407)

This PR modifies our broadly used `formatResult` util to counter act
TypeORM transforming any date time to a `Date` object.

Instead we return the ISO string for any `DATE_TIME`, this way we're not
transporting Date object from one function to another in the backend.

We do this because there was problems working with events utils that
take string date time in parameters and received Date objects.

As this is a recurring problem and because it's an opinionated choice
from TypeORM, we chose to switch to string only in our codebase, from
TypeORM's output to frontend.
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.

3 participants