Logo

dev-resources.site

for different kinds of informations.

Mastering Image Uploads in Node.js: A Beginner-to-Advanced Guide with Multer and Cloudinary

Published at
1/5/2025
Categories
node
backenddevelopment
multer
webdev
Author
yugjadvani
Author
10 person written this
yugjadvani
open
Mastering Image Uploads in Node.js: A Beginner-to-Advanced Guide with Multer and Cloudinary

Efficiently handling image uploads is a critical aspect of backend development, particularly in modern web applications. In this comprehensive guide, we'll demonstrate how to store images using Node.js, TypeScript, PostgreSQL, Multer, and Cloudinary. Whether you're a beginner or looking to enhance your backend skills, this step-by-step tutorial will help you seamlessly implement image uploads.


Prerequisites

Before diving into the implementation, ensure you have the following:

  1. Node.js and npm/yarn installed.
  2. Basic knowledge of TypeScript and Express.js.
  3. A PostgreSQL database instance.
  4. A Cloudinary account for image hosting.

1. Initializing the Project

Start by creating a new Node.js project:

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies:

npm install cloudinary dotenv multer pg cors express
npm install --save-dev typescript ts-node eslint nodemon typescript-eslint @eslint/js @types/express @types/multer @types/pg @types/cors
Enter fullscreen mode Exit fullscreen mode

Create a .env file to store your environment variables:

touch .env
Enter fullscreen mode Exit fullscreen mode

Populate the .env file with the following:

PORT=8080
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=localhost
DB_PORT=5432
DB_NAME=your_db_name
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
Enter fullscreen mode Exit fullscreen mode

2. Configuring TypeScript

Set up TypeScript by creating a tsconfig.json file:

touch tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Add the following configuration:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "target": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "strict": true
  },
  "lib": ["es2015"]
}
Enter fullscreen mode Exit fullscreen mode

3. Setting Up the Express Server

Create the necessary directories:

mkdir src public
cd public && mkdir temp && cd temp && touch .gitkeep
cd ../src && mkdir controllers db middlewares routes utils
Enter fullscreen mode Exit fullscreen mode

Inside the src directory, create app.ts and index.ts:

src/app.ts

import express from 'express';
import cors from 'cors';
import routes from './routes';

const app = express();

app.use(
    cors({
        origin: process.env.CORS_ORIGIN,
        credentials: true,
    })
);
app.use(express.json({ limit: '16kb' }));
app.use(express.urlencoded({ extended: true, limit: '16kb' }));
app.use(express.static('public'));

app.use('/api/v1', routes);

export default app;
Enter fullscreen mode Exit fullscreen mode

src/index.ts

import app from './app';

app.listen(process.env.PORT || 8080, () => {
  console.log(`Server running on port ${process.env.PORT}!`);
});
Enter fullscreen mode Exit fullscreen mode

4. Configuring PostgreSQL

Set up database connectivity in src/db/db.ts:

import { Pool } from 'pg';
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

const pool = new Pool({
    user: process.env.DB_USER,
    host: process.env.DB_HOST,
    database: process.env.DB_NAME,
    password: process.env.DB_PASSWORD,
    port: Number(process.env.DB_PORT),
});

pool.connect(err => {
    if (err) {
        console.error('Error connecting to the database:', err);
    } else {
        console.log('Connected to PostgreSQL database');
    }
});

export default pool;
Enter fullscreen mode Exit fullscreen mode

5. Setting Up Multer Middleware

Handle file uploads using Multer by creating src/middlewares/multer.middleware.ts:

import multer from 'multer';

const storage = multer.diskStorage({
    destination: function (req, file: Express.Multer.File, cb: (error: Error | null, destination: string) => void) {
        cb(null, './public/temp');
    },
    filename: function (req, file: Express.Multer.File, cb: (error: Error | null, filename: string) => void) {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
        cb(null, file.fieldname + '-' + uniqueSuffix);
    },
});

const upload = multer({ storage });

export default upload;
Enter fullscreen mode Exit fullscreen mode

6. Integrating Cloudinary

Create src/utils/cloudinary.ts:

import { v2 as cloudinary } from 'cloudinary';
import fs from 'fs';

(async function () {
    // Configure Cloudinary
    cloudinary.config({
        cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
        api_key: process.env.CLOUDINARY_API_KEY,
        api_secret: process.env.CLOUDINARY_API_SECRET,
        secure: true,
    });
})();

const uploadOnCloudinary = async (file: string) => {
    try {
        if (!file) return null;

        // Upload image to Cloudinary
        const result = await cloudinary.uploader.upload(file, {
            folder: 'ryde-uber-clone',
            resource_type: 'image',
        });

        // file has been uploaded successfully
        console.log('file is uploaded on cloudinary ', result);

        // Check if the file exists before attempting to delete it
        if (fs.existsSync(file)) {
            fs.unlinkSync(file);
        } else {
            console.warn(`File not found: ${file}`);
        }

        return result.url;
    } catch (error) {
        console.error('Error uploading to Cloudinary:', error);

        // Check if the file exists before attempting to delete it
        if (fs.existsSync(file)) {
            fs.unlinkSync(file);
        } else {
            console.warn(`File not found: ${file}`);
        }

        return null;
    }
};

