UNPKG

@jokio/graphql

Version:

High level, pre-configured, GraphQL Server

205 lines (166 loc) 4.64 kB
import { merge } from 'lodash'; import { mergeSchemas, introspectSchema, makeRemoteExecutableSchema } from 'graphql-tools'; import { GraphQLServer, PubSub } from 'graphql-yoga'; import { Engine } from 'apollo-engine'; import { graphiqlExpress } from 'apollo-server-express'; import { HttpLink } from './link-http'; import fetch from 'node-fetch'; import { GraphQLSchema } from 'graphql'; import coreModule, { SYSTEM_INFO_EVENT } from './modules/core'; import scalarModule from './modules/scalars'; import { RunProps, Context, SubscriptionEventProps } from './types'; import { RestAPI } from './api'; import { Request } from 'express'; import * as auth from './auth'; import { execute, subscribe } from 'graphql'; import { withFilter } from 'graphql-subscriptions'; export { Module, Resolvers, Context, RunProps } from './types'; export { RestAPI } from './api' export function devEnv() { if (process.env.NODE_ENV !== 'production') { require('dotenv').load(); } } export async function run(props: RunProps) { // read config const defaultProps: RunProps = { port: 4000, endpoint: '/', tokenName: 'token', localSchemas: [], remoteSchemaUrls: [], subscriptionEndpoint: 'ws://localhost:4000', getHttpToken: auth.getHttpToken, getWsToken: auth.getWsToken, contextObject: {}, yogaOptions: { tracing: { mode: 'http-header' }, disablePlayground: true, } }; const { localSchemas, port: propPort, engineConfig, endpoint, apiUrls, tokenName, disabledScalars, subscriptionEndpoint, remoteSchemaUrls, disableCoreModule, yogaOptions, enableAuthentication, getUserId, getHttpToken, getWsToken, websocketConnectionParams, contextObject, } = merge(defaultProps, props); const port: any = propPort; yogaOptions.port = parseInt(port); if (!yogaOptions.endpoint) yogaOptions.endpoint = endpoint; // validation if (enableAuthentication && !getUserId) { throw new Error('Please set getUserId in RunProps'); } // logic if (!disableCoreModule) { localSchemas.unshift(coreModule); } if (!disabledScalars) { localSchemas.unshift(scalarModule); } let typeDefs = localSchemas.map(x => x.typeDefs).join() || ''; let resolvers = localSchemas.map(x => x.resolvers).reduce(merge) || {}; let remoteSchemas = []; for (let url of remoteSchemaUrls) { remoteSchemas.push(await getRemoteSchema(url)); } const schema = !remoteSchemas.length ? undefined : mergeSchemas({ schemas: [...remoteSchemas, typeDefs], resolvers: resolvers }); const apis = {}; for (let key in apiUrls) { apis[key] = new RestAPI(apiUrls[key]); } const pubsub = new PubSub(); const context = ({ request, connectionParams }) => { let token = null; let userId = null; if (request) token = getHttpToken(request, tokenName); if (connectionParams) token = getWsToken(connectionParams, tokenName); // if (enableAuthentication && token) // userId = await getUserId(token, apis); return { token, userId, pubsub, ...apis, ...contextObject, } }; const server = new GraphQLServer({ schema, typeDefs, resolvers, context, options: yogaOptions }) let engine; if (engineConfig && engineConfig.apiKey) { engine = new Engine({ engineConfig, endpoint, graphqlPort: port, }) engine.start(); } if (yogaOptions && yogaOptions.disablePlayground) server.express.get(endpoint, graphiqlExpress({ endpointURL: endpoint, subscriptionsEndpoint: subscriptionEndpoint, websocketConnectionParams })) if (engineConfig && engineConfig.apiKey) { server.express.use(engine.expressMiddleware()); } await server.start(() => console.log(`Server is running on http://localhost:${port}`)); const subscriptionServer: any = server.subscriptionServer; subscriptionServer.onOperation = null; subscriptionServer.onConnect = async (connectionParams) => await context({ connectionParams, request: null }); return { server, engine, pubsub, } } async function getRemoteSchema(uri): Promise<GraphQLSchema> { const link = new HttpLink({ uri, fetch }) const introspectionSchema = await introspectSchema(link); const graphcoolSchema = makeRemoteExecutableSchema({ schema: introspectionSchema, link }); return graphcoolSchema; } devEnv(); export default { run, devEnv, getRemoteSchema, } export function createSubscriptionEvent<TContext extends Context>({ eventName, filter = () => true, map = x => x }: SubscriptionEventProps<TContext>) { return { resolve: map, subscribe: withFilter( (obj, props, context: TContext, info) => context.pubsub.asyncIterator(eventName), filter ) } }