class-transformer
class-transformer copied to clipboard
Default `group`
Thank you for this library, it works super well. Introducing this notion of groups on my entities (especially to use plainToObject and filter out only the fields that are writable by my API clients), I realized that every other field without any group is not even serialized anymore.
Current situation
In a nutshell, if I have the following entity:
@Expose()
get a_computed_value() {
// ...
}
@Column()
@Expose({ groups: [ MyEntity.class_write_group ]})
firstname: string;
When I serialize this entity without a group option, only the a_computed_value field is serialized. I could introduce another group for read but it would mean I now need to update my entire tree of classes, which is definitely not ideal and put me at risk for BC-break changes on my API.
Potential solution
Introducing a default group. When I serialize, if in the options I give the group undefined (or a constant maybe?) then it means that I want the fields without any group. If I understand correctly the source code, all we would need to update is the following function:
https://github.com/typestack/class-transformer/blob/c3cd7f935adaee70c12d89db7327e51ce9c6679a/src/TransformOperationExecutor.ts#L397-L402
How does this sound?
Hey @sroze, I might not be understanding your proposal correctly, but couldn't you achieve what you're proposing by specifying your own default group? In your case, it could look like:
const DEFAULT_GROUP = 'default';
class SomeClass {
@Expose({ groups: [DEFAULT_GROUP] })
get a_computed_value() {
// ...
}
@Column()
@Expose({ groups: [DEFAULT_GROUP, MyEntity.class_write_group] })
firstname: string;
}
const deserialized = plainToClass(json, { groups: [DEFAULT_GROUP] });
To me, this wouldn't be more effort than specifying a undefined default group which class-transformer should know how to parse.
If this is a common pattern in your codebase, you could even write your own implementation of the plainToClass and Expose methods which call the default ones, but automatically add the DEFAULT_GROUP to groups.
but couldn't you achieve what you're proposing by specifying your own default group?
Indeed, it would be possible but would force me to update every property of every single class within the tree.
To me, this wouldn't be more effort than specifying a undefined default group which class-transformer should know how to parse.
User land, it's very limited effort, it's just that on top of your own groups you have to add undefined. You don't need to do anything if you don't use custom groups because... it will take the fields without @Groups anyway :)
The point I'm making is that it's a usual migration between "no groups" and "some groups" that could be better with such a feature. (i.e. better the way that you don't need to update all your classes with groups while you just need to tweak a few fields for two different endpoints).
I think I understand now, are you after a way to return all @Exposed properties of the class regardless of what groups they have configured? If yes, I wonder if an example could look like this:
import { GroupOption, Expose } from 'class-transformer'
class SomeClass {
@Expose()
get a_computed_value() {
// ...
}
@Column()
@Expose({ groups: [MyEntity.class_write_group] })
firstname: string;
}
const deserialized = plainToClass(json, { groups: [GroupOption.AllExposed] });
Where the expectation is that deserialized contains both firstname and a_computed_value?
Personally I think that makes sense as an addition to class-transformer!
Would you be interested in a PR for this? I think this feature makes sense.
I need same thing. For now I overrided @Expose like this:
import { Expose as BaseExpose, ExposeOptions } from 'class-transformer';
export const NO_GROUP = 'noGroup';
/**
* To use on class properties instead of base expose,
* to allow Expose a property to only a single endpoint:
*
* @Expose() => same as base expose
* @Expose(['custom']) => expose only if 'custom' group is used (base expose will also expose if not group is used)
* @Expose([NO_GROUP, 'custom']) => same as base @Expose(['custom']), expose when no group is provided, or if custom is provided
*
* This implies NO_GROUP group is provided in base configuration.
*/
export const Expose = (options?: ExposeOptions) => BaseExpose({
groups: [NO_GROUP],
...options,
});
Then, for my express controllers I configured like this:
useExpressServer(app, {
classToPlainTransformOptions: {
groups: [NO_GROUP],
},
});
And it works as I expect. "NO_GROUP" or "DEFAULT_GROUP" are both clear to me.