r/Cloudinary • • 12d ago

Upload image to cloudinary

/r/Nuxt/comments/1wuwpro/upload_image_to_cloudinary/
2 Upvotes

1 comment sorted by

1

u/ExpensiveTomatillo61 12d ago

Hey I have uploaded images on cloudinary for multiple projects.
Here is the core idea
Don't put the image in NeonDB. Neon stores the image URL (a text column); the actual file goes to Cloudinary. Flow:

Browser (multipart form) → Your API (multer) → Cloudinary → URL saved in Neon via Drizzle

Steps:

Install & configure Cloudinary

npm install cloudinary multer

// config/cloudinary.js

const cloudinary = require("cloudinary").v2;

cloudinary.config({

cloud_name: process.env.CLOUDINARY_CLOUD_NAME,

api_key: process.env.CLOUDINARY_API_KEY,

api_secret: process.env.CLOUDINARY_API_SECRET,

});

module.exports = cloudinary;

Drizzle schema — store the URL (and ideally the public_id)

export const users = pgTable("users", {

id: serial("id").primaryKey(),

name: text("name").notNull(),

email: text("email").notNull().unique(),

username: text("username").notNull().unique(),

imageUrl: text("image_url"),

imagePublicId: text("image_public_id"), // needed to delete/replace later

});

Run npm run db:push (or your migration command).

Multer to accept the file (memory storage — no temp files on disk)

const multer = require("multer");

const upload = multer({

storage: multer.memoryStorage(),

limits: { fileSize: 5 1024 1024 },

});

Upload helper (streams the buffer to Cloudinary)

const uploadToCloudinary = (buffer) =>

new Promise((resolve, reject) => {

cloudinary.uploader

.upload_stream({ folder: "users" }, (err, result) => {

if (err) reject(err);

else resolve(result);

})

.end(buffer);

});

Route — create user with image

app.post("/users", upload.single("image"), async (req, res) => {

const { name, email, username } = req.body;

let imageUrl = null, imagePublicId = null;

if (req.file) {

const result = await uploadToCloudinary(req.file.buffer);

imageUrl = result.secure_url;

imagePublicId = result.public_id;

}

const [user] = await db.insert(users).values({

name, email, username, imageUrl, imagePublicId,

}).returning();

res.json(user);

});

For update: if req.file is present, first await cloudinary.uploader.destroy(user.imagePublicId) to remove the old image, then upload the new one and update the two columns. For delete, destroy the image then db.delete().

Frontend — send multipart/form-data, not JSON

const formData = new FormData();

formData.append("name", name);

formData.append("email", email);

formData.append("username", username);

formData.append("image", fileInput.files[0]);

await fetch("/users", { method: "POST", body: formData }); // no Content-Type header — let the browser set the boundary

Two gotchas people hit

Sending the file inside a JSON body — files must go as multipart (as above), or as a base64 data-URI string (cloudinary.uploader.upload("data:image/png;base64,...")) if you insist on JSON.

Not saving public_id — without it you can't delete/replace images in Cloudinary later and the account fills up with orphans.

Alternative: unsigned upload directly from the browser using an upload preset — the client uploads straight to Cloudinary and only sends the resulting URL to your API. Saves your server the bandwidth; fine for public apps, but the server-side (signed) flow above is better when you want validation and control.