export default uploadOnCloudinary;
Enter fullscreen mode Exit fullscreen mode

7. Creating Routes and Controllers

Define a route for handling uploads:

src/routes/auth.routes.ts

import express from 'express';
import { signUp } from '../controllers/auth.controllers';
import upload from '../middlewares/multer.middleware';

const router = express.Router();

router.post('/sign-up', upload.single('avatar'), signUp); // User signUp

export default router;
Enter fullscreen mode Exit fullscreen mode

src/routes/index.ts

import express from 'express';
import authRoutes from './auth.routes';

const router = express.Router();

router.use('/auth', authRoutes);

export default router;
Enter fullscreen mode Exit fullscreen mode

src/controllers/auth.controllers.ts

import { Request, Response } from 'express';
import pool from '../db/db';
import asyncHandler from '../utils/asyncHandler';
import uploadOnCloudinary from '../utils/cloudinary';

export const signUp = asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const { firstname, lastname, email, password } = req.body;

    if (!firstname || !lastname || !email || !password) {
        res.status(400).json({ message: 'All fields are required' });
    }

    // Check if user already exists
    const userExists = await pool.query('SELECT * FROM users WHERE email = $1', [email]);

    if (userExists.rows.length > 0) {
        res.status(409).json({
            success: false,
            message: 'User already exists',
        });
        return;
    }

    // Upload avatar
    const avatarPath = req.file?.path;

    let avatar = null;
    if (avatarPath) {
        avatar = await uploadOnCloudinary(avatarPath);
    }

    // Make sure you bcrypt the password before saving in Database
    const newUser = await pool.query(
        'INSERT INTO users (firstname, lastname, email, password, avatar) VALUES ($1, $2, $3, $4, $5) RETURNING id, email, firstname, lastname, avatar',
        [firstname, lastname, email, password, avatar]
    );

    res.status(201).json({
        success: true,
        message: 'User signed up successfully',
        user: newUser.rows[0],
    });
});
Enter fullscreen mode Exit fullscreen mode

8. Testing the Application

Start the server:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Use tools like Postman or curl to test the /auth/signup endpoint by sending a POST request with a file and user data.


Resources

You can find the complete code for this project on GitHub:
GitHub Repository: Image Upload with Node.js
Feel free to explore the repository, clone it, and try it out for your projects!


Conclusion

By following this guide, you've implemented a robust image upload system using Node.js, Multer, and Cloudinary. This approach ensures reliability, scalability, and ease of maintenance. Dive deeper, customize the functionality, and share your thoughts. Together, we're building better backends!


Enjoyed the read? If you found this article insightful or helpful, consider supporting my work by buying me a coffee. Your contribution helps fuel more content like this. Click here to treat me to a virtual coffee. Cheers!

backenddevelopment Article's
30 articles in total
Favicon
How Does This Jewelry Customization Site Work? Need Insights!
Favicon
Building a Secure Authentication API with TypeScript, Node.js, and MongoDB
Favicon
Inter-process Communication with WitCom
Favicon
🚀 Roadmap to Becoming a Full-Stack Web Developer in 2025 🌐
Favicon
JAVA PARA INICIANTES - Orientações gerais e Configurações do Ambiente
Favicon
JAVA PARA INICIANTES - 1. Introdução, Fundamentos e Prática 11/01/25 #Cap1
Favicon
Introduction to Django Authentication: Understanding the Core Components and Benefits
Favicon
WitCom: Modernizing Client-Server Communication
Favicon
POST ABOUT THE TRENDS IN WEB DEVELOPMENT
Favicon
Connecting frontend And Backend In react.js
Favicon
Mastering Image Uploads in Node.js: A Beginner-to-Advanced Guide with Multer and Cloudinary
Favicon
Backend Development Course Structure
Favicon
Introduction to Spring Boot: A Complete Guide
Favicon
Navigating Backend Responsibilities as a Frontend Developer
Favicon
🚀 Introducing DataForge: A Laravel-Based Framework to Transform Backend Development
Favicon
Python's Unstoppable Rise, Dominating The Modern Backend Environment
Favicon
[Boost]
Favicon
How to write Simple Endpoint and Functions
Favicon
Effortless AI Model Integration: Build and Evaluate AI Models (Spring Boot and Hugging Face)
Favicon
Email Verifier using Go
Favicon
No Swagger in .NET 9? Here's What You Need to Know!
Favicon
Microservice communication using Kafka
Favicon
[Volunteer Opportunity] Back-End Developer Needed for a EU Uni Finder/Helper Project 🚀
Favicon
Dart server-side framework for solo project
Favicon
I need a partner, who's interested in making the backend of a messaging app, for free though. Thanks, in advance 😃🙂. Contact me at [email protected]
Favicon
503 errors
Favicon
Top 6 Reasons to Partner with Experts in Mobile App Design and Backend Development
Favicon
Pointers in C Programming - Lay Man's Analogy
Favicon
Navigating the Web Development Landscape: Finding Your Role
Favicon
Excited to Be Part of This Community! 🚀

Featured ones: