September 22, 2024
Updated on September 21, 2024
Multer Guide For Begginers
How to use multer
article cover

The multer npm package is a middleware used for handling multipart/form-data, which is primarily used for file uploads in Node.js applications. It allows you to upload files to your server with ease, making it one of the most popular file upload libraries in the Node.js ecosystem.

Key Features of multer:
  • File Parsing: It automatically parses multipart/form-data requests (usually form submissions containing files), making it easy to handle file uploads in your server.
  • File Storage: multer allows you to specify where and how files should be stored, either in memory or on the disk.
  • Customizable: You can control file size limits, file types, and filenames through its configuration options.
  • Error Handling: It includes built-in error handling, making it easy to manage issues like file size exceeding limits or invalid file types.
  • Why You Might Use multer:
  • Uploading Files: If your application needs to upload images, videos, or any other files from a client to your server, multer simplifies that process.
  • Form Data Parsing: It works seamlessly with form data that contains both text fields and files.
  • Disk Storage or Memory Storage: You can decide whether to temporarily store files on the server's disk or hold them in memory, depending on your needs.
  • javascript
    import multer from 'multer';
    import { v4 as uuid } from 'uuid'; // for unique identification

  • multer: This is a middleware for handling multipart/form-data, which is commonly used for uploading files in Node.js and Express applications.
  • uuid: The uuid library is used to generate unique identifiers. v4 creates a version 4 UUID, which is a universally unique identifier.
  • Multer Storage Configuration
  • javascript
    const storage = multer.diskStorage({
    // Define the destination folder for uploaded files
    destination(req, file, callback) {
    callback(null, 'uploads');
    },
    // Define the filename for the uploaded file
    filename(req, file, callback) {
    const id = uuid(); // Generate a unique ID for each file using UUID v4
    const extName = file.originalname.split('.').pop(); // Extract the file extension from the original name
    callback(null, `${id}.${extName}`); // Construct the new filename with the UUID and original file extension
    },
    });
  • multer.diskStorage: This method allows you to configure how files are stored on disk.
  • 3. Exporting Multer Middleware
    javascript
    export const singleUpload = multer({ storage }).single('image');

  • multer({ storage }): Initializes multer with the custom storage configuration defined above.
  • .single('image'): Configures multer to handle single file uploads. The string 'image' refers to the name of the form field that contains the file. For example, if your HTML form has an input field like <input type="file" name="image" />, this middleware will handle the file uploaded through that field.
  • This code sets up a file upload handler using multer in an Express application. It configures multer to:

  • Save uploaded files to the uploads directory.
  • Generate unique filenames using UUIDs to prevent filename collisions.
  • Preserve the original file extension.
  • typescript
    import express from "express";
    import { singleUpload } from "./middlewares/multer.js";
    const port = 3000;
    const app = express();
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use("/uploads", express.static("uploads"));
    app.post("/register", singleUpload, (req, res) => {
    const { username } = req.body;
    const image = req.file ? req.file.filename : null;
    res.status(200).json({
    message: "User registered successfully",
    user: {
    username,
    image: image ? `/uploads/${image}` : null,
    },
    });
    });
    app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
    });

    javascript
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use("/uploads", express.static("uploads"));
  • app.use(express.json());: Adds middleware to the Express app that parses incoming requests with JSON payloads and makes the parsed data available on req.body.
  • app.use(express.urlencoded({ extended: true }));: Adds middleware that parses incoming requests with URL-encoded payloads (e.g., form data). The extended: true option allows for more complex objects and arrays to be encoded.
  • app.use("/uploads", express.static("uploads"));: Serves static files from the "uploads" directory under the "/uploads" URL path. This means any file in the "uploads" directory can be accessed via a URL like http://localhost:3000/uploads/filename.
  • Post Handler
    javascript
    app.post("/register", singleUpload, (req, res) => {
    const { username } = req.body;
    const image = req.file ? req.file.filename : null;
    javascript
    res.status(200).json({
    message: "User registered successfully",
    user: {
    username,
    image: image ? /uploads/${image} : null,
    },
    });
    });

  • app.post("/register", singleUpload, (req, res) => { ... });: Defines a POST route at /register. When a POST request is made to this route, the singleUpload middleware is invoked to handle file uploads, followed by the callback function that processes the request and sends a response.
  • const { username } = req.body;: Destructures the username property from the request body, which was sent with the POST request.
  • const image = req.file ? req.file.filename : null;: Checks if a file was uploaded (i.e., req.file is defined). If a file was uploaded, it retrieves the filename; otherwise, it sets image to null.
  • res.status(200).json({ ... });: Sends a JSON response with a status code of 200 (OK). The response includes a success message and the user information:
  • Starting the Server
    javascript
    app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
    });

    The code sets up an Express server that handles JSON and URL-encoded form data, serves static files from an "uploads" directory, processes file uploads through middleware, and responds to registration requests with user information and uploaded file URLs

    With Database setup

    typescript
    //app.ts
    import express from "express";
    import { singleUpload } from "./middlewares/multer.js";
    import { connectToDB } from "./models/userSchema.js";
    import User from "./models/userSchema.js";
    const port = 3000;
    const app = express();
    const URI = "mongodb://localhost:27017/multer"; // Ensure your DB name is correct
    // Connect to the database
    connectToDB(URI);
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use("/uploads", express.static("uploads"));
    app.post("/register", singleUpload, async (req, res) => {
    try {
    const { username } = req.body;
    const avatar = req.file ? req.file.filename : null;
    // Check for missing fields
    if (!username || !avatar) {
    return res.status(400).json({
    message: "Please enter all required fields (username and avatar).",
    });
    }
    // Check if the user already exists
    const userExist = await User.findOne({ username });
    if (userExist) {
    return res.status(400).json({
    message: "User already exists.",
    });
    }
    // Create a new user
    await User.create({
    username,
    avatar,
    });
    res.status(201).json({
    message: "User created successfully.",
    });
    } catch (error) {
    console.error("Error creating user:", error);
    res.status(500).json({
    message: "Internal server error.",
    });
    }
    });
    app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
    });

    Delete Image from uploads folder and Db

    typescript
    import express from "express";
    import { rm } from "fs";
    import path from "path";
    import { connectToDB } from "./models/userSchema.js";
    import User from "./models/userSchema.js";
    import mongoose from "mongoose";
    import { singleUpload } from "./middlewares/multer.js";
    const port = 3000;
    const app = express();
    const URI = "mongodb://localhost:27017/multer"; // Ensure your DB name is correct
    // Connect to the database
    connectToDB(URI);
    app.use(express.json());
    app.use("/uploads", express.static("uploads"));
    app.post("/register", singleUpload, async (req, res) => {
    try {
    const { username } = req.body;
    const avatar = req.file ? req.file.filename : null;
    // Check for missing fields
    if (!username || !avatar) {
    return res.status(400).json({
    message: "Please enter all required fields (username and avatar).",
    });
    }
    // Check if the user already exists
    const userExist = await User.findOne({ username });
    if (userExist) {
    return res.status(400).json({
    message: "User already exists.",
    });
    }
    // Create a new user
    await User.create({
    username,
    avatar,
    });
    res.status(201).json({
    message: "User created successfully.",
    });
    } catch (error) {
    console.error("Error creating user:", error);
    res.status(500).json({
    message: "Internal server error.",
    });
    }
    });
    app.delete("/remove/:id", async (req, res) => {
    const { id } = req.params;
    // Validate the ObjectId format
    if (!mongoose.Types.ObjectId.isValid(id)) {
    return res.status(400).json({
    message: "Invalid User ID format.",
    });
    }
    try {
    const user = await User.findById(id);
    if (!user) {
    return res.status(404).json({
    message: "User not found.",
    });
    }
    // Delete the avatar image if it exists
    if (user.avatar) {
    const avatarPath = path.join(process.cwd(), "uploads", user.avatar);
    rm(avatarPath, (err) => {
    if (err) {
    console.error("Error deleting file:", err);
    return res.status(500).json({
    message: "Error deleting user avatar.",
    });
    }
    console.log("User avatar deleted.");
    });
    }
    // Delete the user from the database
    await User.findByIdAndDelete(id);
    res.json({
    message: "User deleted successfully.",
    });
    } catch (error) {
    console.error("Error deleting user:", error);
    res.status(500).json({
    message: "Internal server error.",
    });
    }
    });
    app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
    });

    Update Avatar

    typescript
    app.put("/profile/:id", singleUpload, async (req, res) => {
    const id = req.params.id;
    const { username } = req.body;
    const avatar = req.file ? req.file.filename : null; //get image
    const user = await User.findById(id); //find user
    if (!user)
    return res.status(404).json({
    message: "user not found",
    });
    if (username) user.username = username; //Updates the username if a new value is provided.
    if (avatar) {
    //remove oldSaved Avatar
    const removeOldAvatar = path.join(
    process.cwd(),
    "uploads",
    user.avatar as string
    );
    //if nott remove then show error
    rm(removeOldAvatar, (err) => {
    if (err)
    return res.status(500).json({
    message: "Image not delete for update",
    });
    console.log("image delete");
    });
    user.avatar = avatar; //update the avatr if a new avatar value provide
    }
    await user.save(); //user save after update
    res.status(200).json({
    message: "user updated",
    });
    });

    The PUT /profile/:id endpoint allows you to update a user’s profile, including their username and avatar. It checks for the existence of the user, updates the necessary fields, handles the deletion of the old avatar file if a new one is provided, and finally saves the updated user details to the database. If any errors occur during this process, appropriate status codes and messages are sent back to the client.

    Give a star and follow :

    ayanhasnain03 (Ayan Hasnain) (github.com)

    🔗 Check out my LinkedIn for updates and learn new things with my easy-peasy notes:

    AYAN HASNAIN | LinkedIn

    ;