How to upload images to Cloudinary using Express and Template Engines
oteri
Posted on March 21, 2023
Uploading media images to a central location found in forms and creating your media profile on social media and other applications on the web is necessary to capture user input. Cloudinary as a tool provides such a feature making it possible to store all your media library in the cloud.
In this tutorial, you will learn how to use technologies like Express as the server with the post method to send this image to Cloudinary. The template engine (HBS) would generate HTML and display the image on the client-side (frontend) of the application.
Demo and source code
To follow through, you can find the source code in this repo and try the live demo.
The final app will look something like the following:
Prerequisites
The following requirements are needed:
- Cloudinary account. Sign up for a free account
- Knowledge of JavaScript and Node.js
- Have Node installed on your local machine, which comes with the node package manager (npm) for installing package dependencies
What is Cloudinary?
Cloudinary is a cloud-based image and video management service enabling users to upload, store, manage, manipulate, and deliver engaging media experiences at scale with its image and video APIs.
What are template engines?
Template engines work with server-side applications like Node.js runtime and enable you to use static template files in your application. There are many existing template engines to choose from, and they render data as variables into the HTML template.
Getting started
To get started, create a new directory for the Node project and call it upload-image
.
mkdir upload-image
Next, navigate to the new directory and run the following command to initialize the project:
npm init -y
The -y flag accepts all the initial defaults in the package.json
file
With the creation of the package.json
file, install these dependencies:
npm install cloudinary dotenv express express-fileupload hbs
-
cloudinary
: will allow the use of the Node.js software development kit (SDK) and the classes in your code -
dotenv
: load environment and store secret variables from the.env
file when working locally -
express
: is the Node.js web framework for creating the server -
express-fileupload
: an express middleware for uploading files -
hbs
: Express.js view engine for handlebars
Building the user interface with hbs
In the root directory, create these two folders, views and public. The views folder will contain the following files, index.hbs
, layout.hbs
, and media.hbs
, while the public folder will have all the styles for the project. Create a folder named css and include the style.css
file all within the public folder.
- The views folder is the template that will have the HTML structure
- The public folder is the container recognized by hbs for all the static files used in the project, like the styles
> public
> css
style.css
> views
index.hbs
layout.hbs
media.hbs
Update each of the files in the views folder with the following:
// views/index.hbs
<div class='section'>
<h1>Uploading images to Cloudinary Console</h1>
<form
action='/upload'
method='post'
encType='multipart/form-data'
ref='uploadImage'
id='uploadImage'
>
<div class='form__section'>
<input type='file' name='uploadFile' />
<label>
Please select the image to be uploaded
</label>
</div>
<input type='submit' value='Upload!' class='btn' />
</form>
</div>
The action attribute, /upload
in the <form>
element, represents where the data gets sent.
// layout.hbs
<html lang='en'>
<head>
<meta charset='UTF-8' />
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta
name='description'
content='Upload images using Cloudinary and Node.js'
/>
<title>Take your images to the cloud</title>
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
<link
href='https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap'
rel='stylesheet'
/>
<link rel='stylesheet' href='css/style.css' />
</head>
<body>
{{{body}}}
</body>
</html>
The layout.hbs
file represents the typical markup for every website structure. Within the <body>
, all the other files from the views folder are placed directly in the {{{body}}}
which displays the template of the remaining files.
// media.hbs
<div class='img-upload'>
<h2>Title: MacBook Pro</h2>
<img src="https://images.pexels.com/photos/8020173/pexels-photo-8020173.jpeg?auto=compress&cs=tinysrgb&w=800&lazy=load" alt='Cross pollination by insect' />
<a href='/' class='btn'>Upload another photo</a>
</div>
This particular code will display the final image upon successful upload to Cloudinary in the endpoint, /upload
, which we will set in the index.js file.
For the styles, copy and paste this code into the public/css/style.css
file:
// public/css/style.css
*,
*:before,
*:after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
}
a {
text-decoration: none;
}
h1 {
font-weight: 700;
}
.section {
padding: 2em 3em;
}
.form__section {
margin-top: 2em;
margin-bottom: 2em;
}
label {
display: block;
margin-top: 0.75em;
}
.btn {
border: unset;
background: royalblue;
color: #fff;
padding: 1em 2em;
cursor: pointer;
}
.img-upload {
display: flex;
align-items: center;
flex-direction: column;
}
.img-upload h2 {
margin-top: 1em;
}
.img-upload img {
max-width: 75rem;
width: 85%;
margin-inline: auto;
padding: 2em 0;
}
img {
display: block;
max-width: 100%;
}
Setting up a Node/Express app
Create a file named index.js
in the root directory. The index.js
file will contain all the code for the Node.js/Express server.
Copy and paste the following code into the index.js
file:
// index.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 4000;
// middleware
app.use(express.json());
app.get('/', (req, res) => {
res.send({ title: "Uploading images to Cloudinary Console" });
});
app.listen(PORT, () => {
console.log(`server listening on port ${PORT}`);
});
In the code snippet above, the following occurs:
- Declare the express module with
require(‘express’)
- Run the express function, as this defines the Express instance assigned to a variable app
- Assign a port number,
4000
, which will be accessible in the browser - The middleware,
app.use()
function parses the incoming JSON requests at the path specified - The app.get() handles the GET requests with the passed route to a given endpoint which in this case is the home route, /
- The port environment variable is called in the
app.listen()
function and displays the console message when the app is running
Before testing and running this app, let’s update the package.json
file within the scripts object:
// package.json
{
...
"scripts": {
"start": "node index.js",
},
...
}
Run this command:
node index.js
The development server starts running on http://localhost:4000
, which should look something like this:
Configuring Cloudinary
With the server working, let’s configure the app with the following details from your Cloudinary dashboard.
First, create these files, .env
and config.js
, in the root directory.
Include this code in the .env
file:
// .env
CLOUDINARY_CLOUD_NAME=<cloud-name>
CLOUDINARY_API_KEY=<api-key>
CLOUDINARY_API_SECRET=<api-secret>
Access your variables, like your cloud name, API key, and API secret, from the Cloudinary dashboard. Replace the placeholders with your values.
In the config.js
file, copy and paste this code:
// config.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,
secure: true,
});
module.exports = cloudinary;
The configuration of this file includes the following:
- Importing the Cloudinary dependency
- Set the configured parameters in the config objects with the appropriate variables from the .env file
- Export the cloudinary classes with
module.exports
Let’s update the index.js
file with the following code:
// index.js
...
const path = require("path");
const fileUpload = require("express-fileupload");
const cloudinary = require("./config");
// dotenv
require("dotenv").config();
...
app.set("view engine", "hbs");
app.set("views", path.join(__dirname, "views"));
// middleware
app.use("/", express.static(path.join(__dirname, "public")));
app.use(
fileUpload({
useTempFiles: true,
limits: { fileSize: 50 * 1024 * 1024 },
})
);
app.get("/", (req, res) => {
res.render("index", {
title: "Uploading images to Cloudinary Console",
});
});
app.post("/upload", async (req, res) => {
if (!req.files || Object.keys(req.files).length === 0) {
res.status(400).json({
msg: "No files were uploaded. Try uploading an image",
});
return;
}
const uploadFile = req.files.uploadFile;
const result = await cloudinary.uploader.upload(uploadFile.tempFilePath, {
public_id: uploadFile.name,
resource_type: "auto",
folder: "uploaded",
use_filename: true,
unique_filename: false,
});
if (result.url) {
res.render("media", {
img: result.url,
name: uploadFile.name.replace(/.jpeg|.jpg|.png|.webp/gi, ""),
});
} else {
res.render("/upload");
}
});
...
The code above does the following:
- Imports Node.js
path
module for transforming file paths,express-fileupload
, andcloudinary
- Configure
dotenv
to prevent leaking the environment variables in the.env
file - The
app.set()
function with the set “view engine” will render the files.hbs
in the views folder - The other
app.set()
reads the file path of the directory views - The first
app.use()
middleware serves static files in the public folder - Managing the upload process is handled by the
fileUpload
options, and thefileSize
indicates the maximum file size in bytes - The get request with the
res.render()
method accepts the template index and a title string data in the option object. - The post request with the endpoint /upload that route to the successful page with the final image. Also, within the callback of this HTTP method, it shows a 400 status error code and displays a message to the user of a failed image upload on clicking the button Upload.
- Using the Cloudinary upload method, passing in parameters with the name of the folder to store and maintaining the file name of the image
- Lastly, using regex, you strictly remove the extension from the uploaded image and return only the image name
Displaying the rendered data
With the server complete, let’s return to the index.js
file and pass the rendered data property.
Let’s update the files in the views folder once again.
// views/index.hbs
<div class='section'>
<h1>{{title}}</h1>
...
</div>
Replace the content within the with the expression {{variable name followed by a }}
. When the template is executed, the expression is replaced with the actual input object from the res.render()
method.
Now do the same for the media.hbs
file and update its contents.
// views/media.hbs
<div class='img-upload'>
<h2>Title: {{name}}</h2>
<img src={{img}} alt='Uploaded to server' />
...
</div>
Run the app with this command to start the server once again:
node index.js
If you upload any image from your app, your Cloudinary media library should display the images in an uploaded folder like the screenshot below.
Conclusion
This article showed you the process of using a Node/Express server, connecting it with a template engine hbs to display an uploaded image and storing these images to Cloudinary.
Further reading
Posted on March 21, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.