AEB-labs / graphql-weaver

A tool to combine, link and transform GraphQL schemas
MIT License
240 stars 20 forks source link

woking with uploads via apollo-upload-server #42

Open terion-name opened 6 years ago

terion-name commented 6 years ago

Hello.

Did you try/had success to use weaver together with uploads?

I have such gateway (it fetches backends per request — this is needed by design):

import {HttpLink} from 'apollo-link-http';
import fetch from 'node-fetch';

import express from 'express'
import * as bodyParser from 'body-parser-graphql'
import { apolloUploadExpress, GraphQLUpload } from 'apollo-upload-server'
import {graphiqlExpress, graphqlExpress} from 'apollo-server-express'
import {
  introspectSchema,
  makeRemoteExecutableSchema,
} from 'graphql-tools'
import {weaveSchemas} from "graphql-weaver";
import jwt from 'jsonwebtoken';
import createLocaleMiddleware from 'express-locale';
import fetchServices from "./utils/fetchServices";

require('dotenv').config();

const cors = require('cors');

const start = async function () {

  const app = express();

  const PORT = process.env.PORT || 3000;

  app.use(cors());
  app.post('/graphql', bodyParser.graphql());
  app.post('/graphql', apolloUploadExpress());

  app.use('/graphql', async function (req, res, next) {
    // should to fetch this dynamically
    const services = await fetchServices();

    const serviceContainers = await Promise.all(services.map(async (service) => {
      const dosvitMeta = req._dosvit;
      const headers = {};
      // ... some magic with headers
      const link = new HttpLink({uri: service.url, fetch, headers});

      const remoteSchema = makeRemoteExecutableSchema({
        schema: await introspectSchema(link),
        link
      });
      return {service, remoteSchema};
    }));

    const transformedSchema = await weaveSchemas({
      endpoints: serviceContainers.map(s => {
        return {
          typePrefix: s.service.prefix,
          namespace: s.service.namespace,
          schema: s.remoteSchema
        }
      })
    });

    return graphqlExpress({schema: transformedSchema})(req, res, next);
  });

  app.use('/graphiql',
      graphiqlExpress({
        endpointURL: '/graphql',
      }),
  );

  app.listen(PORT);
};

start();

When I try to upload a file server fails with error:

FileStreamDisconnectUploadError: Request disconnected during file upload stream parsing.
    at IncomingMessage.request.on (.../local-gateway/node_modules/apollo-upload-server/lib/middleware.js:151:15)
    at IncomingMessage.emit (events.js:182:13)
    at resOnFinish (_http_server.js:564:7)
    at ServerResponse.emit (events.js:182:13)
    at onFinish (_http_outgoing.js:683:10)
    at onCorkedFinish (_stream_writable.js:666:5)
    at afterWrite (_stream_writable.js:480:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
Emitted 'error' event at:
    at IncomingMessage.request.on (.../local-gateway/node_modules/apollo-upload-server/lib/middleware.js:149:32)
    at IncomingMessage.emit (events.js:182:13)
    [... lines matching original stack trace ...]
    at process._tickCallback (internal/process/next_tick.js:63:19)

Absolutely undebugable :(

First thought is that apollo-upload-server needs an Upload scalar type, and after weaveSchemas — it is prefixed (to NewsUpload for example).

To begin with: Is there a way to not prefix scalars or certain types?

Or in general did you use this with uploads?

Yogu commented 6 years ago

I don't know apollo-upload-server, so I haven't tried to use it with graphql-weaver. The spec says you need a multipart-request enabled client; the default HttpGraphQLClient only supports HTTP with POST. Maybe you could provide your own GraphQLClient implementation, but you would somehow need to connect this to the apollo-upload-server, e.g. through the GraphQL context object.

The scalar prefixing is another issue, related to #13.