I’ve been working on a new project that uses Keystone.js as the server. One requirement for this project is to upload images to Keystone via a Node.js service that performs a task.

There are potentially some clients like apollo-upload-client that may work to upload files but I found a fetch POST request was the simplest way to upload a file to a GraphQL API when working in a Node.js environment. Most of the documentation I could find was for frontend applications and React. After some time struggling this is the solution I came up with for uploading a file to a Keystone.js GraphQL API.


npm install form-data cross-fetch

The Code

import * as FormData from "form-data"
import fetch from "cross-fetch"

mutation CreatePick ($data: PickCreateInput!) { // Name of the Operation and data type from schema
    createPick(data: $data) {
      id // What is returned after a successful mutation

function createPick(name, stream) {
  const body = new FormData()

      operationName: "CreatePick", // The name of the operation from the mutation above
      query: CREATE_PICK_MUTATION, // Const from above
      variables: {
        data: {
          image: null // Is always null

  body.append("map", JSON.stringify({ 1: ["variables.data.image"] }))
  body.append("1", stream as Blob)

  return fetch($process.env.apiUrl, { // Use your API endpoint /admin/api
    method: "POST",
    body: body

I used the requests from the Admin API to work backwards into how to shape the request. You will need to adjust the data on the createPick function to include all required data for your new entry and then include that data in the variables.data object as well.