Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve TypeScript API wrt relations #1407

Closed
heiko-r opened this issue Feb 8, 2022 · 2 comments
Closed

Improve TypeScript API wrt relations #1407

heiko-r opened this issue Feb 8, 2022 · 2 comments
Labels
design 📐 This issue deals with high-level design of a feature @vendure/core

Comments

@heiko-r
Copy link
Contributor

heiko-r commented Feb 8, 2022

Is your feature request related to a problem? Please describe.
From the Slack discussion:

  1. It's not obvious which relations are loaded for entities returned by Vendure's services, e.g.: OrderService.findOne(): Promise<Order | undefined> loads many relations, while OrderService.findOneByCode(): Promise<Order | undefined> loads none.
  2. Loading all relations in every service method obviously has a huge performance impact.
  3. The EntityHydrator can be used to ensure that certain relations are loaded, but it comes with a performance impact over loading the required relations directly.
  4. Still, in any case, there remains a risk of inadvertently accessing an unhydrated relation.

Describe the solution you'd like

  1. Vendure's services could make use of TypeScript's Omit<> type to indicate which relations of the returned entities are unhydrated. The rough idea in the Slack thread was to wrap the methods of TransactionalConnection to achieve this.
  2. For performance improvement, we could add an optional relations?: EntityRelationPaths<>[] parameter to the standard .find...() methods, e.g.
    async findMultipleBySkus(ctx: RequestContext, skus: string[], relations?: EntityRelationPaths<ProductVariant>[]): Promise<ProductVariant[]> {
         let variants: ProductVariant[] = [];
         const qb = this.connection.getRepository(ctx, ProductVariant).createQueryBuilder('variant');
         if (relations) {
             FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, { relations: relations });
         }
         FindOptionsUtils.joinEagerRelations(qb, qb.alias, qb.expressionMap.mainAlias!.metadata);
         ...
    and possibly not load any relations by default.

Describe alternatives you've considered
Using EntityHydrator works well, but has worse performance than loading the required relations directly, and has a risk of accessing relations which are usually (but not always) loaded and hence possibly forgotten to explicitly hydrate.

Additional context
I wonder, as a first, partial fix, if it might be possible to set a service method's return type based on the relations parameter (for methods which implement it), without wrapping TypeORM.

@michaelbromley michaelbromley added @vendure/core design 📐 This issue deals with high-level design of a feature labels Feb 8, 2022
michaelbromley added a commit that referenced this issue Apr 14, 2022
Relates to #1506. This commit applies the new `@Relations()` decorator to most of the resolvers and
some key entity field resolvers. Also relates to #1407 in that we are introducing a new "relations"
argument to many of the `find*()` methods of the service layer. However, I've not been able to spend
the time needed to get the corresponding type-safety part done. This can be explored separately
later.
@michaelbromley michaelbromley moved this to 🤔 Under consideration in Vendure OS Roadmap Jul 1, 2022
@mschipperheyn
Copy link
Collaborator

I would say that for certain entities and queries there is a presumption on which relations will be loaded without adding entity resolvers to load relations that are not part of the query.

I would recommend that any relation should always have an entity field resolver that gets the data. So that if someone goes outside of the box, they don't run into unexpected errors

@martijnvdbrug
Copy link
Collaborator

Thanks for your report!
Most services in the latest versions of Vendure support passing in relations now, for example OrderService.findOneByCode(ctx, '12354', ['customer']). If your custom code needs specific relations, you should always be explicit about it. It does automatically change the TS return type yet.

If you are missing the relations argument in any of the services, I suggest creating a separate/specific GH issue for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design 📐 This issue deals with high-level design of a feature @vendure/core
Projects
Status: 👀 Under consideration
Development

No branches or pull requests

4 participants