Giter VIP home page Giter VIP logo

graphql-server-express-upload's Introduction

graphql-server-express-upload

Graphql Server Express file upload middleware. Used together with apollo-upload-network-interface.

Usage

1. Add graphqlExpressUpload middleware to your Express GraphQL endpoint

import { graphqlExpress, graphiqlExpress } from 'graphql-server-express'
import bodyParser from 'body-parser'
import graphqlExpressUpload from 'graphql-server-express-upload'
import multer from 'multer'

import schema from './schema'

const upload = multer({
  dest: config.tmp.path,
})

app.use('/graphql',
  upload.array('files'),
  bodyParser.json(),
  graphqlExpressUpload({ endpointURL: '/graphql' }), // after multer and before graphqlExpress
  graphqlExpress((req) => {
    return {
      schema,
      context: {}
    }
  })
)

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

2. Add UploadedFile scalar to your schema

scalar UploadedFile

3. Add UploadedFile resolver

For now we simply use JSON. In the future we should improve this.

const resolvers = {
  UploadedFile: {
    __parseLiteral: parseJSONLiteral,
    __serialize: value => value,
    __parseValue: value => value,
  }
  ...
}

function parseJSONLiteral(ast) {
  switch (ast.kind) {
    case Kind.STRING:
    case Kind.BOOLEAN:
      return ast.value;
    case Kind.INT:
    case Kind.FLOAT:
      return parseFloat(ast.value);
    case Kind.OBJECT: {
      const value = Object.create(null);
      ast.fields.forEach(field => {
        value[field.name.value] = parseJSONLiteral(field.value);
      });

      return value;
    }
    case Kind.LIST:
      return ast.values.map(parseJSONLiteral);
    default:
      return null;
  }
}

4. Add mutation on the server

Schema definition

uploadProfilePicture(id: Int!, files: [UploadedFile!]!): ProfilePicture

And the mutation function

async uploadProfilePicture(root, { id, files }, context) {
  // you can now access files parameter from variables
  console.log('uploadProfilePicture', { id, files })
  //...
}

5. Add mutation on the client

Example using react-apollo. Don't forget that you need to be using UploadNetworkInterface, because apollo-client does not support multipart/form-data out of the box.

import React, { Component, PropTypes } from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'

class UploadProfilePicture extends Component {

  onSubmit = (fields) => {
    const { user, uploadProfilePicture } = this.props
    // fields.files is an instance of FileList
    uploadProfilePicture(user.id, fields.files)
      .then(({ data }) => {
        console.log('data', data);
      })
      .catch(error => {
        console.log('error', error.message);
      })
  }

  render() {
    return (
      //...
    )
  }

}

const UPLOAD_PROFILE_PICTURE = gql`
  mutation uploadProfilePicture($id: Int!, $files: [UploadedFile!]!) {
    uploadProfilePicture(id: $id, files: $files) {
      id url thumb square small medium large full
    }
  }`

const withFileUpload = graphql(UPLOAD_PROFILE_PICTURE, {
  props: ({ ownProps, mutate }) => ({
    uploadProfilePicture: (id, files) => mutate({
      variables: { id, files },
    }),
  }),
})

export default withFileUpload(UploadProfilePicture)

graphql-server-express-upload's People

Contributors

hribb avatar sandervanhooft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

graphql-server-express-upload's Issues

bodyParser.json

Hi @HriBB ,

I'm not sure about the following. Any suggestion?

The Apollo Server Express setup suggests to use bodyParser.json():

app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema }));

Your suggested setup makes no note of bodyParser.json():

app.use('/graphql',
  upload.array('files'),
  // after multer and before graphqlExpress
  graphqlExpressUpload({ endpointURL: '/graphql' }),
  graphqlExpress((req) => {
    return {
      schema,
      context: {}
    }
  })
)

As you suggest that order is important: where should I put bodyParser.json()?

[Meteor + Apollo] get blob url instead file

hi, I'm try to use this package to upload a file following instruction, but I got a blob url instead file. Here is my code:
in server.js:

 {...}
import graphqlExpressUpload from 'graphql-server-express-upload'
import multer from "multer";

//used your interface package when create apollo server
import { createApolloServer } from 'meteor/apollo';

const WS_PORT =  8083;

const schema = makeExecutableSchema({
  typeDefs:Schema,
  resolvers:resolveFunctions,
});

