Giter Club home page Giter Club logo

n1ru4l / envelop Goto Github PK

View Code? Open in Web Editor NEW
759.0 11.0 109.0 18.86 MB

Envelop is a lightweight library allowing developers to easily develop, share, collaborate and extend their GraphQL execution layer. Envelop is the missing GraphQL plugin system.

Home Page: https://envelop.dev

License: MIT License

JavaScript 1.73% TypeScript 66.43% Shell 0.01% MDX 31.83%
graphql nodejs plugins api graphql-execution graphql-schema execution-pipeline javascript the-guild typescript

envelop's Introduction

envelop's People

Contributors

adirishi avatar ardatan avatar avaly avatar charlypoly avatar darren-west avatar dburles avatar dimamachina avatar dios-david avatar dotansimha avatar dthyresson avatar emrysmyrddin avatar enisdenjo avatar gilgardosh avatar github-actions[bot] avatar jeengbe avatar jomik avatar jycouet avatar kamilkisiela avatar klippx avatar n1ru4l avatar nlepage avatar notrab avatar pabloszx avatar renovate[bot] avatar saihaj avatar santino avatar theguild-bot avatar tuvalsimha avatar urigo avatar yassineldeeb 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

envelop's Issues

Empty context value in Envelop v0.3.0

Describe the bug
In Envelop 0.3.0 (and the previous alpha releases of this version), the contextValue available within the args object passed to onExecute is empty.
This eventually breaks some existing plugins, certainly newrelic but potentially also graphql-jit and graphql-modules (since they make use of contextValue).

Any previous version of Envelop (such as latest 0.2.2) are instead properly populating the contextValue with GraphQL's document, operation and variables, amongst other properties.

To Reproduce
I have created this simple project to reproduce the issue:
https://codesandbox.io/s/envelop-empty-context-z4w75?file=/README.md

The README file contains all instructions to reproduce the issue and compare behaviours of v0.3.0 against v0.2.2

Expected behavior
The context value should be populated with GraphQL informations at least, but ideally there should be no difference between how it's populated in v0.2.x vs 0.3.x

Export testkit utils from @envelop/testing

Is your feature request related to a problem? Please describe.

For building custom plugins I was checking how you write specs. And you import a lot of nifty stuff from @envelop/testing. However this seems not to be accessible from userland. What is the suggested approach?

Describe the solution you'd like

I would like to be able to import stuff from @envelop/testing:

import { createTestkit } from '@envelop/testing'

Describe alternatives you've considered

None. It must be supported by the @envelop itself, there's no point for users to go deep and mock internals.

Additional context

None

In the Securing your GraphQL API documentation, the introductory sentence is difficult to read

Describe the bug

In the Securing your GraphQL API documentation the introductory sentence is difficult to read:

Building a secure GraphQL API is hard by design of to the because "Graph" part withing GraphQL.

To Reproduce
Steps to reproduce the behavior:

Expected behavior

Perhaps reword this to:

Building a secure GraphQL API is hard by design because of the "Graph" nature of GraphQL.

Missing context typing at plugin level

Describe the bug

Following the documentation from Typescript Support I'm using this code example but the onExecute function has a context value of any, while onParse is correctly typed.

To Reproduce
Steps to reproduce the behavior:

export const useMyPlugin = (): Plugin<{ myContext: string }> => {
  return {
    onParse({ context }) {
      // Here, context is typed as { myContext: string }
    },
    onExecute({ args }) {
      // Here, args.contextValue is NOT typed as { myContext: string }
    }
  };
};

Expected behavior

export const useMyPlugin = (): Plugin<{ myContext: string }> => {
  return {
    onParse({ context }) {
      // Here, context is typed as { myContext: string }
    },
    onExecute({ args }) {
      // Here, args.contextValue SHOULD be typed as { myContext: string }
    }
  };
};

Environment:

  • OS: macOS 11.4
  • @envelop/core: 0.4.0
  • NodeJS: v16.4.0

Add hook for `onRequest`

Also, we might need to start building the GraphQL context during that phase, to allow easy sharing of state across all hooks that comes later (onParse)

Feedback regarding subscribe/execute API

Execute

https://github.com/dotansimha/envelop/blob/b6573398738208f38a721c82d861d0fee14d1679/packages/types/src/index.ts#L62-L68

I think the API right now is not suited for streams (defer, stream, live) and should be adjusted accordingly.

onExecuteDone

In case of stream/defer/live operations we could go multiple routes:

Possible adjustment options for onExecutionDone

1. Invoke onExecutionDone with the last/final result

Edit: Now that I reflected some more on it I am no longer sure whether this makes the most sense. For a Promise value, we know when something is the last value. With defer/stream we can kind of detect the last result via result.hasNext, but that might again be digging too much into spec details and allows less experimentation.

With my current @live implementation there is no hasNext in the execution result. When should onExecutionDone be invoked? When the client notifies he is no longer interested in results for this operation? And then we do not really have the last execution result unless we start to always store the latest execution result ๐Ÿค”.

2. Invoke onExecutionDone with an array of all results

Potential memory leak for @live operations, and huge @stream lists.
If anyone needs all the results, it can be done by using onExecutionValue (or a similar proposed alternative API) for collecting all those values.

3. Invoke onExecutionDone with the merged results

