Skip to content

Instantly share code, notes, and snippets.

@joeynimu
Last active May 23, 2018 07:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joeynimu/7dd0c4440e7f34db96c2521f993b84ea to your computer and use it in GitHub Desktop.
Save joeynimu/7dd0c4440e7f34db96c2521f993b84ea to your computer and use it in GitHub Desktop.
Sample GraphQL Server with Schema Stitching
import express from 'express';
import bodyParser from 'body-parser';
import Dataloader from 'DataLoader';
import {
graphqlExpress,
graphiqlExpress
} from 'apollo-server-express';
import {
makeRemoteExecutableSchema,
mergeSchemas,
introspectSchema
} from 'graphql-tools';
import {
ApolloEngine
} from 'apollo-engine';
import {
HttpLink
} from 'apollo-link-http';
import fetch from 'node-fetch';
import {
APP_PORT,
BOOKING_SERVICE_URL,
FARMER_SERVICE_URL,
SOURCING_AREA_SERVICE_URL,
PRODUCT_SERVICE_URL,
BASE_API_URL,
STAFF_ALLOC_SERVICE_URL,
USER_SERVICE_URL,
APOLLO_KEY
} from './constants';
const run = async () => {
const createRemoteSchema = async (uri) => {
const link = new HttpLink({
uri,
fetch
})
const schema = await introspectSchema(link);
return makeRemoteExecutableSchema({
schema,
link
});
}
// @todo split this
const linkTypeDefs = `
extend type BookingGQL {
farm: FarmGQL
collection_center: CollectionCenterGQL
product: ProductGQL
scout: UserGQL
}
extend type FarmGQL {
bookings: [BookingGQL]
collection_center: CollectionCenterGQL
harvest_area: HarvestAreaGQL
}`;
const {
BOOKING_SVC,
FARMER_SVC,
SOURCING_AREA_SVC,
PRODUCT_SVC,
STAFF_ALLOC_SVC,
USER_SVC
} = process.env;
const bookingURL = BOOKING_SVC ? BOOKING_SVC : `${BASE_API_URL}${BOOKING_SERVICE_URL}`;
const farmerURL = FARMER_SVC ? FARMER_SVC : `${BASE_API_URL}${FARMER_SERVICE_URL}`;
const sourcingAreaURL = SOURCING_AREA_SVC ? SOURCING_AREA_SVC : `${BASE_API_URL}${SOURCING_AREA_SERVICE_URL}`;
const productURL = PRODUCT_SVC ? PRODUCT_SVC : `${BASE_API_URL}${PRODUCT_SERVICE_URL}`;
const staffAllocationURL = STAFF_ALLOC_SVC ? STAFF_ALLOC_SVC : `${BASE_API_URL}${STAFF_ALLOC_SERVICE_URL}`;
const userURL = USER_SVC ? USER_SVC : `${BASE_API_URL}${USER_SERVICE_URL}`;
const bookingSchema = await createRemoteSchema(bookingURL);
const farmerSchema = await createRemoteSchema(farmerURL);
const sourcingAreaSchema = await createRemoteSchema(sourcingAreaURL);
const productSchema = await createRemoteSchema(productURL);
const staffAllocationSchema = await createRemoteSchema(staffAllocationURL);
const userSchema = await createRemoteSchema(userURL);
const schema = mergeSchemas({
schemas: [farmerSchema, bookingSchema, sourcingAreaSchema, productSchema,staffAllocationSchema,userSchema, linkTypeDefs],
// @todo split this resolvers
resolvers: mergeInfo => ({
FarmGQL: {
bookings: {
fragment: `fragment FarmGQLFragment on FarmGQL { farm_id }`,
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'bookings_by_farm_id', {
farmId: parent.farm_id,
},
context,
info,
);
},
},
collection_center: {
fragment: `fragment FarmGQLFragment on CollectionCenterGQL { collection_center_id }`,
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'collection_center_by_id', {
collection_center_id: parent.collection_center_id,
},
context,
info,
);
},
},
harvest_area: {
fragment: `fragment FarmGQLFragment on HarvestAreaGQL { harvest_area_id }`,
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'harvest_area_by_id', {
harvest_area_id: parent.harvest_area_id,
},
context,
info,
);
},
},
},
BookingGQL: {
farm: {
fragment: 'fragment BookingGQLFragment on BookingGQL { farm_id }',
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'farm_by_id', {
farmId: parent.farm_id,
},
context,
info,
);
},
},
collection_center: {
fragment: 'fragment BookingGQLFragment on CollectionCenterGQL { collection_center_id }',
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'collection_center_by_id', {
collection_center_id: parent.collection_center_id,
},
context,
info,
);
},
},
product: {
fragment: 'fragment BookingGQLFragment on ProductGQL { product_id }',
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'product_by_id',
{
productId: parent.product_id,
},
context,
info,
);
},
},
scout: {
fragment: 'fragment BookingGQLFragment on UserGQL { user_id }',
resolve(parent, args, context, info) {
return mergeInfo.delegate(
'query',
'user_by_id',
{
userId: parent.booked_by,
},
context,
info,
);
},
}
},
})
});
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.options("/*", function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
res.sendStatus(200);
});
app.use('/graphql', bodyParser.json(), graphqlExpress(req => {
const farmersLoader = new Dataloader(
keys => Promise.all(keys.map(
// what do I pass here
))
)
const loader = {
farmers: farmersLoader
}
return {
context: { loaders },
schema,
tracing: true,
cacheControl: true
}
}));
app.use(
'/graphiql',
graphiqlExpress({
endpointURL: '/graphql'
})
);
const engine = new ApolloEngine({
apiKey: APOLLO_KEY
});
engine.listen({
port: APP_PORT,
expressApp: app,
});
console.log(`Server running. Open http://localhost:${APP_PORT}/graphiql to run queries.`);
}
run().catch(e => console.log(e, e.message, e.stack));
@leebyron
Copy link

Note that your run() function will never throw since it’s an async function. You probably want run().catch(error => ...

@joeynimu
Copy link
Author

ooh yeah...overlooked that picked that from one of the examples. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment