Last active
May 23, 2018 07:24
-
-
Save joeynimu/7dd0c4440e7f34db96c2521f993b84ea to your computer and use it in GitHub Desktop.
Sample GraphQL Server with Schema Stitching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
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
Note that your run() function will never throw since it’s an async function. You probably want run().catch(error => ...