How would you do file uploads in a React-Relay app?

后端 未结 5 1287
时光说笑
时光说笑 2020-12-30 03:45

A file upload seems like a mutation. It\'s often accompanied by other data. But it\'s a big binary blob, so I\'m not sure how GraphQL can deal with it. How would you inte

5条回答
  •  清歌不尽
    2020-12-30 03:47

    I am merely sharing the findings of Marc-Andre Giroux from his blog, which is Rails-specific, so I will try to make it more generic, and providing the details of the answer provided by @Nick.

    There are 2 parts:

    • Client-side Javascript code
    • Server-side server-specific code

    Client-side Javascript Code

    The client-side code further consists of 2 parts:

    1. The mutation to upload file, which extends Relay.Mutation (UploadFileMutation)

      // The actual mutation
      class UploadFileMutation extends Relay.Mutation {
        getFiles() {
          return {
            file: this.props.file,
          };
        }
      
        // ... Rest of your mutation
      }
      
    2. The component that contains the React component (FileUploader) to render the UI for selecting the file, and calls the mutation to do the upload

      // A react component to upload a file
      class FileUploader extends React.Component {
        onSubmit() {
          const name = this.refs.name.value;
          const file = this.refs.fileInput.files.item(0);
          Relay.Store.update(
            new UploadFileMutation({
              name: name,
              file: file,
            })
          );
        }
      
        // ... Rest of React component, e.g., render()
      }
      

    Server-side Server-Specific Code

    The server-side code also consists of 2 parts:

    1. The part to handle retrieving the uploaded file in MIME multipart format and pass it to the Mutation defined in the GraphQL schema. We provide NodeJS and Rails examples, which should help you derive solutions for other servers.

    For NodeJS Express server (extracted from express-graqphl test cases as pointed out by @Nick):

        import multer from 'multer';
    
        var app = express();
        var graphqlHTTP = require('express-graphql');
    
        // Multer provides multipart form data parsing.
        var storage = multer.memoryStorage();
    
        app.use(urlString(), multer({ storage }).single('file'));
    
        // Providing the request, which contains the file MIME
        // multipart as `rootValue` to enable it to
        // be accessible from within Schema resolve functions.
        app.use(urlString(), graphqlHTTP(req => {
          return {
            schema: YourMutationSchema,
            rootValue: { request: req }
          };
        }));
    

    Similarly, for a non-JS server, e.g., RubyOnRails:

        def create
          query_string = params[:query]
          query_variables = ensure_hash(params[:variables]) || {}
    
          query = GraphQL::Query.new(
            YourSchema,
            query_string,
            variables: query_variables,
            # Shove the file MIME multipart into context to make it
            # accessible by GraphQL Schema Mutation resolve methods
            context: { file: request.params[:file] }
         )
    
    1. The Mutation can retrieve the file MIME multipart passed to it

    For Javascript GraphQL Schema:

        var YourMutationSchema = new GraphQLSchema({
          query: new GraphQLObjectType({
            // ... QueryType Schema
          }),
          mutation: new GraphQLObjectType({
            name: 'MutationRoot',
            fields: {
              uploadFile: {
                type: UploadedFileType,
                resolve(rootValue) {
                  // Access file MIME multipart using
                  const _file = rootValue.request.file;
    
                  // ... Do something with file
                }
              }
            }
          })
        });
    

    For Rails GraphQL Schema:

        AddFileMutation = GraphQL::Relay::Mutation.define do
          name "AddFile"
          input_field :name, !types.String
    
          # ... Add your standard mutation schema stuff here
    
          resolve -> (args, ctx) {
            # Retrieve the file MIME multipart
            file = ctx[:file]
            raise StandardError.new("Expected a file") unless file
    
            # ... Do something with file
          }
        end
    

提交回复
热议问题