N
Nuxt2y ago
PMDL

I want to instanciate incoming data using useFetch(), should I use transform or onResponse()?

Right now I use transform:
let { data } = await useFetch(
url,
{
method: "get",
transform: (input) => {
return myInstanciationFunction(input)
},
}
)
let { data } = await useFetch(
url,
{
method: "get",
transform: (input) => {
return myInstanciationFunction(input)
},
}
)
That works, but is that the correct way to do this?
14 Replies
manniL
manniL2y ago
instanciate in what way?
PMDL
PMDLOP2y ago
We have models we want our incoming JSON data instantiated into:
class UserModel {
public id: number = 0;
public username: string = "";
public email: string = "";

get user_id() {
return this.id
}
}
class UserModel {
public id: number = 0;
public username: string = "";
public email: string = "";

get user_id() {
return this.id
}
}
(This is just an example) We use plainToClass from class-transformer to turn JSON data into those models.
manniL
manniL2y ago
ah, I see. Transform looks like a good option then. but make sure to check how it works during SSR because of serializability
PMDL
PMDLOP2y ago
Ok thanks! Using SSR, we have a serialization/deserialization logic using reducers and revivers and the same class-transformer module. That works well!
manniL
manniL2y ago
ah perfect, wanted to suggest that otherwise 👍
PMDL
PMDLOP7d ago
Hi! It's been a while since that question, I had actually forgot I asked it. So yes, the transform way works to a point (my data is instantiated and reactive), but now I need to register a reducer / reviver couple for each Model, which is cumbersome:
import { plainToInstance, instanceToPlain } from "class-transformer"

export default definePayloadPlugin((nuxtApp) => {
definePayloadReducer('NewsModel', data => data && data instanceof NewsModel && instanceToPlain(data))
definePayloadReviver('NewsModel', data => plainToInstance(NewsModel, data))

definePayloadReducer('UserModel', data => data && data instanceof UserModel && instanceToPlain(data))
definePayloadReviver('UserModel', data => plainToInstance(UserModel, data))

definePayloadReducer('CharacteristicModel', data => data && data instanceof CharacteristicModel && instanceToPlain(data))
definePayloadReviver('CharacteristicModel', data => plainToInstance(CharacteristicModel, data))
})
import { plainToInstance, instanceToPlain } from "class-transformer"

export default definePayloadPlugin((nuxtApp) => {
definePayloadReducer('NewsModel', data => data && data instanceof NewsModel && instanceToPlain(data))
definePayloadReviver('NewsModel', data => plainToInstance(NewsModel, data))

definePayloadReducer('UserModel', data => data && data instanceof UserModel && instanceToPlain(data))
definePayloadReviver('UserModel', data => plainToInstance(UserModel, data))

definePayloadReducer('CharacteristicModel', data => data && data instanceof CharacteristicModel && instanceToPlain(data))
definePayloadReviver('CharacteristicModel', data => plainToInstance(CharacteristicModel, data))
})
There are many of those classes, is there a better way to do this?
Cue
Cue7d ago
Perhaps define a payload class map and loop? For example:
import { instanceToPlain, plainToInstance } from 'class-transformer'

const payloadClassMap = {
NewsModel,
UserModel,
CharacteristicModel
}

export default definePayloadPlugin(() => {
for (const [name, model] of Object.entries(payloadClassMap)) {
definePayloadReducer(name, data => data instanceof model && instanceToPlain(data))
definePayloadReviver(name, data => plainToInstance(model, data))
}
})
import { instanceToPlain, plainToInstance } from 'class-transformer'

const payloadClassMap = {
NewsModel,
UserModel,
CharacteristicModel
}

export default definePayloadPlugin(() => {
for (const [name, model] of Object.entries(payloadClassMap)) {
definePayloadReducer(name, data => data instanceof model && instanceToPlain(data))
definePayloadReviver(name, data => plainToInstance(model, data))
}
})
PMDL
PMDLOP7d ago
Hi @Cue ! Thanks, yes this is precisely my solution, but I still need to manually add any new class I add to my code to that plugin after I receive an error when developing.
Cue
Cue7d ago
That's expected since you need to explicitly define reducers/revivers. What's the issue you're experiencing?
PMDL
PMDLOP7d ago
Is there a way for that plugin to "discover" the classes in my "classes/*ts" folder, import them all, and make the map itself? I'm asking that because I come from python, not javascript, I don't know if that is easy doable.
Cue
Cue7d ago
You can do, but you'd be better off doing so with a module where you can traverse your directory and extract classes into a virtual module, and define a plugin within the module to execute the above logic. But this depends on how you're defining your classes. For example, are the classes named exports? Are there other exports within these files? Can you extract these easily?
PMDL
PMDLOP7d ago
I have a models/ folder with TS files like users.ts where my classes are. There could be several of those in the same file (class User, class UserStats, class UserProfile, ...), all named exported. As far as I can tell no other exports than those classes that could all be needed to serialise / deserialise. So I guess yes I could traverse that dir? I could try that, but it feels hacky.
Cue
Cue7d ago
IMO I’d be explicit and create an index.ts in that directory and export the necessary classes, this way you’re not only managing your classes in one place, but not having to declare them in a plugin. You’d then do a wildcard import, where all classes are now effectively a map. While it is still a declarative way of addressing your issue, it’s much more controlled so you’re not relying on hacky logic to determine what should and shouldn’t be passed to the payload handlers.
PMDL
PMDLOP7d ago
That is convincing I'm gonna try that! To reply that: If I use const { data } = useFetch('/users') and build some kind of computed ref over data to instantiate data.value using UserModel. And that works but it's very repetitive to do it for every call we have. Instead I could make const { data } = customUseFetch('/users', UserModel) where customUseFetch transforms the received data, instantiating it with the provided model (original question). That's nice but now I get the dreaded ERROR [nuxt] [request error] [unhandled] [500] Cannot stringify arbitrary non-POJOs at hydration stage. I'm still not entirely sure why that problem arises tbh, where that wasn't a problem without using transform. My understanding of nuxt lifecycle is not yet optimal. I'm thinking transform might not be suited for this after all? Maybe that handling should be made "after" somehow?

Did you find this page helpful?