Skip to content

normalize: true mutates objects used in responses #897

@crishoj

Description

@crishoj

What version of Elysia is running?

1.1.23

What platform is your computer?

macOS

What steps can reproduce the bug?

With normalize true (the default), Elysia will mutate objects used in responses.

Consider this example with a long-lived object people:

import {Elysia, t} from 'elysia'

const people = new Map<number, object>([
    [1, {name: 'Jane Doe', age: 80}],
])

const app = new Elysia()
    .get('/names/:id', ({params: {id}}) => {
        return people.get(id)
    }, {
        params: t.Object({id: t.Number()}),
        response: t.Object({name: t.String()})
    })

console.log('people before request:', people)
await app.handle(new Request('http://localhost/names/1'))
console.log('people after request:', people)

What is the expected behavior?

people after request: Map(1) {
  1: {
    name: "Jane Doe",
    age: 80,
  },
}

From a developer perspective, I expect Elysia to not directly mutate objects used in a response body.

If response normalization is active, I expect cleaning to happen on a (deep) clone of the object passed as response body.

What do you see instead?

people after request: Map(1) {
  1: {
    name: "Jane Doe",
    // ⚠️ Notice that the `age` property is now missing from the long-lived object
  },
}

Objects used in responses are directly mutated — additional properties are deleted by the Typebox cleaner:

if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) {
    value[key] = Visit(additionalProperties, references, value[key]);
    continue;
}
delete value[key];

Additional information

This caught me off guard and led to subsequent errors in my app where properties in a long-lived object were suddenly undefined.

For improved DX, please consider doing a structuredClone() of objects before normalization.

Have you try removing the node_modules and bun.lockb and try again yet?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions