dynamodb-toolbox icon indicating copy to clipboard operation
dynamodb-toolbox copied to clipboard

Question regarding transformers for stringified json

Open aaarichter opened this issue 1 year ago • 3 comments

For legacy reasons, we have stringified JSON objects in the dynamoDB tables I'm working on, instead of maps or other constructs When I saw https://www.dynamodbtoolbox.com/docs/schemas/transformers/usage I was pretty hyped, that it would allow me to use the JSON.parse and JSON.stringify directly in the schema.

But I noticed that 1. it only works with primitives (as written in the documentation), 2. it wouldn't allow a change of primitive return back a complex object.

@ThomasAribart Do you think something like this might be possible:

interface complexObject {
  field1: string
  filed2: ...
}

const transformer: Transformer<string, complexObject | undefined> = {
  parse: input => input ? <complexObject>JSON.parse(input): undefined,

  format: saved => JSON.stringify(saved)
}

const pokemonSchema = schema({
  id: string().key(),
  props: string().transform(transformer)
})

so that props will be of type complexObject when used in code, and string when written to DynamoDB

aaarichter avatar Sep 22 '24 16:09 aaarichter

See: https://github.com/dynamodb-toolbox/dynamodb-toolbox/issues/931

icholy avatar Sep 22 '24 18:09 icholy

Yes it will not work at the moment but I definitely believe transformers can be improved, and even with proper type safety (i.e. SavedItem could be correctly inferred) with HotScript High-Order Types.

I actually released a very similar library recently: Onion.JS. The same concepts can be applied to transformers I think.

Not the highest prio right now but that would definitely be dope.

ThomasAribart avatar Sep 22 '24 18:09 ThomasAribart

Hi @aaarichter!

I started to advance on the subject. Since v1.10.0, transformers can now carry hotscript high-order type to derive saved value (so transform(prefix('PREFIX')) will actually result in prefixed type in SavedItem).

Transformers still concern primitives for now but I'll be able to enlarge them to all attribute types easily now.

ThomasAribart avatar Oct 01 '24 18:10 ThomasAribart

Hi @ThomasAribart -- thank you for your work on this library!

I just wanted to second how useful I'd find this feature if implemented -- for one example, it's common to work with timestamps as Luxon DateTimes in application code while serializing them as a string to the DB. It would be extremely convenient to define a transformer on the attribute level (as opposed to writing a transform function over the full entity type).

While transforming primitives can be useful in many situations, transforms from ApplicationType => PrimitiveType seem more widely applicable

mzguimaraes avatar Jan 28 '25 22:01 mzguimaraes

Hi @mzguimaraes and thanks for your input!

There's also a proposition for a date() attribute type but:

  • Adding a new attribute type is a considerable amount of work
  • It would not even fix your need totally as it would deserialize to a JS Date object and not a Luxon DateTimes

What I can do however, and would be quick to implement, is to add the transform option to the any() type, this way you can do something like that:

const birthdaySchema = any()
  .castAs<DateTimes>()
  .transform({
    parse: date => date.toISOString(),
    format: str => new DateTimes(str)
  })

Would that fit your need?

ThomasAribart avatar Jan 29 '25 12:01 ThomasAribart

I think so! Appreciate the quick response 😄

mzguimaraes avatar Jan 29 '25 16:01 mzguimaraes