diff --git a/docs/library.md b/docs/library.md index 9ac0e493d8..8f48dceaf8 100644 --- a/docs/library.md +++ b/docs/library.md @@ -62,6 +62,8 @@ Arguments include: - `jwtRole`: A comma separated list of strings that give a path in the jwt from which to extract the postgres role. If none is provided it will use the key `role` on the root of the jwt. - `jwtPgTypeIdentifier`: The Postgres type identifier for the compound type which will be signed as a JWT token if ever found as the return type of a procedure. Can be of the form: `my_schema.my_type`. You may use quotes as needed: `"my-special-schema".my_type`. - `watchPg`: When true, PostGraphQL will watch your database schemas and re-create the GraphQL API whenever your schema changes, notifying you as it does. This feature requires an event trigger to be added to the database by a superuser. When enabled PostGraphQL will try to add this trigger, if you did not connect as a superuser you will get a warning and the trigger won’t be added. + - `showErrorStack`: Enables adding a `stack` field to the error response. Can be either the boolean `true` (which results in a single stack string) or the string `json` (which causes the stack to become an array with elements for each line of the stack). + - `extendedErrors`: Extends the error response with additional details from the Postgres error. Can be any combination of `['hint', 'detail', 'errcode']`. Default is `[]`. - `disableQueryLog`: Turns off GraphQL query logging. By default PostGraphQL will log every GraphQL query it processes along with some other information. Set this to `true` to disable that feature. - `enableCors`: Enables some generous CORS settings for the GraphQL endpoint. There are some costs associated when enabling this, if at all possible try to put your API behind a reverse proxy. - `exportJsonSchemaPath`: Enables saving the detected schema, in JSON format, to the given location. The directories must exist already, if the file exists it will be overwritten. diff --git a/src/graphql/utils/extendedFormatError.ts b/src/graphql/utils/extendedFormatError.ts new file mode 100644 index 0000000000..6c3ca41ed2 --- /dev/null +++ b/src/graphql/utils/extendedFormatError.ts @@ -0,0 +1,43 @@ +import { GraphQLError } from 'graphql/error' + +/** + * Given a GraphQLError, format it according to the rules described by the + * Response Format, Errors section of the GraphQL Specification, plus a few + * additional fields relevant to postgres errors, including HINT, DETAIL, + * and ERRCODE. + */ +export function extendedFormatError(error: GraphQLError, fields: Array): GraphQLFormattedErrorExtended { + if (!error) { + throw new Error('Received null or undefined error.') + } + const originalError = error.originalError as GraphQLErrorExtended + fields = fields.map(field => field.toLowerCase()) + return { + message: error.message, + locations: error.locations, + path: error.path, + hint: originalError && fields.indexOf('hint') > -1 ? originalError.hint : undefined, + detail: originalError && fields.indexOf('detail') > -1 ? originalError.detail : undefined, + errcode: originalError && fields.indexOf('errcode') > -1 ? originalError.code : undefined, + } +} + +export type GraphQLFormattedErrorExtended = { + message: string, + locations: Array | void, + path: Array | void, + hint: string | void, + detail: string | void, + errcode: string | void, +} + +export type GraphQLErrorLocation = { + line: number, + column: number, +} + +export type GraphQLErrorExtended = GraphQLError & { + hint: string, + detail: string, + code: string, +} diff --git a/src/postgraphql/cli.ts b/src/postgraphql/cli.ts index 9fe5156799..70babc96d2 100755 --- a/src/postgraphql/cli.ts +++ b/src/postgraphql/cli.ts @@ -81,6 +81,7 @@ const { exportSchemaJson: exportJsonSchemaPath, exportSchemaGraphql: exportGqlSchemaPath, showErrorStack, + extendedErrors = [], bodySizeLimit, // tslint:disable-next-line no-any } = program as any @@ -122,6 +123,7 @@ const server = createServer(postgraphql(pgConfig, schemas, { pgDefaultRole, watchPg, showErrorStack, + extendedErrors, disableQueryLog: false, enableCors, exportJsonSchemaPath, diff --git a/src/postgraphql/http/createPostGraphQLHttpRequestHandler.d.ts b/src/postgraphql/http/createPostGraphQLHttpRequestHandler.d.ts index 4c40c66098..dc55ad5d6f 100644 --- a/src/postgraphql/http/createPostGraphQLHttpRequestHandler.d.ts +++ b/src/postgraphql/http/createPostGraphQLHttpRequestHandler.d.ts @@ -65,6 +65,10 @@ export default function createPostGraphQLHttpRequestHandler (config: { // in JSON. Helpful for debugging. showErrorStack?: boolean | 'json', + // Enables reporting of additional details from errors that are thrown in postgres. + // Additional details include the HINT, DETAIL, and ERRCODE. + extendedErrors?: Array, + // Disables the query log. Whenever a GraphQL query is about to be executed, it // will first be logged to the console. disableQueryLog?: boolean, diff --git a/src/postgraphql/http/createPostGraphQLHttpRequestHandler.js b/src/postgraphql/http/createPostGraphQLHttpRequestHandler.js index 7fcff24c6b..4fc14b3f43 100644 --- a/src/postgraphql/http/createPostGraphQLHttpRequestHandler.js +++ b/src/postgraphql/http/createPostGraphQLHttpRequestHandler.js @@ -10,6 +10,7 @@ import { formatError as defaultFormatError, print as printGraphql, } from 'graphql' +import { extendedFormatError } from '../../graphql/utils/extendedFormatError' import { $$pgClient } from '../../postgres/inventory/pgClientFromContext' import renderGraphiQL from './renderGraphiQL' import debugPgClient from './debugPgClient' @@ -78,8 +79,10 @@ export default function createPostGraphQLHttpRequestHandler (options) { // Formats an error using the default GraphQL `formatError` function, and // custom formatting using some other options. const formatError = error => { - // Get the default formatted error object. - const formattedError = defaultFormatError(error) + // Get the appropriate formatted error object, including any extended error + // fields if the user wants them. + const formattedError = options.extendedErrors && options.extendedErrors.length ? + extendedFormatError(error, options.extendedErrors) : defaultFormatError(error) // If the user wants to see the error’s stack, let’s add it to the // formatted error. diff --git a/src/postgraphql/postgraphql.ts b/src/postgraphql/postgraphql.ts index eb60529339..cc0870ce3f 100644 --- a/src/postgraphql/postgraphql.ts +++ b/src/postgraphql/postgraphql.ts @@ -21,6 +21,7 @@ type PostGraphQLOptions = { jwtPgTypeIdentifier?: string, watchPg?: boolean, showErrorStack?: boolean, + extendedErrors?: Array, disableQueryLog?: boolean, disableDefaultMutations?: boolean, enableCors?: boolean,