Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

ErrorAttribute changes mutation payload shape, preventing Relay @prependNode from updating connections correctly #5364

Copy link
Copy link

Description

@wiaanstackworx
Issue body actions

Hi everyone,

Firstly, apologies if this is the wrong repository. I'm not entirely sure whether this belongs here or on the Hot Chocolate repository.

I couldn't find an existing GitHub issue describing this scenario, although it's entirely possible that I'm missing something or that I'm using the framework incorrectly.


Environment

Hot Chocolate – 15.1.14
React – 18.1.0
react-relay – 16.2.0
relay-runtime – 16.2.0


What I'm trying to achieve

I have a page that displays a list of items using a Relay connection and a MUI DataGrid.

A user can create a new item from a modal dialog. The modal performs a Relay mutation, and I use Relay's declarative mutation directive @prependNode so that the newly created node is automatically inserted into the existing Relay connection.

The DataGrid itself contains no local state—it simply renders whatever exists in the Relay store.

The intended flow is:

  • The parent component loads a Relay connection.
  • The parent renders a MUI DataGrid from that connection.
  • The parent passes the Relay connection ID (__id) into a modal.
  • The modal executes a mutation.
  • Relay prepends the newly created node into the connection.
  • The Relay store updates.
  • The parent re-renders.
  • The DataGrid immediately displays the new row.

This works perfectly until I introduce Hot Chocolate's ErrorAttribute.


Relevant implementation

Parent component

The parent owns the Relay query and Relay connection.

items
@connection(key: "ItemList_items") {
    __id

    edges {
        node {
            id
            name
            value
            enabled
        }
    }
}

The Relay connection ID is passed into the modal.

<CreateItemModal
    connectionId={data.items?.__id ?? ""}
/>

The DataGrid rows are derived directly from the Relay connection.

const rows = React.useMemo(() => {
    return data.items?.edges?.map(({ node }) => ({
        id: node?.id,
        name: node?.name,
        value: node?.value,
        enabled: node?.enabled,
    }));
}, [data.items?.edges]);

The DataGrid simply renders these rows.

<DataGridPro
    rows={rows ?? []}
    getRowId={(row) => row.id}
/>

There is no additional React state or manual Relay store manipulation.


Modal component

The modal receives the Relay connection ID.

interface Props {
    connectionId: string;
}

When submitting the mutation, the connection ID is passed to relay.

commitMutation({
    variables: {
        input: {
            ...
        },
        connections: [connectionId],
    },
});

Working mutation

Without ErrorAttribute, my mutation looks like this:

mutation CreateItemMutation(
    $input: CreateItemInput!
    $connections: [ID!]!
) {
    createItem(input: $input)
    @prependNode(
        connections: $connections
        edgeTypeName: "ItemEdge"
    ) {
        id
        name
        value
        enabled
    }
}

Everything works exactly as expected.

Relay prepends the node into the connection, the parent component sees the updated connection, and the DataGrid immediately updates.


After introducing ErrorAttribute

I wanted to return typed domain errors using ErrorAttribute, so my mutation now returns a payload object instead.

It now looks similar to this:

mutation CreateItemMutation(
    $input: CreateItemInput!
) {
    createItem(input: $input) {
        errors {
            ... on ItemAlreadyExistsError {
                message
            }
        }

        item {
            id
            name
            value
            enabled
        }
    }
}

The server-side error handling works perfectly.

However, because the created node is now nested under item, I can no longer use Relay's @prependNode directive.


What happens

The mutation successfully creates the record in the database.

If I refresh the page afterwards, the new record is returned by the query and displays correctly in the DataGrid.

The issue only occurs immediately after the mutation completes.

The DataGrid throws the following error:

MUI X: The data grid component requires all rows to have a unique `id` property.
Alternatively, you can use the `getRowId` prop to specify a custom id for each row.

A row was provided without id in the rows prop: {}

The DataGrid already specifies:

<DataGridPro
    getRowId={(row) => row.id}
/>

When logging the newly inserted node after the mutation completes, I see:

node.id: undefined

The newly added row appears to be an empty object ({}), which causes the DataGrid error.

Refreshing the page fixes everything because the query is executed again and the server returns the correct data.


My understanding

From what I can tell, the issue isn't with MUI DataGrid.

The DataGrid is simply exposing the problem because it's rendering whatever Relay has stored.

It seems like the interaction between ErrorAttribute and Relay's declarative mutation directives is the actual issue.

Questions

  • Is this expected behaviour?
  • Is ErrorAttribute intended to be incompatible with Relay's declarative mutation directives like @prependNode and @appendNode?
  • Is there a recommended pattern for combining ErrorAttribute with Relay connections?
  • Is the recommended solution to always write a custom Relay updater when using ErrorAttribute?
  • Is there another Hot Chocolate feature or pattern that I've overlooked which preserves compatibility with Relay's declarative mutation directives?

Additional context

I've attached both the parent component and the modal component used in this example if seeing the complete implementation is helpful.

The important part is that:

  • The parent owns the Relay query and connection.
  • The parent passes the connection ID (__id) to the modal.
  • The modal executes the mutation.
  • The DataGrid renders directly from the Relay connection.
  • There is no local React state involved.

Without ErrorAttribute, Relay updates the connection automatically and everything works as expected.

After introducing ErrorAttribute, the mutation succeeds and the record is persisted, but the Relay connection no longer appears to contain a valid node until the page is refreshed.

Any guidance would be greatly appreciated.

Thanks!

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.