Comments (12)
I think it might be easiest to look for usage of useQuery and then work back using references. That way we shouldn't need to distinguish between serverside and clientside. The rpc module should know how to find the correct endpoint. It should only need the import path and possibly the named export if we want to allow named exports.
from blitz.
We can't rely on useQuery usage, because you should be able to manually use queries and mutations without useQuery. useQuery is only there as a declarative hook interface.
from blitz.
Hmm ok. A couple of thoughts. How do we warm the lambda for a mutation if all we are doing is importing it and running it in a callback? Seems to me currently useQuery is acting mainly as a proxy for React Query query caching. What about return values from mutations? Surely they need to invalidate the cache key? Could accessing mutations somehow through a hook solve that problem as you would have access to the component render cycle?
Perhaps it is a mistake to try to create and maintain a complex babel transform. One idea would be to build an rpc client that can be run on both the client and the server and based on its environment will either make the HTTP call or directly call the resource. That way the only transformation required would be to change the import path from "app/product/queries/getProduct"
to a pre-rendered isomorphic rpc client at "_rpc/app/product/queries/getProduct"
or something similar. That might make for less confusing babel code? I attempted to look at some old babel transform code I did the other day and... man if stuff gets complex it is not much fun. Also that way if you ever wanted to speed it up and you had no more transformations you could do it with a regex and avoid a babel transform altogether.
from blitz.
Warming the Lambda
Oops, I forgot this! We can do that like shown below.
App code:
import getProduct from 'app/product/queries/getProduct'
const product = await getProduct({where: {id: 1}})
Built code:
import {rpc} from 'blitz'
const warmedRpc = rpc('/api/product/queries/getProduct') //makes HEAD request
const product = await warmedRpc({where: {id: 1}})
Query cache invalidation
Great point. This needs more attention. I'll open another issue on useQuery
, mutations, and cache invalidation.
Once we figure that out, then we can come back here and figure out the best way to handle it under the hood.
Babel transform?
Perhaps it is a mistake to try to create and maintain a complex babel transform. One idea would be to build an rpc client that can be run on both the client and the server and based on its environment will either make the HTTP call or directly call the resource.
I'm totally fine with this approach! We just have to ensure the server code isn't bundled with the client.
from blitz.
Alright, here's the issue on query/mutation usage: #89
from blitz.
@ryardley what are you thinking by now on how to approach this?
from blitz.
I think as far as this transformation task is concerned I think your idea to warm the lambda by transforming every querymutation to be a call to rpc with the absolute import path is great. It might make for babel transform complexities though this could be mitigated during query compilation. I can see advantages to the idea around simply swapping out the path universally with a universal rpc client but then calling the result as a function in place as you only need a single AST traversal even though there will be a couple more transfomations. Would have to test but this might actually be both more efficient and simpler babel code. If we can do everything in a single traversal that would be ideal.
// orig
import getProduct from 'app/product/queries/getProduct';
export async function getServerSideProps() {
const product = await getProduct({where: {id: 1}})
// ...
}
const product = await getProduct({where: {id: 1}})
becomes
import getProduct from '_rpc/app/product/queries/getProduct';
export async function getServerSideProps() {
const product = await getProduct()({where: {id: 1}})
// ...
}
const product = await getProduct()({where: {id: 1}})
vs
import getProduct from 'app/product/queries/getProduct'
import {rpc} from 'blitz'
export async function getServerSideProps() {
const product = await getProduct({where: {id: 1}})
// ...
}
const product = await rpc('app/product/queries/getProduct')({where: {id: 1}})
from blitz.
Yeah, that looks promising! I've never actually written a babel transformation, so I trust your judgement on this!
from blitz.
I had a realization: Next does two builds: server bundle and client bundle. So I think we only have to solve each separately. We don't have to solve both at once.
Therefore I think we can do this without any transform at all.
Server bundle
- Leave the original code as is. No changes
Client bundle
- Next removes gSSP from this build, so it's irrelevant.
- We use webpack to alias query import to the query rpc import
- For warming Lambda, we don't need to transform
useQuery(getProduct, args)
touseQuery(getProduct(), args)
. Instead we make the HEAD call inside the rpc client as a side effect of the es6 import
// orig & server bundle
import getProduct from 'app/product/queries/getProduct';
export async function getServerSideProps({req, res}) {
const product = await ssrQuery(getProduct, {where: {id: 1}})
// ...
}
function Page() {
const product = useQuery(getProduct, {where: {id: 1}})
// ...
for the client bundle, the only change is a webpack alias for the import:
// client bundle
import getProduct from 'app/product/queries/getProduct'; // aliased to '_rpc/something`
function Page() {
const product = useQuery(getProduct, {where: {id: 1}})
// ...
from blitz.
Interesting hack. Really like the idea to just use a webpack alias. I was thinking about that but wrote it off as it might fire off all lambdas on initial page load. However now you bring it up I realise I forgot about code splitting which might save us as only lambdas first defined in the page will be warmed on initial page load and the rest as soon as each subsequent code splitting bundle is loaded. If the app is the kind that is used for a long time lambdas might go to sleep unless we add a keep alive. So we will want to have a setInterval for a minute to run that head call again. If we are doing that would we want to just warm up all lambdas at staggered intervals on page load? The whole thing seems clumsy and blunt though with wasted server client traffic.
from blitz.
Let's just do one initial warm for now and see how it goes. It's impossible to guarantee you won't hit a cold lambda. Best we can do is minimize it to some extent. I think cold starts will be most noticeable for very low traffic apps where there's only 1 person using the app at a time. In this case, warming, at initial load, the lambdas that are likely to be used next will help to some extent.
from blitz.
Closed by #95
from blitz.
Related Issues (20)
- Blitz generate should provide functioning forms for a better scaffolding experience. HOT 2
- Support Bun runtime HOT 6
- ajv dependency was not installed with blitz new HOT 5
- support prisma extensions HOT 5
- Module not found: Can't resolve 'ajv/dist/compile/codegen' HOT 1
- BlitzServerMiddleware types are incorrect HOT 2
- Error Building Blitz in Cloudflare HOT 3
- can't locally silence [blitz-rpc] debug logs HOT 1
- deploy failed on vercel HOT 15
- Cannot find module 'next/dist/client/resolve-href' HOT 1
- BLITZ_PUBLIC_ prefix does not expose env to client HOT 2
- BlitzRoutes: Support app router HOT 4
- Windows Compatibility Issue: process.kill("SIGABRT") not supported HOT 5
- useAuthenticatedBlitzContext fails on vercel HOT 7
- getBlitzContext unusable in api HOT 2
- getBlitzContext() can't be use in edge funcion HOT 2
- nextjs (app router) fails on vercel HOT 1
- v2.0.7 `pnpm blitz dev --turbo` error with Invalid next.config.js options HOT 2
- enhancePrisma has disappeared since v2.0.7
- No matching version found for @blitzjs/[email protected] HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blitz.