Replies: 21 comments 4 replies
-
@flybayer Do you want error to be an object or array? Just thinking that e.g. for validation errors, there may be multiple problems to report. |
Beta Was this translation helpful? Give feedback.
-
@merelinguist I think the top level error field should be an object. Then we can have sub fields that are arrays. I'm headed to bed for the night, but tomorrow I plan to create the other issue on error handling. I'll think about this more then :) |
Beta Was this translation helpful? Give feedback.
-
In case of form validation, maybe the error object should contain an inner object with detail errors about each field: // error object
...
"errors": {
"fieldNameA": ["Error message 1", "Error message 2"],
"fieldNameB": ["Error message 1"]
} Or would something like this follow under the developer's responsibility to do on their own? |
Beta Was this translation helpful? Give feedback.
-
I’d expect it to look a bit more like:
|
Beta Was this translation helpful? Give feedback.
-
Let’s discuss the actual error format in the separate issue on error handing. I’m working on it at the moment and will link here when I’m finished. For the purposes of this specification, error should be an object for maximum flexibility. But the spec shouldn’t dictate anything beyond that. |
Beta Was this translation helpful? Give feedback.
-
I just created the issue on error handling! #87 |
Beta Was this translation helpful? Give feedback.
-
@ryardley what do you think of this? |
Beta Was this translation helpful? Give feedback.
-
Looks good. I guess one concern I see here would be how do we handle security concerns such as CSRF and client authentication in the context of this? This is probably it's own spec and will be handled in an RFC but it will affect this. |
Beta Was this translation helpful? Give feedback.
-
@ryardley yeah, I think all of those will be handled via cookies and headers, not in the actual JSON body. |
Beta Was this translation helpful? Give feedback.
-
Love the sound of the versioning, by the way! |
Beta Was this translation helpful? Give feedback.
-
First of all thanks for the awesome work you have been doing 🙏. I am really impressed by how fast things are evolving. From my point of view handling RPC queries and mutations exactly the same might be a mistake. Nevertheless queries are idempotent. They may be cached so the HTTP That's why I am proposing:
GraphQL background: Many graphql implementations are initially using |
Beta Was this translation helpful? Give feedback.
-
@HaNdTriX thank you for bringing this up! You are right, it would be nice to benefit from HTTP caching. So Blitz queries can also have very large serialized input arguments, but it'll always be less than GraphQL since you are only sending arguments instead of arguments + arbitrary GQL query structure. And because you are only making one query at a time instead of a huge document of queries. Do you happen to know what the practical size limit is for url length? We need to determine exactly what argument size we could support with |
Beta Was this translation helpful? Give feedback.
-
It depends. Based on your CDNs and the Browsers that are being used this limit may vary. As a rule of thumb a size limit of 2.048 Chars for the full URL string used in the Address bar should be safe. On Ajax level this limit may be up to 2.5x higher based on some tests.
Since these limits always target the full URL length it is difficult the determine a limit from the Framework side. Blitz doesn't and shouldn't know about the origin it is running on.
In addition to that, the size of the params is a dynamic variable that changes based on the runtime variation. Warning the user is already done by the Browser/CDN and will be too late anyway. If we want to be really save we "could" check the size of our url at runtime and switch back to a non cacheable |
Beta Was this translation helpful? Give feedback.
-
While we are talking about limits. Please also think about the bodyparser limit of
This limit should not be too high because otherwise it opens the door for all kinds of attacks. If it is too low the request might be aborted. Thinking of a way for the user to configure it may be useful. |
Beta Was this translation helpful? Give feedback.
-
@HaNdTriX wow, that's super helpful! Thank you! It sounds like it will be good to switch to GET and then fallback to POST if it's past 2k chars or so. Also good point on the bodyparser size. We can definitely add a configuration item for that. Do you think we should use a different size limit than default for queries and/or mutations? |
Beta Was this translation helpful? Give feedback.
-
Or just retry with
👍
When it comes to body parser limits I guess Next.js chose a more risky default since Next.js application primary run on lambdas. The damage is not so high here if someone tries a body-parser attack. On a traditional singe process node server (e.g. express) a body-parser attack can bring down your entire process. If an endpoint just listens to |
Beta Was this translation helpful? Give feedback.
-
Copying from my comment in Slack: My company had to disable HTTP caching globally because it caused so many consistency problems, in my experience it causes more problems than it solves. my recommendation there would be that if we want to enable it it should be opt-in in the blitz config. It’s definitely a problem that is more relevant at scale than it is in a small app, but in a high-traffic multi-user app it’s very possible for cached requests to cause serious bugs |
Beta Was this translation helpful? Give feedback.
-
@aem thanks for your Feedback 🙏. I believe caching always depends on the usecase and should be evaluated per route. Disabling it on a global level seems quite extreme to me. When we are looking on the latest developments we can see that the community is working hard on delivering static performance. The raise of the JAMstack has shown that. When you take a deeper look on the current implementations around iSSG you quickly realize, that this technique heavily bets on the RFC5861 a HTTP Cache-Control Extensions for Stale Content allowing the developer to push data to the edge. So it all boils down to one simple cache header ( So Next.js will add it's revalidation api to all static pages and I believe blitz might want to follow this path. For me the topic of caching has changed in the last year. It's is not anymore so much about saving some resources the users browser. Instead I often try to move my "app" closer to the user by moving my entire code & data to the edge. This is done by caching on CDN level (RFC5861).
Thats why I believe allowing queries to be cacheable is a good thing 😊. Links |
Beta Was this translation helpful? Give feedback.
-
Good thoughts @HaNdTriX! A clarification: Blitz uses Next.js under the hood, so Blitz already has static page generation with Pages that can be cached on the CDN should use Most Blitz queries will be private data behind a log in, and so these queries can't be cached on the CDN. But these queries could be cached in the browser. However, we are already caching queries in the browser with our So maybe there's not a strong case for |
Beta Was this translation helpful? Give feedback.
-
Appreciate the thoughtful answers here! As @flybayer said, the actual static pages will always be cached, and I think we both fully agree with you that performance there is non-negotiable! As far as the RPC requests, responses will often be different depending on who is making the requests. In my opinion (and experience, as I mentioned above), HTTP caching for those data queries is incredibly complex and error prone to ensure that you don't leak user data via a bad cache. Instead using a combination of ServiceWorkers and |
Beta Was this translation helpful? Give feedback.
-
Converted this to a discussion because we have generated issues to tackle each of the requirements of this proposal. |
Beta Was this translation helpful? Give feedback.
-
This issue is for defining the RPC specification used for Blitz queries and mutations. Each query and mutation can be used by third parties, so the specification must be clear and documented publicly.
This initial specification is my first pass. I haven't thought real deeply about this, so please help me refine it!
Specification
This specification is used for both queries and mutations. From an HTTP perspective, there is no difference between a query and mutation because they are both function calls.
Details
HEAD
This will be used to warm the lambda. For example, when a component renders on the client that uses a mutation, it will issue a
HEAD
request to the mutation endpoint immediately on page load so that there's a much higher chance the actual mutation request will hit a warm lambda.200
POST
Used to actually execute the RPC
params
(required) - This will be provided as the first argument to the query or mutationversion
(optional) - String containing the version of the built RPC clientresult
- the exact result returned from the query/mutation function ornull
if there was an errorerror
-null
if success, otherwise an object.Example:
Responses
result
valueerror
valueHEAD
null
{message: "Request body is not valid JSON"}
params
null
{message: "Request body is missing the 'params' key"}
null
null
GET
Versioning
Any time you have client-server communication, there is a very real risk of the client & server code becoming out of sync and causing an error. For example, let's say you have a mutation function whose input is a single object. You decide to change the mutation input to be a single array. You make the change and deploy it. Your server code is now updated, but all your users still have the old client code running in the browser. So it's likely you now have client code sending an object to your mutation which is going to error because it's expecting an array.
Now one obvious solution is to only make changes that are backwards compatible, but sometimes you can't do this.
The other solution is to track the version of your client code and your server code. If there is a version mismatch, you can display a friendly message to the user, telling them they need to reload the webpage to continue.
Blitz is well positioned to provide this, so let's do.
versionMismatch
to the error object if execution fails. Then the Blitz dev can check that field in their error handler if they want.Notes
Beta Was this translation helpful? Give feedback.
All reactions