envelop must be aware of how results can be merged and would make it less flexible for experiments such as @live.
If anyone needs the merged result, it can be done by using onExecutionValue (or a similar proposed alternative API) for collecting/merging all those values.

4. Invoke onExecutionDone with the async iterable

The more bare-bones solution; gives a lot of control back to the user. But the wording onExecutionDone would kind of be misleading as the execution is done after the async iterable returns.

Using this kind of API implies that we don't wanna mess with AsyncIterableIterators and give them to the user. So I think this is not the way we should choose.

5. Break stuff more down and add some kind of onExecutionStream API

Have different handlers for Promise<ExecutionResult> | ExecutionResult and AsyncIterableIterator<ExecutionResult>.

onExecutionDone should be invoked for execute calls that result in Promise<ExecutionResult> | ExecutionResult ?
onExecutionStream should be invoked for execute calls that result in AsynIterableIterator<ExecutionResult> ? Further handlers can be returned from this call for hooking into onNext and onReturn.

type OnExecutionStreamHandlerHookResult = {
  /* Alternative name: onNext? */
  onValue?: (
    executionResult: ExecutionResult,
    setExecutionResult: (
      result: ExecutionResult
    ) => void
  ) => void;
  onReturn?: () => void;
};

type OnExecutionStreamHandler = (options: {
  executionResult: AsyncIterableIterator<ExecutionResult>;
  setExecutionResult: (
    result: AsyncIterableIterator<ExecutionResult> | ExecutionResult
  ) => void;
}) => OnExecutionStreamHandlerHookResult | void;

export type OnExecuteHookResult<ContextType = DefaultContext> = {
  /* Invoked if `execute` returns Promise<ExecutionResult> | ExecutionResult */
  onExecuteDone?: (options: {
    result: ExecutionResult;
    setResult: (newResult: ExecutionResult) => void;
  }) => void;
  /* Invoked if `execute` returns AsyncIterableIterator<ExecutionResult> */
  onExecutionStream?: OnExecutionStreamHandler;
};

Conclusion

Option 5 seems to make the most sense.

onExecutionValue

NOTE: This might be redundant with option 5. from the onExecutionDone section.

This should be invoked before onExecuteDone and furthermore be invoked for every value published/returned by the execute the call.

This means for the resolved Promise value (if it is not an AsyncIterable) or each value published by the AsyncIterable.

It would allow modification and manipulation of all the values.

Potential use-cases

Live query: Diff last and next execution result and send JSON patch to the clients instead.

logging

API Draft

type OnExecutionValueHandler = (options: {
  executionResult: ExecutionResult;
  setExecutionResult: (result: ExecutionResult) => void;
}) => void;

export type OnExecuteHookResult<ContextType = DefaultContext> = {
  onExecuteDone?: (options: {
    result: ExecutionResult;
    setResult: (newResult: ExecutionResult) => void;
  }) => void;
  onResolverCalled?: OnResolverCalledHooks<ContextType>;
  onExecutionValue?: OnExecutionValueHandler;
};

Subscribe

https://github.com/dotansimha/envelop/blob/b6573398738208f38a721c82d861d0fee14d1679/packages/types/src/index.ts#L69-L73

Potentially new APIs

Note: All of these can also be done via function composition, in user-land, however, the hooks might be more intuitive and easier to use.

onSubscribe(Success)

Edit: The same API for execute as proposed in solution 5. (differentiate between the stream and single values) above might make more sense.

Invoked when the subscription is set up successfully (subscribe returns/resolves to an AsyncIterableIterator<ExecutionResult>).

This is kind of redundant with onSubscribeResult, but more user-friendly if you wanna do one specific thing when an AsyncIterable subscription has been created.

type OnSubscribeSuccessHandler = (options: {
  asyncIterable: AsyncIterableIterator<ExecutionResult>;
  setAsyncIterable: (
    asyncIterable: AsyncIterableIterator<ExecutionResult>
  ) => void;
}) => void;

export type OnSubscribeHookResult<ContextType = DefaultContext> = {
  onSubscribeResult?: (options: {
    result: AsyncIterableIterator<ExecutionResult> | ExecutionResult;
    setResult: (
      newResult: AsyncIterableIterator<ExecutionResult> | ExecutionResult
    ) => void;
  }) => void;
  onResolverCalled?: OnResolverCalledHooks<ContextType>;
  onSubscribeSuccess?: OnSubscribeSuccessHandler;
};

onSubscribeError

Edit: The same API for execute as proposed in solution 5. (differentiate between the stream and single values) above might make more sense.

Invoked when the subscription is set up successfully (subscribe returns/resolves to an ExecutionResult).

This is kind of redundant with onSubscribeResult, but more user-friendly if you wanna do one specific thing when an AsyncIterable subscription has not been created.

Potential use-cases might be resource cleanup e.g. disposing of a connection that was created for the context or logging.

type OnSubscribeErrorHandler = (options: {
  executionResult: ExecutionResult;
  /** Recover from error and setup subscription via custom asyncIterable, or modify execution result error **/
  setExecutionResult: (
    result: ExecutionResult | AsyncIterableIterator<ExecutionResult>
  ) => void;
}) => void;

export type OnSubscribeHookResult<ContextType = DefaultContext> = {
  onSubscribeResult?: (options: {
    result: AsyncIterableIterator<ExecutionResult> | ExecutionResult;
    setResult: (
      newResult: AsyncIterableIterator<ExecutionResult> | ExecutionResult
    ) => void;
  }) => void;
  onResolverCalled?: OnResolverCalledHooks<ContextType>;
  onSubscribeError?: OnSubscribeErrorHandler;
};

onValuePublished

Edit: The same API for execute as proposed in solution 5. (differentiate between the stream and single values) above might make more sense.

Mapping/modifying the ExecutionResult values published by the AsyncIterable returned by subscribe.

Less cumbersome than having to do an isAsyncIterable check and having to figure out how to map an async iterable compared to doing the same thing within onSubscribeResult. ๐Ÿค”
Also great for logging.

type OnValuePublishedHandler = (options: {
  executionResult: ExecutionResult;
  setExecutionResult: (result: ExecutionResult) => void
}

export type OnSubscribeHookResult<ContextType = DefaultContext> = {
  onSubscribeResult?: (options: {
    result: AsyncIterableIterator<ExecutionResult> | ExecutionResult;
    setResult: (newResult: AsyncIterableIterator<ExecutionResult> | ExecutionResult) => void;
  }) => void;
  onResolverCalled?: OnResolverCalledHooks<ContextType>;
  onValuePublished?: OnValuePublishedHandler;
};

onSubscribeEnd

Edit: The same API for execute as proposed in solution 5. (differentiate between the stream and single values) above might make more sense.

type OnSubscribeEndHandler = () => void;

export type OnSubscribeHookResult<ContextType = DefaultContext> = {
  onSubscribeResult?: (options: {
    result: AsyncIterableIterator<ExecutionResult> | ExecutionResult;
    setResult: (
      newResult: AsyncIterableIterator<ExecutionResult> | ExecutionResult
    ) => void;
  }) => void;
  onResolverCalled?: OnResolverCalledHooks<ContextType>;
  onSubscribeEnd?: OnSubscribeEndHandler;
};

Potential use-cases might be resource cleanup e.g. disposing of a connection that was created for the context or logging.

shared type-safe context across all plugins

It would be great to have the context of all plugins somehow inferred and shared, so all plugin config that gets the context injected (e.g. sentry plugin via appendTags option) have full type-safe access on it.

[depth-limit] causes leaking of internal error when undefined fragment is referenced

Executing the following document:

query {
  ...SomeNonExistingFragment
}

Results in the following error:

TypeError: Cannot read property 'kind' of undefined
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:60:16)
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:72:14)
at /app/node_modules/graphql-depth-limit/index.js:69:9
at Array.map (<anonymous>)
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:68:59)
at /app/node_modules/graphql-depth-limit/index.js:77:9
at Array.map (<anonymous>)
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:76:55)
at /app/node_modules/graphql-depth-limit/index.js:69:9
at Array.map (<anonymous>)
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:68:59)
at /app/node_modules/graphql-depth-limit/index.js:77:9
at Array.map (<anonymous>)
at determineDepth (/app/node_modules/graphql-depth-limit/index.js:76:55)
at /app/node_modules/graphql-depth-limit/index.js:22:27
at /app/node_modules/graphql/validation/validate.js:65:12

This is actually an issue with graphql-depth-limit - but given that it is not really maintained since 4 years we should maybe improve it and inline it?

Support for Mercurius

Hey,
Mercurius and Fastify are being favored over Apollo server.

So you can make an example of how to envelop implement with Mercurius and Nexus

Thank you in advance.

How to serve different schemas based on incoming request?

I think there could be several approaches here, but I'm still not sure which one is the ideal / recommended.

  • Create multiple envelops with share plugins, and just different useSchema calls, then allow users to do the schema routing.
  • Create useSchemaRouting plugin to allow developers to write a custom function that manages the routing. This will probably will be just a call to setSchema? Or probably allow developers to be part of the get schema()?
  • Something else?

Separate plugins context type to input/output

Today we have only a single ContextType provided for plugins, but in fact plugins can have input (what other plugins are injecting) and output (what context extensions does the plugin contributes).

Plugin Idea: `usePreloadAsset`

This is something a saw some time ago for Relay. The idea is to gather a list of resources as an extension field in the response.

The client logic on the network layer can immediately start fetching the resource, so the loading starts as soon as the requests come in and not when the corresponding component ,that consumes the data, updates.

The client logic is specific, but we could also provide a minimal implementation or example.

The plugin implementation should also be straightforward, just inject some function/object into context, that can be used for registering assets.

Enable strict type-checking

Currently, all this monorepo doesn't have TypeScript's strict type-checking configuration enabled, and this means that it's prone to very common errors and kills a little bit of the objective of using TypeScript, especially since it doesn't even have strictNullChecks enabled.

Describe the solution you'd like

To enable "strict" in tsconfig.json

Describe alternatives you've considered

At least start enabling strictNullChecks, which I'd say is the most important one.

Expose generic type system for plugins

Is your feature request related to a problem? Please describe.

Currently, when creating the envelop with typescript there are some plugins that have no type information other than unknown, but they do return a Plugin<DefaultContext> meaning they do have some extensibility potential.

By pain point is using useExtendedSchema which has a function signature of (unknown) => unknown so when I pass my typed function, it says it's not compatible.

Describe the solution you'd like
Expose type generics to extend the plugin and that they match the context type coming from envelop.

envelop<Context extends DefaultContext = DefaultContext>(options: {
  plugins: Plugin<Context>[];
}) => Envelop<Context>;

