-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(graphqlExpressUpload): Initial release
- Loading branch information
Showing
4 changed files
with
224 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
sudo: false | ||
language: node_js | ||
cache: | ||
directories: | ||
- node_modules | ||
notifications: | ||
email: false | ||
node_js: | ||
- '4' | ||
before_install: | ||
- npm i -g npm@^2.0.0 | ||
before_script: | ||
- npm prune | ||
after_success: | ||
- npm run semantic-release | ||
branches: | ||
except: | ||
- /^v\d+\.\d+\.\d+$/ |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,146 @@ | ||
# graphql-server-express-upload | ||
Graphql Server Express file upload middleware | ||
|
||
Graphql Server Express file upload middleware. Used together with [UploadNetworkInterface](https://github.com/HriBB/apollo-upload-network-interface/releases). | ||
|
||
## Usage | ||
|
||
#### 1. Add `graphqlExpressUpload` middleware to your Express GraphQL endpoint | ||
|
||
``` | ||
import { graphqlExpress, graphiqlExpress } from 'graphql-server-express' | ||
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'), | ||
// after multer and before graphqlExpress | ||
graphqlExpressUpload({ endpointURL: '/graphql' }), | ||
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](https://github.com/HriBB/apollo-upload-network-interface/releases), 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 ADD_SALON_RESOURCE_PICTURE = gql` | ||
mutation uploadProfilePicture($id: Int!, $files: [UploadedFile!]!) { | ||
uploadProfilePicture(id: $id, files: $files) { | ||
id url thumb square small medium large full | ||
} | ||
}` | ||
const withFileUpload = graphql(ADD_SALON_RESOURCE_PICTURE, { | ||
props: ({ ownProps, mutate }) => ({ | ||
uploadProfilePicture: (id, files) => mutate({ | ||
variables: { id, files }, | ||
}), | ||
}), | ||
}) | ||
export default withFileUpload(UploadProfilePicture) | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "graphql-server-express-upload", | ||
"description": "GraphQL Server Express file upload middleware", | ||
"main": "src/index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"semantic-release": "semantic-release pre && npm publish && semantic-release post" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/HriBB/graphql-server-express-upload.git" | ||
}, | ||
"keywords": [ | ||
"apollo", | ||
"graphql", | ||
"server", | ||
"express", | ||
"file", | ||
"upload", | ||
"middleware" | ||
], | ||
"author": "Bojan Hribernik", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/HriBB/graphql-server-express-upload/issues" | ||
}, | ||
"homepage": "https://github.com/HriBB/graphql-server-express-upload#readme", | ||
"devDependencies": { | ||
"semantic-release": "^4.3.5" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module.exports = function graphqlServerExpressUpload(options) { | ||
function isUpload(req) { | ||
return Boolean( | ||
req.baseUrl === options.endpointURL && | ||
req.method === 'POST' && | ||
req.is('multipart/form-data') | ||
); | ||
} | ||
return function(req, res, next) { | ||
if (!isUpload(req)) { | ||
return next(); | ||
} | ||
var files = req.files; | ||
var body = req.body; | ||
var variables = JSON.parse(body.variables); | ||
// append files to variables | ||
files.forEach(file => { | ||
if (!variables[file.fieldname]) { | ||
variables[file.fieldname] = []; | ||
} | ||
variables[file.fieldname].push(file); | ||
}) | ||
req.body = { | ||
operationName: body.operationName, | ||
query: body.query, | ||
variables: variables | ||
}; | ||
return next(); | ||
} | ||
} |