Cloudinary Storage Provider

This provider adds Cloudinary SDK configuration, Zod-validated environment variables, and a Multer middleware that accepts in-memory uploads (images, video, PDF) with type and size limits.

Features

  • Zod-validated CLOUDINARY_* environment variables
  • Configured Cloudinary SDK (v2)
  • Multer memory storage with allowlist MIME types and 5 MB max file size
  • MVC and Feature architecture variants

Installation Guide

npx servercn-cli add pr cloudinary-storage

File Structure

  • src/configs/env.ts
  • src/configs/cloudinary.ts
  • src/middlewares/upload-file.ts
  • src/shared/configs/env.ts
  • src/shared/configs/cloudinary.ts
  • src/shared/middlewares/upload-file.ts

Environment Configuration

src/configs/env.ts
import "dotenv-flow/config";
import { z } from "zod";
 
export const envSchema = z.object({
  CLOUDINARY_CLOUD_NAME: z.string(),
  CLOUDINARY_API_KEY: z.string(),
  CLOUDINARY_API_SECRET: z.string()
});
 
export type Env = z.infer<typeof envSchema>;
 
const result = envSchema.safeParse(process.env);
 
if (!result.success) {
  console.error("❌ Invalid environment configuration");
  console.error(z.prettifyError(result.error));
  process.exit(1);
}
 
export const env: Readonly<Env> = Object.freeze(result.data);
 
export default env;

Cloudinary client

src/configs/cloudinary.ts
import { v2 as cloudinary } from "cloudinary";
import env from "./env.ts";
 
cloudinary.config({
  cloud_name: env.CLOUDINARY_CLOUD_NAME,
  api_key: env.CLOUDINARY_API_KEY,
  api_secret: env.CLOUDINARY_API_SECRET
});
 
export default cloudinary;

The template also includes a commented uploadToCloudinary / deleteFileFromCloudinary example in the same file—copy it into a service or util when you wire routes.

Upload middleware (Multer)

src/middlewares/upload-file.ts
import multer from "multer";
 
export const ALLOWED_FILE_TYPES = [
  "image/jpeg",
  "image/png",
  "image/webp",
  "video/mp4",
  "video/mpeg",
  "video/quicktime",
  "application/pdf"
];
 
export const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
 
const storage = multer.memoryStorage();
 
const fileFilter: multer.Options["fileFilter"] = (_req, file, cb) => {
  if (!ALLOWED_FILE_TYPES.includes(file.mimetype)) {
    return cb(null, false);
  }
  cb(null, true);
};
 
const upload = multer({
  storage,
  limits: { fileSize: MAX_FILE_SIZE },
  fileFilter
});
 
export default upload;

Usage

Attach the middleware to a route, then pass req.file?.buffer to an upload helper that streams to Cloudinary (see the commented block in cloudinary.ts in the template).

import upload from "./middlewares/upload-file";
import type { RequestHandler } from "express";
 
export const uploadAvatar: RequestHandler[] = [
  upload.single("file"),
  async (req, res, next) => {
    const buffer = req.file?.buffer;
    if (!buffer) return res.status(400).json({ error: "No file" });
    // call uploadToCloudinary(buffer, { folder: "avatars" })
    next();
  }
];

Example .env

CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

File & Folder Structure

Loading files...

Installation

npx servercn-cli add provider cloudinary-storage