Skip to content

Commit 6831fef

Browse files
committed
Version 1.0.0.
0 parents  commit 6831fef

File tree

9 files changed

+1870
-0
lines changed

9 files changed

+1870
-0
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# http://editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
indent_style = space
9+
indent_size = 2
10+
trim_trailing_whitespace = true
11+
insert_final_newline = true

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
npm-debug.log
3+
.DS_Store
4+
dist

apollo-upload-logo.svg

Lines changed: 5 additions & 0 deletions
Loading

package.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"name": "apollo-upload-server",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"author": {
6+
"name": "Jayden Seric",
7+
"email": "[email protected]",
8+
"url": "http://jaydenseric.com"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/jaydenseric/apollo-upload-server.git"
13+
},
14+
"dependencies": {
15+
"formidable": "^1.1.1",
16+
"mkdirp": "^0.5.1",
17+
"object-path": "^0.11.3"
18+
},
19+
"devDependencies": {
20+
"babel-eslint": "^7.1.1",
21+
"babel-preset-env": "^1.1.8",
22+
"babel-preset-stage-0": "^6.22.0",
23+
"eslint": "^3.16.0",
24+
"eslint-config-standard": "^6.2.1",
25+
"eslint-plugin-promise": "^3.4.2",
26+
"eslint-plugin-standard": "^2.0.1",
27+
"rollup": "^0.41.4",
28+
"rollup-plugin-babel": "^2.7.1",
29+
"rollup-watch": "^3.2.2"
30+
},
31+
"scripts": {
32+
"lint": "eslint .",
33+
"prebuild": "npm run lint",
34+
"build": "rollup --config",
35+
"build:watch": "npm run build -- --watch",
36+
"prepublish": "npm run build"
37+
},
38+
"main": "dist/apollo-upload-server.js",
39+
"module": "dist/apollo-upload-server.module.js",
40+
"files": [
41+
"src",
42+
"dist"
43+
],
44+
"engines": {
45+
"node": ">=6.4"
46+
},
47+
"babel": {
48+
"presets": [
49+
[
50+
"env",
51+
{
52+
"targets": {
53+
"node": 6.4
54+
},
55+
"modules": false,
56+
"loose": true
57+
}
58+
],
59+
"stage-0"
60+
]
61+
},
62+
"eslintConfig": {
63+
"parser": "babel-eslint",
64+
"extends": [
65+
"standard"
66+
]
67+
}
68+
}

