Skip to content
GQLoom

File Upload

GQLoom supports file uploads through GraphQL's Upload scalar.
Below are two common integration approaches:

  • Using the GraphQLUpload scalar from graphql-upload or graphql-upload-minimal
  • Using the File type provided by graphql-yoga

Core Steps

  • Use the silk function to declare the Upload or File scalar.
  • Use the Upload or File scalar in the input of a mutation.

Using GraphQLUpload

The following code example applies to graphql-upload or graphql-upload-minimal.

ts
import { 
mutation
,
resolver
,
silk
,
weave
} from "@gqloom/core"
import {
ValibotWeaver
} from "@gqloom/valibot"
import {
GraphQLNonNull
} from "graphql"
import { type FileUpload,
GraphQLUpload
} from "graphql-upload-minimal"
import {
createServer
} from "node:http"
import {
createWriteStream
} from "node:fs"
import {
pipeline
} from "node:stream/promises"
import * as
path
from "node:path"
import * as
fsPromises
from "node:fs/promises"
import {
createYoga
} from "graphql-yoga"
import * as
v
from "valibot"
const
Upload
=
silk
<
Promise
<FileUpload>>(new
GraphQLNonNull
(
GraphQLUpload
))
const
uploadResolver
=
resolver
({
upload
:
mutation
(
v
.
string
())
.
input
({
fileName
:
v
.
nullish
(
v
.
string
()),
file
:
Upload
,
}) .
resolve
(async ({
fileName
,
file
}) => {
const {
filename
,
createReadStream
} = await
file
const
name
=
fileName
??
filename
const
uploadsDir
=
path
.
join
(import.meta.
dirname
, "uploads")
await
fsPromises
.
mkdir
(
uploadsDir
, {
recursive
: true })
const
rs
=
createReadStream
()
const
ws
=
createWriteStream
(
path
.
join
(
uploadsDir
,
name
))
await
pipeline
(
rs
,
ws
)
return `file uploaded: ${
name
}`
}), })

Key points:

Using File Type

The following code example applies to the File type from graphql-yoga.

ts
import { 
mutation
,
resolver
,
silk
,
weave
} from "@gqloom/core"
import {
ValibotWeaver
} from "@gqloom/valibot"
import {
GraphQLNonNull
,
GraphQLScalarType
} from "graphql"
import {
createServer
} from "node:http"
import * as
path
from "node:path"
import * as
fs
from "node:fs/promises"
import {
createYoga
} from "graphql-yoga"
import * as
v
from "valibot"
const
FileScalar
=
silk
(
new
GraphQLNonNull
(
new
GraphQLScalarType
<File, File>({
name
: "File",
description
: "The `File` scalar type represents a file upload.",
}) ) ) const
uploadResolver
=
resolver
({
upload
:
mutation
(
v
.
string
())
.
input
({
fileName
:
v
.
nullish
(
v
.
string
()),
file
:
FileScalar
,
}) .
resolve
(async ({
fileName
,
file
}) => {
const
name
=
fileName
??
file
.
name
const
uploadsDir
=
path
.
join
(import.meta.
dirname
, "uploads")
await
fs
.
mkdir
(
uploadsDir
, {
recursive
: true })
await
fs
.
writeFile
(
path
.
join
(
uploadsDir
,
name
),
Buffer
.
from
(await
file
.
arrayBuffer
())
) return `file uploaded: ${
name
}`
}), }) const
schema
=
weave
(
ValibotWeaver
,
uploadResolver
)
const
yoga
=
createYoga
({
schema
})
createServer
(
yoga
).
listen
(4000)

Key points:

  • The File provided by Yoga directly supports arrayBuffer(), suitable for small to medium files or writing to disk before processing.
  • Similarly, wrap it as a non-null scalar using silk, and add validation and permission control as needed.