Giter Site home page Giter Site logo

graphql-middleware's People

Contributors

addityasingh avatar alloy avatar arnaud-zg avatar brikou avatar dependabot[bot] avatar dimatill avatar dimatillck avatar hata6502 avatar jcmais avatar johannpinson avatar kuldar avatar marktani avatar maticzav avatar mdlavin avatar mjdickinson avatar namtx avatar pabloszx avatar renovate-bot avatar renovate[bot] avatar rostislavv avatar ryanquinn3 avatar schickling avatar timsuchanek avatar vpctorr avatar ztec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-middleware's Issues

Be able to blacklist queries/mutations

Hey, thanks for a great library!

I was wondering if it's possible to blacklist instead of whitelisting queries/mutations? I.e. this is how we do it today:

export const authMiddleware = {
  Query: {
    getUsers: isAuthenticated,
  }
}

Where isAuthenticated is applied for the getUsers query. However, now I need to add every single query in here or do the following:

export const authMiddleware = {
  Query: isAuthenticated,
}

which applies it to all queries. However, I want some queries to be able to get without being authenticated. It would be nice if you could do something like:

export const authMiddleware = {
  Query: isAuthenticated,
  except: ['getPublicProfile', 'getBlogPost']
}

Or is this possible in any way at the moment?

not working on mutations, but working on query.

hi all, i am using nextjs, api routes, and nexus. trying to implement the middleware like below. its working on query, but not mutation. what could be the possible reason?


