apollo-server-express
Advanced tools
Comparing version
@@ -0,2 +1,3 @@ | ||
export { GraphQLOptions } from 'apollo-server-core'; | ||
export { ExpressGraphQLOptionsFunction, ExpressHandler, ExpressGraphiQLOptionsFunction, graphqlExpress, graphiqlExpress } from './expressApollo'; | ||
export { graphqlConnect, graphiqlConnect } from './connectApollo'; |
{ | ||
"name": "apollo-server-express", | ||
"version": "1.3.2", | ||
"version": "1.3.3", | ||
"description": "Production-ready Node.js GraphQL server for Express and Connect", | ||
@@ -29,4 +29,4 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"apollo-server-core": "^1.3.2", | ||
"apollo-server-module-graphiql": "^1.3.0" | ||
"apollo-server-core": "^1.3.3", | ||
"apollo-server-module-graphiql": "^1.3.3" | ||
}, | ||
@@ -36,10 +36,10 @@ "devDependencies": { | ||
"@types/connect": "3.4.31", | ||
"@types/express": "4.0.39", | ||
"@types/graphql": "0.11.7", | ||
"@types/express": "4.11.1", | ||
"@types/graphql": "0.12.6", | ||
"@types/multer": "1.3.6", | ||
"apollo-server-integration-testsuite": "^1.3.2", | ||
"apollo-server-integration-testsuite": "^1.3.3", | ||
"body-parser": "1.18.2", | ||
"connect": "3.6.5", | ||
"connect": "3.6.6", | ||
"connect-query": "1.0.0", | ||
"express": "4.16.2", | ||
"express": "4.16.3", | ||
"multer": "1.3.0" | ||
@@ -46,0 +46,0 @@ }, |
@@ -59,3 +59,2 @@ --- | ||
Anyone is welcome to contribute to GraphQL Server, just read [CONTRIBUTING.md](https://github.com/apollographql/apollo-server/blob/master/CONTRIBUTING.md), take a look at the [roadmap](https://github.com/apollographql/apollo-server/blob/master/ROADMAP.md) and make your first PR! |
@@ -39,3 +39,3 @@ // tslint:disable | ||
GraphQLError, | ||
BREAK | ||
BREAK, | ||
} from 'graphql'; | ||
@@ -50,10 +50,12 @@ | ||
who: { | ||
type: GraphQLString | ||
} | ||
type: GraphQLString, | ||
}, | ||
}, | ||
resolve: (root, args) => 'Hello ' + (args['who'] || 'World') | ||
resolve: (root, args) => 'Hello ' + (args['who'] || 'World'), | ||
}, | ||
thrower: { | ||
type: new GraphQLNonNull(GraphQLString), | ||
resolve: () => { throw new Error('Throws!'); } | ||
resolve: () => { | ||
throw new Error('Throws!'); | ||
}, | ||
}, | ||
@@ -74,4 +76,4 @@ custom: { | ||
}), | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
@@ -81,4 +83,4 @@ context: { | ||
resolve: (obj, args, context) => context, | ||
} | ||
} | ||
}, | ||
}, | ||
}); | ||
@@ -93,6 +95,6 @@ | ||
type: QueryRootType, | ||
resolve: () => ({}) | ||
} | ||
} | ||
}) | ||
resolve: () => ({}), | ||
}, | ||
}, | ||
}), | ||
}); | ||
@@ -102,3 +104,3 @@ | ||
return p.then( | ||
(res) => { | ||
res => { | ||
// workaround for unknown issues with testing against npm package of express-graphql. | ||
@@ -116,3 +118,3 @@ // the same code works when testing against the source, I'm not sure why. | ||
return error; | ||
} | ||
}, | ||
); | ||
@@ -123,3 +125,3 @@ } | ||
return new Promise((resolve, reject) => { | ||
fn((error, result) => error ? reject(error) : resolve(result)); | ||
fn((error, result) => (error ? reject(error) : resolve(result))); | ||
}); | ||
@@ -129,3 +131,2 @@ } | ||
describe('test harness', () => { | ||
it('expects to catch errors', async () => { | ||
@@ -148,3 +149,5 @@ let caught; | ||
} | ||
expect(caught && caught.message).to.equal('Expected error to be instanceof Error.'); | ||
expect(caught && caught.message).to.equal( | ||
'Expected error to be instanceof Error.', | ||
); | ||
}); | ||
@@ -168,3 +171,2 @@ | ||
}); | ||
}); | ||
@@ -176,3 +178,2 @@ | ||
describe('POST functionality', () => { | ||
it('allows gzipped POST bodies', async () => { | ||
@@ -182,5 +183,8 @@ const app = express(); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress(() => ({ | ||
schema: TestSchema | ||
}))); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress(() => ({ | ||
schema: TestSchema, | ||
})), | ||
); | ||
@@ -190,3 +194,5 @@ const data = { query: '{ test(who: "World") }' }; | ||
// TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? | ||
const gzippedJson = await promiseTo(cb => zlib.gzip(json as any as Buffer, cb)); | ||
const gzippedJson = await promiseTo(cb => | ||
zlib.gzip((json as any) as Buffer, cb), | ||
); | ||
@@ -202,4 +208,4 @@ const req = request(app) | ||
data: { | ||
test: 'Hello World' | ||
} | ||
test: 'Hello World', | ||
}, | ||
}); | ||
@@ -212,5 +218,8 @@ }); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress(() => ({ | ||
schema: TestSchema | ||
}))); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress(() => ({ | ||
schema: TestSchema, | ||
})), | ||
); | ||
@@ -220,3 +229,5 @@ const data = { query: '{ test(who: "World") }' }; | ||
// TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? | ||
const deflatedJson = await promiseTo(cb => zlib.deflate(json as any as Buffer, cb)); | ||
const deflatedJson = await promiseTo(cb => | ||
zlib.deflate((json as any) as Buffer, cb), | ||
); | ||
@@ -232,4 +243,4 @@ const req = request(app) | ||
data: { | ||
test: 'Hello World' | ||
} | ||
test: 'Hello World', | ||
}, | ||
}); | ||
@@ -248,4 +259,4 @@ }); | ||
originalname: { type: GraphQLString }, | ||
mimetype: { type: GraphQLString } | ||
} | ||
mimetype: { type: GraphQLString }, | ||
}, | ||
}); | ||
@@ -257,4 +268,4 @@ | ||
fields: { | ||
test: { type: GraphQLString } | ||
} | ||
test: { type: GraphQLString }, | ||
}, | ||
}), | ||
@@ -271,6 +282,6 @@ mutation: new GraphQLObjectType({ | ||
return rootValue.request.file; | ||
} | ||
} | ||
} | ||
}) | ||
}, | ||
}, | ||
}, | ||
}), | ||
}); | ||
@@ -286,17 +297,23 @@ | ||
// be accessible from within Schema resolve functions. | ||
app.use('/graphql', graphqlExpress(req => { | ||
return { | ||
schema: TestMutationSchema, | ||
rootValue: { request: req } | ||
}; | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress(req => { | ||
return { | ||
schema: TestMutationSchema, | ||
rootValue: { request: req }, | ||
}; | ||
}), | ||
); | ||
const req = request(app) | ||
.post('/graphql') | ||
.field('query', `mutation TestMutation { | ||
.field( | ||
'query', | ||
`mutation TestMutation { | ||
uploadFile { originalname, mimetype } | ||
}`) | ||
}`, | ||
) | ||
.attach('file', __filename); | ||
return req.then((response) => { | ||
return req.then(response => { | ||
expect(JSON.parse(response.text)).to.deep.equal({ | ||
@@ -306,5 +323,5 @@ data: { | ||
originalname: 'apolloServerHttp.test.js', | ||
mimetype: 'application/javascript' | ||
} | ||
} | ||
mimetype: 'application/javascript', | ||
}, | ||
}, | ||
}); | ||
@@ -320,5 +337,8 @@ }); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
schema: TestSchema | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
}), | ||
); | ||
@@ -335,7 +355,9 @@ const response = await request(app) | ||
data: null, | ||
errors: [ { | ||
message: 'Throws!', | ||
locations: [ { line: 1, column: 2 } ], | ||
path:["thrower"] | ||
} ] | ||
errors: [ | ||
{ | ||
message: 'Throws!', | ||
locations: [{ line: 1, column: 2 }], | ||
path: ['thrower'], | ||
}, | ||
], | ||
}); | ||
@@ -348,5 +370,8 @@ }); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
schema: TestSchema | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
}), | ||
); | ||
@@ -361,6 +386,8 @@ const response = await request(app) | ||
expect(JSON.parse(response.text)).to.deep.equal({ | ||
errors: [ { | ||
message: 'Cannot query field \"notExists\" on type \"QueryRoot\".', | ||
locations: [ { line: 1, column: 2 } ], | ||
} ] | ||
errors: [ | ||
{ | ||
message: 'Cannot query field "notExists" on type "QueryRoot".', | ||
locations: [{ line: 1, column: 2 }], | ||
}, | ||
], | ||
}); | ||
@@ -372,15 +399,22 @@ }); | ||
app.use('/graphql', require('connect-query')(), graphqlExpress({ | ||
schema: TestSchema | ||
})); | ||
app.use( | ||
'/graphql', | ||
require('connect-query')(), | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
}), | ||
); | ||
const response = await request(app) | ||
.get('/graphql').query({ query: '{notExists}' }); | ||
.get('/graphql') | ||
.query({ query: '{notExists}' }); | ||
expect(response.status).to.equal(400); | ||
expect(JSON.parse(response.text)).to.deep.equal({ | ||
errors: [ { | ||
message: 'Cannot query field \"notExists\" on type \"QueryRoot\".', | ||
locations: [ { line: 1, column: 2 } ], | ||
} ] | ||
errors: [ | ||
{ | ||
message: 'Cannot query field "notExists" on type "QueryRoot".', | ||
locations: [{ line: 1, column: 2 }], | ||
}, | ||
], | ||
}); | ||
@@ -393,5 +427,8 @@ }); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
schema: TestSchema | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
}), | ||
); | ||
@@ -411,8 +448,11 @@ const response = await request(app) | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
schema: TestSchema, | ||
formatError(error) { | ||
return { message: 'Custom error format: ' + error.message }; | ||
} | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
formatError(error) { | ||
return { message: 'Custom error format: ' + error.message }; | ||
}, | ||
}), | ||
); | ||
@@ -428,5 +468,7 @@ const response = await request(app) | ||
data: null, | ||
errors: [ { | ||
message: 'Custom error format: Throws!', | ||
} ] | ||
errors: [ | ||
{ | ||
message: 'Custom error format: Throws!', | ||
}, | ||
], | ||
}); | ||
@@ -439,12 +481,15 @@ }); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
schema: TestSchema, | ||
formatError(error) { | ||
return { | ||
message: error.message, | ||
locations: error.locations, | ||
stack: 'Stack trace' | ||
}; | ||
} | ||
})); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
formatError(error) { | ||
return { | ||
message: error.message, | ||
locations: error.locations, | ||
stack: 'Stack trace', | ||
}; | ||
}, | ||
}), | ||
); | ||
@@ -460,7 +505,9 @@ const response = await request(app) | ||
data: null, | ||
errors: [ { | ||
message: 'Throws!', | ||
locations: [ { line: 1, column: 2 } ], | ||
stack: 'Stack trace', | ||
} ] | ||
errors: [ | ||
{ | ||
message: 'Throws!', | ||
locations: [{ line: 1, column: 2 }], | ||
stack: 'Stack trace', | ||
}, | ||
], | ||
}); | ||
@@ -476,7 +523,10 @@ }); | ||
const response = await request(app) | ||
.put('/graphql').query({ query: '{test}' }); | ||
.put('/graphql') | ||
.query({ query: '{test}' }); | ||
expect(response.status).to.equal(405); | ||
expect(response.headers.allow).to.equal('GET, POST'); | ||
return expect(response.text).to.contain('Apollo Server supports only GET/POST requests.'); | ||
return expect(response.text).to.contain( | ||
'Apollo Server supports only GET/POST requests.', | ||
); | ||
}); | ||
@@ -486,39 +536,41 @@ }); | ||
describe('Custom validation rules', () => { | ||
const AlwaysInvalidRule = function (context) { | ||
return { | ||
enter() { | ||
context.reportError(new GraphQLError( | ||
'AlwaysInvalidRule was really invalid!' | ||
)); | ||
return BREAK; | ||
} | ||
}; | ||
const AlwaysInvalidRule = function(context) { | ||
return { | ||
enter() { | ||
context.reportError( | ||
new GraphQLError('AlwaysInvalidRule was really invalid!'), | ||
); | ||
return BREAK; | ||
}, | ||
}; | ||
}; | ||
it('Do not execute a query if it do not pass the custom validation.', async() => { | ||
const app = express(); | ||
it('Do not execute a query if it do not pass the custom validation.', async () => { | ||
const app = express(); | ||
app.use('/graphql', bodyParser.json()); | ||
app.use('/graphql', graphqlExpress({ | ||
app.use('/graphql', bodyParser.json()); | ||
app.use( | ||
'/graphql', | ||
graphqlExpress({ | ||
schema: TestSchema, | ||
validationRules: [ AlwaysInvalidRule ], | ||
})); | ||
validationRules: [AlwaysInvalidRule], | ||
}), | ||
); | ||
const response = await request(app) | ||
.post('/graphql') | ||
.send({ | ||
query: '{thrower}', | ||
}) | ||
expect(response.status).to.equal(400); | ||
expect(JSON.parse(response.text)).to.deep.equal({ | ||
errors: [ | ||
{ | ||
message: 'AlwaysInvalidRule was really invalid!' | ||
}, | ||
] | ||
const response = await request(app) | ||
.post('/graphql') | ||
.send({ | ||
query: '{thrower}', | ||
}); | ||
expect(response.status).to.equal(400); | ||
expect(JSON.parse(response.text)).to.deep.equal({ | ||
errors: [ | ||
{ | ||
message: 'AlwaysInvalidRule was really invalid!', | ||
}, | ||
], | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -6,3 +6,6 @@ import * as connect from 'connect'; | ||
import testSuite, { schema as Schema, CreateAppOptions } from 'apollo-server-integration-testsuite'; | ||
import testSuite, { | ||
schema as Schema, | ||
CreateAppOptions, | ||
} from 'apollo-server-integration-testsuite'; | ||
@@ -16,7 +19,7 @@ function createConnectApp(options: CreateAppOptions = {}) { | ||
} | ||
if (options.graphiqlOptions ) { | ||
app.use('/graphiql', graphiqlConnect( options.graphiqlOptions )); | ||
if (options.graphiqlOptions) { | ||
app.use('/graphiql', graphiqlConnect(options.graphiqlOptions)); | ||
} | ||
app.use('/graphql', require('connect-query')()); | ||
app.use('/graphql', graphqlConnect( options.graphqlOptions )); | ||
app.use('/graphql', graphqlConnect(options.graphqlOptions)); | ||
return app; | ||
@@ -23,0 +26,0 @@ } |
import * as express from 'express'; | ||
import * as bodyParser from 'body-parser'; | ||
import { graphqlExpress, graphiqlExpress } from './expressApollo'; | ||
import testSuite, { schema as Schema, CreateAppOptions } from 'apollo-server-integration-testsuite'; | ||
import testSuite, { | ||
schema as Schema, | ||
CreateAppOptions, | ||
} from 'apollo-server-integration-testsuite'; | ||
import { expect } from 'chai'; | ||
@@ -16,7 +19,7 @@ import { GraphQLOptions } from 'apollo-server-core'; | ||
} | ||
if (options.graphiqlOptions ) { | ||
app.use('/graphiql', graphiqlExpress( options.graphiqlOptions )); | ||
if (options.graphiqlOptions) { | ||
app.use('/graphiql', graphiqlExpress(options.graphiqlOptions)); | ||
} | ||
app.use('/graphql', require('connect-query')()); | ||
app.use('/graphql', graphqlExpress( options.graphqlOptions )); | ||
app.use('/graphql', graphqlExpress(options.graphqlOptions)); | ||
return app; | ||
@@ -26,9 +29,12 @@ } | ||
describe('expressApollo', () => { | ||
it('throws error if called without schema', function(){ | ||
expect(() => graphqlExpress(undefined as GraphQLOptions)).to.throw('Apollo Server requires options.'); | ||
it('throws error if called without schema', function() { | ||
expect(() => graphqlExpress(undefined as GraphQLOptions)).to.throw( | ||
'Apollo Server requires options.', | ||
); | ||
}); | ||
it('throws an error if called with more than one argument', function(){ | ||
expect(() => (<any>graphqlExpress)({}, 'x')).to.throw( | ||
'Apollo Server expects exactly one argument, got 2'); | ||
it('throws an error if called with more than one argument', function() { | ||
expect(() => (<any>graphqlExpress)({}, 'x')).to.throw( | ||
'Apollo Server expects exactly one argument, got 2', | ||
); | ||
}); | ||
@@ -35,0 +41,0 @@ }); |
import * as express from 'express'; | ||
import * as url from 'url'; | ||
import { GraphQLOptions, HttpQueryError, runHttpQuery } from 'apollo-server-core'; | ||
import { | ||
GraphQLOptions, | ||
HttpQueryError, | ||
runHttpQuery, | ||
} from 'apollo-server-core'; | ||
import * as GraphiQL from 'apollo-server-module-graphiql'; | ||
export interface ExpressGraphQLOptionsFunction { | ||
(req?: express.Request, res?: express.Response): GraphQLOptions | Promise<GraphQLOptions>; | ||
(req?: express.Request, res?: express.Response): | ||
| GraphQLOptions | ||
| Promise<GraphQLOptions>; | ||
} | ||
@@ -19,3 +25,5 @@ | ||
export function graphqlExpress(options: GraphQLOptions | ExpressGraphQLOptionsFunction): ExpressHandler { | ||
export function graphqlExpress( | ||
options: GraphQLOptions | ExpressGraphQLOptionsFunction, | ||
): ExpressHandler { | ||
if (!options) { | ||
@@ -27,3 +35,5 @@ throw new Error('Apollo Server requires options.'); | ||
// TODO: test this | ||
throw new Error(`Apollo Server expects exactly one argument, got ${arguments.length}`); | ||
throw new Error( | ||
`Apollo Server expects exactly one argument, got ${arguments.length}`, | ||
); | ||
} | ||
@@ -36,22 +46,25 @@ | ||
query: req.method === 'POST' ? req.body : req.query, | ||
}).then((gqlResponse) => { | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.setHeader('Content-Length', Buffer.byteLength(gqlResponse, 'utf8')); | ||
res.write(gqlResponse); | ||
res.end(); | ||
}, (error: HttpQueryError) => { | ||
if ( 'HttpQueryError' !== error.name ) { | ||
return next(error); | ||
} | ||
}).then( | ||
gqlResponse => { | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.setHeader('Content-Length', Buffer.byteLength(gqlResponse, 'utf8')); | ||
res.write(gqlResponse); | ||
res.end(); | ||
}, | ||
(error: HttpQueryError) => { | ||
if ('HttpQueryError' !== error.name) { | ||
return next(error); | ||
} | ||
if ( error.headers ) { | ||
Object.keys(error.headers).forEach((header) => { | ||
res.setHeader(header, error.headers[header]); | ||
}); | ||
} | ||
if (error.headers) { | ||
Object.keys(error.headers).forEach(header => { | ||
res.setHeader(header, error.headers[header]); | ||
}); | ||
} | ||
res.statusCode = error.statusCode; | ||
res.write(error.message); | ||
res.end(); | ||
}); | ||
res.statusCode = error.statusCode; | ||
res.write(error.message); | ||
res.end(); | ||
}, | ||
); | ||
}; | ||
@@ -61,3 +74,5 @@ } | ||
export interface ExpressGraphiQLOptionsFunction { | ||
(req?: express.Request): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>; | ||
(req?: express.Request): | ||
| GraphiQL.GraphiQLData | ||
| Promise<GraphiQL.GraphiQLData>; | ||
} | ||
@@ -76,11 +91,16 @@ | ||
export function graphiqlExpress(options: GraphiQL.GraphiQLData | ExpressGraphiQLOptionsFunction) { | ||
export function graphiqlExpress( | ||
options: GraphiQL.GraphiQLData | ExpressGraphiQLOptionsFunction, | ||
) { | ||
return (req: express.Request, res: express.Response, next) => { | ||
const query = req.url && url.parse(req.url, true).query; | ||
GraphiQL.resolveGraphiQLString(query, options, req).then(graphiqlString => { | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.write(graphiqlString); | ||
res.end(); | ||
}, error => next(error)); | ||
GraphiQL.resolveGraphiQLString(query, options, req).then( | ||
graphiqlString => { | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.write(graphiqlString); | ||
res.end(); | ||
}, | ||
error => next(error), | ||
); | ||
}; | ||
} |
@@ -0,1 +1,5 @@ | ||
// Expose types which can be used by both middleware flavors. | ||
export { GraphQLOptions } from 'apollo-server-core'; | ||
// Express Middleware | ||
export { | ||
@@ -9,5 +13,3 @@ ExpressGraphQLOptionsFunction, | ||
export { | ||
graphqlConnect, | ||
graphiqlConnect, | ||
} from './connectApollo'; | ||
// Connect Middleware | ||
export { graphqlConnect, graphiqlConnect } from './connectApollo'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
29828
3.05%725
13.64%0
-100%60
-1.64%