UNPKG

7.74 kBMarkdownView Raw
1# graphql upload typescript (graphql-upload-ts)
2
3[![NPM version](https://badgen.net/npm/v/graphql-upload-ts)](https://npm.im/graphql-upload-ts)
4[![Build Status](https://github.com/meabed/graphql-upload-ts/workflows/CI/badge.svg)](https://github.com/meabed/graphql-upload-ts/actions)
5[![Downloads](https://img.shields.io/npm/dm/graphql-upload-ts.svg)](https://www.npmjs.com/package/graphql-upload-ts)
6[![UNPKG](https://img.shields.io/badge/UNPKG-OK-179BD7.svg)](https://unpkg.com/browse/graphql-upload-ts@latest/)
7
8Minimalistic and developer friendly middleware and an [`Upload` scalar](#class-graphqlupload) to add support for [GraphQL multipart requests](https://github.com/jaydenseric/graphql-multipart-request-spec) (file uploads via queries and
9mutations) to various Node.js GraphQL servers.
10
11#### Acknowledgements
12
13This module was forked from the amazing [`graphql-upload-minimal`](https://npm.im/graphql-upload-minimal). The original module is exceptionally well documented and well written. It was very easy to fork and amend.
14
15I needed to support typescript to use it properly in typescript projects.
16
17#### This project is written in typescript
18
19- TypeScript support.
20- And using a bit less memory.
21- And a bit faster.
22- More Examples and documentation
23
24
25#### Does not follow strict [specification](https://github.com/jaydenseric/graphql-multipart-request-spec)
26
27You can't have same file referenced twice in a GraphQL query/mutation.
28
29## Support
30
31The following environments are known to be compatible:
32
33- [Node.js](https://nodejs.org) versions 12, 14, 16, and 18. It works in Node 10 even though the unit tests fail.
34- [Koa](https://koajs.com)
35- [Express.js](https://expressjs.com)
36
37See also [GraphQL multipart request spec server implementations](https://github.com/jaydenseric/graphql-multipart-request-spec#server).
38
39## Setup
40
41To install [`graphql-upload-ts`](https://npm.im/graphql-upload-ts) and the [`graphql`](https://npm.im/graphql) peer dependency from [npm](https://npmjs.com) run:
42
43```shell
44npm install graphql-upload-ts graphql
45# or
46yarn add graphql-upload-ts graphql
47```
48
49Use the [`graphqlUploadKoa`](#function-graphqluploadkoa) or [`graphqlUploadExpress`](#function-graphqluploadexpress) middleware just before GraphQL middleware. Alternatively, use [`processRequest`](#function-processrequest) to create a
50custom middleware.
51
52A schema built with separate SDL and resolvers (e.g. using [`makeExecutableSchema`](https://apollographql.com/docs/graphql-tools/generate-schema#makeExecutableSchema)) requires the [`Upload` scalar](#class-graphqlupload) to be setup.
53
54## Usage
55
56[Clients implementing the GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec#client) upload files as [`Upload` scalar](#class-graphqlupload) query or mutation variables. Their resolver values are
57promises that resolve [file upload details](#type-fileupload) for processing and storage. Files are typically streamed into cloud storage but may also be stored in the filesystem.
58
59### Express.js
60
61Minimalistic code example showing how to upload a file along with arbitrary GraphQL data and save it to an S3 bucket.
62
63Express.js middleware. You must put it before the main GraphQL sever middleware. Also, **make sure there is no other Express.js middleware which parses `multipart/form-data`** HTTP requests before the `graphqlUploadExpress` middleware!
64
65```js
66const express = require('express');
67const expressGraphql = require('express-graphql');
68const { graphqlUploadExpress } = require('graphql-upload-ts');
69
70express()
71 .use(
72 '/graphql',
73 graphqlUploadExpress({
74 maxFileSize: 10000000,
75 maxFiles: 10,
76 // If you are using framework around express like [ NestJS or Apollo Serve ]
77 // use this options overrideSendResponse to allow nestjs to handle response errors like throwing exceptions
78 overrideSendResponse: false
79 }),
80 expressGraphql({ schema: require('./my-schema') })
81 )
82 .listen(3000);
83```
84
85GraphQL schema:
86
87```graphql
88scalar Upload
89input DocumentUploadInput {
90 docType: String!
91 file: Upload!
92}
93
94type SuccessResult {
95 success: Boolean!
96 message: String
97}
98type Mutations {
99 uploadDocuments(docs: [DocumentUploadInput!]!): SuccessResult
100}
101```
102
103GraphQL resolvers:
104
105```js
106const { S3 } = require('aws-sdk');
107
108const resolvers = {
109 Upload: require('graphql-upload-ts').GraphQLUpload,
110
111 Mutations: {
112 async uploadDocuments(root, { docs }, ctx) {
113 try {
114 const s3 = new S3({ apiVersion: '2006-03-01', params: { Bucket: 'my-bucket' } });
115
116 for (const doc of docs) {
117 const { createReadStream, filename /*, fieldName, mimetype, encoding */ } = await doc.file;
118 const Key = `${ctx.user.id}/${doc.docType}-${filename}`;
119 await s3.upload({ Key, Body: createReadStream() }).promise();
120 }
121
122 return { success: true };
123 } catch (error) {
124 console.log('File upload failed', error);
125 return { success: false, message: error.message };
126 }
127 },
128 },
129};
130```
131
132### Koa
133
134See the [example Koa server and client](https://github.com/jaydenseric/apollo-upload-examples).
135
136
137### Uploading multiple files
138
139When uploading multiple files you can make use of the `fieldName` property to keep track of an identifier of the uploaded files. The fieldName is equal to the passed `name` property of the file in the `multipart/form-data` request. This can
140be modified to contain an identifier (like a UUID), for example using the `formDataAppendFile` in the commonly used [`apollo-upload-link`](https://github.com/jaydenseric/apollo-upload-client#function-formdataappendfile) library.
141
142GraphQL schema:
143
144```graphql
145scalar Upload
146input DocumentUploadInput {
147 docType: String!
148 files: [Upload!]
149}
150
151type SuccessResult {
152 success: Boolean!
153 message: String
154}
155type Mutations {
156 uploadDocuments(docs: [DocumentUploadInput!]!): SuccessResult
157}
158```
159
160GraphQL resolvers:
161
162```js
163const { S3 } = require('aws-sdk');
164
165const resolvers = {
166 Upload: require('graphql-upload-ts').GraphQLUpload,
167
168 Mutations: {
169 async uploadDocuments(root, { docs }, ctx) {
170 try {
171 const s3 = new S3({ apiVersion: '2006-03-01', params: { Bucket: 'my-bucket' } });
172
173 for (const doc of docs) {
174 // fieldName contains the "name" property from the multipart/form-data request.
175 // Use it to pass an identifier in order to store the file in a consistent manner.
176 const { createReadStream, filename, fieldName /*, mimetype, encoding */ } = await doc.file;
177 const Key = `${ctx.user.id}/${doc.docType}-${fieldName}`;
178 await s3.upload({ Key, Body: createReadStream() }).promise();
179 }
180
181 return { success: true };
182 } catch (error) {
183 console.log('File upload failed', error);
184 return { success: false, message: error.message };
185 }
186 },
187 },
188};
189```
190
191## Tips
192- If you are using framework around express like [ NestJS or Apollo Serve ] use the option `overrideSendResponse` eg: `graphqlUploadExpress({ overrideSendResponse: false })` to allow nestjs to handle response errors like throwing exceptions.
193
194## Architecture
195
196The [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec) allows a file to be used for multiple query or mutation variables (file deduplication), and for variables to be used in multiple places.
197GraphQL's resolvers need to be able to manage independent file streams.
198
199[`busboy`](https://npm.im/busboy) parses multipart request streams. Once the `operations` and `map` fields have been parsed, [`Upload` scalar](#class-graphqlupload) values in the GraphQL operations are populated with promises, and the
200operations are passed down the middleware chain to GraphQL resolvers.