Closed raulk closed 6 years ago
@pcardune – it would be great if you could add your thoughts here!
I think the primary use case for this is when you are developing an application that is a mix between a decentralized web app and a centralized web app, and you want to query all the data through a single API.
Our application collects data from a bunch of different data sources for which the ethereum blockchain is just one. On any given page, we might be querying data from 3 different sources. To reduce the number of round trips, it's important for all these data sources to be queryable through a single graphql endpoint.
There are three ways (that I know of) that you can combine multiple data sources in a single graphql API:
Have separate graphql endpoints for each data source and merge the associated schemas using the mergeSchemas
and makeRemoteExecutableSchema
utilities from the graphql-tools
library. There are two major downsides to this approach:
Have a single graphql endpoint where you directly import GraphQLSchema
objects and merge them with mergeSchemas
. This solves some problems and not others:
Have a single graphql endpoint where you directly import resolvers, which then get combined into a single GraphQLSchema
. This solves all the problems in the above two scenarios.
I think ethql
should support all three of the above scenarios. Here is an example of what this might look like in code:
Case 1: proxy to a separate ethql server
const EthQL = require('ethql');
const express = require('express');
const graphqlHTTP = require('express-graphql');
const fetch = require('node-fetch');
const { HttpLink } = require('apollo-link-http');
const {
mergeSchemas,
makeRemoteExecutableSchema,
transformSchema,
introspectSchema,
RenameTypes,
} = require('graphql-tools');
const otherSchema = require('./someSchema');
export default async function startServer() {
await EthQL.startServer(4000);
const link = new HttpLink({ uri: 'http://localhost:4000', fetch });
const ethqlSchema = makeRemoteExecutableSchema({
schema: introspectSchema(link),
link,
});
const schema = mergeSchemas({
schemas: [
transformSchema(ethqlSchema, [new RenameTypes(name => `EthQL${name}`)]),
otherSchema,
],
});
const app = express();
app.use('/graphql', graphqlHTTP({ schema }));
app.listen(4001);
}
Case 2: delegate to ethql schema without starting a separate server
const { schema: ethqlSchema } = require('ethql');
const express = require('express');
const graphqlHTTP = require('express-graphql');
const { mergeSchemas, transformSchema, RenameTypes } = require('graphql-tools');
const otherSchema = require('./someSchema');
export default async function startServer() {
const schema = mergeSchemas({
schemas: [
transformSchema(ethqlSchema, [new RenameTypes(name => `EthQL${name}`)]),
otherSchema,
],
});
const app = express();
app.use('/graphql', graphqlHTTP({ schema }));
app.listen(4001);
}
Case 3: directly embedding ethql fields / resolvers into your own graphql object types
const { accountField: ethqlAccountField } = require('ethql');
const { GraphQLSchema, GraphQLObjectType } = require('graphql');
const express = require('express');
const graphqlHTTP = require('express-graphql');
const otherField = require('./otherField');
export default async function startServer() {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: () => ({
account: ethqlAccountField,
otherField: otherField,
}),
}),
});
const app = express();
app.use('/graphql', graphqlHTTP({ schema }));
app.listen(4001);
}
I think at the very least, ethql should support case 2. Case 3 will require a bit more thought to figure out the granularity of the APIs that ethql would export.
ethql should be published as an npm module that other projects can import and add to their applications. This is a placeholder description that will be enhanced as the requirement is specified further.