benawad / type-graphql-series

Typescript GraphQL Server built with TypeGraphQL
326 stars 129 forks source link

Feature request - Flexible reusable filters on queries #2

Closed jakelowen closed 5 years ago

jakelowen commented 5 years ago

Hey Ben - great series so far. I'm wondering how to best implement something in type-graphql / typeorm and hoping you consider covering it in this series.

One thing I really like about prisma or hasura is how it reduces boilerplate by automatically generating very extensive arguments when you want to do advanced queries. I.e.

{
    users(where:{ firstName_beginswith:'ja', email_contains:'@gmail' })...
}

and so on. By studying prisma I've been able to implement something similar using knex. First I define all the different type args I want to filter on like: https://github.com/jakelowen/apollo-server-2-auth-starter/blob/master/src/typeDefs/user.graphql

And then passing args through a custom knex query builder function: https://github.com/jakelowen/apollo-server-2-auth-starter/blob/master/src/utils/filterQuery.js

And then tie it all together in a resolver like:

let query = db.table(fetchPayload.table.name);
query = filterQuery(query, fetchPayload.where);

data = await paginator(
      db,
      query,
      fetchPayload.orderBy,
      fetchPayload.limit,
      fetchPayload.after,
      fetchPayload.table.uniqueColumn,
);

I think I could figure out a way to do something similar in type-graphql / typeorm by defining the args manually: i.e. @Arg("firstName_beginswith" { nullable: true }) f?: string,

Can you think of a way using higher order type-graphql resolvers to iterate over the exposed field()'s of an entity and automatically create those kinds of filter args?

OR can you think of another automated way to enable rich querying? Would love to hear your thoughts on that.

jakelowen commented 5 years ago

Or might one approach this entirely differently by accepting as graphql args something that can plugged in directly to typeorm? Perhaps accepting as an arg something that can be directly plugged into typeorm's find options?

where: { 
        firstName: "Timber", 
        lastName: "Saw" 
    },
    order: {
        name: "ASC",
        id: "DESC"
    },
    skip: 5,
    take: 10,

http://typeorm.io/#/find-options

benawad commented 5 years ago

I think it would involve creating a function that takes an entity, reads the fields, returns a new class with fields and decorators added to it at runtime which I'm not sure how to do. And maybe that's not possible.

Alternatively you could take an approach similar to how Prisma does and introduce a build step. Where you take an entity and generate a bunch classes.

As for reading the properties from a class with the @Field() decorator, I don't think that's possible (I could be wrong). I think what you will need to do is create your own decorator which you add.

Anyway, I haven't tried to implement something like that before, but I will be giving it a try shortly. I don't think I'll have something finished in time to include in this series and it may warrant an entire series of its own.

jakelowen commented 5 years ago

Thanks @benawad - I ALMOST got the querybuilder side working, but ran into some problems getting queries to nest as expected.. You can see more at https://stackoverflow.com/questions/54192483/typeorm-dynamic-query-builder-from-structured-object

I'll keep hacking on it.

jakelowen commented 5 years ago

Thanks for your help @benawad on the above stack overflow question. I would a love to see a video/series on advanced filtering. Feel free to use any / all of that code from the SO question if it would be helpful. No attribution necessary. Feel free to close this issue.