QwikDev / qwik

Instant-loading web apps, without effort
https://qwik.dev
MIT License
20.49k stars 1.26k forks source link

[✨] How to load dynamic configuration and settings for both server-side and client-side? #5911

Open hosseinitabar opened 4 months ago

hosseinitabar commented 4 months ago

Is your feature request related to a problem?

I have the same codebase in Qwik and Qwik City, and I want to give it to many customers. I want to connect to different APIs for each customer. Right now what I do is that I have many API URLs in my .env file and I comment other URLs and build and deploy for a specific customer, then repeat it for others. This is very time-consuming and difficult.

For example in my .env file I have these lines:

#VITE_API_URL=https://api.first-customer.com
#VITE_API_URL=https://api.second-customer.com
VITE_API_URL=https://api.third-customer.com
#VITE_API_URL=https://api.fourth-customer.com

I have more than 50 lines in my .env file.

I want to be able to dynamically get this to prevent this madness. In your docs you talk about server-side variables and client-side variables. You do not talk about omni-variables that can be both used in server-side and client-side and be loaded dynamically and not be built hardcoded into the production code.

Describe the solution you'd like

I need API URLs both in server-side and in client-side. In server-side code I need it to access some data from API in my routeLoader$ code. In client-side I need it to submit the contact form for example directly to the API.

Right now I do not know what to do. I have to manually comment/uncomment lines in my .env file to be able to deploy for my customers.

Describe alternatives you've considered

I would rather have a settingsLoader that I can use wherever I am, server or client code, and it should be dynamic to load settings from a settings.json file in the production environment that is inside the server directory for example, and the responsibility of security is at my side. In other words I should use it at my own risks.

Additional context

No response

wmertens commented 4 months ago

You can make this as follows. It would be great if you contribute a cookbook entry that explains this in a way that others at your company and elsewhere understand it easily.

The idea is that during SSR, you store certain data that never changes at the root level, and since root never rerenders, it will never need to be loaded again on the client.

export const useClientProvider = () => { const loc = useLocation() const clientS = useSignal({theme: {}} as ClientConfig) useContextProvider(clientCtx, clientS)

useTask$(async () => {
    console.log('get client')
    clientS.value = await getClient(loc.url.hostname)
    console.log('got client')
})

} const o = c => console.log(c) || c // Note, this removes reactivity but the value is static so it's fine export const useClient = () => o(useContext(clientCtx).value)


- In your root layout, add `useClientProvider()`
- Anywhere you need the data, add `const client = useClient()`

If you need to use the client info in some server function, use the getClient function. You can also cache the result in module scope.
PatrickJS commented 2 months ago

in entry.ssr.ts you can also pass props to <Root yourProps={yourData}> then in root.tsx you can set the props to any context you need

you can grab the request object in entry.ssr.ts and if you want something to run before that use a plugin@.ts

const url opt.serverData?.url;
const requestEvent = opts.serverData?.qwikcity?.ev;
const sharedMap = opts.serverData?.qwikcity?.ev?.sharedMap;

image