Skip to content

Conversation

@prastoin
Copy link
Contributor

@prastoin prastoin commented Oct 26, 2025

Introduction

Please first review this PR initial base #15358
In a nutshell refactored the frontend fetchers to display v2 errors format smoothly
Please note that the v2 now finished the whole validation and does fail fast anymore ( summary is hardcoded for the moment )

[
  {
    "extensions": {
      "code": "BAD_USER_INPUT",
      "errors": {
        "cronTrigger": [],
        "databaseEventTrigger": [],
        "fieldMetadata": [
          {
            "errors": [
              {
                "code": "INVALID_FIELD_INPUT",
                "message": "Default value should be as quoted string",
                "value": "",
              },
              {
                "code": "INVALID_FIELD_INPUT",
                "message": "Default value "" must be one of the option values",
                "value": "",
              },
            ],
            "flatEntityMinimalInformation": {
              "id": Any<String>,
              "name": "testField",
              "objectMetadataId": Any<String>,
            },
            "status": "fail",
            "type": "create_field",
          },
        ],
        "index": [],
        "objectMetadata": [],
        "routeTrigger": [],
        "serverlessFunction": [],
        "view": [],
        "viewField": [],
        "viewFilter": [],
        "viewGroup": [],
      },
      "message": "Validation failed for 0 object(s) and 0 field(s)",
      "summary": {
        "invalidCronTrigger": 0,
        "invalidDatabaseEventTrigger": 0,
        "invalidFieldMetadata": 0,
        "invalidIndex": 0,
        "invalidObjectMetadata": 0,
        "invalidRouteTrigger": 0,
        "invalidServerlessFunction": 0,
        "invalidView": 0,
        "invalidViewField": 0,
        "invalidViewFilter": 0,
        "invalidViewGroup": 0,
        "totalErrors": 0,
      },
      "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
    },
    "message": "Multiple validation errors occurred while creating fields",
    "name": "GraphQLError",
  },
]

What's done

  • usePersistView tool ( CRUD )
  • renamed usePersistViewX tools accordingly ( no more records or core )
  • Now catching a lot of before unhandled exceptions
  • refactored each services to handle their own exception handlers and return either the response or the error within a discriminated union record

Result

Primary entity error

When performing an metadata operation on a given metadata, if validation errors occurs we will display each of them in a toast
Here while creating an object metadata.
image

Related entity error

Still while creating an object
image

Translated

image

Conclusion

This PR is an extract of #15331
close twentyhq/core-team-issues#1776

Notes

  • Not refactor around triggers services as they're not consumed directly by any frontend services

@prastoin prastoin self-assigned this Oct 26, 2025
@prastoin prastoin force-pushed the shared-server-types-centralization branch from 76a5c57 to 1406b00 Compare October 27, 2025 08:06
@prastoin prastoin force-pushed the twenty-front-handle-v2-error-format branch from 050b24d to 1950ea5 Compare October 27, 2025 08:07
@prastoin prastoin marked this pull request as ready for review October 27, 2025 08:08
Copy link
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.

Greptile Overview

Greptile Summary

This PR refactors frontend metadata API services to handle v2 error responses with improved user experience. The refactoring introduces a discriminated union pattern (MetadataRequestResult<T>) for all metadata operations, returning either {status: 'successful', response} or {status: 'failed', error}.

Key Changes:

  • Centralized error handling via useMetadataErrorHandler hook that classifies errors as v1, v2-validation, or v2-internal
  • Consolidated CRUD operations into unified hooks (usePersistView, usePersistViewField, usePersistServerlessFunction)
  • All object/field metadata hooks now return discriminated unions instead of throwing exceptions
  • Consumers check result.status === 'successful' before proceeding with navigation or state updates
  • V2 validation errors display individual field-level error messages with translations

Architecture:
The new pattern creates clear separation between error handling (centralized in the hook) and error display (via snackbar), with consumers only needing to check status for control flow decisions.