createApolloServer({
  schema,
},{ graphiql:true});

const httpServer = createServer((request, response) => {
  response.writeHead(404);
  response.end();
});

httpServer.listen(WS_PORT);
const server = new SubscriptionServer({ subscriptionManager }, httpServer);

const app = express();

const storage = multer.diskStorage({
    destination: function (req, file, callback) {
        callback(null, 'uploads');
    },
    filename: function (req, file, callback) {
        console.log(file);
        var filename = file.originalname;
        var fileExtension = filename.split(".")[1];
        callback(null, Date.now() + "." + fileExtension);
    }
});

const upload = multer({
    storage: storage
});

app.use('/graphql',
    upload.array('files'),
    bodyParser.json(),
    graphqlExpressUpload({ endpointURL: '/graphql' }), // after multer and before graphqlExpress
    graphqlExpress((req) => {
        console.log('req', req);
      return {
        schema,
        context: {}
      }
    })
);

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

in resolver.js

async uploadProfilePicture(root, { id, files }, context) {

           //the result of files was like [ { preview: 'blob:http://localhost:3000/8d38f565-7846' } ] }

            console.log('uploadProfilePicture', { id, files })

            return uploadProfilePicture(id, files);
        },

in the result I got a blob url like { preview: 'blob:http://localhost:3000/8d38f565-7846' }
Please help.

Example for handling file on server

@HriBB Do you perhaps have an example how to handle the file on the graphql server?

If I forward it to my REST endpoint, it doesn't seem to be recognised as a file.

My resolver:

...
uploadUserAvatar(root: Function, args: Object) {
      console.log(args.files) // see output below
      Viewer.uploadAvatar(args.token, args.files[0])
      return Viewer.fromToken(args.token)
}
...

Viewer connection:

uploadAvatar(token: string, avatar: Object){
    const options = {
      method: 'POST',
      uri: this.buildRestURL('settings/profile/uploadavatar'),
      headers: {
        'Authorization': `Bearer ${token}`,
      },
      body: {
        avatar,
      },
      json: true,
    }
    return rp(options)
      .then((res) => {
        console.log("\nuploadAvatar SUCCESS:", res)
        return {
          ...res.data,
          token
        }
      })
      .catch((err) => console.log("\nuploadAvatar ERROR:", err))

Console.log(args.files) from resolver:

[ { fieldname: 'files',
       originalname: 'elephant.jpeg',
       encoding: '7bit',
       mimetype: 'image/jpeg',
       destination: '../tmp/',
       filename: '5b4af2fa02e1cfde3995873a2d7ea959',
       path: '../tmp/5b4af2fa02e1cfde3995873a2d7ea959',
       size: 3536 } ] }

Only works with top-level variables

We should probably support FileLists nested in variables. It's common to pass an input type rather than scalars for mutation and query params. It seems like a simple bit of recursion could make this work.

Kind.x in function parseJSONLiteral(ast)

Hi @HriBB,

In the readme.md file the function parseJSONLiteral(ast) references Kind.STRING, Kind.BOOLEAN, Kind.INT, Kind.FLOAT, Kind.OBJECT.

Where are these coming from? / How should I define them?

From the readme.md:

function parseJSONLiteral(ast) {
  switch (ast.kind) {
    case Kind.STRING:
    case Kind.BOOLEAN:
      return ast.value;
    case Kind.INT:
    case Kind.FLOAT:
      return parseFloat(ast.value);
    case Kind.OBJECT: {
      const value = Object.create(null);
      ast.fields.forEach(field => {
        value[field.name.value] = parseJSONLiteral(field.value);
      });

      return value;
    }
    case Kind.LIST:
      return ast.values.map(parseJSONLiteral);
    default:
      return null;
  }
}

Export UploadedFile scalar resolver

Step 3 in the instructions, "Add UploadedFile resolver" is too hard to follow.

Pasting the example code into a typical Apollo resolvers.js file wont work because Kind is undefined. Where does that come from, how do you import it? And what is this ast parameter?

This looks pretty boilerplate, why not export the scalar resolver for easy setup?

Something like:

import {UploadedFileResolver} from 'graphql-server-express-upload'

export default {
  UploadedFile: UploadedFileResolver,
  Query: { ... },
  Mutation: {  ... }
}

In the meantime, I can't work out how to get the resolver to work so any clarification would be super helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.