Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • 17:41
    MichalLytek commented #477
  • 17:38
    MichalLytek commented #110
  • 17:36
    MichalLytek commented #110
  • 17:36
    MichalLytek commented #110
  • 17:02
    irimiab commented #110
  • 15:49
    MichalLytek commented #110
  • 15:25
    irimiab commented #110
  • 14:52
    codecov[bot] commented #482
  • 14:50
    glen-84 commented #419
  • 14:50
    glen-84 opened #482
  • 14:34
    MichalLytek commented #419
  • 14:32
    glen-84 commented #419
  • 06:53
    apprithm commented #477
  • Dec 04 21:26
    codecov[bot] commented #479
  • Dec 04 21:17
    codecov[bot] commented #479
  • Dec 04 21:17
    MichalLytek synchronize #479
  • Dec 04 21:17

    MichalLytek on simple-resolvers

    docs(changelog): add entry abou… docs(perf): add performance doc… (compare)

  • Dec 04 21:16
    MichalLytek ready_for_review #479
  • Dec 04 17:07

    MichalLytek on prisma

    Configure Jest tests and add su… (compare)

  • Dec 04 12:23

    MichalLytek on prisma

    Update prisma deps (compare)

Michał Lytek
@MichalLytek
@EdouardBougon npm i type-graphql@beta
Evan Wu (Wen Yao Wu)
@wenyaowu

I'm pretty new to graphql and type-graphql and hoping I can get some help here.
I wonder if it's possible to resolve a field of an object differently in two different resolver

So I have two type defined here:

@ObjectType()
export class Provider {
  @Field(type => ID)
  id: number;
  @Field()
  name: string;
  @Field(type => [Location])
  locations: Location[];
}

and

@ObjectType()
export class ProviderResponse {
  @Field(type => [Provider])
  providers: Provider[];
  @Field(type => Int)
  total: number;
  @Field(type => ProviderFilters)
  filters: ProviderFilters;
}

and I have resolver:

@Resolver(of => Provider)
export class ProviderResolver {
  constructor(
    private _providerService: ProviderService,
  ) {}

  @Query(returns => Provider)
  Provider(@Args('id') id: number) {
    this._providerService.getById(id);
  }

  @ResolveProperty(returns => [Location])
  async locations(@Root() provider) {
      return await this._providerService.getLocationsByProviderId(provider.id);
   }
}

and

@Resolver(of => ProviderResponse)
export class ProviderSearchResolver {
  constructor(private _providerService: ProviderService) {}

  @Query(returns => ProviderResponse)
  async searchProviders(@Args() request: ProviderRequest) {
    return this._providerService.searchProviders(request);
  }
}

the problem here is that, in the second resolver, the this._providerService.searchProviders(request) returns the complete Provider object so we don't need to get location separately as a FieldResolve but its still calling the Location FieldResolver regardless.

Is it possible to have Location FieldResolver being called only when I query Provider but not ProviderSearch?