useSchema<Context extends DefaultContext = DefaultContext>(schema: GraphQLSchema) => Plugin<Context>;

useExtendSchema<Context extends DefaultContext = DefaultContext, PrevContext  extends DefaultContext = DefaultContext>(createContext: (PrevContext) => Context) => Plugin<Context>;

// It could even go like this, since the previous context is something the entire envelop will receive at some point

envelop<
  Context extends DefaultContext = DefaultContext,
  PrevContext  extends DefaultContext = DefaultContext
>(options: {
  plugins: Plugin<Context, PrevContext>[];
}) => Envelop<Context, PrevContext>;

useSchema<Context extends DefaultContext = DefaultContext, PrevContext  extends DefaultContext = DefaultContext>(schema: GraphQLSchema) => Plugin<Context, PrevContext>;

useExtendSchema<Context extends DefaultContext = DefaultContext, PrevContext  extends DefaultContext = DefaultContext>(createContext: (PrevContext) => PromiseLike<Context>) => Plugin<Context, PrevContext>;

// This could even help type 

const {
        parse,
        validate,
        contextFactory,
        execute,
        schema
      } = getEnveloped();

contextFactory // (PrevContext) => PromiseLike<Context>

Describe alternatives you've considered
Using as to cast the types but it's not a very nice solution.

Add `useLazyLoadedAynscSchema`

Is your feature request related to a problem? Please describe.

I'd like to be able to resolve a schema asynchronous based on the information in a request, which is currently not possible with

  • useLazyLoadSchema, which can't return a Promise, or with
  • useAsyncSchema, which doesn't have access to the request

I can resolve this myself by extracting the information I need from the req and defining my function that returns Promise<GraphQLSchema> for useAsyncSchema such that the info from req is enclosed, but this would let me clean up my code, avoiding interacting with req externally, and may be useful for other users who don't realize the closure solution.

Describe alternatives you've considered
useLazyLoadSchema could also just optionally accept a Promise<GraphQLSchema> returning function as well

Some plugins are not exposed from package

Describe the bug

Some plugins, such as for example useExtendContext, are mentioned in the documentation but not exposed as part of the package.

To Reproduce
Steps to reproduce the behavior:

Write

import { envelop, useSchema, useExtendContext } from '@envelop/core';

and get Error: Cannot find module '@envelop/core/plugins/use-extend-context'

I expect to be able to get the required plugin imported to my file.

Environment:

  • OS: macOS 11.2.3
  • @envelop/core:
  • NodeJS: v15.13.0

useAuth0 causes internal server error

Hi, i'm really enjoying using envelop.
Thank you for creating great libraries.

Describe the bug

I set up envelop with useAuth0 and if I make a request without authorization header, it causes internal server error and the http status code will be 500.

To Reproduce

Make envelop setup below and serve it in the same way of graphql-helix example.

const getEnveloped = envelop({
  plugins: [
    useSchema(executableSchema),
    useExtendContext(createContext),
    useLogger(),
    useAuth0({
      onError: (e: any) => {
        throw new EnvelopError('request not authenticated', {
          code: 'NOT_AUTHENTICATED',
        })
      },
      domain: process.env.AUTH0_DOMAIN!,
      audience: process.env.AUTH0_AUDIENCE!,
      headerName: 'authorization',
      preventUnauthenticatedAccess: true,
      extendContextField: 'auth0',
      tokenType: 'Bearer',
    }),
    useMaskedErrors(),
    useErrorHandler((error: any) => {
      console.log('ERROR: ' + JSON.stringify(error))
    }),
    useTiming(),
    useDepthLimit({
      maxDepth: 10,
    }),
  ],
})

Expected behavior

I think it should be caught internally and server should send back with http status 200.

Environment:

Subscription: Create Context per published value

Is your feature request related to a problem? Please describe.

I have been thinking about how context and subscription behave after finding out that in almost every server implementation the context is retained for the whole subscription execution.

In a production app that somehow got slipped and lead to caching issues with DataLoaders, as each time the AsyncIterator of the subscription field published a new value the DataLoaders were hitting the cache (but the underlying data in the database had changed). The temporary solution was to disable the cache altogether and instead only use the DataLoaders for solving the n+1 database queries issue.

However, I don't think that this should be the way to go. A DataLoader cache should only be disabled in the context of a mutation operation as per spec definition a query and subscription operation do not change/mutate data and thus if a nested resolver tries to load the same data as a parent data using a cache instead of taking another roundtrip to the database server is perfectly fine.

Since a subscription can be long-running it makes sense to clear the cache after a value published by an AsyncIterator went through the resolver phase.

Describe the solution you'd like

Currently, there is no straightforward way to do this and I have the feeling that from a UX experience it would be the simplest to just dispose of the context after the resolver phase and re-create it once a new value is published by the AsyncIteraror (vs. calling a lot of cache.clear() functions for each DataLoader or other caching mechanism).

One unsolved question is how that might work if the AsyncIterator event source is attached to the context. Usually, the source is passed to the subscribe handler via the context, but instead of having one source for each incoming request the source is often only initiated once upon server start and them simply forwarded by attaching it to the context (which is usually created for each incoming operation). So that wouldn't be an issue I guess.

Maybe, such behaviour could be a fit for envelop ๐Ÿ˜Š

Describe alternatives you've considered