const middleware = {
  Query: {
    findManyProduct: async (resolve, parent, args, context, info) => {
      console.log('this gets called')
      const result = await resolve(parent, args, context, info)

      return result
    }
  },
  Mutation: {
    // createOneCompany: slugCounter,
    createOneProduct: async (resolve, parent, args, context, info) => {
      console.log('this doesnt')
      const result = await resolve(parent, args, context, info)

      return result
}}
import * as types from './graphql'
import { paljs } from '@paljs/nexus'
import { join } from 'path'
import { applyMiddleware } from 'graphql-middleware'
import permissions from './middleware/permissions'
import slugCounter from './middleware/slugCounter'

export const schema = applyMiddleware(
  makeSchema({
    types,
    // plugins: [paljs({ includeAdmin: true })],
    plugins: [paljs()],
    outputs: {
      schema: join(process.cwd(), 'src', 'generated', 'schema.graphql'),
      typegen: join(process.cwd(), 'src', 'generated', 'nexus-typegen.ts'),
    },
    typegenAutoConfig: {
      sources: [
        {
          source: '@prisma/client',
          alias: 'prisma',
        },
        {
          source: require.resolve('./context'),
          alias: 'Context',
        },
      ],
      contextType: 'Context.Context',
    },
  }),
  slugCounter,
  permissions,
)

How to send error from middleware

Hi! Thanks for this project! 😸

Sorry if it looks like a silly question, but I can't figure out how can I send an error to frontend from the middleware. I tried to implement something like this:

const midAuth = async (resolve, root, args, context, info) => {
  if (!context.auth)
    return {
      error: 'wrong token',
    };
  const result = await resolve(root, args, context, info);
  return result;
};

(or just throw an error)

but it doesn't work as I expected

Apply middleware by data sources

Is there a way to apply the middleware just for some data sources?

I have multiple data sources and not all of them need to use my middlewares for its resolvers.

Does it make sense at all ?

Thanks

Loop logger

I using with logger middleware and console.log loop many in console ( i don't query or call graphql )

screen shot 2018-08-08 at 4 09 06 pm

screen shot 2018-08-08 at 4 07 53 pm

how to get passport's Auth info in this middleware

hi all, i use express + passport-oauth2 in my web.
and i want to use graphql to wrap exist restapi, with permission check.

i follow the examples/permissions , but in this line

https://github.com/prismagraphql/graphql-middleware/blob/6889d229181e49b4e8711d22a960900b103c2632/examples/permissions/index.js#L40

the context.request.isAuthenticated() alway false,
and i debug into the code, it looks like the this have nothing about auth info.

anybody can tell me how to use this library whit passport ?

Incompatibility with apollo-server ^2.2.0

Hello,

It seems that there is an incompatibility issue between graphql-middleware and apollo-server@^2.2.0

Here you can find an explanation about what is breaking : apollographql/apollo-server#1935 (comment)

So apparently something in graphql-middleware is turning the introspection query execution (from graphql/execution into a Promise instead of returning an object.

Another related issue : apollographql/apollo-server#1934

In the meantime, the last working version is [email protected]

Introduce a `compose` api

Would like to apply multiple middlewares to a schema in an easier:

e.g.

compose(
  applyFieldMiddleware(metricsMiddleware, authMiddleware, beepMiddleware),
  applyDocumentMiddleware(responseSizeMiddleware)
)(schema)

Path and locations info improvement when type error returned

Using this library for permission management.
But there is some misleading info in path when validation of type failed.

Example.

Schema:

type Query {
  user(id: Int!): User
}

type User {
  id: Int!
  email: String!
  username: String!
}

Apply middleware on User type:

schema = applyMiddleware(schema, {
  User: () => new ForbiddenError(`Access denied`)
})

then following query (that return User type)

{
  user(id: 1) {
    username
    email
  }
}

throwing error:

{
  "errors": [
    {
      "message": "Access denied",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "user",
        "username"
      ],
      "extensions": {
        "code": "FORBIDDEN"
      }
    }
  ],
  "data": {
    "user": null
  }
}

Look at locations and path - it includes first queried attribute of User (username). Which is misleading.
Ideally it should show information regarding user query only:

"locations": [
  {
    "line": 2,
    "column": 3
  }
],
"path": [
  "user"
]

Basically following middlewares currently return same path and location in error when they shouldn't:

[
  {
    User: () => new ForbiddenError(`Access denied`)
  },
  {
     User: {
       username: () => new ForbiddenError(`Access denied`)
     }
   }
]

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

TypeScript error - Interface IResolverOptions incorrectly extends interface GraphQLField

node_modules/graphql-middleware/dist/types.d.ts:40:18 - error TS2430: Interface 'IResolverOptions<TSource, TContext>' incorrectly extends interface 'GraphQLField<any, any, any>'.
  Types of property 'resolve' are incompatible.
    Type 'IFieldResolver<TSource, TContext> | undefined' is not assignable to type 'GraphQLFieldResolver<any, any, any> | undefined'.
      Type 'IFieldResolver<TSource, TContext>' is not assignable to type 'GraphQLFieldResolver<any, any, any>'.
        Types of parameters 'info' and 'info' are incompatible.
          Type 'GraphQLResolveInfo' is not assignable to type 'GraphQLResolveInfo & { mergeInfo: MergeInfo; }'.
            Type 'GraphQLResolveInfo' is not assignable to type '{ mergeInfo: MergeInfo; }'.
              Property 'mergeInfo' is missing in type 'GraphQLResolveInfo'.

40 export interface IResolverOptions<TSource = any, TContext = any> extends GraphQLField<any, any, any>

issue with remote schema

Let me first thank you for this great lib!

I came across a use case where I would like to use a middleware to change the parent argument of a resolver:

const typeDefs = `
 type Query {
   hello(name: String): String
 }
`

const myMiddleware = {
 Query: {
   hello: (resolve, parent, args, context, info) => {
     const newParent = { foo: 'bar' };
     return resolve(newParent);
   },
 },
}

But in my resolver the parent is always null... Any reason why we do not allow modifying the parent object for a root query?

Thanks!

Typescript source file missing in npm package

I have a problem with Sentry. It complains about missing sources when stack with graphql-middleware is reached.

  1. In node_modules/graphql-middleware/dist/index.js there is a reference to //# sourceMappingURL=index.js.map which is ok since node_modules/graphql-middleware/dist/index.js.map does exist in the npm package.
  2. However in node_modules/graphql-middleware/dist/index.js.map there is reference to "sources":["../src/index.ts"] which is not shipped with the package.

Currently, my workaround is to manually pull git repository to get these .ts file and upload it to sentry.
I am not sure what is the best practice here, i.e. should src/index.ts be shipped with npm package?
I believe that would make sense since you are already shipping .map file but that would increase package size.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Exclude resolvers

For example there are 30 resolvers and we know that 27 of them need auth middleware and only 3 of them don't. The way that I know about from the docs is to specify all the 27 resolvers that require the middleware. Is there a way to do the opposite, i.e. write only the 3 resolvers to be excluded for the auth middleware?

apply multiple middelwares to executable schema

Hi, I'm trying to add multiple middlewares to a resolver function (for authorization purposes)
the problem is that when I try to do that like this :
const middlewares = [bodyParser, passport.authenticate('jwt', { session : false })];
const schemaWithMiddleware = applyMiddleware(schema, middlewares);
I get this error :
/graphql-middleware/dist/validation.js:24
throw new MiddlewareError("Type " + type + " exists in middleware but is missing in Schema.");

Error: Type 0 exists in middleware but is missing in Schema.

Performance

I'm seeing a noticeable performance hit when using graphql-middleware. I was hoping to use middleware to log resolver errors. Before going all in, I added a very simple middleware and deployed to production to test under load:

export const errorLogger = async (resolve, root, args, context, info) => {
  try {
    return await resolve(root, args, context, info);
  } catch (err) {
    // TODO: log errors
    throw err;
  }
};

Instrumentation showed time spent in Node doubled and overall response time was 10-15% slower.

The server is apollo-server-express. I've previously added schema directives to add some instrumentation to resolvers and saw no performances hit with those.

I really like the idea of graphql-middleware for something like logging/error reporting but the extra overhead. Any ideas on how to optimize?

How does one combine graphql-middleware with nexus-prisma?

The instructions for graphql-middleware are pretty straightforward — but they only seem to apply to schema-first development if I’m not mistaken.

I’ve now implemented nexus-prisma, but since this approach is code-first, the resolvers, when called, haven’t yet compiled into the actual resolvers (if that makes sense?), so I’m receiving the following error message:

.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/validation.js:1
Error: Field Query.name exists in middleware but is missing in Schema.
    at .../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/validation.js:17:27
    at Array.forEach (<anonymous>)
    at .../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/validation.js:15:43
    at Array.forEach (<anonymous>)
    at Object.validateMiddleware (.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/validation.js:9:29)
    at addMiddlewareToSchema (.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/middleware.js:19:42)
    at normalisedMiddlewares.reduceRight.schema.schema (.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/middleware.js:52:87)
    at Array.reduceRight (<anonymous>)
    at applyMiddlewareWithOptions (.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/middleware.js:51:79)
    at applyMiddleware (.../node_modules/graphql-yoga/node_modules/graphql-middleware/dist/middleware.js:78:12)

Example

index.js

// Packages
import { GraphQLServer } from 'graphql-yoga'
import { nexusPrismaPlugin } from 'nexus-prisma'
import { makeSchema } from 'nexus'

// Types
import types from './types'

// Middleware
import systemLogCreate from './resolvers/mutations/systemLogCreate'
import systemLogErrorCreate from './resolvers/mutations/systemLogErrorCreate'

new GraphQLServer({
  schema: makeSchema({
    types: { ...types, ...resolvers },
    plugins: [nexusPrismaPlugin()]
  }),
  middlewares: [
    systemLogCreate,
    systemLogErrorCreate
]

mutations.js

// Packages
import { mutationType } from 'nexus'
// Components
import systemLogCreate from './System/SystemLog/systemLogCreate'
import systemLogErrorCreate from './System/SystemLog/systemLogErrorCreate'

export default {
  Mutation: mutationType({
    definition(t) {
      t.field('systemLogCreate', {
        type: 'SystemLog',
        resolve: systemLogCreate
      })
      t.field('systemLogErrorCreate', {
        type: 'SystemLogError',
        resolve: systemLogErrorCreate
      })
    }
  })
}

systemLogErrorCreate.js

export default async (resolve, root, args, ctx, info) => {
  // First resolve all resolvers...
  try {
    return await resolve(root, args, ctx, info)
  } catch (err) {
    // ...and if an error occurs in any of them, log it and tie it to session.
    const sessionId = args.sessionId
    delete args.sessionId
    const error = ctx.photon.systemLogErrors.create({
      data: {
        args,
        error: err
      }
    })
    // Throw it to communicate it to browser.
    throw new Error(err)
  }
}

Cannot return null for non-nullable field Message.id.

I'm calling setMessage trying to return id from the created message. But if there's an error I get the following message:

Cannot return null for non-nullable field Message.id.

The only way to avoid this is to change from ID! to ID. Is there anything I can do to get the actual error instead of this?

Request:

POST http://localhost:4000/graphql
Content-Type: application/json
X-REQUEST-TYPE: GraphQL

mutation setMessage {
  setMessage(input: { message: "a@@gmail.com"}) {
    error
    message
    id
  }
}

resolver

import * as yup from 'yup'

export const messageMutations = {
  setMessage: {
    validationSchema: yup.object().shape({
      input: yup.object().shape({ message: yup.string().email() }),
    }),

    resolve(_, { input }) {
      return { id: (Math.random() * 100) | 0, message: input.message }
    },
  },
}

3.0.2 throws TypeError: definitions.trim is not a function

In prisma/photon TS example graphql-auth a comment references non-working graphql-shield in issue #361

The problem appears when graphql-middleware 3.0.2 is installed as a dependency of graphql-yoga.

Resolving graphql-middleware to 3.0.1 eliminates the error and graphql-shield works as expected.
Only works with yarn, npm install does not process resolutions

"resolutions": {
  "graphql-middleware": "3.0.1"
}
$ ts-node-dev --no-notify --respawn --transpileOnly ./src
Using ts-node version 8.3.0, typescript version 3.5.3
TypeError: definitions.trim is not a function
    at parseFragmentToInlineFragment (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/fragments.ts:85:21)
    at /Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/fragments.ts:41:24
    at Array.map (<anonymous>)
    at Object.extractFragmentReplacements (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/fragments.ts:40:6)
    at addMiddlewareToSchema (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/middleware.ts:40:32)
    at normalisedMiddlewares.reduceRight.schema (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/middleware.ts:86:13)
    at Array.reduceRight (<anonymous>)
    at applyMiddlewareWithOptions (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/middleware.ts:78:77)
    at applyMiddleware (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-middleware/src/middleware.ts:129:36)
    at new GraphQLServer (/Users/glen/Documents/JavaScript/Research/hello-photon-graphql-auth/node_modules/graphql-yoga/src/index.ts:137:13)
[ERROR] 18:09:25 TypeError: definitions.trim is not a function

Question: wrap all top-level queries / mutations only

Hey all, trying to figure out if I'm approaching this wrong or just not understanding. So, what I'd like to do is put a simple tracing middleware around any "top level" query. I.e. the request comes in and they ask for PersonA and PersonB query.

I've got one that runs, and I can filter out decently well by only measuring if parent is null, which seems to work. But the downside is that it's making my introspection query SUPER slow – proabbly because it's hitting every prop and we have a huuuge schema which is split into multiple files and stitched together. Also, we're on lambda, so we recompile this on every invocation (at least locally) so our dev cycles are suffering.

I'm wondering: is there some easy way to say wrap Query.* and Mutation.* but not anything deeper? I saw #13 and looked at the applyMiddlewareToDeclaredResolvers but it doesn't seem to have much impact.

Thoughts? Am I missing something simple?

Support for multiple middlewares

When tried to use multiple middle-wares with a query it failed with the errors below.

/Users/user/Desktop/example/node_modules/graphql-middleware/dist/validation.js:20
                    throw new MiddlewareError(`Expected ${type}.${field} to be a function but found ` +
                    ^

MiddlewareError: Expected Query.secured to be a function but found object
    at /Users/user/Desktop/example/node_modules/graphql-middleware/dist/validation.js:20:27
    at Array.forEach (<anonymous>)
    at /Users/user/Desktop/example/node_modules/graphql-middleware/dist/validation.js:15:43
    at Array.forEach (<anonymous>)
    at Object.validateMiddleware (/Users/user/Desktop/example/node_modules/graphql-middleware/dist/validation.js:9:29)
    at addMiddlewareToSchema (/Users/user/Desktop/example/node_modules/graphql-middleware/dist/middleware.js:19:42)
    at normalisedMiddlewares.reduceRight.schema.schema (/Users/user/Desktop/example/node_modules/graphql-middleware/dist/middleware.js:52:87)
    at Array.reduceRight (<anonymous>)
    at applyMiddlewareWithOptions (/Users/user/Desktop/example/node_modules/graphql-middleware/dist/middleware.js:51:79)
    at applyMiddleware (/Users/user/Desktop/example/node_modules/graphql-middleware/dist/middleware.js:78:12)

const {
    GraphQLServer
} = require('graphql-yoga')

const typeDefs = `
  type Query {
    open: String!
    secured: String!
    me: Me!
  }

  type Me {
    name: String!
    surname: String!
    age: Int!
  }
`

const code = 'supersecret'
const isLoggedIn = async (resolve, parent, args, ctx, info) => {
    // Include your agent code as Authorization: <token> header.
    const permit = ctx.request.get('Authorization') === code

    if (!permit) {
        throw new Error(`Not authorised!`)
    }

    return resolve()
}

+ const log = async (resolve, parent, args, ctx, info) => {
+     console.log('Aqwertyuiop');
+     return resolve()
+ }

const resolvers = {
    Query: {
        open: () => `Open data, everyone's welcome!`,
        secured: () => `Personal diary - this is for my eyes only!`,
        me: () => ({}),
    },
    Me: {
        name: () => 'Ben',
        surname: () => 'Cool',
        age: () => 18,
    },
}

// Middleware - Permissions

const permissions = {
    Query: {
+        secured: [log, isLoggedIn],
-        secured: isLoggedIn,
    },
    Me: isLoggedIn,
}

// Server

const server = new GraphQLServer({
    typeDefs,
    resolvers,
    context: req => ({
        ...req
    }),
    middlewares: [permissions],
})

server.start(() => console.log('Server is running on http://localhost:4000'))

Is there any support for multiple middle-wares?
There can be an utility function or in-build support for multiple middleware.

Why does `applyMiddleware` mutate the schema passed into it?

Hi there,

Thanks for all of the hard work on this library.

I just battled a bug for a while, it was very confusing trying to figure out what was happening. The answer it seems is that applyMiddleware is actually mutating the schema passed into it. I was under the assumption that the API was immutable, considering the modified schema is returned from applyMiddleware. This assumption led me to believe that I could reuse the schema passed in without the middleware applied. This caused big problems.

It would be nice if applyMiddleware could be immutable as the API implies. Is there a reason for it to not be immutable?

If mutations are necessary for some reason, could this be documented somewhere?

Thanks!

Question: adding middleware around a (mock) type resolver?

I've been able to successfully apply middleware for fields, but when I try to apply one to a type resolver, it only applies the middleware to that type's field resolvers, not the type resolver itself.
Is there a way to only have it apply to the type resolver?
For example, given the below resolvers, I'd like to wrap the Contact type resolver, but I can only seem to get it to apply to that type's fields (e.g, name below).

// RESOLVERS
{
  // mock type resolver
  Contact: () => {
    return {
      name: () => { /* field resolver */ }
    }
  },
  Query: () => {
    contacts: () => { /* field resolver */ }
  }
}
// MIDDLEWARE
applyMiddleware(schema, {
  Contact: myMiddleware()
})

Add option to support introspection queries

I see that graphql-middleware intentionally doesn't run on introspection queries. This makes sense, but I have a use case where it's causing problems.

I am trying to manage database transactions at the middleware and context level. I create the transaction in the context factory (because I have dataloaders created in context that I want to provide the transaction to). The middleware then handles committing or rolling back the transaction based on whether the query was successful.

This works great except for the introspection query, because that query triggers context creation but not the middleware, resulting in transactions that are opened but never committed. I could fix this by creating and committing the transactions entirely in the middleware, but as I mentioned before I need the transaction available when creating the context.

So yeah, being able to opt in to this somehow would be helpful.

Wrong resolve argument in nested types

Hello !

I have the following resolvers, (schema created with makeExecutableSchema)

const resolvers = {
  Muation: {
    viewerMutation: (_, args, context) => {
      book: () => {
          author: ({id}) => getAuthorForBook(id)
      }  
    },
  },
}

And I am trying to do something like this:

const logMiddleware = {
  Muation: {
    viewerMutation: async (resolve, parent, args, context, info) => {
      book: () => {
          author: ({id}) => {
               console.log('request author for book:', id)
               return resolve(root, {id}, context, info)
          }
      }  
    },
  },
}

But I am getting an issue, the resolve inside author is not calling getAuthorForBook.
I would have expected to find a resolver argument on author but nested types seem to behave differently from root types (under Mutation or Query)
Is this a normal behavior ?

Naming of middleware

Before publishing our timeout middleware to npm, I figured I’d ask if you had a specific naming scheme in mind for middleware? I settled on graphqlTimeoutMiddleware for now: artsy/metaphysics#1068

Thanks for this library btw!

No license specified

Hi!

I’d love to use this library, but there’s no license specified. Would you mind committing one?

Thanks!

issue: middleware with schema stitching

So I think this might just be an limitation of working with schema stitching, but in case things aren't working as intended:

I have a remote database which accepts graphql queries. There is a Person type like so

type Person {
  id: ID!
  primaryEmailAddresses: [EmailAddress!]!
  primaryEmailAddress(ownerId: ID!): EmailAddress
}

type Query {
  person(id: ID!): Person
}

I've stitched the database's schema to the another "main" schema running in ApolloServer. The main schema shows the public:

type Person {
  id: ID!
  primaryEmailAddress: EmailAddress
}

type Query {
  person(id: ID, emailAddress: String): Person
}

I have a middleware function (associated with the "main" schema) which gets the "ownerId" from the context, and automatically adds it as an argument to the Person.primaryEmailAddress resolver.

{
  Person: {
    primaryEmailAddress: async(resolve: Function, parent, args, context, info) => {
      if (!context.user) return null;

      const newArgs = args
        ? { ...args, ownerId: context.user.perspective }
        : { ownerId: context.user.perspective };

      return await resolve(parent, newArgs, context, info)
    }
  }
}

Additionally, I have a custom resolver function associated with Query.person which delegates the response to the database schema. This means that Query.person gets delegated to the database schema before person.primaryEmailAddress is called.

Unfortunately, I'm finding that, after the main schema delegates the response to the database schema, the Person.primaryEmailAddress middleware is not triggered.

Provide function name for 'resolve' parameter

Hello, thanks for the package!
I am trying to setup logging with middleware. It would be nice to be able to log name of actual resolver function like so:

const loggingMiddleware = async (
  resolve,
  root,
  args,
  context,
  info
) => {
  console.log(`${resolve.name} args:`, args)
  const result = await resolve(root, args, context, info)
  console.log(`${resolve.name} result:`, result)
  return result
}

and I expect it to log: "getUser args: {} ".
I think it would be required for resolvers to be an actual function and not anonymous, but I am ok with it as long as I can map log entry to the resolver file.

Better error for not found fields

Hi
Now when the middleware has a field which is not declared in the schema, an error will be thrown as below:

TypeError: Cannot read property 'resolve' of undefined
    at applyMiddlewareToField (.../node_modules/graphql-middleware/src/index.ts:61:24)
    at .../node_modules/graphql-middleware/src/index.ts:93:18
    at Array.reduce (<anonymous>)
    at applyMiddlewareToType (.../node_modules/graphql-middleware/src/index.ts:90:47)
    at .../node_modules/graphql-middleware/src/index.ts:138:17
    at Array.reduce (<anonymous>)
    at generateResolverFromSchemaAndMiddleware (.../node_modules/graphql-middleware/src/index.ts:135:47)
    at addMiddlewareToSchema (.../node_modules/graphql-middleware/src/index.ts:156:21)
    at .../node_modules/graphql-middleware/src/index.ts:171:7
    at Array.reduce (<anonymous>)
    at applyMiddleware (.../node_modules/graphql-middleware/src/index.ts:169:43)
    at Object.<anonymous> (.../src/schema/index.js:13:16)
    at Module._compile (internal/modules/cjs/loader.js:678:30)
    at loader (.../node_modules/babel-register/lib/node.js:144:5)
    at Object.require.extensions.(anonymous function) [as .js] (.../node_modules/babel-register/lib/node.js:154:7)
    at Module.load (internal/modules/cjs/loader.js:589:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
    at Function.Module._load (internal/modules/cjs/loader.js:520:3)
    at Module.require (internal/modules/cjs/loader.js:626:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (.../src/app/app.js:6:1)
    at Module._compile (internal/modules/cjs/loader.js:678:30)

Specific Field Middleware Problem.

Hello,
let's say we have the following book type:
type Book { title: String author: String pagecount: Int cover: Cover }
A problem I've noticed in this implementation is that if a middleware is specific to a field (the Book query in our case), then this middleware will be executed 5 times. First time in the Book query resolver, then 4 more times for every field resolver in the Book type.

Is there any way how I can limit the middleware to only be executed once (for the Book query resolver only)? Or did I miss someting?

Problems getting scoped middleware working

I know I have to be misunderstanding something, but I can get middlewares to work if I apply them directly, but not if I scope them to a certain part of the tree. Everything in the docs and issues makes me thing at least two of these logs should print on every query, but only #3 prints:

schema = applyMiddleware(
    schema,
    {
        Query: (resolve, root, args, context, info) => {
            console.log('1>>>>')
            return resolve(root, args, context, info)
        },
        Mutation: (resolve, root, args, context, info) => {
            console.log('2>>>>')
            return resolve(root, args, context, info)
        }
    },
    (resolve, root, args, context, info) => {
        console.log('3>>>>')
        return resolve(root, args, context, info)
    }
)

Any idea what I'm missing?

Bug: repeatedly calling middleware for each key of returning value

Hi everyone, first I want to thank you for this great library, I have used it with Apollo server, all worked great intend of repeatedly calling, it's calling middleware for each field returned from a handler, and args argument is empty

export const authMiddleware = async (resolve, root, args, ctx: Context, info) => {
    const mutationField = info.schema.getMutationType();
    const mutationDefinition = mutationField.getFields()[info.fieldName];

    if (mutationDefinition && mutationDefinition.auth) {
        try {
            const token = ctx.headers['authorization'].replace('Bearer ', '');
            const data = await Crypto.decodeJwt<{ id: string }>(token);
            const user = await User.findOne(data.id);

            ctx.state.auth = {
                user,
                data,
                token
            };
        } catch (e) {
            throw new AuthenticationError('unauthorized');
        }
    }

    const data = await resolve(root, args, ctx, info);

    console.log(data);
    return data;
};

Allow applyMiddlewareToField to spread extra properties or add specific key for extra metadata

Currently applyMiddlewareToField only spreads the properties it wants to:

https://github.com/prisma/graphql-middleware/blob/500925ade884a025cb6eb2ba99672c4f4a52b13c/src/applicator.ts#L41-L84

It would be really useful if extra properties were also added to the final field definition, that way we could declare some custom properties that could be used directly in the middlewares.

For instance, we could have a middleware added to all mutations, but that only does something to mutations that have a specific flag.

// ...
const resolvers = {
  // ...
  Mutation: {
    // ...
    DoSomething: {
      flags: [FLAGS.SPECIFIC_FLAG],
      resolve: async (root, args, context, info)=> {
        // ...
      },
    }
  },
}

then on the middleware:

const customValidation = {
  async Mutation(resolve, root, args, context, info) {
    // use info to get the _typeConfig of the mutation being executed
    const mutationConfig = ...;
    if (mutationConfig.flags && mutationConfig.flags.includes(FLAGS.SPECIFIC_FLAG)) {
      // Do something
    }
  }
}

I'm asking for that because I'm migrating some code that uses graphql-add-middleware to use graphql-middleware instead.

I'm going to write a post about validations with graphql-middleware, but for it to work I need this "feature".

Thoughts?

I can get some time to work on that if it's acceptable.

addResolveFunctionsToSchema from graphql-tools does spreads all other properties, so it's not a problem.

See related code there:
https://github.com/apollographql/graphql-tools/blob/3b77c2071e4b2328b1a4c4471c61d4ad93a6ae57/src/generate/addResolveFunctionsToSchema.ts#L121

https://github.com/apollographql/graphql-tools/blob/3b77c2071e4b2328b1a4c4471c61d4ad93a6ae57/src/generate/addResolveFunctionsToSchema.ts#L140-L147

Logo ideas

As the title suggests this issue is used to find a great logo for graphql-middleware.

The best idea I got so far, which would also somehow reflect the name, is a group or three cartoon animals with the middle one wearing a GraphQL T-Shirt - something like npm’s wombat.

I would love to hear all the ideas you’ve come up with! 😄

Bug: applyMiddlewareToDeclaredResolvers works only for first middleware

On top of this issue maticzav/graphql-shield#416 we faced noticeable performance degradation when graphql-shield applied. After few stages of investigation it turned out that there is a small bug in graphql-middleware itself, where for applyMiddlewareToDeclaredResolvers it will apply to declared resolver only for first middleware in sequence here, and all fields will be affected for next middlewares, due to presence of field, and passing of this condition

Bad marketing copy in the README

README.md currently says

  • Easiest way to handle GraphQL middleware: Intuitive, yet familiar API will get under your skin in a second.

"To get under someone's skin" means to annoy someone (dictionary.com reference). It would take a very remarkable API to accomplish this in just one second. :-)

Consider alternative phrasing like

An intuitive, yet familiar API that you will pick up in a second.

Easier way to apply multiple middlewares to specific resolvers

I'm not sure if I'm missing something but as far as I can tell, the only way to apply multiple middlewares to a specific resolver is to do the following as per this article

const middleware1 = {
  Query: {
    hello: logInput,
    bye: logInput,
  },
}

const middleware2 = {
  Query: {
    bye: logResult,
  },
}

const middlewares = [middleware1, middleware2]

Wouldn't it be better to do it this way instead? I feel like the above method would get out of hand pretty fast.

const loggingMiddleware = {
  Query: {
    hello: logInput,
    bye: {logInput, logResult} //OR [logInput, logResult]
  },
}

const middlewares = [loggingMiddleware]

I tried to check if this was possible but I got the following error:

Error: Expected Query.hello to be a function but found object

I don't mean to complain or anything but I also feel like the documentation needs to be a little better. It's incomplete and a bit unclear.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.