N
Nuxt9mo ago
letoast

Adding to composable using a Layer

What would be the best way to add to a composable using layers? For example, I have my base layer with /composables/useXY.ts which has for example:
// layers/base/composables/useXY.ts
export default function () {
return {
get: () => {},
post: () => {}
}
}
// layers/base/composables/useXY.ts
export default function () {
return {
get: () => {},
post: () => {}
}
}
and I'd like to extend this composable's functionality with another method. I'd like to use layers to do this. If I define this same composable with just the added method in another layer, it just overrides the original one:
// layers/anotherLayer/composables/useXY.ts
export default function () {
return {
delete: () => {},
}
}
// layers/anotherLayer/composables/useXY.ts
export default function () {
return {
delete: () => {},
}
}
What would be the correct way to do this? The final composable should look like:
export default function () {
return {
get: () => {},
post: () => {},
delete: () => {} // Added in another layer
}
}
export default function () {
return {
get: () => {},
post: () => {},
delete: () => {} // Added in another layer
}
}
16 Replies
pyplacca
pyplacca9mo ago
Is there a reason why the base layer can’t have the final version of the composable? PS: layers don’t merge conflicting elements. One overrides the other
letoast
letoastOP9mo ago
The reason is that there would be multiple layers that would add different functionalities depending on the layer's needs, but the core functionality should be there. I don't want to duplicate the core functionality to each layer. I'll give you an example layers/tenant1/ layers/tenant2/ layers/tenant3/ layers/base/ ... where the app is basically made to be multi tenant/version. There are different developers working on this and I would like to force this composable to have a certain basic structure. I know I could create a type definition for the composable and extend it in each layer, but is there any way to force the composable to have a type in each layer without importing the type each time? The only way I see this being possible now is splitting the composable to multiple composables or the type way which I wrote above.
pyplacca
pyplacca9mo ago
You can create a configurable composable in the base layer, so that whoever is consuming it can pick which features to use and which to drop. Picture it as a component
letoast
letoastOP9mo ago
how do you mean a configurable composable? let's say layer/tenant2 needs to implement the delete property inside the composable in a different way than the base layer has it implemented, how would you do that?
pyplacca
pyplacca9mo ago
I’d have to know what tenant2 is doing differently that can’t be covered by the base layer
letoast
letoastOP9mo ago
transforming the request in a different way, calling another endpoint in the same function, can be whatever so much so that you don't want to cover 10 different cases in the base layer if you have 10 tenants another way i could do this is just splitting each method inside the composable into it's own composable usePost useGet useDelete etc so then the next layer can override each but i was thinking if there's a better way than that
pyplacca
pyplacca9mo ago
Sure, then your composable can simply house all the defaults such that when a tenant imports to use it, the tenant can override parts of it to suit its needs So you just have to decide when and where the base composable needs to encapsulate what and where the consumer needs to take up the rest of the work
letoast
letoastOP9mo ago
the problem i have with this is that the composable has to be defined then in each layer and the layer's author might forget to import some methods that should be there by default
pyplacca
pyplacca9mo ago
That’s going to happen if you don’t make those methods required by the base composable This is an example of how a tab and tab item component group works. You wouldn’t be able to fully create views in a tab if you don’t include the tab items Documenting what’s needed where and when for the tenants would also help
letoast
letoastOP9mo ago
hmm do you see any way of enforcing a type on a composable with the same name as the composable in the base layer without having to import the type manually? as in "each function in composables named XYZ should have this type" there's also one more problem i noticed. if there's a composable useX.ts in layer A that's defined as
// layerA/composables/useX.ts
import { get } from './x/get.ts'
...
// layerA/composables/useX.ts
import { get } from './x/get.ts'
...
and we override get.ts in layer B
// layerB/composables/x/get.ts
...
// layerB/composables/x/get.ts
...
layer B's get.ts doesn't override layer A's get.ts - it doesn't seem to include them
pyplacca
pyplacca9mo ago
Mmm not sure how that would work if there’s no single source of truth. Here’s my suggestion: when creating the compsable under a tenant, let the tenant name it differently from that of the base layer. Eg;
// base-layer/useSomeCompsable.ts
export default function() {

}
// base-layer/useSomeCompsable.ts
export default function() {

}
// tenant/useTenantSomeCompsable.ts
export default function() {
return useSomeComposable(…overrides)
}
// tenant/useTenantSomeCompsable.ts
export default function() {
return useSomeComposable(…overrides)
}
letoast
letoastOP9mo ago
hmmm but then they're not switchable
pyplacca
pyplacca9mo ago
Is layer B extending layer A? Meaning?
letoast
letoastOP9mo ago
meaning if you have a page/component/layout using useSomeComposable you have to replace it with the one that overrides it everywhere yes layer B is extending layer A
pyplacca
pyplacca9mo ago
That depends on whether the layout is okay with how the base layer’s composable functions for its sole use case as against the customized version in the tenant. Otherwise, yes If you want to have a customized/extended useFetch composable for instance, you’d need to export it under a different name with the necessary customizations Take note that the consumer always takes the highest precedence, so if there’re name conflicts the base will be overlooked in favor of the consumer/tenant
letoast
letoastOP9mo ago
yeah, the idea is to keep the naming of composables the same so that another layer just overrides/adds on top and there's no need to change anything in the consumer's code
Want results from more Discord servers?
Add your server