Geek Culture
Published in

Geek Culture

Multiple File Upload with Apollo Server 3, React & GraphQL

How to set up your multiple files upload with GraphQL

Photo by Aarón Blanco Tejedor on Unsplash

To introduce this paper, and why I’m writing it:

For a school project, we had to build a web App with this stack :

  • MongoDb => database
  • GraphQL with Apollo => back/api
  • React with GrapQL => front

The purpose of this project is a resources-painless-aggregation-location, where you can share those resources with friends/colleague ezzi-pizzi..

If we say resources, we want to mean docs, tutorials, or other helpful information that you can share =>File Upload Required…..

I’m new in graphql and Apollo, usually working with REST API and his multer package to manage the formData interface. I’ll spend two days trying get something from my front App file input without any success, read a lot of tutorial, from Apollo documentation, GraphQL-upload github, google, stack-overflow (I’m sure you know what I mean…).

That’s why you’ll find here how I figured out a few days ago.

We start our project with Apollo server V2, and last July what’s happened ?

You got it => Apollo server V3

With V2 many errors:

  • Empty object sent from the front
  • Json Error with a P at position 0
  • CreateReadStream error cause it’s deprecated (nodeJS > 14)

So my last attempt was to switch all the project to Apollo Serve V3, I’ll try to explain it here..

Let’s jump into that process…

Photo by Joseph Frank on Unsplash

The server:

  • npm init -y
  • npm i apollo-server apollo-server-express graphql graphql-upload typescript ts-node-dev

Those command will install everything you need for this demo, especially Apollo Server 3.

See the structure above, and our index.ts content below :

const { ApolloServer, gql } = require("apollo-server-express");
const express = require("express");
import * as path from "path";
import * as fs from "fs";
import { GraphQLUpload, graphqlUploadExpress } from "graphql-upload";
const typeDefs = gql`
scalar Upload
type File {
url
}
type Query {
otherFields: Boolean!
}
type Mutation {
fileUpload(file: [Upload]!): [File]!
}
`;
const fileRenamer = (filename: string): string => {
const queHoraEs = Date.now();
const regex = /[\s_-]/gi;
const fileTemp = filename.replace(regex, ".");
let arrTemp = [fileTemp.split(".")];
return `${arrTemp[0]
.slice(0, arrTemp[0].length - 1)
.join("_")}${queHoraEs}.${arrTemp[0].pop()}`;
};
const resolvers = {
Upload: GraphQLUpload,
Mutation: {
fileUpload: async (parent, { file }) => {
let url = [];
for (let i = 0; i < file.length; i++) {
const { createReadStream, filename, mimetype } = await file[i];
const stream = createReadStream();
const assetUniqName = fileRenamer(filename);
const pathName = path.join(__dirname, `./upload/${assetUniqName}`);
await stream.pipe(fs.createWriteStream(pathName));
const urlForArray = `http://localhost:4000/${assetUniqName}`;
url.push({ url: urlForArray });
}
return url;
},
},
};
async function startServer() {
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
const app = express();
app.use(graphqlUploadExpress());
server.applyMiddleware({ app });
app.use(express.static(path.join(__dirname, "./upload")));
await new Promise((r) => app.listen({ port: 4000 }, r));console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
}
startServer();

Pay attention to:

  • We use apollo-server-express
  • Path and fs for file manipulation
  • graphql-upload for….file upload
  • Don’t forget the Scalar Upload typeDefs
  • Same for GraphQlUpload resolver
  • The fileRenamer function is here to manage “ “, “_” and “-” in our file name.

Here our singleUpload mutation will accept an array of files and will reprocess by renaming, and moving it to the right folder.

Our startServer function will start our server with a middleware for file upload who do all the stuff under the hood, and don’t forget to use express.static to make your folders accessible from the front :)

The front App

We gonna use a very fast react app builder:

npx create-react-app client --template clean-cra

Install all those packages:

npm i @apollo/client apollo-upload-client

The structure:

We configure our App.js like this:

import React from "react";
import { ApolloProvider, ApolloClient, InMemoryCache } from "@apollo/client";
import { createUploadLink } from "apollo-upload-client";
import FileUpload from "./FileUPload";
function App() {
const uploadLink = createUploadLink({
uri: "http://localhost:4000/graphql" });
const client = new ApolloClient({
link: uploadLink,
cache: new InMemoryCache(),
});
return (
<div className="App">
<ApolloProvider client={client}>
<FileUpload />
</ApolloProvider>
</div>
);
}
export default App;

the main important part here is our createUploadLink wich come from the apollo-upload-client package that manage the multipart request created by GraphQL for us.

The FileUpload component:

import React from "react";
import { useMutation, gql } from "@apollo/client";
const UPLOAD_FILE = gql`
mutation fileUpload($file: [Upload]!) {
fileUpload(file: $file) {
url
}
}
`;
const FileUPload = () => {
const [fileUpload] = useMutation(UPLOAD_FILE, {
onCompleted: (data) => console.log(data),
});
const handleFileChange = (e) => {
const file = e.target.files;
if (!file) return;
fileUpload({ variables: { file } });
};
return (
<>
<input
type="file"
name="GraphQLUploadForMedium"
onChange={handleFileChange}
/>
</>
);
};
export default FileUPload;

And here we are, with this method you are able to send multiple files on your GraphQl API, and you can access them with their urls :)

If you have any advice to manage it better, feel free to comment or contact me ;)

--

--

--

A new tech publication by Start it up (https://medium.com/swlh).

Recommended from Medium

Best Nodejs Frameworks for Web Apps in 2022

Best Nodejs Frameworks for Web Apps in 2022

Deep Dive into Async, Promises & Fetch

Client side error logging in React web application

Why Angular?

In an Ideal World: Features I Miss in Kotlin

Web Components for everyone

Make your Vue.js application SEO friendly

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
AntoineGGG

AntoineGGG

Clean Code Padawan

More from Medium

Jest — Filenames without test or spec

next-export-i18n v1.3.0: respecing the user’s default language

React Modal Component Implementation

Making a Web Scraper in Node + TypeScript