Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ npm-debug.log*
build/
TODO.md
package-lock.json
*.log
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,64 @@ for (const [name, createServerFromHandler] of Array.from(serverCreators)) {
})
})

test('will allow user to customize errors when handleErrors is set', async () => {
pgPool.connect.mockClear()
pgClient.query.mockClear()
pgClient.release.mockClear()
const server = createServer({
handleErrors: (errors) => {
return errors.map(error => {
error.message = 'my custom error message'
error.hint = 'my custom error hint'
error.detail = 'my custom error detail'
return error
})
},
})
await request(server)
.post('/graphql')
.send({ query: '{testError}' })
.expect(200)
.expect('Content-Type', /json/)
.expect({
data: { testError: null },
errors: [
{
message: 'my custom error message',
locations: [{ line: 1, column: 2 }],
path: ['testError'],
hint: 'my custom error hint',
detail: 'my custom error detail',
},
],
})
})

test('will allow user to send custom responses when handleErrors is set and sends a response', async () => {
pgPool.connect.mockClear()
pgClient.query.mockClear()
pgClient.release.mockClear()
const server = createServer({
handleErrors: (errors, req, res) => {
res.statusCode = 401
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return errors - tests can act as demo code.

return errors
},
})
await request(server)
.post('/graphql')
.send({ query: '{testError}' })
.expect(401, {
errors: [
{
message: 'test message',
locations: [{ line: 1, column: 2 }],
path: ['testError'],
},
],
data: { testError: null },
})
})

test('will serve a favicon when graphiql is enabled', async () => {
const server1 = createServer({ graphiql: true })
const server2 = createServer({ graphiql: true, route: '/graphql' })
Expand Down
11 changes: 8 additions & 3 deletions src/postgraphile/http/createPostGraphileHttpRequestHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,16 @@ export default function createPostGraphileHttpRequestHandler(options) {
} finally {
// Finally, we send the client the contents of `result`.
debugRequest('GraphQL query has been executed.')
// Format our errors so the client doesn’t get the full thing.
if (result && result.errors)
result.errors = result.errors.map(formatError)

res.setHeader('Content-Type', 'application/json; charset=utf-8')

// Format our errors so the client doesn’t get the full thing.
const handleErrors = options.handleErrors || (errors => errors.map(formatError))

if (result && result.errors) {
result.errors = handleErrors(result.errors, req, res)
}

res.end(JSON.stringify(result))

debugRequest('GraphQL query request finished.')
Expand Down
17 changes: 15 additions & 2 deletions src/postgraphile/postgraphile.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Pool, PoolConfig } from 'pg'
import { parse as parsePgConnectionString } from 'pg-connection-string'
import { GraphQLSchema } from 'graphql'
import { GraphQLSchema, GraphQLError } from 'graphql'
import { EventEmitter } from 'events'
import { createPostGraphileSchema, watchPostGraphileSchema } from 'postgraphile-core'
import createPostGraphileHttpRequestHandler, { HttpRequestHandler } from './http/createPostGraphileHttpRequestHandler'
import exportPostGraphileSchema from './schema/exportPostGraphileSchema'
import { IncomingMessage, ServerResponse } from 'http'
import jwt = require('jsonwebtoken')
import { GraphQLErrorExtended } from './extendedFormatError'

// Please note that the comments for this type are turned into documentation
// automatically. We try and specify the options in the same order as the CLI.
Expand Down Expand Up @@ -45,6 +46,14 @@ type PostGraphileOptions = {
// `json` (which causes the stack to become an array with elements for each
// line of the stack).
showErrorStack?: boolean,
// Enables ability to modify errors before sending them down to the client
// optionally can send down custom responses
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add /* @middlewareOnly */ as it only affects the HTTP middleware (not the createPostGraphileSchema function)

/* @middlewareOnly */
handleErrors?: ((
errors: Array<GraphQLError>,
req: IncomingMessage,
res: ServerResponse,
) => Array<GraphQLErrorExtended>);
// Extends the error response with additional details from the Postgres
// error. Can be any combination of `['hint', 'detail', 'errcode']`.
// Default is `[]`.
Expand Down Expand Up @@ -134,7 +143,7 @@ type PostGraphileOptions = {
// function which will return the same (or a Promise to the same) based on
// the incoming web request (e.g. to extract session data)
/* @middlewareOnly */
pgSettings?: { [key: string]: mixed } | ((req: IncomingMessage) => Promise<{[key: string]: mixed }>),
pgSettings?: { [key: string]: mixed } | ((req: IncomingMessage) => Promise<{ [key: string]: mixed }>),
// Some graphile-build plugins may need additional information available on
// the `context` argument to the resolver - you can use this function to
// provide such information based on the incoming request - you can even use
Expand All @@ -161,6 +170,10 @@ export function getPostgraphileSchemaBuilder(pgPool: Pool, schema: string | Arra
console.warn('WARNING: jwtSecret provided, however jwtPgTypeIdentifier (token identifier) not provided.')
}

if (options.handleErrors && (options.extendedErrors || options.showErrorStack)) {
throw new Error(`You cannot combine 'handleErrors' with the other error options`)
}

// Creates the Postgres schemas array.
const pgSchemas: Array<string> = Array.isArray(schema) ? schema : [schema]

Expand Down