question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BadRequestError: Missing multipart field ‘operations’

See original GitHub issue

My server index.ts code :

import formidable from "formidable";
import { graphqlUploadExpress } from "graphql-upload";
import "reflect-metadata";
import {
  ApolloServer,
  buildSchema,
  Channel,
  ChannelResolver,
  connectRedis,
  COOKIE_NAME,
  cors,
  createConnection,
  createMessageCreatorLoader,
  createServer,
  DirectMessage,
  DirectMessageResolver,
  express,
  isProduction,
  Member,
  MemberResolver,
  Message,
  MessageResolver,
  Redis,
  session,
  Team,
  TeamResolver,
  User,
  UserResolver,
} from "./indexImports";
import { MyContext } from "./types/MyContext";

const PORT = process.env.PORT || 4000;

const main = async () => {
  const isTestMode = !!process.env.TEST_DB;
  await createConnection({
    type: "postgres",
    database: process.env.TEST_DB || "gignal",
    username: "postgres",
    password: "postgres",
    logging: !isTestMode,
    synchronize: !isTestMode,

    entities: [User, Message, Team, Channel, Member, DirectMessage],
  });

  const app = express();

  const RedisStore = connectRedis(session);
  const redis = new Redis();

  // app.set("trust proxy", 1);
  app.use(
    cors({
      origin: "http://localhost:3000",
      credentials: true,
    })
  );



  const uploadDir = "files";

  const fileMiddleware = (req, res, next) => {
    if (!req.is("multipart/form-data")) {
      return next();
    }

    const form = formidable.IncomingForm({
      uploadDir,
    });

    form.parse(req, (error, { operations }, files) => {
      console.log("next", files);


      const document = JSON.parse(operations);

      if (Object.keys(files).length) {
        const { type, path: filePath } = files["1"];
        console.log(type);
        console.log(filePath);
        document.variables.file = {
          type,
          path: filePath,
        };
      }

      req.body = document;
      next();
    });
  };

  app.use(fileMiddleware);

  app.use(
    "/graphql",
    graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 })
  );

  const sessionMiddleware = session({
    name: COOKIE_NAME,
    store: new RedisStore({
      client: redis as any,
      disableTouch: true,
    }),
    cookie: {
      maxAge: 1000 * 60 * 60 * 24 * 366 * 10, //10 years
      httpOnly: true,
      sameSite: "lax", //csrf
      secure: isProduction, // cookie only works in https
    },
    secret: "hello world",
    resave: false,
    saveUninitialized: false,
  });
  app.use(sessionMiddleware);

  const schema = await buildSchema({
    resolvers: [
      UserResolver,
      MessageResolver,
      TeamResolver,
      ChannelResolver,
      MemberResolver,
      DirectMessageResolver,
    ],
    validate: false,
  });

  const apolloServer = new ApolloServer({
    schema,

    context: ({ req, res, connection }: MyContext) => ({
      req,
      res,
      redis,
      connection,
      createMessageCreatorLoader: createMessageCreatorLoader(),
    }),

    subscriptions: {
      path: "/subscriptions",
      onConnect: async (_, { upgradeReq }: any) =>
        new Promise((res) =>
          sessionMiddleware(upgradeReq, {} as any, () => {
            res({ req: upgradeReq });
          })
        ),
    },
  });

  apolloServer.applyMiddleware({
    app,
    cors: false,
  });
  const httpServer = createServer(app);

  apolloServer.installSubscriptionHandlers(httpServer);
  //a
  httpServer.listen(PORT, () => {
    console.log(`server listening on port ${PORT}`);
    console.log(
      `Subscriptions ready at ws://localhost:${PORT}${apolloServer.subscriptionsPath}`
    );
  });
};
main().catch((err) => {
  console.log(err);
});

Resolver code :

  @Mutation(() => Message)
  @UseMiddleware(isAuth)
  async createMessage(
    @Arg("input") input: CreateMessageInput,
    @Ctx() { req }: MyContext
  ) {
    const { text, channelId, file } = input;
    // const { createReadStream, filename } = await file;

    console.log("file here", file);

    const message = await Message.create({
      text,
      channelId,
      // url: file.path,
      // fileType: file.type,

      creatorId: req.session.userId,
      createdAt: new Date().toISOString(),
    }).save();

    const asyncFo = async () => {
      const currentUser = await User.findOne(message.creatorId);

      pubsub.publish(NEW_CHANNEL_MESSAGE, {
        newMessageAdded: {
          ...message,
          creator: currentUser,
        },
      });
    };
    asyncFo();
    return message;
  }

Create Message Input :

import { GraphQLUpload } from "graphql-upload";
import { Field, InputType, Int } from "type-graphql";
import { File } from "./File";

@InputType()
export class CreateMessageInput {
  @Field(() => Int)
  channelId: number;

