Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop GraphQLYogaError #1473

Merged
merged 6 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/slow-wasps-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'graphql-yoga': major
---

**BREAKING**: Remove `GraphQLYogaError` in favor of `GraphQLError`
[Check the documentation to see how to use `GraphQLError`](https://www.graphql-yoga.com/docs/guides/error-masking)
23 changes: 11 additions & 12 deletions packages/graphql-yoga/__tests__/node.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getIntrospectionQuery } from 'graphql'
import { getIntrospectionQuery, GraphQLError } from 'graphql'
import { useDisableIntrospection } from '@envelop/disable-introspection'
import EventSource from 'eventsource'
import request from 'supertest'
Expand All @@ -7,12 +7,7 @@ import * as fs from 'fs'
import * as path from 'path'
import * as os from 'os'
import * as crypto from 'crypto'
import {
CORSOptions,
createYoga,
GraphQLYogaError,
Plugin,
} from '../src/index.js'
import { CORSOptions, createYoga, Plugin } from '../src/index.js'
import { getCounterValue, schema } from '../test-utils/schema.js'
import { createTestSchema } from './__fixtures__/schema.js'
import { renderGraphiQL } from '@graphql-yoga/render-graphiql'
Expand Down Expand Up @@ -83,7 +78,7 @@ describe('Masked Error Option', () => {
const resolvers = {
Query: {
hello: () => {
throw new GraphQLYogaError('This error never gets masked.')
throw new GraphQLError('This error never gets masked.')
},
hi: () => {
throw new Error('This error will get mask if you enable maskedError.')
Expand Down Expand Up @@ -255,11 +250,11 @@ describe('Context error', () => {
`)
})

it('GraphQLYogaError thrown within context factory with error masking is not masked', async () => {
it('GraphQLError thrown within context factory with error masking is not masked', async () => {
const yoga = createYoga({
logging: false,
context: () => {
throw new GraphQLYogaError('I like turtles')
throw new GraphQLError('I like turtles')
},
})

Expand All @@ -279,11 +274,15 @@ describe('Context error', () => {
`)
})

it('GraphQLYogaError thrown within context factory has error extensions exposed on the response', async () => {
it('GraphQLError thrown within context factory has error extensions exposed on the response', async () => {
const yoga = createYoga({
logging: false,
context: () => {
throw new GraphQLYogaError('I like turtles', { foo: 1 })
throw new GraphQLError('I like turtles', {
extensions: {
foo: 1,
},
})
},
})

Expand Down
2 changes: 1 addition & 1 deletion packages/graphql-yoga/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"dependencies": {
"@graphql-tools/code-file-loader": "^7.3.0",
"@graphql-tools/mock": "^8.7.0",
"@envelop/core": "^2.4.0",
"@envelop/core": "^2.4.1",
"@envelop/parser-cache": "^4.4.0",
"@envelop/validation-cache": "^4.4.0",
"@graphql-typed-document-node/core": "^3.1.1",
Expand Down
3 changes: 0 additions & 3 deletions packages/graphql-yoga/src/GraphQLYogaError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EnvelopError } from '@envelop/core'
import { createGraphQLError } from '@graphql-tools/utils'
import { GraphQLError } from 'graphql'

Expand All @@ -12,8 +11,6 @@ declare module 'graphql' {
}
}

export { EnvelopError as GraphQLYogaError }

function isAggregateError(obj: any): obj is AggregateError {
return obj != null && typeof obj === 'object' && 'errors' in obj
}
Expand Down
1 change: 0 additions & 1 deletion packages/graphql-yoga/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export {
shouldRenderGraphiQL,
renderGraphiQL,
} from './plugins/useGraphiQL.js'
export { GraphQLYogaError } from './GraphQLYogaError.js'
export { Plugin } from './plugins/types.js'
184 changes: 97 additions & 87 deletions packages/graphql-yoga/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import { useCheckGraphQLQueryParam } from './plugins/requestValidation/useCheckG
import { useHTTPValidationError } from './plugins/requestValidation/useHTTPValidationError.js'
import { usePreventMutationViaGET } from './plugins/requestValidation/usePreventMutationViaGET.js'
import { useUnhandledRoute } from './plugins/useUnhandledRoute.js'
import { formatError } from './utils/formatError.js'

interface OptionsWithPlugins<TContext> {
/**
Expand All @@ -99,89 +100,89 @@ export type YogaServerOptions<
TServerContext extends Record<string, any>,
TUserContext extends Record<string, any>,
TRootValue,
> = {
/**
* Enable/disable logging or provide a custom logger.
* @default true
*/
logging?: boolean | YogaLogger
/**
* Prevent leaking unexpected errors to the client. We highly recommend enabling this in production.
* If you throw `GraphQLYogaError`/`EnvelopError` within your GraphQL resolvers then that error will be sent back to the client.
*
* You can lean more about this here:
* @see https://graphql-yoga.vercel.app/docs/features/error-masking
*
* Default: `true`
*/
maskedErrors?: boolean | UseMaskedErrorsOpts
/**
* Context
*/
context?:
> = {
/**
* Enable/disable logging or provide a custom logger.
* @default true
*/
logging?: boolean | YogaLogger
/**
* Prevent leaking unexpected errors to the client. We highly recommend enabling this in production.
* If you throw `EnvelopError`/`GraphQLError` within your GraphQL resolvers then that error will be sent back to the client.
*
* You can lean more about this here:
* @see https://graphql-yoga.vercel.app/docs/features/error-masking
*
* Default: `true`
*/
maskedErrors?: boolean | UseMaskedErrorsOpts
/**
* Context
*/
context?:
| ((
initialContext: YogaInitialContext & TServerContext,
) => Promise<TUserContext> | TUserContext)
initialContext: YogaInitialContext & TServerContext,
) => Promise<TUserContext> | TUserContext)
| Promise<TUserContext>
| TUserContext

cors?: CORSPluginOptions<TServerContext>
cors?: CORSPluginOptions<TServerContext>

/**
* GraphQL endpoint (defaults to '/graphql')
* So you need to define it explicitly if GraphQL API lives in a different path other than `/graphql`
*/
graphqlEndpoint?: string
/**
* GraphQL endpoint (defaults to '/graphql')
* So you need to define it explicitly if GraphQL API lives in a different path other than `/graphql`
*/
graphqlEndpoint?: string

/**
* Readiness check endpoint (defaults to '/readiness')
*/
readinessCheckEndpoint?: string
/**
* Readiness check endpoint (defaults to '/readiness')
*/
readinessCheckEndpoint?: string

/**
* Readiness check endpoint (defaults to '/readiness')
*/
healthCheckEndpoint?: string
/**
* Readiness check endpoint (defaults to '/readiness')
*/
healthCheckEndpoint?: string

/**
* Whether the landing page should be shown.
*/
landingPage?: boolean
/**
* Whether the landing page should be shown.
*/
landingPage?: boolean

/**
* GraphiQL options
*
* Default: `true`
*/
graphiql?: GraphiQLOptionsOrFactory<TServerContext>
/**
* GraphiQL options
*
* Default: `true`
*/
graphiql?: GraphiQLOptionsOrFactory<TServerContext>

renderGraphiQL?: (options?: GraphiQLOptions) => PromiseOrValue<BodyInit>
renderGraphiQL?: (options?: GraphiQLOptions) => PromiseOrValue<BodyInit>

schema?:
schema?:
| GraphQLSchema
| {
typeDefs: TypeSource
resolvers?:
| IResolvers<
TRootValue,
TUserContext & TServerContext & YogaInitialContext
>
| Array<
IResolvers<
TRootValue,
TUserContext & TServerContext & YogaInitialContext
>
>
}
typeDefs: TypeSource
resolvers?:
| IResolvers<
TRootValue,
TUserContext & TServerContext & YogaInitialContext
>
| Array<
IResolvers<
TRootValue,
TUserContext & TServerContext & YogaInitialContext
>
>
}

parserCache?: boolean | ParserCacheOptions
validationCache?: boolean | ValidationCache
fetchAPI?: FetchAPI
multipart?: boolean
id?: string
} & Partial<
OptionsWithPlugins<TUserContext & TServerContext & YogaInitialContext>
>
parserCache?: boolean | ParserCacheOptions
validationCache?: boolean | ValidationCache
fetchAPI?: FetchAPI
multipart?: boolean
id?: string
} & Partial<
OptionsWithPlugins<TUserContext & TServerContext & YogaInitialContext>
>

export function getDefaultSchema() {
return makeExecutableSchema({
Expand Down Expand Up @@ -226,7 +227,7 @@ export class YogaServer<
TServerContext extends Record<string, any>,
TUserContext extends Record<string, any>,
TRootValue,
> {
> {
/**
* Instance of envelop
*/
Expand Down Expand Up @@ -258,9 +259,9 @@ export class YogaServer<
? isSchema(options.schema)
? options.schema
: makeExecutableSchema({
typeDefs: options.schema.typeDefs,
resolvers: options.schema.resolvers,
})
typeDefs: options.schema.typeDefs,
resolvers: options.schema.resolvers,
})
: getDefaultSchema()

const logger = options?.logging != null ? options.logging : true
Expand All @@ -269,14 +270,28 @@ export class YogaServer<
? logger === true
? defaultYogaLogger
: {
debug: () => {},
error: () => {},
warn: () => {},
info: () => {},
}
debug: () => { },
error: () => { },
warn: () => { },
info: () => { },
}
: logger

const maskedErrors = options?.maskedErrors ?? true
let maskedErrorsOpts: UseMaskedErrorsOpts | null =
options?.maskedErrors === false
? null
: {
formatError,
}
if (
options?.maskedErrors != null &&
typeof options?.maskedErrors === 'object'
) {
maskedErrorsOpts = {
formatError,
...options.maskedErrors,
}
}

this.graphqlEndpoint = options?.graphqlEndpoint || '/graphql'

Expand Down Expand Up @@ -407,12 +422,7 @@ export class YogaServer<
// We make sure that the user doesn't send a mutation with GET
usePreventMutationViaGET(),

enableIf(
!!maskedErrors,
useMaskedErrors(
typeof maskedErrors === 'object' ? maskedErrors : undefined,
),
),
enableIf(maskedErrorsOpts != null, useMaskedErrors(maskedErrorsOpts!)),
useUnhandledRoute({
graphqlEndpoint: this.graphqlEndpoint,
// TODO: make this a config option
Expand Down Expand Up @@ -661,8 +671,8 @@ export function createYoga<
TServerContext extends Record<string, any> = {},
TUserContext extends Record<string, any> = {},
TRootValue = {},
>(
options?: YogaServerOptions<TServerContext, TUserContext, TRootValue>,
>(
options?: YogaServerOptions<TServerContext, TUserContext, TRootValue>,
): YogaServerInstance<TServerContext, TUserContext, TRootValue> {
const server = new YogaServer<TServerContext, TUserContext, TRootValue>(
options,
Expand Down
2 changes: 0 additions & 2 deletions packages/graphql-yoga/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ export type GraphQLServerInject<
? { serverContext?: TServerContext }
: { serverContext: TServerContext })

export { EnvelopError as GraphQLYogaError } from '@envelop/core'

declare global {
interface ReadableStream<R = any> {
[Symbol.asyncIterator]: () => AsyncIterator<R>
Expand Down
32 changes: 32 additions & 0 deletions packages/graphql-yoga/src/utils/formatError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FormatErrorHandler } from '@envelop/core'
import { createGraphQLError } from '@graphql-tools/utils'
import { GraphQLError } from 'graphql'

export const formatError: FormatErrorHandler = (err, message, isDev) => {
if (err instanceof GraphQLError) {
if (err.originalError) {
if (err.originalError.name === 'GraphQLError') {
return err
}
// Original error should be removed
const extensions = {
...err.extensions,
}
if (isDev) {
extensions.originalError = {
message: err.originalError.message,
stack: err.originalError.stack,
}
}
return createGraphQLError(message, {
nodes: err.nodes,
source: err.source,
positions: err.positions,
path: err.path,
extensions,
})
}
return err
}
return new GraphQLError(message)
}
Loading