Arthur Yeti / April 11, 2020
3 min read • ––– vues
How many times you needed a way to upload files in your sideprojects? Me, every single time. But, I always found it difficult to implement it with graphQL.
BTW, you don't want to upload the files on your own server, when there are great services like Cloudinary. They take care of everything bro.
I assume you already have a React + Apollo app going on. We're going to create a simple React component to pick a file and trigger a graphQL mutation with @apollo/react-hooks.
import React, { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';
const UPLOAD_AVATAR = gql`
mutation uploadAvatar($avatar: Upload) {
uploadAvatar(avatar: $avatar) {
id
}
}
`;
const UploadAvatar = ({ url }) => {
const [uploadAvatarMutation] = useMutation(UPLOAD_AVATAR);
const [avatar, setAvatar] = useState(null);
// Store in the state the file
const handleChange = (e) => {
setAvatar(e.target.files[0]);
};
// Trigger the mutation when we click the submit button
const handleClick = () => {
uploadAvatarMutation({
variables: {
avatar
}
});
};
return (
<div>
<input id="logo" type="file" onChange={handleChange} />
<button type="button" onClick={handleClick}>
Submit
</button>
</div>
);
};
export default UploadAvatar;
There is no fancy concept to grasp from this component. It's a graphql mutation with a simple file input.
I suggest you're using apollo-express-server, but you can also plug the graphql-upload scalar in any graphql server.
Here is my tiny Apollo server.
import { ApolloServer, gql } from 'apollo-server-express';
const typeDefs = gql`
type Avatar {
id: ID!
url: String
}
type Mutation {
uploadAvatar(avatar: Upload): Avatar!
}
`;
const resolvers = {
Mutation: {
uploadAvatar: (parent, args, context, info) => {
const file = await uploadFile(args.avatar);
return saveToDatabase(file.secure_url);
},
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen()
And this is the function that does the magic:
import cloudinary from 'cloudinary';
// A simple function to upload to Cloudinary
const uploadFile = async (file) => {
// The Upload scalar return a a promise
const { createReadStream } = await file;
const fileStream = createReadStream();
// Initiate Cloudinary with your credentials
cloudinary.v2.config({
cloud_name: 'CLOUDINARY_CLOUD_NAME',
api_key: 'CLOUDINARY_API_KEY',
api_secret: 'CLOUDINARY_API_SECRET'
});
// Return the Cloudinary object when it's all good
return new Promise((resolve, reject) => {
const cloudStream = cloudinary.v2.uploader.upload_stream(function (
err,
fileUploaded
) {
// In case something hit the fan
if (err) {
rejet(err);
}
// All good :smile:
resolve(fileUploaded);
});
fileStream.pipe(cloudStream);
});
};
If you have any questions, I'm on twitter :smile:
Reçois des emails à propos de Next.js/Hasura, ma vie de maker, et mon lifestyle à Bali.