Skip to content

Commit 2ba21e7

Browse files
committed
graphql-upload -> yoga file uploads
1 parent a095e2c commit 2ba21e7

File tree

8 files changed

+39
-112
lines changed

8 files changed

+39
-112
lines changed

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
"graphql": "^16.9.0",
8383
"graphql-parse-resolve-info": "^4.14.0",
8484
"graphql-scalars": "^1.22.4",
85-
"graphql-upload": "^16.0.2",
8685
"graphql-yoga": "^5.7.0",
8786
"human-format": "^1.2.0",
8887
"image-size": "^1.0.2",
@@ -126,7 +125,6 @@
126125
"@tsconfig/strictest": "^2.0.2",
127126
"@types/common-tags": "^1.8.3",
128127
"@types/ffprobe": "^1.1.7",
129-
"@types/graphql-upload": "^16.0.4",
130128
"@types/jest": "^29.5.7",
131129
"@types/jsonwebtoken": "^9.0.4",
132130
"@types/lodash": "^4.14.200",

src/common/file-upload.scalar.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { GraphQLError, GraphQLScalarType } from 'graphql';
2+
3+
export const FileUploadScalar = new GraphQLScalarType({
4+
name: 'Upload',
5+
description: 'The `Upload` scalar type represents a file upload.',
6+
parseValue(value) {
7+
if (value instanceof File) {
8+
return value;
9+
}
10+
throw new GraphQLError('Upload value invalid.');
11+
},
12+
parseLiteral(node) {
13+
throw new GraphQLError('Upload literal unsupported.', { nodes: node });
14+
},
15+
serialize() {
16+
throw new GraphQLError('Upload serialization unsupported.');
17+
},
18+
});
19+
20+
export type FileUpload = File;

src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export * from './db-unique.decorator';
1515
export * from './disabled.decorator';
1616
export * from './mutation-placeholder.output';
1717
export * from './exceptions';
18+
export * from './file-upload.scalar';
1819
export * from './field-selection';
1920
export * from './fields.pipe';
2021
export * from './filter-field';

src/common/scalars.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Type } from '@nestjs/common';
22
import { CustomScalar } from '@nestjs/graphql';
33
import { GraphQLScalarType } from 'graphql';
4-
import UploadScalar from 'graphql-upload/GraphQLUpload.mjs';
4+
import { FileUploadScalar } from './file-upload.scalar';
55
import { DateScalar, DateTimeScalar } from './luxon.graphql';
66
import { InlineMarkdownScalar, MarkdownScalar } from './markdown.scalar';
77
import { RichTextScalar } from './rich-text.scalar';
@@ -14,7 +14,7 @@ export const getRegisteredScalars = (): Scalar[] => [
1414
DateScalar,
1515
DateTimeScalar,
1616
RichTextScalar,
17-
UploadScalar,
17+
FileUploadScalar,
1818
UrlScalar,
1919
MarkdownScalar,
2020
InlineMarkdownScalar,

src/components/file/dto/upload.dto.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Field, InputType, ObjectType } from '@nestjs/graphql';
22
import { stripIndent } from 'common-tags';
3-
import UploadScalar from 'graphql-upload/GraphQLUpload.mjs';
4-
import type { FileUpload } from 'graphql-upload/Upload.mjs';
5-
import { ID, IdField } from '~/common';
3+
import { FileUpload, FileUploadScalar, ID, IdField } from '~/common';
64
import { MediaUserMetadata } from '../media/media.dto';
75

86
@ObjectType()
@@ -31,15 +29,15 @@ export abstract class CreateDefinedFileVersionInput {
3129
})
3230
readonly uploadId?: ID;
3331

