How to Upload and Manage Pictures on your Website (using Cloudinary API)
Ever thought of making a website or web app that supports uploading and managing pictures but don’t know how to? I certainly have, and thankfully, there is an easy way to do just that. Although in order to properly benefit from this tutorial, there are a few things that you should know including the basics of server-side (back-end) programming and how HTTP requests work.
Setting Up The Front-End
For the purposes of this quick tutorial, the UI will just be a simple one with only one function: uploading pictures. I've built it using React, although vanilla JS, HTML, and CSS would work just fine.
Note that only two HTML elements are actually needed to make this initial UI.
<input name="image" type="file" />
<button><span>Upload</span></button>
Although, an additional p element for logging purposes (e.g., image is uploading, image has been successfully uploaded, error: image could not be added) is helpful and informative for any user.
The rest of the design for the front-end is arbitrary and eventually depends on the nature of your web app.
Setting Up the Back-End and Cloudinary API
Go ahead and sign up here for an account. It's completely free. Next, set up the back-end. I'll be using NodeJS (Express) and MongoDB (With Mongoose) for the database.
/* server.js */
const express = require('express');
const app = express();
// Setting up Mongo and Mongoose
const mongoose = require('mongoose')
/* Connnect to our database */
const mongoURI = process.env.MONGODB_URI || "mongodb://localhost:27017/ImageAPI"
mongoose.connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
Now, let's create a Mongoose model for an Image object and export it to server.js.
/* image.js */
const mongoose = require('mongoose');
// create an image schema
const imageSchema = mongoose.Schema({
image_id: {
type: String,
required: true
},
image_url: {
type: String,
required: true
},
created_at: String
});
// create an image model using the schema
const Image = mongoose.model('Image', imageSchema);
module.exports = { Image };
Next, import the multipart middleware. This middleware basically allows you to access uploaded files from req.file.
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();
Finally, set up Cloudinary using your credentials which can be found on your Cloudinary dashboard.
const cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: 'REPLACE_WITH_CLOUD_NAME_HERE',
api_key: 'REPLACE_WITH_API_KEY_HERE',
api_secret: 'REPLACE_WITH_API_SECRET_HERE'
});
Building the API Routes
POST route to create an image
app.post("/images", multipartMiddleware, (req, res) => {
// Use uploader.upload API to upload image to cloudinary server.
cloudinary.uploader.upload(
req.files.file.path, // req.files contains uploaded files
function (result) {
// Create a new image using the Image mongoose model
const img = new Image({
image_id: result.public_id, // image id on cloudinary server
image_url: result.url, // image url on cloudinary server
created_at: new Date(),
});
// Save image to the database
img.save().then(
saveRes => {
res.send(saveRes);
},
error => {
res.status(400).send(error); // 400 for bad request
}
);
});
});
GET route to get all images
app.get("/images", (req, res) => {
Image.find().then(
images => {
res.send({ images }); // can wrap in object if want to add more properties
},
error => {
res.status(500).send(error); // server error
}
);
});
GET route to get an image by its database id
app.get("/images", (req, res) => {
const query = {_id: req.query.id}
Image.findOne(query).then(image => {
if (!image) {
res.status(404).send(); // could not find this img
} else {
res.send(image);
}
}).catch((error) => {
res.status(500).send() // server error
})
});
A DELETE route to remove an image by its cloudinary id
app.delete("/images/:imageId", (req, res) => {
const imageId = req.params.imageId;
// Delete an image by its id (NOT the database ID, but its id on the cloudinary server)
// on the cloudinary server
cloudinary.uploader.destroy(imageId, function (result) {
// Delete the image from the database
Image.findOneAndRemove({ image_id: imageId })
.then(img => {
if (!img) {
res.status(404).send();
} else {
res.send(img);
}
})
.catch(error => {
res.status(500).send(); // server error, could not delete.
});
});
});
Wrapping Up
To wrap up, we finalize server.js by setting our static directory, default routing, and where our server is listening.
app.use(express.static(__dirname + "/build"));
// All routes other than above will go to index.html
app.get("*", (req, res) => {
res.sendFile(__dirname + "/build/index.html");
});
const port = process.env.PORT || 3000
app.listen(port, () => {
log(`Listening on port ${port}...`)
})
And now, we are ready to send HTTP requests to create, fetch, and delete images through POST, GET, and DELETE requests from the front-end!
For example, to create an image, we can send a request like so:
const url = "/images";
const request = new Request(url, {
method: "post",
body: newImage, /* the image selected from the file input */
});
// Send the request with fetch()
fetch(request)
.then(function (res) {
if (res.status === 200) {
console.log("Image was successfully uploaded")
} else {
console.log("Error: Could not upload image.")
}
})
.catch(error => {
console.log(error);
});
For those who have made it this far, congrats! Especially to those of you who are new to this. I've been there before and it's definitely not easy to grasp it the first time.