MichalLytek / type-graphql

Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
https://typegraphql.com
MIT License
8.02k stars 675 forks source link

Scoping middlewares #200

Open MichalLytek opened 5 years ago

MichalLytek commented 5 years ago

Right now there are to ways to register a middleware:

In case of @UseMiddleware decorator, it should be possible to place in on the whole class to avoid manually placing on every method.

In case of global middlewares, it should be possible to register a global middleware that will be run:

In case of subscriptions, It should be also possible to choose if the middleware should be run only on subscribing, only on resolving the payload, or both.

This should also affect the @Authorized decorator which will be overwritten by a resolver's @Authorized, not concatenated like middlewares.

Yrobot commented 3 years ago

So, if i want to the log middleware just run once in one query, what should i do?

MichalLytek commented 3 years ago

@Yrobot Use @UseMiddleware decorator to scope middleware to single query. If by "one query" you mean one request, you need to use apollo server plugin or something similar.

Yrobot commented 3 years ago

@Yrobot Use @UseMiddleware decorator to scope middleware to single query. If by "one query" you mean one request, you need to use apollo server plugin or something similar.

thanks, i use apollo server plugin in the end.

joonatanvanhala commented 2 years ago

Hi! Just asking if there is any updates on this feature, right now I am forced to implement @UseMiddleware on each query/mutation since I don't want to trigger middleware multiple times

dallenbaldwin commented 1 year ago

I came upon something there that is probably worth mentioning. Either it's a bug or the documentation is misleading.

I cannot get a custom middleware to apply to an ObjectType Field when it has a Field Resolver in the ObjectType's Resolver. When attached to the Field Resolver, I get a callback for every returned instance of that resolved field rather than once for the fact that the field was requested. Since this is a OneToMany resolution, I can't just get rid of the field resolver (without a lot of work)

I do think there's utility in having middleware run for every instance of a field when attached to a field resolver, however I think it would be more convenient to have middleware attached to a field run once if the field appears in a request, regardless of the return type

It's worth noting I'm using typeorm and dataloaders with type-graphql and maybe they're breaking something in the decorator stack

Here's a barebones example of how I have it setup right now. Once again, attaching middleware to a Field doesn't appear to work

Entity + ObjectType + Relationship definition

@ObjectType({ description: 'example description' })
@Entity()
export class Example { 
  @Field(() => [Another], { deprecationReason: 'this is a deprecated field' })
  @UseMiddleware(LogDeprecated)
  @OneToMany(() => Another, ({example}) => example)
  anothers!: Another[]
}

Resolver

@Resolver(Example)
export class ExampleResolver implements ResolverInterface<Example> {
  // relations
  @FieldResolver(() => [Another])
  @UseMiddleware(LogDeprecated)
  anothers(
    @Root() { id }: Example,
    @Ctx() context: Context
  ) {
   // use data loader batch load function to resolve one to many relation
  }
}

and a sample of my LogDeprecated middleware

export const LogDeprecated: MiddlewareFn<Context> = async function (
  { info, context },
  next
) {
  try {
    // pull information out of context and info
    // track who and when deprecated queries, mutations, fields were requested
  catch (err) {
    // handle errors
  } finally {
    return next()
  }
}

this example query will result in x number of logs for each instance of Another associated with Example

query {
  example(id: 1 ) {
    anothers {
      ...
    }
  }
}