Recommendations for a performing typescript web app #155
Replies: 10 comments
-
How we use this tool in our project, we have a |
Beta Was this translation helpful? Give feedback.
-
Thank you very much, @bluskript.
An example, please? |
Beta Was this translation helpful? Give feedback.
-
Here's a simple one that should work: import type { AuthServiceClient } from "../protocol/auth/v1/auth";
import { HrpcTransport } from "./hrpcTransport";
export class Connection {
host: string;
private auth?: AuthServiceClient;
private session?: string;
private transport: HrpcTransport;
constructor(host: string) {
this.host = host;
this.transport = new HrpcTransport({
baseUrl: host,
});
}
setSession(session: string) {
this.session = session;
this.transport.setSession(session);
}
getSession() {
return this.session;
}
async getAuthClient() {
if (!this.auth) {
this.auth = new (await import("../protocol/auth/v1/auth")).AuthServiceClient(this.transport);
}
return this.auth;
}
} If the async getter gets too tiring for you, I would recommend having a |
Beta Was this translation helpful? Give feedback.
-
The import() function is the way to go for lazy loading. See https://v8.dev/features/dynamic-import It depends on your framework where and how to use it properly. Angular supports lazy loading on a module level, see https://angular.io/guide/lazy-loading-ngmodules You would register the generated service in a module, like this: protobuf-ts/packages/example-angular-app/src/app/app.module.ts Lines 39 to 45 in 85a3c5b And let angular inject the client into your component, like this: The example angular app does not use lazy loading (it does not even use routing). This is just an example for how generated clients can be used. This is not a recommendation for angular, there are certainly much more simple frameworks out there. |
Beta Was this translation helpful? Give feedback.
-
Ok. Thanks for your ideas. What do you think abtou the code below? import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
import type { PlayerServiceClient } from "./proto/player.client";
import type { TeamServiceClient } from "./proto/team.client";
import type { RefereeServiceClient } from "./proto/referee.client";
class Connection {
private transport: GrpcWebFetchTransport;
private playerService: PlayerServiceClient;
private teamService: TeamServiceClient;
private refereeService: RefereeServiceClient;
constructor() {
this.transport = new GrpcWebFetchTransport({
baseUrl: "http://localhost:3000/api",
});
}
get player(): PlayerServiceClient {
if (!this.playerService) {
import("./proto/player.client").then(
(imported) =>
(this.playerService = new imported.PlayerServiceClient(
this.transport
))
);
}
return this.playerService;
}
get team(): TeamServiceClient {
if (!this.teamService) {
import("./proto/team.client").then(
(imported) =>
(this.teamService = new imported.TeamServiceClient(this.transport))
);
}
return this.teamService;
}
get referee(): RefereeServiceClient {
if (!this.refereeService) {
import("./proto/referee.client").then(
(imported) =>
(this.refereeService = new imported.RefereeServiceClient(
this.transport
))
);
}
return this.refereeService;
}
}
export default new Grpc(); Not comfortable things:
|
Beta Was this translation helpful? Give feedback.
-
Normal getters won't work here since import is asynchronous and you will run into a race condition since the promise isn't resolved. |
Beta Was this translation helpful? Give feedback.
-
Oh. Can you enlight me more, please? 🙏 |
Beta Was this translation helpful? Give feedback.
-
Now I understand what you mean, @bluskript.
Do you mean it is called by the individual components, right? Is there any way to make the class call it and not the components? |
Beta Was this translation helpful? Give feedback.
-
@Guerric-P answered my question: https://stackoverflow.com/a/67037262. His answer taught me a lot: thanks bro! @timostamm, I think we need something like this (to be optionally generated). He proposed to use: import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
const transport = new GrpcWebFetchTransport({ baseUrl: 'http://localhost:3000/api' });
const serviceMap = {};
export function getService(serviceName: string) {
return serviceMap[serviceName] ?? (serviceMap[serviceName] = import(serviceName).then(x => new x.default));
} which is great because I can use it like this: import { getService } from './serviceFactory';
const service: IPlayerServiceClient = await getService('./proto/player.service');
const players = await service.queryPlayers(); and I can even use these for each service: export const playerService: IPlayerServiceClient = getService('./proto/player.service'); calling it from components with: import { playerService } from "./services/player"
const { response } = await (await playerService).queryPlayers(); but there is the If with Can we |
Beta Was this translation helpful? Give feedback.
-
I take the liberty of opening this issue here too because perhaps you have already faced this problem and you could show me the right way.
And I could also create a PR for future people.
I'm building for the first time a new app in Typescript designing it with Proto files.
I'm having doubts about which is the best strategy to manage the many service clients in this web app.
"Best" in terms of a good compromise between user's device RAM and Javascript execution speed (main thread ops).
This is what I'm doing right now, this is the main file:
As you can see there are many service clients.
I'm using this code because I thought it was better given my web app structure based on routes almost overlapping with client services:
I mean, if the player goes from
/home
to/players
page I can use it like this:In this way, if the
PlayerService
does not exist, it is created at the moment and returned, otherwise it returns the one created before.Since the user switches pages frequently this way I can avoid the sudden creation and destruction of those clients, right?
But in this way I am using global variables which I don't like to use.
What do you suggest me to do?
Is there a better way?
Can I refactor this code into only one class?
Beta Was this translation helpful? Give feedback.
All reactions