Closed brandonroberts closed 1 year ago
What about dependency injection? It would've been great to define loaders as route resolvers or methods on components.
Dependency injection works as normal on the client side, as it's a resolver that calls a backend API. The useLoader
is a wrapper around the ActivatedRoute
and a resolve property. Nothing too complex there to start. Loaders on route components feel too invasive and require going outside the normal component path
Dependency injection works as normal on the client side, as it's a resolver that calls a backend API. The
useLoader
is a wrapper around theActivatedRoute
and a resolve property. Nothing too complex there to start. Loaders on route components feel too invasive and require going outside the normal component path
I'm referring to inversion of control/dependency injection on the server side :)
Ahh, no its not doing that. I don't see much value in having DI there as you would mostly be using imports to access server-only code like Prisma, file-system access, etc.
Ahh, no its not doing that. I don't see much value in having DI there as you would mostly be using imports to access server-only code like Prisma, file-system access, etc.
I don't necessarily agree with that statement. I've used Remix extensively, to build a subscription-based content creation platform, and it's a pain in the ass without DI on the server side. The amount of boilerplate code and workarounds required is crazy. Angular already supports DI for SSR, so I don't see why loaders shouldn't.
If you have some more concrete examples that would be great. Angular DI in SSR also assumes you're using the component as an API endpoint though, correct? I'm not intending to run Angular components on the server (yet π), so I'm trying to see where DI would fit in
How hard would it be to do something similar to NextJs and provide two loaders:
Do you think thereβs merit in doing that? Interested to hear your opinion!
Those features are worth considering, but we don't necessarily have to mimic them as-is. NextJS is moving away from getServerSideProps
and getStaticProps
also with the 13.x release and app directory.
I didn't even know that until you pointed it out! Seems like the tree shaken loader that you describe would be perfect then. I need to try it, but I assume that Nitro automatically serves a pre-rendered route and if no static html exists it generates the page on each request. Then, having two functions definitely seems unnecessary.
I don't want to hijack this RFC with (sort of) unrelated things but looking at this NextJS documentation and this configuration option for Nitro made me think that it would be cool if you could add some config snippet (like 'use static';
or 'use swc';
) to the top of the file and based on that the route would be marked as static, cached, etc.
Very much looking forward to this feature being available! Rich Harris had a recent talk that covered some of the important details and downfalls of these approaches which might be worth considering when deciding on the approach to take:
https://youtu.be/uXCipjbcQfM?t=1070
Key points:
Thanks @ashley-hunter, I'll take a look. I've also been watching projects like TanStack Bling and I'm really interested in adopting this pattern. It supports server-only functions, as well as server-only imports.
You use the fetch$
function to wrap your call in, it returns you an RPC function to call using fetch
, but everything inside the function is run on the server and not on the client.
import { fetch$ } from '@tanstack/bling'
const fetchFn = fetch$(async (payload) => {
// do something
return 'result'
})
fetchFn() <- Promise<string>
In Angular, we already have HttpClient
so the thought is to have something like http$
import { http$ } from '@analogjs/platform/something';
const httpFn = http$(async (payload) => {
// do something
return 'result'
})
httpFn() <- Promise<string>
Where HttpClient
does the work internally instead of fetch
, so we could potentially take advantage of automatic cache transfer with Angular v16. Using the "loader" pattern would still be an option
// page.component.ts
import { AsyncPipe, JsonPipe } from "@angular/common";
import { Component } from "@angular/core";
import db from './db';
import { http$ } from '@analogjs/platform/something';
// runs on server
export const loader = http$(() => {
const items = db.getItems();
return {
items: items
};
});
Thoughts?
Thanks @ashley-hunter, I'll take a look. I've also been watching projects like TanStack Bling and I'm really interested in adopting this pattern. It supports server-only functions, as well as server-only imports.
You use the
fetch$
function to wrap your call in, it returns you an RPC function to call usingfetch
, but everything inside the function is run on the server and not on the client.import { fetch$ } from '@tanstack/bling' const fetchFn = fetch$(async (payload) => { // do something return 'result' }) fetchFn() <- Promise<string>
In Angular, we already have
HttpClient
so the thought is to have something likehttp$
import { http$ } from '@analogjs/platform/something'; const httpFn = http$(async (payload) => { // do something return 'result' }) httpFn() <- Promise<string>
Where
HttpClient
does the work internally instead offetch
, so we could potentially take advantage of automatic cache transfer with Angular v16. Using the "loader" pattern would still be an option// page.component.ts import { AsyncPipe, JsonPipe } from "@angular/common"; import { Component } from "@angular/core"; import db from './db'; import { http$ } from '@analogjs/platform/something'; // runs on server export const loader = http$(() => { const items = db.getItems(); return { items: items }; });
Thoughts?
@marcus-sa not exactly. Qwik is much more fine-grained with its use of module extraction across the board. This isn't supported in Angular today, so this is isolated to data fetching
I really enjoy the simplicity that can be achieved through using TRPC, and compared to traditional, type-unsafe REST calls, reverting back to them can be quite painful. So I'm strongly in favour of a straightforward and type-safe approach like the one you are proposing.
I find the approach adopted by SvelteKit, where the code executed on the server is located in a distinct file labelled with .server in its name, to be quite nice. This approach has several advantages:
But looking forward to a feature like this, will be very useful!
Definitely some good points in the talk to be considered! I like the .server
files approach also. It would keep the Angular components clean and maybe less confusing for those coming to use Analog
Draft PR is up https://github.com/analogjs/analog/pull/446. Decided to go with the separate .server.ts
support.
Draft PR is up https://github.com/analogjs/analog/pull/446. Decided to go with the separate
.server.ts
support.
This is awesome! Just for my understanding the load function would be executed on the server and transfer the data via TransferState?
Also, is it possible to pass the H3 event as a param to the load function?
So excited for this πππππ
Draft PR is up #446. Decided to go with the separate
.server.ts
support.This is awesome! Just for my understanding the load function would be executed on the server and transfer the data via TransferState?
Also, is it possible to pass the H3 event as a param to the load function?
So excited for this πππππ
Yes. The load function/resolver is like an RPC. Its called through an API endpoint on the server via HttpClient, and returns the results. That way it can be used with TransferState also without any extra work.
The H3 event is passed to the load function also. Need to add some types for the load function also
On the server call
load({
params: event.context.params, // params/queryParams from the request
req: event.node.req, // H3 Request
res: event.node.res, // H3 Response
fetch: $fetch // allows you to call API internal endpoints without an additional network request
});
// index.server.ts
export const load = async ({ req, res, params, fetch }) => {
return {
data: true,
};
};
Draft PR is up #446. Decided to go with the separate
.server.ts
support.This is awesome! Just for my understanding the load function would be executed on the server and transfer the data via TransferState? Also, is it possible to pass the H3 event as a param to the load function? So excited for this πππππ
Yes. The load function/resolver is like an RPC. Its called through an API endpoint on the server via HttpClient, and returns the results. That way it can be used with TransferState also without any extra work.
The H3 event is passed to the load function also. Need to add some types for the load function also
On the server call
load({ params: event.context.params, // params/queryParams from the request req: event.node.req, // H3 Request res: event.node.res, // H3 Response fetch: $fetch // allows you to call API internal endpoints without an additional network request });
// index.server.ts export const load = async ({ req, res, params, fetch }) => { return { data: true, }; };
It'd be great if it could be abstracted away so that one could use Deepkt RPC for example.
Which scope/s are relevant/related to the feature request?
platform
Information
This would add inline/external for components to fetch data, and be exposed to the component through an injectable function.
Requirements
Inline
External
Describe any alternatives/workarounds you're currently using
No response
I would be willing to submit a PR to fix this issue