How to upload an image using MERN stack
Tags: react, node, mongodb
Published: 2020-11-16 15:18:17

I'm currently learning the MERN stack by making a project, one of this project's functionalities is to create a user profile, by doing so I needed to implement a file input in the form in order to add the user's profile picture, and this is where I got stuck! the process of uploading a photo seemed complicated but don't worry I figured out (hence the title).

# Setting up the project
Inside the project's folder, I made two folders: **frontend** and **backend**
```
|
|-- project
    |
    |-- frontend
    |-- backend
```
## Setting up the frontend part
First cd into the frontend folder
```
cd frontend
```
Then set up your react app
```
npx create-react-app .
```
we're also going to upload the axios package which is a promise-based HTTP client for the browser and node.js.
```
npm i axios 
```
Now let's get to coding, create a **User.js** component in the **src** folder.
the data that we're sending to the server includes name, birth date, and a photo that could only be a png, jpg, or jpeg.
```react
import React, { useState } from 'react';
import axios from 'axios';

const User = () => {
    const [newUser, setNewUser] = useState(
        {
            name: '',
            birthdate: '',
            photo: '',
        }
    );
    
    const handleSubmit = (e) => {
        e.preventDefault();
        const formData = new FormData();
        formData.append('photo', newUser.photo);
        formData.append('birthdate', newUser.birthdate);
        formData.append('name', newUser.name);
        
        axios.post('http://localhost:5000/users/add/', formData)
             .then(res => {
                console.log(res);
             })
             .catch(err => {
                console.log(err);
             });
    }

    const handleChange = (e) => {
        setNewAuthor({...newUser, [e.target.name]: e.target.value});
    }

    const handlePhoto = (e) => {
        setNewAuthor({...newUser, photo: e.target.files[0]});
    }

    return (
        <form onSubmit={handleSubmit} encType='multipart/form-data'>
            <input 
                type="file" 
                accept=".png, .jpg, .jpeg"
                name="photo"
                onChange={handlePhoto}
            />

            <input 
                type="text"
                placeholder="name"
                name="name"
                value={newUser.name}
                onChange={handleChange}
            />

            <input 
                type="date"
                name="birthdate"
                value={newUser.date}
                onChange={handleChange}
            />

            <input 
                type="submit"
            />
        </form>
    );
}

export default User;
```
Import the User component inside App.js
```react
import './App.css';
import './User';

function App() {
  return (
    <User />
  );
}

export default App;
```
## Setting up the backend part
In the backend folder, we need to upload the needed packages:
```
npm i express cors mongoose multer uuid
```
Here's the reason why we need these packages:
* [express](https://www.npmjs.com/package/express): is a minimalist web framework for node
* [cors](https://www.npmjs.com/package/cors): is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options, according to the [cors package repository](https://github.com/expressjs/cors)
* [mongoose](https://www.npmjs.com/package/mongoose): Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
* [multer](https://www.npmjs.com/package/multer): is a node.js middleware for handling multipart/form-data.
* [uuid](https://www.npmjs.com/package/uuid): is a package that generates random and unique ids, I use it in this project to make sure that every uploaded image has a unique name.

create app.js
```javascript
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');

const app = express();
require('dotenv').config();

const port = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());

const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true});

const connection = mongoose.connection;
connection.once('open', () => {
    console.log('mongo DB success');
});

const userRouter = require('./routes/users');
app.use('/users', userRouter);

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})
```
Create **.env** file and store in it the port and your atlas URI. 
Make a routes folder and create user.js inside it; also create the **images** folder inside the backend folder.
```javascript
const router = require('express').Router();
const multer = require('multer');
const { v4: uuidv4 } = require('uuid');
let path = require('path');
let User = require('../models/user.modal');

const storage = multer.diskStorage({
    destination: function(req, file, cb) {
        cb(null, 'images');
    },
    filename: function(req, file, cb) {   
        cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname));
    }
});

const fileFilter = (req, file, cb) => {
    const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
    if(allowedFileTypes.includes(file.mimetype)) {
        cb(null, true);
    } else {
        cb(null, false);
    }
}

let upload = multer({ storage, fileFilter });

router.route('/add').post(upload.single('photo'), (req, res) => {
    const name = req.body.name;
    const birthdate = req.body.birthdate;
    const photo = req.file.filename;

    const newUserData = {
        name,
        birthdate,
        photo
    }

    const newUser = new User(newUserData);

    newUser.save()
           .then(() => res.json('User Added'))
           .catch(err => res.status(400).json('Error: ' + err));
});

module.exports = router;
```
and don't forget to make the user.modal.js in the modals folder
```javascript
const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const userSchema = new Schema({
    name: {
        type: String,
        required: true,
        trim: true
    }, 

    photo: {
        type: String
    },

    birthdate: {
        type: String
    }
});

const User = mongoose.model('User', userSchema);

module.exports = User;
```
And that's it! that's how you upload an image to the server.