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

Scoping middlewares #200

Open
MichalLytek opened this issue Nov 19, 2018 · 5 comments
Open

Scoping middlewares #200

MichalLytek opened this issue Nov 19, 2018 · 5 comments
Assignees
Labels
Enhancement 🆕 New feature or request
Projects
Milestone

Comments

@MichalLytek
Copy link
Owner

Right now there are to ways to register a middleware:

  • @UseMiddleware above the resolver method
  • globalMiddlewares in buildSchema

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:

  • only once (e.g. dataloaders initialization)
  • only in resolvers (query/mutation/subscription)
  • only on advanced field resolvers (resolver's class method)
  • only on simple field resolvers (getters, inline methods and simple fields)
    and any combination of this possible options.
    I have to investigate if the @Middleware decorator for class-based and .scope static property for function-based solution will be better than a detailed config object in buildSchema

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
Copy link

Yrobot commented Jul 30, 2021

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

@MichalLytek
Copy link
Owner Author

@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
Copy link

Yrobot commented Aug 22, 2021

@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
Copy link

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
Copy link

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 {
      ...
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement 🆕 New feature or request
Projects
Board
  
Backlog
Development

No branches or pull requests

4 participants