Issues Found:

  • Several view-related hooks (useSaveCurrentViewFields, useSaveCurrentViewGroups, useSaveRecordSortsToViewSorts) do not check operation status, potentially swallowing errors silently
  • These hooks execute operations but ignore the discriminated union results, which defeats the purpose of the refactoring

Confidence Score: 4/5

  • This PR is mostly safe to merge with some view operation hooks that need error handling improvements
  • The core refactoring is well-designed with proper discriminated unions and centralized error handling. Object/field metadata operations correctly check status before proceeding. However, several view-related hooks ignore operation results, which could lead to silent failures in production. These are not critical blockers since errors are still shown to users via snackbars, but the inconsistent status checking pattern reduces reliability.
  • Pay close attention to useSaveCurrentViewFields.ts, useSaveCurrentViewGroups.ts, and useSaveRecordSortsToViewSorts.ts - these ignore operation status and may silently fail

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts 5/5 new centralized error handler classifying v1/v2 errors and displaying user-friendly messages
packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewFields.ts 3/5 refactored to use usePersistViewField but does not check operation status, potentially swallowing errors
packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts 3/5 refactored to use usePersistViewGroupRecords but does not check operation status, potentially swallowing errors
packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts 3/5 refactored to use usePersistViewSortRecords but does not check operation status, potentially swallowing errors

Sequence Diagram

sequenceDiagram
    participant UI as UI Component
    participant Hook as Metadata Hook
    participant Handler as useMetadataErrorHandler
    participant Classifier as classifyMetadataError
    participant Mutation as GraphQL Mutation
    participant Snackbar as useSnackBar
    
    UI->>Hook: createOneObjectMetadataItem(input)
    Hook->>Mutation: Execute mutation
    
    alt Success
        Mutation-->>Hook: Success response
        Hook->>Hook: refreshObjectMetadataItems()
        Hook-->>UI: {status: 'successful', response}
        UI->>UI: Navigate to new object
    else Error
        Mutation-->>Hook: ApolloError
        Hook->>Handler: handleMetadataError(error, primaryMetadataName)
        Handler->>Classifier: classifyMetadataError(error, primaryMetadataName)
        
        alt V2 Validation Error
            Classifier-->>Handler: {type: 'v2-validation', extensions, relatedFailingMetadataNames}
            Handler->>Handler: Extract primary entity errors
            loop For each validation error
                Handler->>Snackbar: enqueueErrorSnackBar(userFriendlyMessage)
            end
            alt No primary errors but related errors
                Handler->>Snackbar: enqueueErrorSnackBar(related entity message)
            end
        else V2 Internal Error
            Classifier-->>Handler: {type: 'v2-internal', code, message}
            Handler->>Snackbar: enqueueErrorSnackBar(internal error message)
        else V1 Error
            Classifier-->>Handler: {type: 'v1', error}
            Handler->>Snackbar: enqueueErrorSnackBar(apolloError)
        end
        
        Hook-->>UI: {status: 'failed', error}
        UI->>UI: Stay on current page
    end
Loading

55 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@github-actions
Copy link
Contributor

github-actions bot commented Oct 27, 2025

🚀 Preview Environment Ready!

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

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

prastoin added a commit that referenced this pull request Oct 27, 2025
# Introduction
Followup of #15331 ( Reducing
size by concerns )
This PR centralizes v2 format error types in `twenty-shared` and
consuming them in the existing v2 error format logic in `twenty-server`

## Next
This #15360 handles the frontend
v2 format error refactor

## Conclusion
Related to twentyhq/core-team-issues#1776
Base automatically changed from shared-server-types-centralization to main October 27, 2025 08:44
@prastoin prastoin force-pushed the twenty-front-handle-v2-error-format branch from bcfa284 to f8f8ac6 Compare October 27, 2025 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adapt frontend to display v2 errors

1 participant