Uploading files to image server using ImageKit and Multer in MERN stack
Tags: javascript, tutorial, react, node
Published: 2022-10-07 12:34:55

So, you're a fair beginner in backend development and it's common knowledge that hosting images on your server can be pretty dangerous and annoying to your end users especially when your project/solution is content-intensive (e.g. social app, e-commerce etc.) because it'll cause it to load slowly. Your solution? Giving the content heavy-lifting to dedicated server.

Well, this was my case as I already had a functioning REST API for my social app built with ExpressJS on NodeJS.

We'll be using Multer to handle file uploads on our server and [imagekit](https://imagekit.io) will do all our media heavy lifting. I chose these tools because I just found them easier to use and the latter has a very elaborate documentation (and a free tier too 😋).

My approach to solving this since I already had my tools were to upload to the file to the imageKit server then get the URL of the uploaded media and then save it to my DB.

<br />
<br />
<h3><b>Let's get to work</b></h3>
We'll start by setting up an account on the [imagekit](https://imagekit.io) website and then going ahead to install the necessary libraries:

```
`npm i imagekit multer`
```

Now we'll need to get some credentials from our imageKit account i.e., urlEndPoint, publicKey and privateKey(this is needed because we'll be doing server-side upload). The keys can be gotten from our imageKit [account](https://imagekit.io/dashboard/developer/api-keys) and the default urlEndPoint is usually something like this: `https://ik.imagekit.io/{your_imageKit_ID}`

Let's grab all of that and put it in our `'.env'` file (that's what I did for security's sake), so we have something like

```
IMAGEKIT_PUBLIC_KEY="your_public_key_here"
IMAGEKIT_PRIVATE_KEY="your_private_key_here"
IMAGEKIT_URL_ENDPOINT="your_urlEndPoint_here"
```

Next, we initialize our imageKit library (I did this in a separate file) with our details gotten from our `'.env'` file 

```
const ImageKit = require("imagekit")
require("dotenv").config()

const imageKit = new ImageKit({
    publicKey: process.env.IMAGEKIT_PUBLIC_KEY,
    privateKey: process.env.IMAGEKIT_PRIVATE_KEY,
    urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT,
})

module.exports = imageKit
```
Now that we have all of that out of the way let's set Multer up!

Multer handles file uploads to our server, depending on how you set it, by appending a `'file'` property to our API's `'req'` object (pretty much like a middleware). So, to set it up we have to initialize its storage type like so:

```
const Multer = require("multer");

const storage = Multer.memoryStorage()

const multer = Multer({ storage })

module.exports = multer
```

Multer has two basic types of storage which is the disk storage and the memory storage like we're using since we won't be storing the files on our server (which is entirely what we're trying to avoid.). Also notice that the initiated Multer setup is exported too(I just like to think long-term maintenance).

To use our `'multer'` instance, we will import it in the routes of the API we want to use it in: 

```
//express router
const router = require('express').Router();

// our example controller(creating posts)
const postController = require('../controllers/postController');

// our multer instance
const multer = require("_pathToMulterInstance_");

router.post("/create", upload.single("file"), postController.createPost);
```
Multer has some functions used to determine what type of file(s) to expect from the API request. Here we have used the `'multer.single("file")'` function to let multer know that we're expecting a single file. (The full multer documentation can be found [here](https://github.com/expressjs/multer)).

<center>Now for the Fun part, THE LOGIC!!!</center>
We move to where we have our controller 

If we send a request to our API now with a file, we should see our file details in the `'req'` object. 

![showing the details of the file object in debug mode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0walnbxlqwj1ndox7tx.jpg)

Note the `'buffer'` property of the file object? this is there because of the type of storage we set while setting up multer. It would've been something different (or even absent) if we had used disk storage. 

We should have our imageKit object instance imported into the file we have our controller 'cause this is where we would be using it. We would be using the `'upload()'` function of the imageKit class which takes two parameters: 

1. The upload options which requires two necessary properties 
  - file: the file to be uploaded(base64)
  - file: the name to give the uploaded file(string)

2. The callback function to trigger when the file upload is done which carries two parameters
  - error: if there is any and null if there isn't
  - response: which carries the whole necessary information of the file uploaded.
_(full imageKit docs can be found [here](https://docs.imagekit.io/))_

For now, we'll only need the url from the image response and I'm sure you already know how this ends.

The image below shows the full implementation of the ImageKit file Upload for my scenario (creating posts with images so I just send the image URL with the other post details to the backend).


![final implementation image in the controller](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwa8h8ya5vz8z2zmo02s.png)

In the image above, I made a checking to see if there are images and uploads just like that if there are no images. In the upload Options object, I specified the folder which the images uploaded from this API be saved to on the image server. It can be set differently based on your own settings. After necessary processing and saving, it sends a response of the new post which will now include the url of the image that has been saved in the database.

End Note: You can take the viewing of the images further the the imageKit frontend libraries because it'll give total control over how the images are viewed and scaled.



Finally, I'd like to know what you would've done differently and any other of your thoughts on this.

STAY CURIOUS!!!!