Closed artola closed 2 years ago
Hi @artola 👋🏻 and yeah excellent question.
I guess the use-case has never been there, so not quite sure how to effectively design an api around that. But perhaps you can give me a tangible example of how the data is produced, and intended consumption?
Seeing as meros is intended for the client side (whether that is server or browser), this has never had much of a use-case? Most if not all of the time, the response is tied to a single request, where messages arrive (in any order) over time for that request, and probably json.
To now nest that such that half way down you can instill a secondary boundary and flush messages against that boundary, I personally never seen how it has any benefit than to just encode the asynchronicity in a property of the message itself.
I dare say the benefit could be where you're using 1 transport to encode multiple unique payloads. Which can most certainly be encoded in layer 6, layer 7. For example graphql defer/stream does that through a label
property in the json message.
You mentioned "compose components"; so guessing youre talking some frontend javascript component?
Yeah, front-end Relay. For example, I have a Dashboard for crypto currencies, something like "show me a ticker with some currencies, plus top gainers and losers". The ticker renders first, while the rest can be deferred. As the development evolves, more fragments/components are spread, as they should be kinda blackbox I should not either care about if someone has already a defer
in place.
query Dashboard {
...DashboardTickerFragment
...DashboardHighlightsFragment
}
fragment DashboardTickerFragment on Query {
ticker: assets(first: 3, order: { tradableMarketCapRank: ASC }) {
nodes {
symbol
...DashboardTickerItemFragment
}
}
}
fragment DashboardTickerItemFragment on Asset {
symbol
color
price {
currency
lastPrice
}
}
fragment DashboardHighlightsFragment on Query {
gainers: assets(
first: 3
where: { change24Hour: { gt: 0 } }
order: { change24Hour: DESC }
) {
...DashboardCardFragment @defer
}
losers: assets(
first: 3
where: { change24Hour: { lt: 0 } }
order: { change24Hour: ASC }
) {
...DashboardCardFragment @defer
}
}
fragment DashboardCardFragment on AssetsConnection {
nodes {
id
symbol
name
price {
currency
lastPrice
}
}
}
Here I have NO nested defer. The server's response:
---
Content-Type: application/json; charset=utf-8
{"data":{"ticker":{"nodes":[{"symbol":"BTC","color":"#F7931A","price":{"currency":"USD","lastPrice":36855.68}},{"symbol":"ADA","color":"#0033AD","price":{"currency":"USD","lastPrice":1.04}},{"symbol":"MATIC","color":"#8247E5","price":{"currency":"USD","lastPrice":1.54}}]},"gainers":{},"losers":{}},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"path":["gainers"],"data":{"nodes":[{"id":"QXNzZXQKaTM5","symbol":"REQ","name":"Request","price":{"currency":"USD","lastPrice":0.22}},{"id":"QXNzZXQKaTMz","symbol":"VGX","name":"Voyager Token","price":{"currency":"USD","lastPrice":1.79}},{"id":"QXNzZXQKaTQ0","symbol":"ALCX","name":"Alchemix","price":{"currency":"USD","lastPrice":158.61}}]},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"path":["losers"],"data":{"nodes":[{"id":"QXNzZXQKaTM3","symbol":"IMX","name":"Immutable X","price":{"currency":"USD","lastPrice":3.2}},{"id":"QXNzZXQKaTU0","symbol":"FORTH","name":"Ampleforth Governance Token","price":{"currency":"USD","lastPrice":7.45}},{"id":"QXNzZXQKaTUx","symbol":"MPL","name":"Maple","price":{"currency":"USD","lastPrice":15.04}}]},"hasNext":false}
-----
This case works OK, other combinations are possible as long as there are no nested defer.
Now imagine that I change the query to "defer" the fragment DashboardHighlightsFragment
too, in such case HC resolves without problem, but I have no access to the data while Relay prints an error.
This is just an example, same situation happens if we have nested defer in any other fragment spread.
query DashboardContainerQuery {
...DashboardTickerFragment
...DashboardHighlightsFragment @defer(label: "highlights")
}
"Invariant Violation: OperationExecutor: invalid incremental payload, expected `path` and `label` to either both be null/undefined, or `path` to be an `Array<string | number>` and `label` to be a `string`.
at invariant (webpack-internal:///./node_modules/invariant/browser.js:38:15)
at eval (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:1464:25)
at Array.forEach (<anonymous>)
at partitionGraphQLResponses (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:1458:13)
at Executor._handleNext (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:470:33)
at eval (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:343:16)
at withDuration (webpack-internal:///./node_modules/relay-runtime/lib/util/withDuration.js:27:16)
at eval (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:342:28)
at Executor._schedule (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:300:7)
at Executor._next (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:341:10)
at Object.next (webpack-internal:///./node_modules/relay-runtime/lib/store/OperationExecutor.js:157:17)
at Object.next (webpack-internal:///./node_modules/relay-runtime/lib/network/RelayObservable.js:535:20)
at Object.eval [as next] (webpack-internal:///./node_modules/relay-runtime/lib/network/RelayObservable.js:190:40)
at Object.next (webpack-internal:///./node_modules/relay-runtime/lib/network/RelayObservable.js:535:20)
at _callee$ (webpack-internal:///./client/index.js:381:30)
at tryCatch (webpack-internal:///./node_modules/next/dist/compiled/regenerator-runtime/runtime.js:45:40)
at Generator.invoke [as _invoke] (webpack-internal:///./node_modules/next/dist/compiled/regenerator-runtime/runtime.js:274:22)
at Generator.prototype.<computed> [as next] (webpack-internal:///./node_modules/next/dist/compiled/regenerator-runtime/runtime.js:97:21)
at asyncGeneratorStep (webpack-internal:///./client/index.js:51:28)
---
Content-Type: application/json; charset=utf-8
{"data":{"ticker":{"nodes":[{"symbol":"BTC","color":"#F7931A","price":{"currency":"USD","lastPrice":37078.15}},{"symbol":"ADA","color":"#0033AD","price":{"currency":"USD","lastPrice":1.0419}},{"symbol":"MATIC","color":"#8247E5","price":{"currency":"USD","lastPrice":1.5489}}]}},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"data":{"gainers":{},"losers":{}},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"path":["gainers"],"data":{"nodes":[{"id":"QXNzZXQKaTM5","symbol":"REQ","name":"Request","price":{"currency":"USD","lastPrice":0.22}},{"id":"QXNzZXQKaTMz","symbol":"VGX","name":"Voyager Token","price":{"currency":"USD","lastPrice":1.77}},{"id":"QXNzZXQKaTQ0","symbol":"ALCX","name":"Alchemix","price":{"currency":"USD","lastPrice":157.19}}]},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"path":["losers"],"data":{"nodes":[{"id":"QXNzZXQKaTM3","symbol":"IMX","name":"Immutable X","price":{"currency":"USD","lastPrice":3.21}},{"id":"QXNzZXQKaTU0","symbol":"FORTH","name":"Ampleforth Governance Token","price":{"currency":"USD","lastPrice":7.46}},{"id":"QXNzZXQKaTUx","symbol":"MPL","name":"Maple","price":{"currency":"USD","lastPrice":15.05}}]},"hasNext":false}
-----
@maraisr It seems just a small thing with HC, but not related with multiparts. I will recheck in future and back if needed. THANKS A LOT.
For reference, escape hatch:
if (part.body.label && !part.body.path) {
part.body.path = [];
}
As properly stated in the README (caveats) the are some known limitations, from my point of view,
Nested multiparts
is the more important, as we use to compose components, soon we might get caught in this trap.@maraisr Do you have something in mind on this topic?