Currently, the only suboptimal solution would be to either disable caching completely (which could be expensive) or raise this question on the graphql-js reference implementation

Additional context

useResponseCache: incorporate operationName into cache key construction

Technically people can send a document with many executable operations and specify which one should be executed via the operationName parameter, thus we should also use that value within the name.

However, if you want an efficient service you should only send the relevant document definitions, so this is pretty much a minor issue imho.

Plugin idea: `useHttpErrorCode`

We can create a simple plugin for creating HTTP code based on the GraphQL operation.

The result can be communicated through extensions and then the request pipeline can consume it. Just like graphql-helix does with headers (see https://github.com/contra/graphql-helix/blob/a796f9d2afdd8c2d91b745edbc53c5d49b716ba6/packages/core/lib/process-request.ts#L64)

Here's a simple mapping we can do:

  • 422 (Unprocessable Entity) - in case of parse error.
  • 400 (Bad Request) - in case of validate error.
  • 500 (Internal Server Error) - in case of execute error coming from the server.

And some more specific to some Envelop plugins we have:

  • 429 (Too Many Requests) - in case of error thrown by rate-limit plugin.
  • 401 (Unauthorized) - in case of authentication error.
  • 403 Forbidden - in case authorization error.

And we can allow users to customize the error code based on the results, using a custom callback function.

Plugin API: Allow to short-circuit and fall back to `ExecutionResult`

I think maybe we can have an API that allow phases that happens before execute to do short-circuit and then other steps and being skipped and execute returns ExecutionResult with the custom error.

For example:

  • parse do short-circuit with the parsing error
  • validate skipped (0 errors)
  • contextFactory skipped
  • rootValue skipped
  • execute is not being called and returns the parsing error from parse phase as { data: undefined, errors: [THE_ERROR] }

Rough draft: #358

This should allow developers to intercept parse/validate errors using useErorrHandler and similar plugins, and also simplify some aspects of the request pipeline.

Allow to extend parser

Maybe as something like:

onParse({ extendParser }) {
  extendParser("parseFragmentDefinition", (lexer) => {
    // ... lexer ..
  });
}

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/benchmark.yml
  • actions/checkout v4
.github/workflows/pr.yml
.github/workflows/release.yml
.github/workflows/tests.yml
  • actions/checkout v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/cache v4
  • actions/checkout v4
  • actions/cache v4
  • actions/checkout v4
.github/workflows/website.yml
  • actions/checkout v4
npm
benchmark/package.json
  • @envelop/core *
  • @envelop/graphql-jit *
  • @envelop/parser-cache *
  • @envelop/prometheus *
  • @envelop/validation-cache *
  • @graphql-tools/schema 10.0.3
  • fastify 4.26.2
  • graphql 16.8.1
examples/apollo-server/package.json
  • @apollo/server ^4.3.2
  • @apollo/server-plugin-landing-page-graphql-playground ^4.0.0
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/azure-functions/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @azure/functions 4.3.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/cloudflare-workers/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • worktop 0.7.3
  • @cloudflare/workers-types 4.20240312.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/express-graphql/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • express 4.18.3
  • express-graphql 0.12.0
  • graphql 16.8.1
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/google-cloud-functions/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • firebase-admin 12.0.0
  • firebase-functions 4.8.0
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @azure/functions 4.3.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-helix-auth0/package.json
  • @envelop/auth0 *
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • fastify 4.26.2
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-helix-defer-stream/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • fastify 4.26.2
  • graphql-helix 1.13.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-helix/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • fastify 4.26.2
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-http/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-http ^1.18.0
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-socket.io/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • @n1ru4l/socket-io-graphql-client 0.13.0
  • @n1ru4l/socket-io-graphql-server 0.13.0
  • graphql 16.8.1
  • graphql-ws ^5.0.0
  • socket.io 4.7.4
  • socket.io-client 4.7.4
  • ws 8.16.0
  • @types/node 20.11.27
  • @types/ws 8.5.10
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-sse/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-sse ^2.0.0
  • @types/node 20.11.27
  • @types/ws 8.5.10
  • ts-node 10.9.2
  • typescript 5.1.3
examples/graphql-ws/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-ws ^5.0.0
  • ws 8.16.0
  • @types/node 20.11.27
  • @types/ws 8.5.10
  • ts-node 10.9.2
  • typescript 5.1.3
examples/lambda-aws/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • aws-sdk 2.1577.0
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @types/aws-lambda 8.10.136
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/nexus/package.json
  • @envelop/core *
  • fastify 4.26.2
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • nexus 1.3.0
  • reflect-metadata 0.2.1
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/simple-http/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/typegraphql/package.json
  • @envelop/core *
  • class-validator 0.14.1
  • fastify 4.26.2
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • reflect-metadata 0.2.1
  • type-graphql 1.1.1
  • @types/node 20.11.27
  • ts-node 10.9.2
  • typescript 5.1.3
examples/with-esm/package.json
  • @envelop/core *
  • @graphql-tools/schema 10.0.3
  • fastify 4.26.2
  • graphql 16.8.1
  • graphql-helix 1.13.0
  • @types/node 20.11.27
  • tsup 8.0.2
  • typescript 5.1.3
package.json
  • @babel/core 7.24.0
  • @babel/plugin-proposal-class-properties 7.18.6
  • @babel/plugin-proposal-decorators 7.24.0
  • @babel/preset-env 7.24.0
  • @babel/preset-typescript 7.23.3
  • @changesets/changelog-github 0.5.0
  • @changesets/cli 2.24.2
  • @theguild/prettier-config 2.0.5
  • @types/benchmark 2.1.5
  • @types/jest 29.5.12
  • @types/k6 0.49.1
  • @types/node 20.11.27
  • @typescript-eslint/eslint-plugin 7.2.0
  • @typescript-eslint/parser 7.2.0
  • bob-the-bundler 7.0.1
  • chalk 5.3.0
  • eslint 8.57.0
  • eslint-config-prettier 9.1.0
  • eslint-config-standard 17.1.0
  • eslint-plugin-import 2.29.1
  • eslint-plugin-n 16.6.2
  • eslint-plugin-package-json ^0.10.0
  • eslint-plugin-promise 6.1.1
  • eslint-plugin-unicorn 51.0.1
  • globby 14.0.1
  • husky 9.0.11
  • jest 29.7.0
  • lint-staged 15.2.2
  • prettier 3.2.5
  • rimraf 5.0.5
  • ts-jest 29.1.2
  • ts-node 10.9.2
  • typescript 5.1.3
  • wait-on 7.2.0
  • node >=18.0.0
  • pnpm >=8
  • pnpm 8.15.4
packages/core/package.json
  • @envelop/types 5.0.0
  • tslib ^2.5.0
  • @graphql-tools/schema 10.0.3
  • @graphql-tools/utils 10.1.2
  • @repeaterjs/repeater 3.0.5
  • graphql 16.8.1
  • typescript 5.1.3
  • node >=18.0.0
packages/plugins/apollo-datasources/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • apollo-datasource 3.3.2
  • apollo-server-caching 3.3.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • apollo-datasource ^3
  • apollo-server-caching ^3
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/apollo-federation/package.json
  • apollo-server-caching ^3.1.0
  • apollo-server-types ^3.2.0
  • tslib ^2.5.0
  • @apollo/federation 0.38.1
  • @apollo/gateway 0.54.1
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • graphql-tag 2.12.6
  • typescript 5.1.3
  • @apollo/gateway ^0.54.0
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/apollo-server-errors/package.json
  • apollo-server-errors ^3.3.1
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • apollo-server-core 3.13.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/apollo-tracing/package.json
  • @envelop/on-resolve ^4.1.0
  • apollo-tracing ^0.16.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • @envelop/types ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/auth0/package.json
  • jsonwebtoken ^9.0.0
  • jwks-rsa ^3.0.1
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @types/jsonwebtoken 9.0.6
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • node >=18.0.0
packages/plugins/dataloader/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • dataloader 2.2.2
  • graphql 16.8.1
  • reflect-metadata 0.2.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • dataloader ^2.0.0
  • node >=18.0.0
packages/plugins/depth-limit/package.json
  • graphql-depth-limit ^1.1.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @types/graphql-depth-limit 1.1.6
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/disable-introspection/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/execute-subscription-event/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @n1ru4l/push-pull-async-iterable-iterator 3.2.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/extended-validation/package.json
  • @graphql-tools/utils ^10.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/filter-operation-type/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/fragment-arguments/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • @graphql-tools/utils 10.1.2
  • @types/common-tags 1.8.4
  • common-tags 1.8.2
  • graphql 16.8.1
  • jest-diff 29.7.0
  • oneline 1.0.3
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/generic-auth/package.json
  • @envelop/extended-validation ^4.0.0
  • @graphql-tools/utils ^10.0.6
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/graphql-jit/package.json
  • graphql-jit 0.8.2
  • tslib ^2.5.0
  • value-or-promise ^1.0.12
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • lru-cache ^10.0.0
  • reflect-metadata 0.2.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/graphql-middleware/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • graphql-middleware 6.1.35
  • graphql-shield 7.6.5
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • graphql-middleware ^6.0.0
  • node >=18.0.0
packages/plugins/graphql-modules/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • graphql-modules 2.3.0
  • reflect-metadata 0.2.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • graphql-modules ^1 || ^2.0.0
  • node >=18.0.0
packages/plugins/immediate-introspection/package.json
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • @sentry/node ^6 || ^7
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/live-query/package.json
  • @graphql-tools/utils ^10.0.0
  • @n1ru4l/graphql-live-query ^0.10.0
  • @n1ru4l/graphql-live-query-patch ^0.7.0
  • @n1ru4l/in-memory-live-query-store ^0.10.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • @n1ru4l/graphql-live-query-patch-jsondiffpatch 0.8.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/newrelic/package.json
  • @envelop/on-resolve ^4.1.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema ^10.0.0
  • @newrelic/test-utilities 6.5.5
  • @types/newrelic 9.14.3
  • graphql 16.8.1
  • newrelic ^11.0.0
  • typescript 4.9.5
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • newrelic >=7 <12
  • node >=18.0.0
packages/plugins/on-resolve/package.json
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/opentelemetry/package.json
  • @envelop/on-resolve ^4.1.0
  • @opentelemetry/api ^1.0.0
  • @opentelemetry/sdk-trace-base ^1.11.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema ^10.0.2
  • @opentelemetry/context-async-hooks ^1.20.0
  • @repeaterjs/repeater ^3.0.5
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/operation-field-permissions/package.json
  • @envelop/extended-validation ^4.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/parser-cache/package.json
  • lru-cache ^10.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/persisted-operations/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/preload-assets/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • node >=18.0.0
packages/plugins/prometheus/package.json
  • @envelop/on-resolve ^4.1.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • prom-client 15.1.0
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • prom-client ^15.0.0
  • node >=18.0.0
packages/plugins/rate-limiter/package.json
  • @envelop/on-resolve ^4.1.0
  • graphql-rate-limit 3.3.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/resource-limitations/package.json
  • @envelop/extended-validation ^4.0.0
  • @graphql-tools/utils ^10.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/response-cache-cloudflare-kv/package.json
  • tslib ^2.6.2
  • @envelop/response-cache ^6.1.2
  • jest-environment-miniflare ^2.14.1
  • ts-jest 29.1.2
  • typescript ^5.2.2
  • @cloudflare/workers-types ^4.20231121.0
  • @envelop/response-cache ^6.1.2
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/response-cache-redis/package.json
  • @envelop/response-cache ^6.1.2
  • ioredis ^4.27.9
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • @types/ioredis 4.28.10
  • ioredis-mock 5.9.1
  • typescript 5.1.3
  • node >=18.0.0
packages/plugins/response-cache/package.json
  • @graphql-tools/utils ^10.0.3
  • @whatwg-node/fetch ^0.9.0
  • fast-json-stable-stringify ^2.1.0
  • lru-cache ^10.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/executor ^1.1.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • ioredis-mock 5.9.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/sentry/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • @sentry/node 7.106.1
  • @sentry/tracing 7.106.1
  • @sentry/types 7.106.1
  • graphql 16.8.1
  • sentry-testkit 5.0.9
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • @sentry/node ^6 || ^7
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/plugins/statsd/package.json
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • hot-shots 9.3.0
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • hot-shots ^8.0.0 || ^9.0.0
  • node >=18.0.0
packages/plugins/validation-cache/package.json
  • hash-it ^6.0.0
  • lru-cache ^10.0.0
  • tslib ^2.5.0
  • @envelop/core ^5.0.0
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/testing/package.json
  • @graphql-tools/utils ^10.0.0
  • tslib ^2.5.0
  • @envelop/core 5.0.0
  • @graphql-tools/schema 10.0.3
  • graphql 16.8.1
  • typescript 5.1.3
  • @envelop/core ^5.0.0
  • @envelop/types ^5.0.0
  • graphql ^14.0.0 || ^15.0.0 || ^16.0.0
  • node >=18.0.0
packages/types/package.json
  • tslib ^2.5.0
  • typescript 5.1.3
  • node >=18.0.0
website/package.json
  • @theguild/components 5.2.6
  • date-fns 3.4.0
  • next 13.5.1
  • next-sitemap 4.2.3
  • react 18.2.0
  • react-dom 18.2.0
  • @theguild/algolia 1.1.9
  • @theguild/tailwind-config 0.3.2
  • @types/node 20.11.27
  • @types/react 18.2.65
  • sharp 0.33.2

  • Check this box to trigger a request for Renovate to run again on this repository

`@envelop/generic-auth`: docs and implementation are different

Describe the bug

There seems to be several differences between docs and implementation.

  1. @envelop/generic-auth has no exported member named 'resolveUser'.

in the docs:

import { useGenericAuth, resolveUser, ValidateUserFn } from '@envelop/generic-auth';

But resolveUser does not exist. So this should be ResolveUserFn.

  1. resolveUser does not exist in the type of GenericAuthPluginOptions

in the docs:

    useGenericAuth({
      resolveUser,
      validateUser,
      mode: 'protect-all',
    }),

But resolveUser does not exist in the type of GenericAuthPluginOptions as I saw implementation.
Here, resolveUserFn: ResolveUserFn<UserType, ContextType>; and validateUser?: ValidateUserFn<UserType, ContextType>; are different in naming convention. I think it's better to use same naming rule in terms of consistency.

Environment:

  • "@envelop/generic-auth": "^1.0.0"
  • "@envelop/core": "^1.0.3"
  • NodeJS:

explore using envelop for validation

What if we could annotate input fields/args in SDL (or extension fields) with validation rules e.g. string length 50 and enforce the validation in the extended validation phase ๐Ÿค”

People also use custom scalars for this ๐Ÿค”

I personally prefer doing it within resolvers and avoid adding any scalars.

Talked with someone on Crisp who wishes to have something like that.

I think we could support all of those flows and let people choose what they want, but still give our opinion and recommendations on how we personally would do stuff.

Operation validation based on schema access permissions

Is your feature request related to a problem? Please describe.

I need to limit schema access for different GraphQL API users.

Describe the solution you'd like

The Type.attribute syntax should be used for validating whether the viewer is allowed to execute an operation with a given selection set. In addition, it should be possible to use wildcards e.g. Query.* (allow selecting all fields on the Query type).

This is a perfect fit for useExtendedValidation, as it has access to the context form which we can read the user scope, which is a set of schema coordinates). Developers can customize from where they want to load the list of allowed fields for each API user.

