@freshsqueezed/mammothgql
Advanced tools
Comparing version
@@ -0,1 +1,9 @@ | ||
## [1.0.5](https://github.com/freshsqueezed/mammothgql/compare/v1.0.4...v1.0.5) (2024-12-18) | ||
### Bug Fixes | ||
* **mammoth:** release updated graphiql ([2663dfe](https://github.com/freshsqueezed/mammothgql/commit/2663dfe42d4caa0c0e9fecb5f1a13a45460ae695)) | ||
* **request:** update request pipeline ([#3](https://github.com/freshsqueezed/mammothgql/issues/3)) ([e2fe768](https://github.com/freshsqueezed/mammothgql/commit/e2fe76820d6f4fc1ba90dc17c0f3326d3a98e15d)) | ||
## [1.0.4](https://github.com/freshsqueezed/mammothgql/compare/v1.0.3...v1.0.4) (2024-12-18) | ||
@@ -2,0 +10,0 @@ |
@@ -5,2 +5,7 @@ "use strict"; | ||
function graphiqlHtml(req, res) { | ||
const protocol = req.protocol; | ||
const host = req.get('host'); | ||
const path = req.path; | ||
const fullUrl = `${protocol}://${host}${path}`; | ||
const wsUrl = `${protocol === 'https' ? 'wss' : 'ws'}://${host}${path}`; | ||
res.send(` | ||
@@ -78,5 +83,5 @@ <!-- | ||
const fetcher = GraphiQL.createFetcher({ | ||
url: '${req.path}', | ||
url: '${fullUrl}', | ||
wsClient: graphqlWs.createClient({ | ||
url: '${req.path}', | ||
url: '${wsUrl}', | ||
}), | ||
@@ -83,0 +88,0 @@ }); |
import { NextFunction, Request, Response } from 'express'; | ||
import { GraphQLError } from 'graphql'; | ||
import type { GraphQLSchema, GraphQLFormattedError, ValidationRule } from 'graphql'; | ||
export interface MammothOptions<TContext> { | ||
import type { GraphQLSchema, ValidationRule } from 'graphql'; | ||
interface MammothOptions<TContext> { | ||
schema: GraphQLSchema; | ||
@@ -15,9 +14,3 @@ context: ({ req, res }: { | ||
export declare function mammothGraphql<TContext>(options: MammothOptions<TContext>): (req: Request, res: Response, next: NextFunction) => Promise<void>; | ||
export declare const errorMessages: (messages: string[], graphqlErrors?: readonly GraphQLError[] | readonly GraphQLFormattedError[]) => { | ||
errors: readonly GraphQLError[] | readonly GraphQLFormattedError[]; | ||
} | { | ||
errors: { | ||
message: string; | ||
}[]; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=mammoth.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.errorMessages = void 0; | ||
exports.mammothGraphql = mammothGraphql; | ||
const graphql_1 = require("graphql"); | ||
const utils_1 = require("./utils"); | ||
const html_1 = require("./html"); | ||
function mammothGraphql(options) { | ||
const schema = options.schema; | ||
const pretty = options.pretty ?? false; | ||
const validationRules = options.validationRules ?? []; | ||
const showGraphiQL = options.graphiql ?? false; | ||
const { schema, pretty = false, graphiql: showGraphiQL = false, validationRules = [], } = options; | ||
return async (req, res, next) => { | ||
@@ -17,19 +12,19 @@ if (req.method !== 'GET' && req.method !== 'POST') { | ||
.status(405) | ||
.json((0, exports.errorMessages)(['GraphQL only supports GET and POST requests.'])); | ||
.json(createErrorMessages(['GraphQL only supports GET and POST requests.'])); | ||
return; | ||
} | ||
const { query, variables, operationName } = req.body; | ||
if (query == null) { | ||
if (!query) { | ||
if (showGraphiQL && req.method === 'GET') { | ||
return (0, html_1.graphiqlHtml)(req, res); | ||
(0, html_1.graphiqlHtml)(req, res); | ||
return; | ||
} | ||
res.status(400).json((0, exports.errorMessages)(['Must provide query string.'])); | ||
res.status(400).json(createErrorMessages(['Must provide query string.'])); | ||
return; | ||
} | ||
const schemaValidationErrors = (0, graphql_1.validateSchema)(schema); | ||
if (schemaValidationErrors.length > 0) { | ||
// Return 500: Internal Server Error if invalid schema. | ||
const schemaErrors = (0, graphql_1.validateSchema)(schema); | ||
if (schemaErrors.length > 0) { | ||
res | ||
.status(500) | ||
.json((0, exports.errorMessages)(['GraphQL schema validation error.'], schemaValidationErrors)); | ||
.json(createErrorMessages(['GraphQL schema validation error.'], schemaErrors)); | ||
return; | ||
@@ -41,15 +36,12 @@ } | ||
} | ||
catch (syntaxError) { | ||
// Return 400: Bad Request if any syntax errors errors exist. | ||
if (syntaxError instanceof Error) { | ||
console.error(`${syntaxError.stack || syntaxError.message}`); | ||
const e = new graphql_1.GraphQLError(syntaxError.message, { | ||
originalError: syntaxError, | ||
}); | ||
res.status(400).json((0, exports.errorMessages)(['GraphQL syntax error.'], [e])); | ||
return; | ||
} | ||
throw syntaxError; | ||
catch (error) { | ||
const syntaxError = error instanceof Error ? error : new Error('Unknown parsing error'); | ||
const graphQLError = new graphql_1.GraphQLError(syntaxError.message, { | ||
originalError: syntaxError, | ||
}); | ||
res | ||
.status(400) | ||
.json(createErrorMessages(['GraphQL syntax error.'], [graphQLError])); | ||
return; | ||
} | ||
// Validate AST, reporting any errors. | ||
const validationErrors = (0, graphql_1.validate)(schema, documentAST, [ | ||
@@ -60,17 +52,14 @@ ...graphql_1.specifiedRules, | ||
if (validationErrors.length > 0) { | ||
// Return 400: Bad Request if any validation errors exist. | ||
res | ||
.status(400) | ||
.json((0, exports.errorMessages)(['GraphQL validation error.'], validationErrors)); | ||
.json(createErrorMessages(['GraphQL validation error.'], validationErrors)); | ||
return; | ||
} | ||
if (req.method === 'GET') { | ||
// Determine if this GET request will perform a non-query. | ||
const operationAST = (0, graphql_1.getOperationAST)(documentAST, operationName); | ||
if (operationAST && operationAST.operation !== 'query') { | ||
// Otherwise, report a 405: Method Not Allowed error. | ||
if (operationAST?.operation !== 'query') { | ||
res | ||
.status(405) | ||
.json((0, exports.errorMessages)([ | ||
`Can only perform a ${operationAST.operation} operation from a POST request.`, | ||
.json(createErrorMessages([ | ||
`Can only perform ${operationAST?.operation} operations via POST.`, | ||
])); | ||
@@ -80,13 +69,4 @@ return; | ||
} | ||
let result; | ||
try { | ||
// Parse and validate the query | ||
const source = new graphql_1.Source(query, 'Mammoth Request'); | ||
const document = (0, graphql_1.parse)(source); | ||
const validationErrors = (0, graphql_1.validate)(schema, document, graphql_1.specifiedRules); | ||
if (validationErrors.length > 0) { | ||
return (0, utils_1.handleValidationErrors)(validationErrors, res); | ||
} | ||
// Prepare context and execute the query | ||
result = await (0, graphql_1.execute)({ | ||
const result = await (0, graphql_1.execute)({ | ||
schema, | ||
@@ -96,54 +76,29 @@ document: documentAST, | ||
variableValues: variables, | ||
operationName: operationName, | ||
operationName, | ||
}); | ||
} | ||
catch (contextError) { | ||
if (contextError instanceof Error) { | ||
console.error(`${contextError.stack || contextError.message}`); | ||
const e = new graphql_1.GraphQLError(contextError.message, { | ||
originalError: contextError, | ||
nodes: documentAST, | ||
}); | ||
// Return 400: Bad Request if any execution context errors exist. | ||
res | ||
.status(400) | ||
.json((0, exports.errorMessages)(['GraphQL execution context error.'], [e])); | ||
if (!result.data && result.errors) { | ||
res.status(500).json(createErrorMessages(result.errors.map((e) => e.message), result.errors)); | ||
return; | ||
} | ||
throw contextError; | ||
const payload = pretty ? JSON.stringify(result, null, 2) : result; | ||
res.status(200).json(payload); | ||
} | ||
if (!result.data) { | ||
if (result.errors) { | ||
res | ||
.status(500) | ||
.json((0, exports.errorMessages)([result.errors.toString()], result.errors)); | ||
return; | ||
} | ||
catch (error) { | ||
const executionError = error instanceof Error ? error : new Error('Unknown execution error'); | ||
const graphQLError = new graphql_1.GraphQLError(executionError.message, { | ||
originalError: executionError, | ||
nodes: documentAST, | ||
}); | ||
res | ||
.status(400) | ||
.json(createErrorMessages(['GraphQL execution context error.'], [graphQLError])); | ||
} | ||
if (pretty) { | ||
const payload = JSON.stringify(result, null, pretty ? 2 : 0); | ||
res.status(200).send(payload); | ||
return; | ||
finally { | ||
next(); | ||
} | ||
else { | ||
res.json(result); | ||
return; | ||
} | ||
}; | ||
} | ||
const errorMessages = (messages, graphqlErrors) => { | ||
if (graphqlErrors) { | ||
return { | ||
errors: graphqlErrors, | ||
}; | ||
} | ||
return { | ||
errors: messages.map((message) => { | ||
return { | ||
message: message, | ||
}; | ||
}), | ||
}; | ||
}; | ||
exports.errorMessages = errorMessages; | ||
const createErrorMessages = (messages, graphqlErrors) => ({ | ||
errors: graphqlErrors ?? messages.map((message) => ({ message })), | ||
}); | ||
//# sourceMappingURL=mammoth.js.map |
@@ -6,3 +6,3 @@ { | ||
"types": "lib/index.d.ts", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"author": "Matt Gordon <matt@lemonade.tech>", | ||
@@ -9,0 +9,0 @@ "license": "MIT", |
import { Request, Response } from 'express'; | ||
export function graphiqlHtml(req: Request, res: Response) { | ||
const protocol = req.protocol; | ||
const host = req.get('host'); | ||
const path = req.path; | ||
const fullUrl = `${protocol}://${host}${path}`; | ||
const wsUrl = `${protocol === 'https' ? 'wss' : 'ws'}://${host}${path}`; | ||
res.send(` | ||
@@ -76,5 +82,5 @@ <!-- | ||
const fetcher = GraphiQL.createFetcher({ | ||
url: '${req.path}', | ||
url: '${fullUrl}', | ||
wsClient: graphqlWs.createClient({ | ||
url: '${req.path}', | ||
url: '${wsUrl}', | ||
}), | ||
@@ -81,0 +87,0 @@ }); |
@@ -20,5 +20,5 @@ import { NextFunction, Request, Response } from 'express'; | ||
import { handleValidationErrors } from './utils'; | ||
import { customLandingHtml, disabledLandingPage, graphiqlHtml } from './html'; | ||
import { graphiqlHtml } from './html'; | ||
export interface MammothOptions<TContext> { | ||
interface MammothOptions<TContext> { | ||
schema: GraphQLSchema; | ||
@@ -32,6 +32,8 @@ context: ({ req, res }: { req: Request; res: Response }) => TContext; | ||
export function mammothGraphql<TContext>(options: MammothOptions<TContext>) { | ||
const schema = options.schema; | ||
const pretty = options.pretty ?? false; | ||
const validationRules = options.validationRules ?? []; | ||
const showGraphiQL = options.graphiql ?? false; | ||
const { | ||
schema, | ||
pretty = false, | ||
graphiql: showGraphiQL = false, | ||
validationRules = [], | ||
} = options; | ||
@@ -46,3 +48,5 @@ return async ( | ||
.status(405) | ||
.json(errorMessages(['GraphQL only supports GET and POST requests.'])); | ||
.json( | ||
createErrorMessages(['GraphQL only supports GET and POST requests.']), | ||
); | ||
return; | ||
@@ -53,19 +57,19 @@ } | ||
if (query == null) { | ||
if (!query) { | ||
if (showGraphiQL && req.method === 'GET') { | ||
return graphiqlHtml(req, res); | ||
graphiqlHtml(req, res); | ||
return; | ||
} | ||
res.status(400).json(errorMessages(['Must provide query string.'])); | ||
res.status(400).json(createErrorMessages(['Must provide query string.'])); | ||
return; | ||
} | ||
const schemaValidationErrors = validateSchema(schema); | ||
if (schemaValidationErrors.length > 0) { | ||
// Return 500: Internal Server Error if invalid schema. | ||
const schemaErrors = validateSchema(schema); | ||
if (schemaErrors.length > 0) { | ||
res | ||
.status(500) | ||
.json( | ||
errorMessages( | ||
createErrorMessages( | ||
['GraphQL schema validation error.'], | ||
schemaValidationErrors, | ||
schemaErrors, | ||
), | ||
@@ -79,16 +83,16 @@ ); | ||
documentAST = parse(new Source(query, 'GraphQL request')); | ||
} catch (syntaxError: unknown) { | ||
// Return 400: Bad Request if any syntax errors errors exist. | ||
if (syntaxError instanceof Error) { | ||
console.error(`${syntaxError.stack || syntaxError.message}`); | ||
const e = new GraphQLError(syntaxError.message, { | ||
originalError: syntaxError, | ||
}); | ||
res.status(400).json(errorMessages(['GraphQL syntax error.'], [e])); | ||
return; | ||
} | ||
throw syntaxError; | ||
} catch (error: unknown) { | ||
const syntaxError = | ||
error instanceof Error ? error : new Error('Unknown parsing error'); | ||
const graphQLError = new GraphQLError(syntaxError.message, { | ||
originalError: syntaxError, | ||
}); | ||
res | ||
.status(400) | ||
.json(createErrorMessages(['GraphQL syntax error.'], [graphQLError])); | ||
return; | ||
} | ||
// Validate AST, reporting any errors. | ||
const validationErrors = validate(schema, documentAST, [ | ||
@@ -100,6 +104,7 @@ ...specifiedRules, | ||
if (validationErrors.length > 0) { | ||
// Return 400: Bad Request if any validation errors exist. | ||
res | ||
.status(400) | ||
.json(errorMessages(['GraphQL validation error.'], validationErrors)); | ||
.json( | ||
createErrorMessages(['GraphQL validation error.'], validationErrors), | ||
); | ||
return; | ||
@@ -109,11 +114,9 @@ } | ||
if (req.method === 'GET') { | ||
// Determine if this GET request will perform a non-query. | ||
const operationAST = getOperationAST(documentAST, operationName); | ||
if (operationAST && operationAST.operation !== 'query') { | ||
// Otherwise, report a 405: Method Not Allowed error. | ||
if (operationAST?.operation !== 'query') { | ||
res | ||
.status(405) | ||
.json( | ||
errorMessages([ | ||
`Can only perform a ${operationAST.operation} operation from a POST request.`, | ||
createErrorMessages([ | ||
`Can only perform ${operationAST?.operation} operations via POST.`, | ||
]), | ||
@@ -125,55 +128,40 @@ ); | ||
let result: FormattedExecutionResult; | ||
try { | ||
// Parse and validate the query | ||
const source = new Source(query, 'Mammoth Request'); | ||
const document = parse(source); | ||
const validationErrors = validate(schema, document, specifiedRules); | ||
if (validationErrors.length > 0) { | ||
return handleValidationErrors(validationErrors, res); | ||
} | ||
// Prepare context and execute the query | ||
result = await execute({ | ||
const result = await execute({ | ||
schema, | ||
document: documentAST, | ||
contextValue: options.context({ req, res }) as TContext, | ||
contextValue: options.context({ req, res }), | ||
variableValues: variables, | ||
operationName: operationName, | ||
operationName, | ||
}); | ||
} catch (contextError: unknown) { | ||
if (contextError instanceof Error) { | ||
console.error(`${contextError.stack || contextError.message}`); | ||
const e = new GraphQLError(contextError.message, { | ||
originalError: contextError, | ||
nodes: documentAST, | ||
}); | ||
// Return 400: Bad Request if any execution context errors exist. | ||
res | ||
.status(400) | ||
.json(errorMessages(['GraphQL execution context error.'], [e])); | ||
return; | ||
} | ||
throw contextError; | ||
} | ||
if (!result.data) { | ||
if (result.errors) { | ||
res | ||
.status(500) | ||
.json(errorMessages([result.errors.toString()], result.errors)); | ||
if (!result.data && result.errors) { | ||
res.status(500).json( | ||
createErrorMessages( | ||
result.errors.map((e) => e.message), | ||
result.errors, | ||
), | ||
); | ||
return; | ||
} | ||
} | ||
if (pretty) { | ||
const payload = JSON.stringify(result, null, pretty ? 2 : 0); | ||
res.status(200).send(payload); | ||
return; | ||
} else { | ||
res.json(result); | ||
return; | ||
const payload = pretty ? JSON.stringify(result, null, 2) : result; | ||
res.status(200).json(payload); | ||
} catch (error: unknown) { | ||
const executionError = | ||
error instanceof Error ? error : new Error('Unknown execution error'); | ||
const graphQLError = new GraphQLError(executionError.message, { | ||
originalError: executionError, | ||
nodes: documentAST, | ||
}); | ||
res | ||
.status(400) | ||
.json( | ||
createErrorMessages( | ||
['GraphQL execution context error.'], | ||
[graphQLError], | ||
), | ||
); | ||
} finally { | ||
next(); | ||
} | ||
@@ -183,19 +171,7 @@ }; | ||
export const errorMessages = ( | ||
const createErrorMessages = ( | ||
messages: string[], | ||
graphqlErrors?: readonly GraphQLError[] | readonly GraphQLFormattedError[], | ||
) => { | ||
if (graphqlErrors) { | ||
return { | ||
errors: graphqlErrors, | ||
}; | ||
} | ||
return { | ||
errors: messages.map((message) => { | ||
return { | ||
message: message, | ||
}; | ||
}), | ||
}; | ||
}; | ||
) => ({ | ||
errors: graphqlErrors ?? messages.map((message) => ({ message })), | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
59595
-4.8%1200
-4.99%