This commit is contained in:
Nicolás López 2016-10-15 16:03:43 -03:00
commit b0eb066dd1
16 changed files with 265 additions and 114 deletions

View file

@ -1,5 +1,18 @@
# Changelog
### VNEXT
* Fix passHeader option in GraphiQL (Both Hapi and Koa)
* Pass `ctx` instead of `ctx.request` to options function in Koa integration ([@HriBB](https://github.com/HriBB)) in [PR #154](https://github.com/apollostack/apollo-server/pull/154)
* Manage TypeScript declaration files using npm. ([@od1k](https:/github.com/od1k) in [#162](https://github.com/apollostack/apollo-server/pull/162))
* Fix connect example in readme. ([@conrad-vanl](https://github.com/conrad-vanl) in [#165](https://github.com/apollostack/apollo-server/pull/165))
### v0.3.2
* Added missing exports for hapi integration ([@nnance](https://github.com/nnance)) in [PR #152](https://github.com/apollostack/apollo-server/pull/152)
### v0.3.1
* Fixed dependency issue with boom package that affected the hapi integration. ([@sammkj](https://github.com/sammkj) in [#150](https://github.com/apollostack/apollo-server/pull/150))
### v0.3.0
* Refactor Hapi integration to improve the API and make the plugins more idiomatic. ([@nnance](https://github.com/nnance)) in
[PR #127](https://github.com/apollostack/apollo-server/pull/127)
@ -11,6 +24,11 @@
[PR #132](https://github.com/apollostack/apollo-server/pull/132)
* Fix error handling when parsing variables parameter. Issue #130. ([@nnance](https://github.com/nnance)) in
[PR #131](https://github.com/apollostack/apollo-server/pull/131)
* Improve logging function. Issue #79. ([@nnance](https://github.com/nnance)) in
[PR #136](https://github.com/apollostack/apollo-server/pull/136)
* Output stack trace for errors in debug mode. Issue #111. ([@nnance](https://github.com/nnance)) in
[PR #137](https://github.com/apollostack/apollo-server/pull/137)
* Allow to pass custom headers in GraphiQL ([@nicolaslopezj](https://github.com/nicolaslopezj) in [#133](https://github.com/apollostack/apollo-server/pull/133)).
### v0.2.6
* Expose the OperationStore as part of the public API. ([@nnance](https://github.com/nnance))

View file

@ -21,18 +21,11 @@ Anyone is welcome to contribute to Apollo Server, just read [CONTRIBUTING.md](./
## Getting started
Apollo Server is super-easy to set up. Just npm-install apollo-server, write a GraphQL schema, and then use one of the following snippets to get started. For more info, read the [Apollo Server docs](http://docs.apollostack.com/apollo-server).
Apollo Server is super-easy to set up. Just npm-install apollo-server, write a GraphQL schema, and then use one of the following snippets to get started. For more info, read the [Apollo Server docs](http://dev.apollodata.com/tools/apollo-server/index.html).
### TypeScript
### Installation
If you want to build your GraphQL server using TypeScript, Apollo Server is the project for you. **NOTE**: All typings mentioned below must be included in your project in order for it to compile.
```sh
npm install apollo-server
typings i -SG dt~express dt~express-serve-static-core dt~serve-static dt~mime dt~hapi dt~boom dt~cookies dt~koa
```
For using the project in JavaScript, just run `npm install --save apollo-server` and you're good to go!
Just run `npm install --save apollo-server` and you're good to go!
### Express
@ -53,24 +46,27 @@ app.listen(PORT);
### Connect
```js
import connect from 'connect';
import bodyParser from 'body-parser';
import { apolloConnect } from 'apollo-server';
import http from 'http';
const PORT = 3000;
var app = connect();
app.use('/graphql', bodyParser.json(), apolloConnect({ schema: myGraphQLSchema }));
app.use('/graphql', bodyParser.json());
app.use('/graphql', apolloConnect({ schema: myGraphQLSchema }));
app.listen(PORT);
http.createServer(app).listen(PORT);
```
### Hapi
Now with the Hapi plugins `ApolloHapi` and `GraphiQLHapi` you can pass a route object that includes options to be applied to the route. The example below enables CORS on the `/graphql` route.
Now with the Hapi plugins `apolloHapi` and `graphiqlHapi` you can pass a route object that includes options to be applied to the route. The example below enables CORS on the `/graphql` route.
```js
import hapi from 'hapi';
import { ApolloHapi } from 'apollo-server';
import { apolloHapi } from 'apollo-server';
const server = new hapi.Server();
@ -83,7 +79,7 @@ server.connection({
});
server.register({
register: ApolloHapi,
register: apolloHapi,
options: {
path: '/graphql',
apolloOptions: {
@ -166,3 +162,28 @@ Apollo Server and express-graphql are more or less the same thing (GraphQL middl
Despite express-graphql being a reference implementation, Apollo Server is actually easier to understand and more modular than express-graphql.
That said, Apollo Server is heavily inspired by express-graphql (it's the reference implementation after all). Rather than seeing the two as competing alternatives, we think that they both have separate roles in the GraphQL ecosystem: express-graphql is a reference implementation, and Apollo Server is a GraphQL server to be used in production and evolve quickly with the needs of the community. Over time, express-graphql can adopt those features of Apollo Server that have proven their worth and become established more widely.
## Apollo Server Development
If you want to develop apollo server locally you must follow the following instructions:
* Fork this repository
* Install the Apollo Server project in your computer
```
git clone https://github.com/[your-user]/apollo-server
cd apollo-server
npm install -g typescript live-server
npm install
npm run typings
npm run compile
npm link
```
* Install your local Apollo Server in other App
```
cd ~/myApp
npm link apollo-server
```

View file

@ -11,7 +11,7 @@ import * as bodyParser from "body-parser";
import { graphqlHTTP, renderGraphiQL } from "apollo-server";
const port = 3000;
const endpointURL = "/grahpql";
const endpointURL = "/graphql";
const app = express();
const schema = new graphql.GraphQLSchema({

View file

@ -1,6 +1,6 @@
{
"name": "apollo-server",
"version": "0.2.8",
"version": "0.3.2",
"description": "Production-ready Node.js GraphQL server for Express, Hapi, Koa",
"main": "dist/index.js",
"directories": {
@ -37,20 +37,40 @@
},
"homepage": "https://github.com/apollostack/apollo-proxy#readme",
"dependencies": {
"@types/body-parser": "0.0.33",
"@types/boom": "0.0.32",
"@types/chai": "^3.4.34",
"@types/connect": "^3.4.30",
"@types/cookies": "^0.5.30",
"@types/express": "^4.0.33",
"@types/express-serve-static-core": "^4.0.36",
"@types/fibers": "0.0.29",
"@types/hapi": "^13.0.35",
"@types/http-errors": "^1.3.29",
"@types/koa": "^2.0.33",
"@types/koa-bodyparser": "^3.0.19",
"@types/koa-router": "^7.0.21",
"@types/mime": "0.0.29",
"@types/multer": "0.0.32",
"@types/node": "^6.0.41",
"@types/serve-static": "^1.7.31",
"boom": "^4.0.0",
"http-errors": "^1.5.0",
"source-map-support": "^0.4.2"
"source-map-support": "^0.4.2",
"typed-graphql": "^1.0.2"
},
"devDependencies": {
"@types/mocha": "^2.2.32",
"@types/sinon": "^1.16.31",
"babel-cli": "^6.11.4",
"babel-core": "^6.11.4",
"babel-polyfill": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"body-parser": "^1.15.2",
"boom": "^4.0.0",
"chai": "^3.5.0",
"connect": "^3.4.1",
"express": "^4.14.0",
"fibers": "^1.0.13",
"fibers": "^1.0.15",
"graphql": "^0.7.0",
"hapi": "^15.0.3",
"istanbul": "1.0.0-alpha.2",
@ -58,13 +78,14 @@
"koa-bodyparser": "^3.0.0",
"koa-router": "^7.0.1",
"meteor-promise": "^0.7.3",
"mocha": "^3.0.0",
"mocha": "^3.1.1",
"multer": "^1.1.0",
"remap-istanbul": "^0.6.4",
"remap-istanbul": "^0.7.0",
"sinon": "^1.17.5",
"supertest": "^2.0.0",
"supertest-as-promised": "^4.0.0",
"tslint": "^3.13.0",
"typescript": "^1.8.10",
"typescript": "^2.0.3",
"typings": "^1.3.2"
},
"peerDependencies": {

View file

@ -1,6 +1,5 @@
import {
expect,
} from 'chai';
import { expect } from 'chai';
import { stub } from 'sinon';
import {
GraphQLSchema,
@ -11,7 +10,11 @@ import {
parse,
} from 'graphql';
import { runQuery } from './runQuery';
import {
runQuery,
LogAction,
LogStep,
} from './runQuery';
// Make the global Promise constructor Fiber-aware to simulate a Meteor
// environment.
@ -58,6 +61,12 @@ const QueryType = new GraphQLObjectType({
return 'it ' + (<any>Promise).await('works');
},
},
testError: {
type: GraphQLString,
resolve() {
throw new Error('Secret error message');
},
},
},
});
@ -84,18 +93,46 @@ describe('runQuery', () => {
});
});
it('returns a syntax error if the query string contains one', () => {
const query = `query { test`;
const expected = /Syntax Error GraphQL/;
return runQuery({
schema: Schema,
query: query,
variables: { base: 1 },
}).then((res) => {
expect(res.data).to.be.undefined;
expect(res.errors.length).to.equal(1);
return expect(res.errors[0].message).to.match(expected);
});
it('returns a syntax error if the query string contains one', () => {
const query = `query { test `;
const expected = /Syntax Error GraphQL/;
return runQuery({
schema: Schema,
query: query,
variables: { base: 1 },
}).then((res) => {
expect(res.data).to.be.undefined;
expect(res.errors.length).to.equal(1);
return expect(res.errors[0].message).to.match(expected);
});
});
it('sends stack trace to error if in an error occurs and debug mode is set', () => {
const query = `query { testError }`;
const expected = /at resolveOrError/;
const logStub = stub(console, 'error');
return runQuery({
schema: Schema,
query: query,
debug: true,
}).then((res) => {
logStub.restore();
expect(logStub.callCount).to.equal(1);
return expect(logStub.getCall(0).args[0]).to.match(expected);
});
});
it('does not send stack trace if in an error occurs and not in debug mode', () => {
const query = `query { testError }`;
const logStub = stub(console, 'error');
return runQuery({
schema: Schema,
query: query,
debug: false,
}).then((res) => {
logStub.restore();
return expect(logStub.callCount).to.equal(0);
});
});
it('returns a validation error if the query string does not pass validation', () => {
@ -219,9 +256,7 @@ describe('runQuery', () => {
testString
}`;
const logs = [];
const logFn = (...args) => {
logs.push(args);
};
const logFn = (obj) => logs.push(obj);
const expected = {
testString: 'it works',
};
@ -235,14 +270,11 @@ describe('runQuery', () => {
.then((res) => {
expect(res.data).to.deep.equal(expected);
expect(logs.length).to.equals(11);
expect(logs[0][0]).to.equals('request.start');
expect(logs[1][0]).to.equals('request.query');
expect(logs[1][1]).to.deep.equals(query);
expect(logs[2][0]).to.equals('request.variables');
expect(logs[2][1]).to.deep.equals({ test: 123 });
expect(logs[3][0]).to.equals('request.operationName');
expect(logs[3][1]).to.equals('Q1');
expect(logs[10][0]).to.equals('request.end');
expect(logs[0]).to.deep.equals({action: LogAction.request, step: LogStep.start});
expect(logs[1]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'query', data: query});
expect(logs[2]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'variables', data: { test: 123 }});
expect(logs[3]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'operationName', data: 'Q1'});
expect(logs[10]).to.deep.equals({action: LogAction.request, step: LogStep.end});
});
});
});

View file

@ -16,6 +16,25 @@ export interface GqlResponse {
errors?: Array<string>;
}
export enum LogAction {
request, parse, validation, execute
}
export enum LogStep {
start, end, status
}
export interface LogMessage {
action: LogAction;
step: LogStep;
key?: string;
data?: Object;
}
export interface LogFunction {
(message: LogMessage);
}
export interface QueryOptions {
schema: GraphQLSchema;
query: string | Document;
@ -23,13 +42,14 @@ export interface QueryOptions {
context?: any;
variables?: { [key: string]: any };
operationName?: string;
logFunction?: Function;
logFunction?: LogFunction;
validationRules?: Array<ValidationRule>;
// WARNING: these extra validation rules are only applied to queries
// submitted as string, not those submitted as Document!
formatError?: Function;
formatResponse?: Function;
debug?: boolean;
}
const resolvedPromise = Promise.resolve();
@ -43,8 +63,10 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
let documentAST: Document;
const logFunction = options.logFunction || function(){ return null; };
const debugDefault = process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test';
const debug = typeof options.debug !== 'undefined' ? options.debug : debugDefault;
logFunction('request.start');
logFunction({action: LogAction.request, step: LogStep.start});
function format(errors: Array<Error>): Array<Error> {
// TODO: fix types! shouldn't have to cast.
@ -54,19 +76,24 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
return errors.map(options.formatError || formatError as any) as Array<Error>;
}
logFunction('request.query', typeof options.query === 'string' ? options.query : print(options.query));
logFunction('request.variables', options.variables);
logFunction('request.operationName', options.operationName);
function printStackTrace(error: Error) {
console.error(error.stack);
}
const qry = typeof options.query === 'string' ? options.query : print(options.query);
logFunction({action: LogAction.request, step: LogStep.status, key: 'query', data: qry});
logFunction({action: LogAction.request, step: LogStep.status, key: 'variables', data: options.variables});
logFunction({action: LogAction.request, step: LogStep.status, key: 'operationName', data: options.operationName});
// if query is already an AST, don't parse or validate
if (typeof options.query === 'string') {
try {
// TODO: time this with log function
logFunction('parse.start');
logFunction({action: LogAction.parse, step: LogStep.start});
documentAST = parse(options.query as string);
logFunction('parse.end');
logFunction({action: LogAction.parse, step: LogStep.end});
} catch (syntaxError) {
logFunction('parse.end');
logFunction({action: LogAction.parse, step: LogStep.end});
return Promise.resolve({ errors: format([syntaxError]) });
}
@ -76,9 +103,9 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
if (options.validationRules) {
rules = rules.concat(options.validationRules);
}
logFunction('validation.start');
logFunction({action: LogAction.validation, step: LogStep.start});
const validationErrors = validate(options.schema, documentAST, rules);
logFunction('validation.end');
logFunction({action: LogAction.validation, step: LogStep.end});
if (validationErrors.length) {
return Promise.resolve({ errors: format(validationErrors) });
}
@ -87,7 +114,7 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
}
try {
logFunction('execution.start');
logFunction({action: LogAction.execute, step: LogStep.start});
return execute(
options.schema,
documentAST,
@ -96,13 +123,16 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
options.variables,
options.operationName
).then(gqlResponse => {
logFunction('execution.end');
logFunction('request.end');
logFunction({action: LogAction.execute, step: LogStep.end});
logFunction({action: LogAction.request, step: LogStep.end});
let response = {
data: gqlResponse.data,
};
if (gqlResponse.errors) {
response['errors'] = format(gqlResponse.errors);
if (debug) {
gqlResponse.errors.map(printStackTrace);
}
}
if (options.formatResponse) {
response = options.formatResponse(response, options);
@ -110,8 +140,8 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
return response;
});
} catch (executionError) {
logFunction('execution.end');
logFunction('request.end');
logFunction({action: LogAction.execute, step: LogStep.end});
logFunction({action: LogAction.request, step: LogStep.end});
return Promise.resolve({ errors: format([executionError]) });
}
}

View file

@ -1,8 +1,8 @@
export { runQuery } from './core/runQuery'
export { runQuery, LogFunction, LogMessage, LogStep, LogAction } from './core/runQuery'
export { renderGraphiQL} from './modules/renderGraphiQL'
export { OperationStore } from './modules/operationStore'
export { apolloExpress, graphiqlExpress } from './integrations/expressApollo'
export { ApolloHapi, GraphiQLHapi } from './integrations/hapiApollo'
export { apolloHapi, graphiqlHapi, HapiPluginOptions, HapiOptionsFunction } from './integrations/hapiApollo'
export { apolloKoa, graphiqlKoa } from './integrations/koaApollo'
export { apolloConnect, graphiqlConnect } from './integrations/connectApollo'
export { default as ApolloOptions} from './integrations/apolloOptions'

View file

@ -1,4 +1,5 @@
import * as graphql from 'graphql';
import { GraphQLSchema, ValidationRule } from 'graphql';
import { LogFunction } from '../core/runQuery';
/*
* ExpressApolloOptions
@ -11,17 +12,19 @@ import * as graphql from 'graphql';
* - (optional) formatParams: a function applied to the parameters of every invocation of runQuery
* - (optional) validationRules: extra validation rules applied to requests
* - (optional) formatResponse: a function applied to each graphQL execution result
* - (optional) debug: a boolean that will print additional debug logging if execution errors occur
*
*/
interface ApolloOptions {
schema: graphql.GraphQLSchema;
schema: GraphQLSchema;
formatError?: Function;
rootValue?: any;
context?: any;
logFunction?: Function;
logFunction?: LogFunction;
formatParams?: Function;
validationRules?: Array<graphql.ValidationRule>;
validationRules?: Array<ValidationRule>;
formatResponse?: Function;
debug?: boolean;
}
export default ApolloOptions;

View file

@ -98,6 +98,7 @@ export function apolloExpress(options: ApolloOptions | ExpressApolloOptionsFunct
validationRules: optionsObject.validationRules,
formatError: formatErrorFn,
formatResponse: optionsObject.formatResponse,
debug: optionsObject.debug,
};
if (optionsObject.formatParams) {
@ -154,6 +155,7 @@ export function graphiqlExpress(options: GraphiQL.GraphiQLData) {
query: query || options.query,
variables: JSON.parse(variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
});
res.setHeader('Content-Type', 'text/html');
res.write(graphiQLString);

View file

@ -1,5 +1,5 @@
import * as hapi from 'hapi';
import { ApolloHapi, GraphiQLHapi, HapiPluginOptions } from './hapiApollo';
import { apolloHapi, graphiqlHapi, HapiPluginOptions } from './hapiApollo';
import testSuite, { Schema } from './integrations.test';
@ -12,7 +12,7 @@ function createApp(createOptions: HapiPluginOptions) {
});
server.register({
register: ApolloHapi,
register: apolloHapi,
options: {
apolloOptions: createOptions ? createOptions.apolloOptions : { schema: Schema },
path: '/graphql',
@ -20,7 +20,7 @@ function createApp(createOptions: HapiPluginOptions) {
});
server.register({
register: GraphiQLHapi,
register: graphiqlHapi,
options: {
path: '/graphiql',
graphiqlOptions: {

View file

@ -21,7 +21,7 @@ export interface HapiPluginOptions {
apolloOptions: ApolloOptions | HapiOptionsFunction;
}
const ApolloHapi: IRegister = function(server: Server, options: HapiPluginOptions, next) {
const apolloHapi: IRegister = function(server: Server, options: HapiPluginOptions, next) {
server.method('verifyPayload', verifyPayload);
server.method('getGraphQLParams', getGraphQLParams);
server.method('getApolloOptions', getApolloOptions);
@ -51,8 +51,8 @@ const ApolloHapi: IRegister = function(server: Server, options: HapiPluginOption
path: options.path || '/graphql',
config,
handler: function(request, reply) {
const responses = request.pre.graphQL;
if (request.pre.isBatch) {
const responses = request.pre['graphQL'];
if (request.pre['isBatch']) {
return reply(responses);
} else {
const gqlResponse = responses[0];
@ -68,7 +68,7 @@ const ApolloHapi: IRegister = function(server: Server, options: HapiPluginOption
return next();
};
ApolloHapi.attributes = {
apolloHapi.attributes = {
name: 'graphql',
version: '0.0.1',
};
@ -141,6 +141,7 @@ async function processQuery(graphqlParams, optionsObject: ApolloOptions, reply)
validationRules: optionsObject.validationRules,
formatError: formatErrorFn,
formatResponse: optionsObject.formatResponse,
debug: optionsObject.debug,
};
if (optionsObject.formatParams) {
@ -171,7 +172,7 @@ export interface GraphiQLPluginOptions {
graphiqlOptions: GraphiQL.GraphiQLData;
}
const GraphiQLHapi: IRegister = function(server: Server, options: GraphiQLPluginOptions, next) {
const graphiqlHapi: IRegister = function(server: Server, options: GraphiQLPluginOptions, next) {
server.method('getGraphiQLParams', getGraphiQLParams);
server.method('renderGraphiQL', renderGraphiQL);
@ -193,13 +194,13 @@ const GraphiQLHapi: IRegister = function(server: Server, options: GraphiQLPlugi
path: options.path || '/graphql',
config,
handler: (request, reply) => {
reply(request.pre.graphiQLString).header('Content-Type', 'text/html');
reply(request.pre['graphiQLString']).header('Content-Type', 'text/html');
},
});
next();
};
GraphiQLHapi.attributes = {
graphiqlHapi.attributes = {
name: 'graphiql',
version: '0.0.1',
};
@ -219,8 +220,9 @@ function renderGraphiQL(route, graphiqlParams: any, reply) {
query: graphiqlParams.query || graphiqlOptions.query,
variables: JSON.parse(graphiqlParams.variables) || graphiqlOptions.variables,
operationName: graphiqlParams.operationName || graphiqlOptions.operationName,
passHeader: graphiqlOptions.passHeader,
});
reply(graphiQLString);
}
export { ApolloHapi, GraphiQLHapi };
export { apolloHapi, graphiqlHapi };

View file

@ -1,6 +1,5 @@
import {
expect,
} from 'chai';
import { expect } from 'chai';
import { stub } from 'sinon';
import {
GraphQLSchema,
@ -425,6 +424,45 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => {
});
});
it('sends stack trace to error if debug mode is set', () => {
const expected = /at resolveOrError/;
const stackTrace = [];
const origError = console.error;
console.error = (...args) => stackTrace.push(args);
app = createApp({apolloOptions: {
schema: Schema,
debug: true,
}});
const req = request(app)
.post('/graphql')
.send({
query: 'query test{ testError }',
});
return req.then((res) => {
console.error = origError;
return expect(stackTrace[0][0]).to.match(expected);
});
});
it('sends stack trace to error log if debug mode is set', () => {
const logStub = stub(console, 'error');
const expected = /at resolveOrError/;
app = createApp({apolloOptions: {
schema: Schema,
debug: true,
}});
const req = request(app)
.post('/graphql')
.send({
query: 'query test{ testError }',
});
return req.then((res) => {
logStub.restore();
expect(logStub.callCount).to.equal(1);
return expect(logStub.getCall(0).args[0]).to.match(expected);
});
});
it('applies additional validationRules', () => {
const expected = 'AlwaysInvalidRule was really invalid!';
const AlwaysInvalidRule = function (context) {

View file

@ -5,7 +5,7 @@ import ApolloOptions from './apolloOptions';
import * as GraphiQL from '../modules/renderGraphiQL';
export interface KoaApolloOptionsFunction {
(req: koa.Request): ApolloOptions | Promise<ApolloOptions>;
(ctx: koa.Context): ApolloOptions | Promise<ApolloOptions>;
}
export interface KoaHandler {
@ -25,7 +25,7 @@ export function apolloKoa(options: ApolloOptions | KoaApolloOptionsFunction): Ko
let optionsObject: ApolloOptions;
if (isOptionsFunction(options)) {
try {
optionsObject = await options(ctx.request);
optionsObject = await options(ctx);
} catch (e) {
ctx.status = 500;
return ctx.body = `Invalid options provided to ApolloServer: ${e.message}`;
@ -75,6 +75,7 @@ export function apolloKoa(options: ApolloOptions | KoaApolloOptionsFunction): Ko
validationRules: optionsObject.validationRules,
formatError: formatErrorFn,
formatResponse: optionsObject.formatResponse,
debug: optionsObject.debug,
};
if (optionsObject.formatParams) {
@ -118,6 +119,7 @@ export function graphiqlKoa(options: GraphiQL.GraphiQLData) {
query: query || options.query,
variables: JSON.parse(variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
});
ctx.set('Content-Type', 'text/html');
ctx.body = graphiQLString;

View file

@ -15,6 +15,8 @@
* - (optional) variables: a JS object of variables to pre-fill in the GraphiQL UI
* - (optional) operationName: the operationName to pre-fill in the GraphiQL UI
* - (optional) result: the result of the query to pre-fill in the GraphiQL UI
* - (optional) passHeader: a string that will be added to the header object.
* For example "'Authorization': localStorage['Meteor.loginToken']" for meteor
*/
export type GraphiQLData = {
@ -23,6 +25,7 @@ export type GraphiQLData = {
variables?: Object,
operationName?: string,
result?: Object,
passHeader?: string
};
// Current latest version of GraphiQL.
@ -41,6 +44,7 @@ export function renderGraphiQL(data: GraphiQLData): string {
data.variables ? JSON.stringify(data.variables, null, 2) : null;
const resultString = null;
const operationName = data.operationName;
const passHeader = data.passHeader ? data.passHeader : '';
/* eslint-disable max-len */
return `
@ -94,7 +98,7 @@ export function renderGraphiQL(data: GraphiQLData): string {
otherParams[k] = parameters[k];
}
}
// We don't use safe-serialize for location, becuase it's not client input.
// We don't use safe-serialize for location, because it's not client input.
var fetchURL = locationQuery(otherParams, '${endpointURL}');
// Defines a GraphQL fetcher using the fetch API.
function graphQLFetcher(graphQLParams) {
@ -102,7 +106,8 @@ export function renderGraphiQL(data: GraphiQLData): string {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
'Content-Type': 'application/json',
${passHeader}
},
body: JSON.stringify(graphQLParams),
credentials: 'include',

1
src/typings.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="typed-graphql" />

View file

@ -1,25 +1 @@
{
"dependencies": {
"chai": "registry:npm/chai#3.5.0+20160723033700",
"graphql": "github:nitintutlani/typed-graphql#ffe7e46e2249cc8f3824a5d15a44938f4354afe9",
"http-errors": "registry:npm/http-errors#1.4.0+20160723033700"
},
"globalDependencies": {
"body-parser": "registry:dt/body-parser#0.0.0+20160619023215",
"boom": "registry:dt/boom#0.0.0+20160724101333",
"connect": "registry:dt/connect#3.4.0+20160317120654",
"cookies": "registry:dt/cookies#0.5.1+20160316171810",
"express": "registry:dt/express#4.0.0+20160708185218",
"express-serve-static-core": "registry:dt/express-serve-static-core#4.0.0+20160805091045",
"fibers": "registry:dt/fibers#0.0.0+20160317120654",
"hapi": "registry:dt/hapi#13.0.0+20160803202811",
"koa": "registry:dt/koa#2.0.0+20160724024233",
"koa-bodyparser": "registry:dt/koa-bodyparser#3.0.0+20160414124440",
"koa-router": "registry:dt/koa-router#7.0.0+20160314083221",
"mime": "registry:dt/mime#0.0.0+20160316155526",
"mocha": "registry:dt/mocha#2.2.5+20160720003353",
"multer": "registry:dt/multer#0.0.0+20160726135055",
"node": "registry:dt/node#6.0.0+20160801161248",
"serve-static": "registry:dt/serve-static#0.0.0+20160606155157"
}
}
{}