WIP name: useOperationSelectionSetAuthorization

Describe alternatives you've considered

Do the validation within the resolvers. But it is fiddly and does not stop the execution in the first place.

Quick prototype:

import { envelop, Plugin, useEnvelop, useExtendContext } from "@envelop/core";
import {
  ExtendedValidationRule,
  useExtendedValidation,
} from "@envelop/extended-validation";
import { GraphQLError, TypeInfo, visitWithTypeInfo } from "graphql";
import { getWrappedType } from "./get-wrapped-type";

const getWildcardTypes = (scope: Set<string>): Set<string> => {
  const wildcardTypes = new Set<string>();
  for (const item of scope) {
    if (item.endsWith("*")) {
      const [typeName] = item.split(".");
      wildcardTypes.add(typeName);
    }
  }
  return wildcardTypes;
};

const operationScopeSymbol = Symbol();

const getSchemaCoordinatesFromContext = (input: unknown): Set<string> => {
  if (typeof input !== "object" || !input || !(operationScopeSymbol in input)) {
    throw new Error("OperationScopeRule was used without context.");
  }
  return input[operationScopeSymbol];
};

/**
 * Validate whether a user is allowed to execute a certain GraphQL operation.
 */
const OperationScopeRule: ExtendedValidationRule = (context, executionArgs) => {
  const typeInfo = new TypeInfo(context.getSchema());

  const allowedSchemaCoordinates = getSchemaCoordinatesFromContext(
    executionArgs.contextValue
  );
  const allowedWildcardTypes = getWildcardTypes(allowedSchemaCoordinates);
  return visitWithTypeInfo(typeInfo, {
    Field(node) {
      const parentType = typeInfo.getParentType();
      if (parentType) {
        const wrappedType = getWrappedType(parentType);
        const schemaCoordinate = `${wrappedType.name}.${node.name.value}`;

        if (
          !allowedWildcardTypes.has(wrappedType.name) &&
          !allowedSchemaCoordinates.has(schemaCoordinate) &&
          !allowedSchemaCoordinates.has("*") // everything!
        ) {
          context.reportError(
            new GraphQLError(
              `Insufficient permissions for selecting '${schemaCoordinate}'.`,
              [node]
            )
          );
        }
      }
    },
  });
};