  @Field(() => String, { nullable: true })
  text: string;

  @Field(() => GraphQLUpload, { nullable: true })
  file: File;
}

File :

import { Stream } from "stream";


export interface File {
  filename: string;
  mimetype: string;
  encoding: string;
  createReadStream: () => Stream;
}

Client(Next js) Code :

import { ApolloClient, InMemoryCache, split, createHttpLink } from '@apollo/client';
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
import { setContext } from "apollo-link-context";
import { createUploadLink } from 'apollo-upload-client';
import { NextPageContext } from "next";
// import { createFileLink } from './createFileLink';
// import createUploadLink from "../utils/createUploadLink";
import createWithApollo from "./createWithApollo";

const httpLink = createUploadLink({ uri: "http://localhost:4000/graphql" });

const wsLink = process.browser
  ? new WebSocketLink({
    uri: `ws://localhost:4000/subscriptions`,

    options: {
      reconnect: true,
      lazy: true,
    },
  })
  : null;

const createClient = (ctx: NextPageContext) => {
  const middlewareLink = setContext(() => ({
    credentials: "include",
    headers: {
      cookie:
        typeof window === "undefined" ? ctx?.req?.headers.cookie : undefined,
    },
  }));

  const httpLinkWithMiddleware = middlewareLink.concat(httpLink as any);

  const link = process.browser
    ? split(
      ({ query }) => {
        const def = getMainDefinition(query);
        return (
          def.kind === "OperationDefinition" &&
          def.operation === "subscription"
        );
      },
      wsLink,
      httpLinkWithMiddleware as any
    )
    : httpLinkWithMiddleware;

  return new ApolloClient({
    uri: "http://localhost:4000/graphql",
    link: link as any,
    cache: new InMemoryCache(),
  });
};

export const withApollo = createWithApollo(createClient);

I am using react-dropzone :

import { useRouter } from "next/router";
import React, { useCallback } from "react";
import { useDropzone } from "react-dropzone";
import { useCreateMessageMutation } from "../generated/graphql";
import { useGetIdFromUrl } from "../utils/hooks/useGetIdFromUrl";

interface Props {
  disableClick?: boolean;
}

const FileUpload: React.FC<Props> = ({ children, disableClick }) => {
  const [createMessage] = useCreateMessageMutation();
  const router = useRouter();

  const onDrop = useCallback(async (files) => {
    console.log("files", files[0]);

    const channelId = useGetIdFromUrl(router.query.channelId);
    await createMessage({
      variables: { input: { channelId, text: null, file: files[0] } },
    });
  }, []);

  const handleOnClick = useCallback((e) => {
    if (disableClick) {
      e.stopPropagation();
    }
  }, []);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
  });

  const outlineStyle = isDragActive
    ? {
        outlineColor: "lightgray",
        outlineStyle: "dashed",
      }
    : {};

  const disableClickStyle =
    disableClick && !isDragActive ? { outline: "none" } : {};
  const style = { ...outlineStyle, ...disableClickStyle };

  return (
    <div
      {...getRootProps({
        style: { ...style, height: "100%" },
        onClick: handleOnClick,
      })}
    >
      <input {...getInputProps()} />
      {children}
    </div>
  );
};

export default FileUpload;

When i submit file it created in /files directory but I got this error : BadRequestError: Missing multipart field ‘operations’

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
jaydensericcommented, Mar 11, 2021

Please refer to Apollo for support with using Apollo Server; none of your problems have anything to do with a bug in this package.

Piece of advice; follow the example (adjusting for Express, vs Koa) for setting up graphql-upload with Apollo Server:

https://github.com/jaydenseric/apollo-upload-examples/blob/be1444a0aa8768c6fe2e2c96119eb2b6344fcc50/api/server.js#L78-L81

Note that you need to disable their outdated file upload support and set it up yourself.

Good luck!

1reaction
alisalim17commented, Mar 11, 2021

Yeah,you are true. I do what you said.And that works,thanks for your help !

Read more comments on GitHub >

github_iconTop Results From Across the Web

BadRequestError: Missing multipart field 'operations' #115
Hello there, I've been struggling to get apollo-upload-server working in my local environment, along side apollo-upload-client inside my ...
Read more >
Relay Modern BadRequestError: Missing multipart field ...
js for uploading it server throws following error. BadRequestError: Missing multipart field 'operations' (https://github.com/jaydenseric/graphql ...
Read more >
File uploads - graphql-compose
apollo-upload-server - for parsing multipart/form-data POST requests via busboy and providing File s data to resolve function as argument. Tutorial. 1.
Read more >
Missing multipart field 'operations' with graphql-upload and ...
Coding example for the question Missing multipart field 'operations' with graphql-upload and apollo-server-node.js.
Read more >
File Upload - gqlgen
Graphql server has an already built-in Upload scalar to upload files using a multipart request. It implements the following spec ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found