readme.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# ![Apollo upload server](https://cdn.rawgit.com/jaydenseric/apollo-upload-server/v1.0.0/apollo-upload-logo.svg)
2+
3+
![NPM version](https://img.shields.io/npm/v/apollo-upload-server.svg?style=flat-square) ![Github issues](https://img.shields.io/github/issues/jaydenseric/apollo-upload-server.svg?style=flat-square) ![Github stars](https://img.shields.io/github/stars/jaydenseric/apollo-upload-server.svg?style=flat-square)
4+
5+
In combination with [Apollo upload client](https://github.com/jaydenseric/apollo-upload-client), enhances [Apollo](http://apollodata.com) for intuitive file uploads via GraphQL mutations or queries.
6+
7+
- Node >= 6.4 supported.
8+
- [MIT license](https://en.wikipedia.org/wiki/MIT_License).
9+
10+
## Setup
11+
12+
Install with [Yarn](https://yarnpkg.com):
13+
14+
```
15+
yarn add apollo-upload-server
16+
```
17+
18+
Add the server middleware just before `graphqlExpress`:
19+
20+
```js
21+
import {apolloUploadExpress} from 'apollo-upload-server'
22+
```
23+
24+
```js
25+
app.use(
26+
'/graphql',
27+
bodyParser.json(),
28+
apolloUploadExpress({
29+
// Optional, defaults to OS temp directory
30+
uploadDir: '/tmp/uploads'
31+
}),
32+
graphqlExpress()
33+
)
34+
```
35+
36+
Add this type to your schema:
37+
38+
```graphql
39+
input File {
40+
name: String!,
41+
type: String!,
42+
size: Int!,
43+
path: String!
44+
}
45+
```
46+
47+
Also setup [Apollo upload client](https://github.com/jaydenseric/apollo-upload-client).
48+
49+
## Usage
50+
51+
Once setup, you will be able to use [`File`](https://developer.mozilla.org/en/docs/Web/API/File) objects, [`FileList`](https://developer.mozilla.org/en/docs/Web/API/FileList) objects, or `File` arrays within query or mutation input variables. See the [client usage](https://github.com/jaydenseric/apollo-upload-client#Usage).
52+
53+
The files upload to a temp directory. The file path and metadata will be avalable under the variable name in the resolver in the shape of the input `File` type in the GraphQL schema.
54+
55+
The resolver variable will hold an array if it is populated as a list (`FileList` or `File` array) on the clienteven if the list has only 1 file.
56+
57+
### Single file
58+
59+
In types:
60+
61+
```graphql
62+
type Mutation {
63+
updateUserAvatar (userId: String!, avatar: File!): User!
64+
}
65+
```
66+
67+
In resolvers:
68+
69+
```js
70+
updateUserAvatar (root, {userId, avatar}) {
71+
// Auth
72+
// Update avatar
73+
console.log(`New avatar for user ${userId} is ${avatar.size} bytes`)
74+
// Return fresh user data
75+
}
76+
```
77+
78+
See [client usage for this example](https://github.com/jaydenseric/apollo-upload-client#Single-file).
79+
80+
### Multiple files
81+
82+
In types:
83+
84+
```graphql
85+
type Mutation {
86+
updateGallery (galleryId: String!, images: [File!]!): Gallery!
87+
}
88+
```
89+
90+
In resolvers:
91+
92+
```js
93+
updateGallery (root, {galleryId, images}) {
94+
// Auth
95+
// Update gallery
96+
console.log(`New images for gallery ${userId}:`)
97+
images.forEach((image, index) => console.log(`Image ${index} is ${image.size} bytes`))
98+
// Return fresh gallery data
99+
}
100+
```
101+
102+
See [client usage for this example](https://github.com/jaydenseric/apollo-upload-client#Multiple-files).
103+
104+
## Caveats
105+
106+
- No max upload file size option yet.
107+
108+
## Inspiration
109+
110+
- [@HriBB](https://github.com/HriBB)’s [apollo-upload-network-interface](https://github.com/HriBB/apollo-upload-network-interface) and [graphql-server-express-upload](https://github.com/HriBB/graphql-server-express-upload) projects.
111+
- [@danielbuechele](https://github.com/danielbuechele)’s [Medium article](https://medium.com/@danielbuechele/file-uploads-with-graphql-and-apollo-5502bbf3941e).
112+
- [@jessedvrs](https://github.com/jessedvrs)’s [example code](https://github.com/HriBB/apollo-upload-network-interface/issues/5#issuecomment-280018715).

rollup.config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import babel from 'rollup-plugin-babel'
2+
3+
const pkg = require('./package.json')
4+
5+
export default {
6+
entry: 'src/index.js',
7+
external: Object.keys(pkg.dependencies),
8+
plugins: [
9+
babel()
10+
],
11+
targets: [{
12+
dest: pkg['main'],
13+
format: 'cjs',
14+
sourceMap: true
15+
}, {
16+
dest: pkg['module'],
17+
format: 'es',
18+
sourceMap: true
19+
}]
20+
}

src/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import mkdirp from 'mkdirp'
2+
import formidable from 'formidable'
3+
import objectPath from 'object-path'
4+
5+
export function apolloUploadExpress ({
6+
// Defaults to the OS temp directory
7+
uploadDir
8+
}) {
9+
// Ensure provided upload directory exists
10+
if (uploadDir) mkdirp.sync(uploadDir)
11+
12+
return (request, response, next) => {
13+
// Skip if there are no uploads
14+
if (!request.is('multipart/form-data')) return next()
15+
16+
// Parse the multipart request
17+
const form = formidable.IncomingForm({
18+
uploadDir
19+
})
20+
form.parse(request, (error, fields, files) => {
21+
if (error) return next(error)
22+
23+
// Decode the GraphQL variables
24+
fields.variables = JSON.parse(fields.variables)
25+
26+
// File field names contain the original path to
27+
// the File object in the GraphQL input variables.
28+
// Relevent data for each uploaded file now gets
29+
// placed back in the variables.
30+
const variables = objectPath(fields.variables)
31+
Object.keys(files).forEach(variablesPath => {
32+
const {name, type, size, path} = files[variablesPath]
33+
variables.set(variablesPath, {name, type, size, path})
34+
})
35+
36+
// Apollo expects the fields in the request body
37+
request.body = fields
38+
39+
// Request ready for Apollo middleware
40+
next()
41+
})
42+
}
43+
}

0 commit comments

Comments
 (0)