type PromiseOrValue<TValue> = Promise<TValue> | TValue;

type OperationScopeOptions<TContext> = {
  getPermissionsFromContext: (context: TContext) => PromiseOrValue<Set<string>>;
};

export const useOperationSelectionSetAuthorization = <TContext>(
  opts: OperationScopeOptions<TContext>
): Plugin =>
  useEnvelop(
    envelop({
      plugins: [
        useExtendedValidation({
          rules: [OperationScopeRule],
        }),
        useExtendContext(async (context) => ({
          [operationScopeSymbol]: await opts.getPermissionsFromContext(
            context as TContext
          ),
        })),
      ],
    })
  );

extended-validation: Published bundle includes code bundled from graphql

See https://unpkg.com/browse/@envelop/[email protected]/index.cjs.js

This took me a while to realize and debug, it seems like bob/rollup embeds the code from graphql within the bundle. It could be because of the deep import here: https://github.com/dotansimha/envelop/blob/fd76b26671254784e81e647fc4889326647d786f/packages/plugins/extended-validation/src/rules/one-of.ts#L2

stevenbenisek/rollup-plugin-auto-external#16 - so this is an issue with bob.

This causes the classic realm issue

GraphQL Server listening on port 4000.
Error: Cannot use GraphQLScalarType "Boolean" from another module or realm.

Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other       
relied on modules, use "resolutions" to ensure only one version is installed.     

