Skip to content

Commit

Permalink
fix(ts): make 'export *' explicit; type PostGraphileOptions (#1043)
Browse files Browse the repository at this point in the history
`export *` doesn't work with declaration merging (apparently). Explicit exports seems to solve this.

People using PostGraphileOptions in Express need a way of accessing things like `req.user` in `pgSettings` without TypeScript being upset at them.
  • Loading branch information
benjie authored Apr 12, 2019
1 parent bb116ef commit f477f5e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 38 deletions.
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
export * from './interfaces';
export {
mixed,
Middleware,
PostGraphileOptions,
CreateRequestHandlerOptions,
GraphQLFormattedErrorExtended,
GraphQLErrorExtended,
HttpRequestHandler,
WithPostGraphileContextOptions,
} from './interfaces';

export {
Plugin,
Expand Down
45 changes: 30 additions & 15 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@ import { EventEmitter } from 'events';
*/
export type mixed = {} | string | number | boolean | undefined | null;

export type Middleware = (
req: IncomingMessage,
res: ServerResponse,
next: (err?: Error) => void,
) => void;

// 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.
// Anything tagged `@middlewareOnly` will not appear in the schema-only docs.
// Only comments written beginning with `//` will be put in the docs.
export interface PostGraphileOptions {
export interface PostGraphileOptions<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
> {
// When true, PostGraphile will update the GraphQL API whenever your database
// schema changes. This feature requires some changes to your database in the
// form of the
Expand All @@ -49,6 +58,13 @@ export interface PostGraphileOptions {
subscriptions?: boolean;
// [EXPERIMENTAL] Enables live-query support via GraphQL subscriptions (sends updated payload any time nested collections/records change)
live?: boolean;
// [EXPERIMENTAL] If you're using websockets (subscriptions || live) then you
// may want to authenticate your users using sessions or similar. You can
// pass some simple middlewares here that will be executed against the
// websocket connection in order to perform authentication. We current only
// support express (not Koa) middlewares here.
/* @middlewareOnly */
websocketMiddlewares?: Array<Middleware>;
// The default Postgres role to use. If no role was provided in a provided
// JWT token, this role will be used.
pgDefaultRole?: string;
Expand Down Expand Up @@ -104,7 +120,7 @@ export interface PostGraphileOptions {
handleErrors?: (
errors: ReadonlyArray<GraphQLError>,
req: IncomingMessage,
res: ServerResponse,
res: Response,
) => Array<GraphQLErrorExtended>;
// An array of [Graphile Engine](/graphile-build/plugins/) schema plugins to load
// after the default plugins.
Expand Down Expand Up @@ -217,16 +233,14 @@ export interface PostGraphileOptions {
// 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: Request) => Promise<{ [key: string]: mixed }>);
// Some Graphile Engine schema 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 this to change the response [experimental], e.g. setting
// cookies.
/* @middlewareOnly */
additionalGraphQLContextFromRequest?: (req: IncomingMessage, res: ServerResponse) => Promise<{}>;
additionalGraphQLContextFromRequest?: (req: Request, res: Response) => Promise<{}>;
// [experimental] Plugin hook function, enables functionality within
// PostGraphile to be expanded with plugins. Generate with
// `makePluginHook(plugins)` passing a list of plugin objects.
Expand All @@ -240,8 +254,6 @@ export interface PostGraphileOptions {
// Max query cache size in MBs of queries. Default, 50MB.
/* @middlewareOnly */
queryCacheMaxSize?: number;
// Allow arbitrary extensions for consumption by plugins.
[propName: string]: any;
}

export interface CreateRequestHandlerOptions extends PostGraphileOptions {
Expand Down Expand Up @@ -274,23 +286,26 @@ export type GraphQLErrorExtended = GraphQLError & {
/**
* A request handler for one of many different `http` frameworks.
*/
export interface HttpRequestHandler {
(req: IncomingMessage, res: ServerResponse, next?: (error?: mixed) => void): Promise<void>;
(ctx: { req: IncomingMessage; res: ServerResponse }, next: () => void): Promise<void>;
export interface HttpRequestHandler<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
> {
(req: Request, res: Response, next?: (error?: mixed) => void): Promise<void>;
(ctx: { req: Request; res: Response }, next: () => void): Promise<void>;
formatError: (e: GraphQLError) => GraphQLFormattedErrorExtended;
getGraphQLSchema: () => Promise<GraphQLSchema>;
pgPool: Pool;
withPostGraphileContextFromReqRes: (
req: IncomingMessage,
res: ServerResponse,
req: Request,
res: Response,
moreOptions: any,
fn: (ctx: mixed) => any,
) => Promise<any>;
options: CreateRequestHandlerOptions;
handleErrors: (
errors: ReadonlyArray<GraphQLError>,
req: IncomingMessage,
res: ServerResponse,
req: Request,
res: Response,
) => Array<GraphQLErrorExtended>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default function createPostGraphileHttpRequestHandler(
pgDefaultRole,
queryCacheMaxSize = 50 * MEGABYTE,
} = options;
if (options.absoluteRoutes) {
if (options['absoluteRoutes']) {
throw new Error(
'Sorry - the `absoluteRoutes` setting has been replaced with `externalUrlBase` which solves the issue in a cleaner way. Please update your settings. Thank you for testing a PostGraphile pre-release 🙏',
);
Expand Down
13 changes: 6 additions & 7 deletions src/postgraphile/http/subscriptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Server, ServerResponse } from 'http';
import { HttpRequestHandler, mixed } from '../../interfaces';
import { Server, IncomingMessage, ServerResponse } from 'http';
import { HttpRequestHandler, mixed, Middleware } from '../../interfaces';
import {
subscribe as graphqlSubscribe,
ExecutionResult,
Expand All @@ -9,7 +9,6 @@ import {
parse,
DocumentNode,
} from 'graphql';
import { RequestHandler, Request, Response } from 'express';
import * as WebSocket from 'ws';
import { SubscriptionServer, ConnectionContext, ExecutionParams } from 'subscriptions-transport-ws';
import parseUrl = require('parseurl');
Expand Down Expand Up @@ -88,9 +87,9 @@ export async function enhanceHttpServerWithSubscriptions(
};

const applyMiddleware = async (
middlewares: Array<RequestHandler> = [],
req: Request,
res: Response,
middlewares: Array<Middleware> = [],
req: IncomingMessage,
res: ServerResponse,
) => {
for (const middleware of middlewares) {
// TODO: add Koa support
Expand Down Expand Up @@ -128,7 +127,7 @@ export async function enhanceHttpServerWithSubscriptions(
socket.close();
}
};
await applyMiddleware(options.websocketMiddlewares || options.middlewares, req, dummyRes);
await applyMiddleware(options.websocketMiddlewares, req, dummyRes);
socket['__postgraphileRes'] = dummyRes;
}
return { req, res: dummyRes };
Expand Down
44 changes: 30 additions & 14 deletions src/postgraphile/postgraphile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Pool, PoolConfig } from 'pg';
import { IncomingMessage, ServerResponse } from 'http';
import { GraphQLSchema } from 'graphql';
import { EventEmitter } from 'events';
import { createPostGraphileSchema, watchPostGraphileSchema } from 'postgraphile-core';
Expand All @@ -18,21 +19,27 @@ function isPlainObject(obj: any) {
return false;
}

export interface PostgraphileSchemaBuilder {
export interface PostgraphileSchemaBuilder<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
> {
_emitter: EventEmitter;
getGraphQLSchema: () => Promise<GraphQLSchema>;
options: PostGraphileOptions;
options: PostGraphileOptions<Request, Response>;
}

/**
* Creates a PostGraphile Http request handler by first introspecting the
* database to get a GraphQL schema, and then using that to create the Http
* request handler.
*/
export function getPostgraphileSchemaBuilder(
export function getPostgraphileSchemaBuilder<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
>(
pgPool: Pool,
schema: string | Array<string>,
incomingOptions: PostGraphileOptions,
incomingOptions: PostGraphileOptions<Request, Response>,
): PostgraphileSchemaBuilder {
if (incomingOptions.live && incomingOptions.subscriptions == null) {
// live implies subscriptions
Expand All @@ -59,7 +66,7 @@ export function getPostgraphileSchemaBuilder(
// Creates the Postgres schemas array.
const pgSchemas: Array<string> = Array.isArray(schema) ? schema : [schema];

const _emitter = options._emitter || new EventEmitter();
const _emitter: EventEmitter = options['_emitter'] || new EventEmitter();

// Creates a promise which will resolve to a GraphQL schema. Connects a
// client from our pool to introspect the database.
Expand Down Expand Up @@ -108,24 +115,33 @@ export function getPostgraphileSchemaBuilder(
}
}
}
export default function postgraphile(
export default function postgraphile<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
>(
poolOrConfig?: Pool | PoolConfig | string,
schema?: string | Array<string>,
options?: PostGraphileOptions,
options?: PostGraphileOptions<Request, Response>,
): HttpRequestHandler;
export default function postgraphile(
export default function postgraphile<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
>(
poolOrConfig?: Pool | PoolConfig | string,
options?: PostGraphileOptions,
options?: PostGraphileOptions<Request, Response>,
): HttpRequestHandler;
export default function postgraphile(
export default function postgraphile<
Request extends IncomingMessage = IncomingMessage,
Response extends ServerResponse = ServerResponse
>(
poolOrConfig?: Pool | PoolConfig | string,
schemaOrOptions?: string | Array<string> | PostGraphileOptions,
maybeOptions?: PostGraphileOptions,
schemaOrOptions?: string | Array<string> | PostGraphileOptions<Request, Response>,
maybeOptions?: PostGraphileOptions<Request, Response>,
): HttpRequestHandler {
let schema: string | Array<string>;
// These are the raw options we're passed in; getPostgraphileSchemaBuilder
// must process them with `pluginHook` before we can rely on them.
let incomingOptions: PostGraphileOptions;
let incomingOptions: PostGraphileOptions<Request, Response>;

// If the second argument is a string or array, it is the schemas so set the
// `schema` value and try to use the third argument (or a default) for
Expand Down Expand Up @@ -175,7 +191,7 @@ export default function postgraphile(
console.error('PostgreSQL client generated error: ', err.message);
});

const { getGraphQLSchema, options, _emitter } = getPostgraphileSchemaBuilder(
const { getGraphQLSchema, options, _emitter } = getPostgraphileSchemaBuilder<Request, Response>(
pgPool,
schema,
incomingOptions,
Expand Down

0 comments on commit f477f5e

Please sign in to comment.