Michał Lytek
@MichalLytek
if (provider.locations) return provider.locations :wink:
Rex Raphael
@juicycleff
Trying to generate filter type dynamically and I am quite frustrated since input or args type has to be a class, due to the use of decorators. Has anyone been able to do something similar
export function FilterMongo<TItem>(TItemClass: ClassType<TItem>): any {

  const temp = new TItemClass();

  const fields = getMetadataStorage().fields.filter(value => value.objectType === temp.constructor.name

  @InputType(`Filter${TItemClass.name}`)
  class FilterMongoClass {

    constructor() {
      const obj = {};
      fields.map(value => {
        //
      });
    }

    @Field(() => Int)
    total?: number;
  }

  @ArgsType()
  abstract class WhereFilter {
    @Field(() => FilterMongoClass, { nullable: true })
    where?: FilterMongoClass;
  }

  return WhereFilter;
}
Michał Lytek
@MichalLytek
@juicycleff don't rely on internal stuff - they are subject to change without notice even in patch release!
Rex Raphael
@juicycleff
@MichalLytek by internal stuff you mean the getMetaStorage, that's a special meta storage I wrote to collect all fields I want to be filterable. I have a custom decorator called Filterable and gets stored into the meta storage. It was not imported from typegraphql
@MichalLytek please do you have any idea or thoughts about what I'm trying to achieve?
Michał Lytek
@MichalLytek
@juicycleff I tought that it was from TypeGraphQL :P
you have to call the decorators as function (like in transpiled JS code) to register fields in schema
it's just a dirty workaround for dynamically created classes
Rex Raphael
@juicycleff
@MichalLytek thanks for that insight, I was thinking the same, will let you know how it goes and when successful, will share it for others to learn too :)
Michał Lytek
@MichalLytek
maybe read that first as it might be a better approach to generate arguments :wink:
MichalLytek/type-graphql#347
Rex Raphael
@juicycleff
Thanks @MichalLytek
Rex Raphael
@juicycleff
@MichalLytek I just did I will like to share the code snippet, it looks clean
Where would be good place to share an example? This would help a lot of people
Here's a small snippet of what I did
fields.map(value => {
    if (value.getType() === Boolean) {
      Field(() => BooleanComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    } else if (value.getType() === String) {
      Field(() => StringComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    } else if (value.getType() === Number) {
      Field(() => NumberComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    }
  });
Rex Raphael
@juicycleff
export function FilterMongo<TItem>(TItemClass: ClassType<Partial<TItem>>): any {

  const temp = new TItemClass();
  const fields = getMetadataStorage().fields.filter(value => value.objectType === temp.constructor.name);

  @InputType(`Filter${TItemClass.name}`)
  class FilterMongoClass {
    [key: string]: any;

    @Field(() => [FilterMongoClass], { nullable: true })
    _OR?: FilterMongoClass[];

    @Field(() => [FilterMongoClass], { nullable: true })
    _AND?: FilterMongoClass[];

    @Field(() => [FilterMongoClass], { nullable: true })
    _NOR?: FilterMongoClass[];
  }

  fields.map(value => {
    if (value.getType() === Boolean) {
      Field(() => BooleanComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    } else if (value.getType() === String) {
      Field(() => StringComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    } else if (value.getType() === Number) {
      Field(() => NumberComparisonFilter, { nullable: true })(FilterMongoClass.prototype, value.name);
    }
  });

  @ArgsType()
  abstract class WhereFilter {
    @Field(() => FilterMongoClass, { nullable: true })
    where?: FilterMongoClass;
  }

  return WhereFilter;
}
Michał Lytek
@MichalLytek
and how gave you achieved type safety for the filter classes? like typing where.foo
*have
Rex Raphael
@juicycleff
I defined my possible filters for each field type
example of a comparison I wrote for my MongoDB parser
@InputType()
export class NumberComparisonFilter {
  @Field({ nullable: true })
  _EQ?: number;

  @Field({ nullable: true })
  _NE?: number;

  @Field({ nullable: true })
  _LT?: number;

  @Field({ nullable: true })
  _LTE?: number;

  @Field({ nullable: true })
  _GT?: number;

  @Field({ nullable: true })
  _GTE?: number;
}
I improved it to a recursive function and process custom types and arrays too
Evan Wu (Wen Yao Wu)
@wenyaowu
@MichalLytek Haha I guess I'm overthinking/ overengineering
Marcelo Serpa
@fullofcaffeine
So, I have to expose some of my system through a RESTful API for some clients to consume. Most of the biz logic is implemented in resolvers. What would you guys do in this case? Just call resolvers directly from REST controllers? Not a hard problem to solve I guess, Just looking for some insights.
Marcelo Serpa
@fullofcaffeine

So, I have to expose some of my system through a RESTful API for some clients to consume. Most of the biz logic is implemented in resolvers. What would you guys do in this case? Just call resolvers directly from REST controllers? Not a hard problem to solve I guess, Just looking for some insights.

I know this is a strange use-case and not really related to typgraphql. I’m thinking to actually wrap a small rest controller that queries the graphql endpoint locally, I think this is simpler than trying to use the resolver as an object directly.

Michał Lytek
@MichalLytek
You can query your schema object directly, without using HTTP endpoint
this approach is more universal, you don't couple your internal implementation with REST endpoint, you only rely on the GraphQL API
Marcelo Serpa
@fullofcaffeine
@MichalLytek Thanks, I’ll do that.
da club has asbestos
@jukebapes_twitter

What do you guys think about reusing resolver code in other parts of your application.
Say for example I have some code like this (simplified)

class UserResolver  {
  @Mutation 
   async updateUser() { 
       return await this.userService.updateUser() 
   }   
}

In this same service another engineer is developing a file upload/validation feature that make more sense as a standard HTTP POST endpoint.
But this endpoint requires also updating a user.

This case which approach would be better

1.) The endpoint controller makes a query to /graphql to make a mutation. Treating it if it was a standard client of the service.
2.) The controller reuses the resolver code

```
    const userResolver = new UserResolver() 
    userResolver.updateUser() 
 ```

3.) The controller ignores graphql and just calls the service method directly.
4.) Some other way i am not thinking of

Michał Lytek
@MichalLytek
@jukebapes_twitter don't reuse the resolvers - either execute the schema object with the query string (without using http) or just call the services from your controllers
Marcelo Serpa
@fullofcaffeine
That use-case is similar to mine above, just that my service object is the actual resolver, so it makes sense to just reuse it using the graphql interface. If you have another abstraction as service objects then it might make sense to use call them indeed.
Chris Pawlukiewicz
@paynoattn_gitlab
hey, has anybody hooked up appollo servers file upload to type-graphql?
da club has asbestos
@jukebapes_twitter

@MichalLytek How do I execute the schema object directly.

Also what about reusing operations from within a resolver from

for example (names and code simplified for example sake)

 async function itemsByIdQuery(idList) {
     return this.itemsService.getItemsById(idList)
} 

async function searchItems(inputString) { 
    const  itemIds =  await this.searchService.searchItems(inputString);

     // pass found ids to above operation
     return this.itemsByIdQuery(itemIds) 
}
Michał Lytek
@MichalLytek

How do I execute the schema object directly.

@jukebapes_twitter see in my tests - execute or graphql

da club has asbestos
@jukebapes_twitter
@MichalLytek Cool are you just talking about the tests for TypeGraphQl?
also what do you think about the above pattern of composing operations?
thanks for your help
though
Michał Lytek
@MichalLytek

just that my service object is the actual resolver,

@fullofcaffeine you shouldn't execute the resolver class in your controller - you would have to prepare GraphQLContext for each call and you will lose validation, middlewares, authorization and other features

so call the schema or replicate the goodness in the controller using NestJS or something
@jukebapes_twitter I don't get your composing idea - do you call resolver from resolver?
da club has asbestos
@jukebapes_twitter
@Query(of => [Item])
 async function itemsByIdQuery(idList) {
     return this.itemsService.getItemsById(idList)
} 

@Query(of => [Item])
async function searchItems(inputString) { 
    const  itemIds =  await this.searchService.searchItems(inputString);

     // pass found ids to above operation
     return this.itemsByIdQuery(itemIds) 
}
Yes they are both querys
within the same resolver class
Search items translates a search query into an item list.
And itemsById just returns the items based on a list of ids
itemsService/searchService are independent Java services that my resolver calls
Michał Lytek
@MichalLytek
Looks like related to #193 which is using the same pattern :+1: