The only real free external media storage you'll need!
Tags: vue, nuxt, webdev, javascript
Published: 2023-12-19 18:58:30

## The problem
I have Nuxt3/ExpressJS project. I need to store images somewhere. I had two options:
1. Store as Base64 in my SQL database. Pros - no external storage, cons - storing Base64 in SQL isn't quite a good practice :D And if I do like that, I will have to compress my images on client side to, let's say, 128kb max. That will cause images to look like, you know 💩
2. Use external storage. Pros - images will be perfect, plus these storages often give you some optimization on your images and thumbnails and more cool stuff. Cons - most of them are paid.
So after few hours of investigation and work, I've decided to share with you my experience about implementing external media storage for my app. Spoiler - it's free :)

## What to choose?
I've browser through some of them and [imagekit.io](https://imagekit.io/) grabbed my attention. There was the most free gigabytes of media storage (20GB at the moment) so I've decided to try out that one.

## Implementation

First of what i want to say - docs are amazingly understandable. They have SDKs for all popular frameworks and libraries:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fld0gm5omztv1d9lw3i7.png)
I've encountered lots of docs and the were not so good, haha. So, where from do we start?

### Client side
That would be quite easy and nothing special if you've worked with images and FormData before, but I still gonna wrap it up.
1. Create an `input` with `type="file"`, for it to accept files.
2. Add handler, that will create `new FormData()` and append image from `input` into newly created `FormData`. To get image from `input` you can simply do `const [file] = fileInput.target.files`, checks and validations are on your own for now.
3. Send it to your api.

### Server side
Now more interesting part, how do we send and store image to imagekit.io from node?
1. Install package [express-fileupload](https://www.npmjs.com/package/express-fileupload)
That will allow us to work with a bit more comfortable.
2. Do `const fileUpload = require('express-fileupload')` in your main file, it's `app.js` by default.
3. Add `app.use(fileUpload())` below.
4. Install package [imagekit](https://www.npmjs.com/package/imagekit)
5. Create in your `/utils` folder (or anywhere, but I would recommend this approach) file `imagekit.js` with basic configuration for our imagekit:
    ```
    const ImageKit = require('imagekit')

    module.exports = new ImageKit({
      publicKey: process.env.IMAGEKIT_PUBLIC_KEY,
      privateKey: process.env.IMAGEKIT_PRIVATE_KEY,
      urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT
    })
    ```
6. If you use another framework or library, check page [https://imagekit.io/dashboard/developer/api-keys](https://imagekit.io/dashboard/developer/api-keys) for another configurations.
7. In your controllers, import that imagekit by doing `const imagekit = require('../utils/imagekit')`
8. I've wrote very basic snippet, that's responsible for uploading files to imagekit and returns `Promise`. It looks like that:
    ```
    const uploadImageToStorage = (file, params) => {
      const uploadOptions = {
        file: file.data,
        fileName: file.name,
        ...params
    }
      return imagekit.upload(uploadOptions)
        .then(response => response)
        .catch(error => error)
    }
    ```

    You can write it in utils and import it to any controller file where you need to upload files, but I've temporarily wrote it right inside controller file (not good, gonna fix it later).
9. Now use that snippet! But how? As you see, i have `params` in my snippet. I won't write about all parameters imagekit provides, for now I've needed only one - `folder`.
For now, I've decided to store images structured by folders with user id. How to do it? Easy.
10. I've created params object:
    ```
    const uploadImageParams = {
      folder: `user-${user.id}`
    }
    ```
    And made a variable with result of uploading that file:
`const uploadedImageResult = await uploadImageToStorage(req.files.image, uploadImageParams)`
11. If your request succeeded, it will return object that looks like this:
    ```
    {
      fileId: '6581e02044c257da433888de',
      name: 'somefilename.jpg',
      size: 2508483,
      versionInfo: { id: '6581e02044c257da433888de', name: 'Version 1' },
      filePath: '/user-3/somefilename.jpg',
      url: 'https://ik.imagekit.io/${storageName}/user-3/somefilename.jpg',
      fileType: 'image',
      height: 1560,
      width: 1302,
      thumbnailUrl: 'https://ik.imagekit.io/leera/tr:n-ik_ml_thumbnail/user-3/somefilename.jpg'',
      AITags: null
    }
    ```
The most needed basic props we need from here are `url` and `thumbnailUrl` to display image on client side.

## Conclusion
Definitely one of the most comfortable and easy-to-use bucket for files, very friendly DX and tons of free features.

## P.S.
Also it can add tags to your images, optimize 'em, connect external buckets (like AWS, etc.) and do lots of custom stuff.

I hope you will find it useful and will never struggle in searching for free image storage again :)