https://yarnpkg.com/en/docs/selective-version-resolutions

Duplicate "graphql" modules cannot be used at the same time since different       
versions may have different capabilities and behavior. The data from one
version used in the function from another could produce confusing and
spurious results.
    at instanceOf (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:1552:13)
    at isScalarType (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:1867:10)
    at isLeafType (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:1902:10)
    at valueFromAST (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:2906:7)
    at getArgumentValues (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:2995:24)
    at Object.Field (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:3026:32)
    at Object.enter (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\graphql\language\visitor.js:323:29)
    at Object.enter (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\graphql\utilities\TypeInfo.js:370:25)
    at Object.visit (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\graphql\language\visitor.js:243:26)
    at onExecute (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\@envelop\extended-validation\index.cjs.js:20:21)
(node:6200) UnhandledPromiseRejectionWarning: RangeError: The message must not be 
greater than 123 bytes
    at Sender.close (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\ws\lib\sender.js:120:15)
    at WebSocket.close (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\ws\lib\websocket.js:233:18)
    at WebSocket.<anonymous> (C:\Users\lauri\Projects\graphql-bleeding-edge-playground\node_modules\graphql-ws\lib\use\ws.js:72:28)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:6200) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). 
(rejection id: 2)
(node:6200) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

[Feature Request]: Pass data from onEnvelop to parser

The Problem
I'm working on a plugin for Apollo compatible automatic persisted queries (issue), but it's not possible in a clean way with the current architecture. Specifically, the protocol allows for the graphql query field to be undefined if the persisted query extension is present in the request.

https://github.com/apollographql/apollo-link-persisted-queries#apollo-engine

In envelop, onParse expects a non-empty params.source, so the only opportunity to intercept is in onEnvelop. For automatic persisted queries, we lookup queries by hash which we need to provide to the parser. However, there's no way of passing along information between init and parse.

Describe the solution you'd like
Add setQuery/setSource or setParsedDocument to OnEnvelopedHookEventPayload, and the ability to pass the change on to the parser.

Describe alternatives you've considered
An after hook for onEnvelop.

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.