34-
@Field(() => UploadScalar, {
32+
@Field(() => FileUploadScalar, {
3533
description: stripIndent`
3634
A file directly uploaded.
3735
This is mainly here to allow usage with Apollo Studio/Sandbox.
38-
For production, prefer the \`url\` from the \`RequestUploadOutput\`.
36+
For production, prefer a PUT request to the \`url\` from the \`RequestUploadOutput\`.
3937
`,
4038
nullable: true,
4139
})
42-
readonly file?: Promise<FileUpload>;
40+
readonly file?: FileUpload;
4341

4442
@Field({
4543
description: stripIndent`

src/components/file/file.service.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from '@aws-sdk/client-s3';
55
import { Injectable } from '@nestjs/common';
66
import { bufferFromStream, cleanJoin, Nil } from '@seedcompany/common';
7-
import { fileTypeFromStream } from 'file-type';
7+
import { fileTypeFromBuffer } from 'file-type';
88
import { intersection } from 'lodash';
99
import { Duration } from 'luxon';
1010
import mime from 'mime';
@@ -297,19 +297,19 @@ export class FileService {
297297
'A file with this ID already exists. Request an new upload ID.',
298298
);
299299
}
300-
const file = await uploadingFile;
301300

302-
let type: string | Nil = file.mimetype;
301+
const body = await uploadingFile.arrayBuffer();
302+
303+
let type: string | Nil = uploadingFile.type;
303304
type = type === 'application/octet-stream' ? null : type;
304-
type ??= (await fileTypeFromStream(file.createReadStream()))?.mime;
305+
type ??= (await fileTypeFromBuffer(body))?.mime;
305306
type ??= mime.getType(name);
306307
type ??= 'application/octet-stream';
307308

308309
await this.bucket.putObject({
309310
Key: `temp/${uploadId}`,
310311
ContentType: type,
311-
ContentEncoding: file.encoding,
312-
Body: file.createReadStream(),
312+
Body: Buffer.from(body),
313313
});
314314
}
315315

@@ -441,8 +441,7 @@ export class FileService {
441441
return sanitizeFilename(input.name);
442442
}
443443
if (input?.file) {
444-
const file = await input.file;
445-
const sanitized = sanitizeFilename(file.filename);
444+
const sanitized = sanitizeFilename(input.file.name);
446445
if (sanitized) {
447446
return sanitized;
448447
}

src/core/graphql/graphql.module.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ import { YogaDriver } from '@graphql-yoga/nestjs';
22
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
33
import { APP_INTERCEPTOR } from '@nestjs/core';
44
import { GraphQLModule as NestGraphqlModule } from '@nestjs/graphql';
5-
import processUploadRequest, {
6-
UploadOptions,
7-
} from 'graphql-upload/processRequest.mjs';
85
import { HttpAdapterHost } from '~/core/http';
96
import { TracingModule } from '../tracing';
107
import { GqlContextHost, GqlContextHostImpl } from './gql-context.host';
@@ -16,8 +13,6 @@ import { GraphqlOptions } from './graphql.options';
1613

1714
import './types';
1815

19-
const FileUploadOptions: UploadOptions = {};
20-
2116
@Module({
2217
imports: [TracingModule],
2318
providers: [
@@ -59,23 +54,8 @@ export class GraphqlModule implements NestModule {
5954

6055
// Setup file upload handling
6156
const fastify = this.app.httpAdapter.getInstance();
62-
const multipartRequests = new WeakSet();
63-
fastify.addContentTypeParser(
64-
'multipart/form-data',
65-
(req, payload, done) => {
66-
multipartRequests.add(req);
67-
done(null);
68-
},
57+
fastify.addContentTypeParser('multipart/form-data', (req, payload, done) =>
58+
done(null),
6959
);
70-
fastify.addHook('preValidation', async (req, reply) => {
71-
if (!multipartRequests.has(req) || !req.url.startsWith('/graphql')) {
72-
return;
73-
}
74-
req.body = await processUploadRequest(
75-
req.raw,
76-
reply.raw,
77-
FileUploadOptions,
78-
);
79-
});
8060
}
8161
}

yarn.lock

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,15 +3734,6 @@ __metadata:
37343734
languageName: node
37353735
linkType: hard
37363736

3737-
"@types/busboy@npm:^1.5.0":
3738-
version: 1.5.2
3739-
resolution: "@types/busboy@npm:1.5.2"
3740-
dependencies:
3741-
"@types/node": "npm:*"
3742-
checksum: 10c0/6b62474716320052e52891e4c5539aabb69a91cef5a99eec8e94baf09f46a40f02218c336670d47c5127bd1e8ca717561cbe86caa78d2e25168f45be67d483cf
3743-
languageName: node
3744-
linkType: hard
3745-
37463737
"@types/cls-hooked@npm:^4.3.3":
37473738
version: 4.3.7
37483739
resolution: "@types/cls-hooked@npm:4.3.7"
@@ -3759,7 +3750,7 @@ __metadata:
37593750
languageName: node
37603751
linkType: hard
37613752

3762-
"@types/express-serve-static-core@npm:@types/stack-trace@*, @types/express@npm:@types/stack-trace@*, @types/koa@npm:@types/stack-trace@*":
3753+
"@types/express-serve-static-core@npm:@types/stack-trace@*, @types/express@npm:@types/stack-trace@*":
37633754
version: 0.0.33
37643755
resolution: "@types/stack-trace@npm:0.0.33"
37653756
checksum: 10c0/cc8345f042f5de17f960652974d67aac71bf864b748f3efbd10a8c5315c0a7a8a13ab17931c239b9fe6b531a44378347509dc8a97a24dfa1247b93af3f943650
@@ -3782,19 +3773,6 @@ __metadata:
37823773
languageName: node
37833774
linkType: hard
37843775

3785-
"@types/graphql-upload@npm:^16.0.4":
3786-
version: 16.0.4
3787-
resolution: "@types/graphql-upload@npm:16.0.4"
3788-
dependencies:
3789-
"@types/express": "npm:*"
3790-
"@types/koa": "npm:*"
3791-
"@types/node": "npm:*"
3792-
fs-capacitor: "npm:^8.0.0"
3793-
graphql: "npm:0.13.1 - 16"
3794-
checksum: 10c0/b678f9abb6ff01188c1347e747a3eedb1a968c31a766abff51faa345499451de2dc400b04c665d2ae4627c405ad39c131128975ca99e4f9fabfa1b7c9a37bc6e
3795-
languageName: node
3796-
linkType: hard
3797-
37983776
"@types/http-cache-semantics@npm:^4.0.4":
37993777
version: 4.0.4
38003778
resolution: "@types/http-cache-semantics@npm:4.0.4"
@@ -3914,13 +3892,6 @@ __metadata:
39143892
languageName: node
39153893
linkType: hard
39163894

3917-
"@types/object-path@npm:^0.11.1":
3918-
version: 0.11.3
3919-
resolution: "@types/object-path@npm:0.11.3"
3920-
checksum: 10c0/7512efbd1687bbfa3e0e19acaa821008c311b46b049266c040a628e64e4e3b0c1525f48cb52edbc5967fc7732a681f9a17476ba3bcdf5faff11768d8cbd23af5
3921-
languageName: node
3922-
linkType: hard
3923-
39243895
"@types/parse-json@npm:^4.0.0":
39253896
version: 4.0.1
39263897
resolution: "@types/parse-json@npm:4.0.1"
@@ -5628,7 +5599,6 @@ __metadata:
56285599
"@tsconfig/strictest": "npm:^2.0.2"
56295600
"@types/common-tags": "npm:^1.8.3"
56305601
"@types/ffprobe": "npm:^1.1.7"
5631-
"@types/graphql-upload": "npm:^16.0.4"
56325602
"@types/jest": "npm:^29.5.7"
56335603
"@types/jsonwebtoken": "npm:^9.0.4"
56345604
"@types/lodash": "npm:^4.14.200"
@@ -5669,7 +5639,6 @@ __metadata:
56695639
graphql: "npm:^16.9.0"
56705640
graphql-parse-resolve-info: "npm:^4.14.0"
56715641
graphql-scalars: "npm:^1.22.4"
5672-
graphql-upload: "npm:^16.0.2"
56735642
graphql-yoga: "npm:^5.7.0"
56745643
human-format: "npm:^1.2.0"
56755644
husky: "npm:^4.3.8"
@@ -7365,13 +7334,6 @@ __metadata:
73657334
languageName: node
73667335
linkType: hard
73677336

7368-
"fs-capacitor@npm:^8.0.0":
7369-
version: 8.0.0
7370-
resolution: "fs-capacitor@npm:8.0.0"
7371-
checksum: 10c0/6638b61e25a815ae5ffb86426c961457bcd5a6dfd194e51a57cb7094f943d4a1a2d2e7323952dc9ff8fe1e6e017908951f342b939785af4feec58148eda5b805
7372-
languageName: node
7373-
linkType: hard
7374-
73757337
"fs-minipass@npm:^2.0.0":
73767338
version: 2.1.0
73777339
resolution: "fs-minipass@npm:2.1.0"
@@ -7733,30 +7695,6 @@ __metadata:
77337695
languageName: node
77347696
linkType: hard
77357697

7736-
"graphql-upload@npm:^16.0.2":
7737-
version: 16.0.2
7738-
resolution: "graphql-upload@npm:16.0.2"
7739-
dependencies:
7740-
"@types/busboy": "npm:^1.5.0"
7741-
"@types/node": "npm:*"
7742-
"@types/object-path": "npm:^0.11.1"
7743-
busboy: "npm:^1.6.0"
7744-
fs-capacitor: "npm:^8.0.0"
7745-
http-errors: "npm:^2.0.0"
7746-
object-path: "npm:^0.11.8"
7747-
peerDependencies:
7748-
"@types/express": ^4.0.29
7749-
"@types/koa": ^2.11.4
7750-
graphql: ^16.3.0
7751-
peerDependenciesMeta:
7752-
"@types/express":
7753-
optional: true
7754-
"@types/koa":
7755-
optional: true
7756-
checksum: 10c0/292e786cc20cb1edc1744dd12a40be17955cf66f7e0ebd47ed69a2570b01c19d3f631d7353d1329f80f666ac39bdafc70d3b46ed8c26d9ce72abe46d77102503
7757-
languageName: node
7758-
linkType: hard
7759-
77607698
"graphql-ws@npm:5.14.3":
77617699
version: 5.14.3
77627700
resolution: "graphql-ws@npm:5.14.3"
@@ -7787,7 +7725,7 @@ __metadata:
77877725
languageName: node
77887726
linkType: hard
77897727

7790-
"graphql@npm:0.13.1 - 16, graphql@npm:^16.9.0":
7728+
"graphql@npm:^16.9.0":
77917729
version: 16.9.0
77927730
resolution: "graphql@npm:16.9.0"
77937731
checksum: 10c0/a8850f077ff767377237d1f8b1da2ec70aeb7623cdf1dfc9e1c7ae93accc0c8149c85abe68923be9871a2934b1bce5a2496f846d4d56e1cfb03eaaa7ddba9b6a
@@ -7966,7 +7904,7 @@ __metadata:
79667904
languageName: node
79677905
linkType: hard
79687906

7969-
"http-errors@npm:2.0.0, http-errors@npm:^2.0.0":
7907+
"http-errors@npm:2.0.0":
79707908
version: 2.0.0
79717909
resolution: "http-errors@npm:2.0.0"
79727910
dependencies:
@@ -10881,13 +10819,6 @@ __metadata:
1088110819
languageName: node
1088210820
linkType: hard
1088310821

10884-
"object-path@npm:^0.11.8":
10885-
version: 0.11.8
10886-
resolution: "object-path@npm:0.11.8"
10887-
checksum: 10c0/73b1f33bb30a7032d8cce2e3dcffd82b80a83d8304e80b4f83b4f456165625de9907f1ca7f7441d4dfb5e73429ace1e5bf9d9315636ac0aacc76392cc21d1672
10888-
languageName: node
10889-
linkType: hard
10890-
1089110822
"object.assign@npm:^4.1.4":
1089210823
version: 4.1.4
1089310824
resolution: "object.assign@npm:4.1.4"

0 commit comments

Comments
 (0)