{"id":21576,"date":"2017-08-16T18:32:21","date_gmt":"2017-08-16T18:32:21","guid":{"rendered":"http:\/\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary"},"modified":"2023-08-12T10:35:51","modified_gmt":"2023-08-12T17:35:51","slug":"build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","title":{"rendered":"Build the Back-End For Your Own Instagram-style App with Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p><a href=\"https:\/\/github.com\/scotch-io\/node-cloudinary-instagram\">Github Repo<\/a><\/p>\n<hr \/>\n<p>Managing media files (processing, storage and transformation) is one of the biggest challenges we encounter as practical developers. These challenges include:<\/p>\n<ul>\n<li>Media uploads<\/li>\n<li>Resource management (leads to heavy costing)<\/li>\n<li>Media storage<\/li>\n<li>Media transformations<\/li>\n<li>Poor delivery<\/li>\n<li>Administration<\/li>\n<\/ul>\n<p>A great service called Cloudinary can help us overcome many of these challenges. Together with Cloudinary, let\u2019s work on solutions to these challenges and hopefully have a simpler mental model towards media management.<\/p>\n<p>The process to building this solution is a hands on example which will get our hands dirty but a practical approach. Instagram and Flickr are known for heavy image processing so we can take inspiration from these apps and build something similar.<\/p>\n<h2>What We\u2019ll Build<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/MgX7tWe.png\" alt=\"Scotchgram\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1127\"\/>\n<img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/QwC4YQp.png\" alt=\"Scotchgram\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>The image above shows what we are going to be building. We are going to build the app using Node.js, Express (for routing) and EJS (for templating).<\/p>\n<p>The big question is, how do we intend to solve the problems we listed earlier while building this app?<\/p>\n<h2>Meet Cloudinary<\/h2>\n<p>Meet <a href=\"https:\/\/cloudinary.com\/\">Cloudinary<\/a>, if you haven\u2019t!<\/p>\n<p style=\"text-align:center; font-size: 26px; font-style: italic;margin: 30px;line-height:36px\">\n    &#8220;Cloudinary is the media back-end for web and mobile developers. An end-to-end solution for all your image and video needs.&#8221;\n<\/p>\n<p>Cloudinary is a powerful tool for managing not just images but also videos. The interesting thing about this tool is that it abstracts a lot (actually all) of the challenges we encounter when managing images and other media files, including the ones we listed above.<\/p>\n<p>We are not just going to discuss how Cloudinary helps us figure these puzzles, rather, we will use Cloudinary in building the above app which will expose all the solutions to these puzzles.<\/p>\n<p>Cloudinary gives us the power to:<\/p>\n<ul>\n<li>Handle image uploads effectively using a customizable widget<\/li>\n<li>Optimize images for web and mobile consumption using transformations<\/li>\n<li>Perform administrative operations including: renaming, deleting, etc<\/li>\n<li>Store and backup images\/videos<\/li>\n<li>Transform media files<\/li>\n<li>Deliver media files<\/li>\n<\/ul>\n<h2>Project Setup, Directory Structure and Dependencies<\/h2>\n<p>First things, first. Let\u2019s setup a project environment for our app to live in. The GitHub URL provided in the beginning and end of this tutorial is multi-branched. Each branch is prefixed with <code>step-{step-number}-<\/code> where \u201cstep-number\u201d is the an increment for each step.<\/p>\n<p>The first step is <code>step-0-project-structure<\/code> so you can switch to that branch and follow along from there. The master branch contains the final solution.<\/p>\n<p>To have an overview of the project, it is always a good idea to present a directory structure which we can then build upon:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">|---app\n|------controller.js <span class=\"hljs-comment\">\/\/ Route handlers<\/span>\n|------model.js <span class=\"hljs-comment\">\/\/ Mongoose model<\/span>\n|---<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-comment\">\/\/ Public contents (style, js, imgs)<\/span>\n|---views <span class=\"hljs-comment\">\/\/ EJS views<\/span>\n|-----admin\n|-------index.ejs <span class=\"hljs-comment\">\/\/ Admin home page<\/span>\n|-----pages\n|-------index.ejs <span class=\"hljs-comment\">\/\/ Home page<\/span>\n|-------<span class=\"hljs-keyword\">new<\/span>.ejs <span class=\"hljs-comment\">\/\/ New post page<\/span>\n|-------edit.ejs <span class=\"hljs-comment\">\/\/ Edit post page<\/span>\n|-------single.ejs <span class=\"hljs-comment\">\/\/ Preview post page<\/span>\n|-----partials\n|-------header.ejs <span class=\"hljs-comment\">\/\/ Header partial<\/span>\n|-------head.ejs <span class=\"hljs-comment\">\/\/ Styles partial<\/span>\n|-------scripts.ejs <span class=\"hljs-comment\">\/\/ Scripts partial<\/span>\n|---package.json\n|---routes.js <span class=\"hljs-comment\">\/\/ Routes file<\/span>\n|---server.js <span class=\"hljs-comment\">\/\/ Entry<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Something minimal and less overwhelming so we can focus on discussing on the features we are implementing rather than spend time moving codes around.<\/p>\n<p>Update <code>package.json<\/code> dependencies with the third-party libraries that we will be working with:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-string\">\"dependencies\"<\/span>: {\n    <span class=\"hljs-string\">\"body-parser\"<\/span>: <span class=\"hljs-string\">\"^1.15.2\"<\/span>,\n    <span class=\"hljs-string\">\"cloudinary\"<\/span>: <span class=\"hljs-string\">\"^1.4.2\"<\/span>,\n    <span class=\"hljs-string\">\"connect-multiparty\"<\/span>: <span class=\"hljs-string\">\"^2.0.0\"<\/span>,\n    <span class=\"hljs-string\">\"ejs\"<\/span>: <span class=\"hljs-string\">\"^2.5.2\"<\/span>,\n    <span class=\"hljs-string\">\"express\"<\/span>: <span class=\"hljs-string\">\"^4.14.0\"<\/span>,\n    <span class=\"hljs-string\">\"mongoose\"<\/span>: <span class=\"hljs-string\">\"^4.6.0\"<\/span>\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You can install the dependencies by running:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install\n<\/code><\/span><\/pre>\n<p>We are focusing on backend in this tutorial but that doesn\u2019t mean we can\u2019t afford a good looking design for the frontend. Rather than waste time crafting that, we can use Semantic UI by updating <code>head.ejs<\/code> and <code>scripts.ejs<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/partials\/head.ejs --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"stylesheet\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"https:\/\/cdn.jsdelivr.net\/semantic-ui\/2.2.4\/semantic.min.css\"<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/partials\/scripts.ejs --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/code.jquery.com\/jquery-3.1.0.min.js\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/cdn.jsdelivr.net\/semantic-ui\/2.2.4\/semantic.min.js\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>API Access with SDKs<\/h2>\n<p>You get redirected to your Cloudinary dashboard once you create an account by <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">signing up for free<\/a>. This is what the dashboard looks like:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/Console_insta.png\" alt=\"Console\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>The dashboard shows a list of SDKs that you can use to talk to Cloudinary in most of the popular languages including Node.js.<\/p>\n<p>Cloudinary core exposes APIs based on your <code>cloud name<\/code> and all these SDKs do is serve as a language wrapper to these URL. So instead of littering your app with these URLs, you have a better intuitive language based method APIs to work with.<\/p>\n<p>The <code>cloud name<\/code> is not your name but the name you chose when signing up as <code>cloud name<\/code>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/wv4qeM1.jpg\" alt=\"Dashboard\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<h2>Installing the Node.js Cloudinary SDK<\/h2>\n<p>We are interested in the Node.js SDK so let\u2019s install it in our existing project:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install cloudinary --save\n<\/code><\/span><\/pre>\n<h2>Handling Image Uploads<\/h2>\n<p>Getting your images to the server\/cloud is the first and most important stage in managing images in your project. In this section, we are will cover<\/p>\n<ul>\n<li>how to upload files from the web in your browser\/mobile<\/li>\n<li>upload using Cloudinary\u2019s upload widget<\/li>\n<li>transform images<\/li>\n<li>display images<\/li>\n<li>and more\u2026<\/li>\n<\/ul>\n<h2>Custom Image Upload<\/h2>\n<p>Let\u2019s see how we can upload images to the cloud using the SDK we have installed. Image upload will always require some form of input to grab the image data, so let\u2019s create a page that:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/pages\/new.ejs --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n\t<span class=\"hljs-comment\">&lt;!-- Head partial --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">head<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\t<span class=\"hljs-comment\">&lt;!-- Header partial --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">header<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui two column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n\t            <span class=\"hljs-comment\">&lt;!-- Enctype is multipart to support file upload --&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"\/create\"<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span> <span class=\"hljs-attr\">enctype<\/span>=<span class=\"hljs-string\">\"multipart\/form-data\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui form\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Title<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Title\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Description<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span> <span class=\"hljs-attr\">rows<\/span>=<span class=\"hljs-string\">\"4\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Description\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">textarea<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Image<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui primary button\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Post<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- Scripts partial --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">scripts<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The form is not just a regular form. As you can see, the <code>enctype<\/code> property is set to <code>multipart\/form-data<\/code> so as to properly support and handle <a href=\"https:\/\/cloudinary.com\/blog\/automating_file_upload_and_sharing\">file uploads<\/a> via the form.<\/p>\n<p>You can see how we are injecting our template partials into the HTML document. We have already seen the <code>head<\/code> and <code>scripts<\/code> partials so what is left is the <code>header<\/code> partial. The partial just holds the nav bar:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui secondary pointing menu\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span>&gt;<\/span>\n        Home\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/new\"<\/span>&gt;<\/span>\n        New Post\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/span>&gt;<\/span>\n        About Scotchgram\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We have our markup all set, but we can\u2019t serve the markup yet because there is route handling that. Let\u2019s create a route and controller action method to do so:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-keyword\">var<\/span> controller = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'.\/app\/controller'<\/span>);\n\n<span class=\"hljs-built_in\">module<\/span>.exports = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">app<\/span>) <\/span>{\n    app.get(<span class=\"hljs-string\">'\/new'<\/span>, controller.new);\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We are pointing to a none existing controller and action method. This action method contains logic that renders the <code>ejs<\/code> so we can create that now:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n\n<span class=\"hljs-built_in\">module<\/span>.exports = {\n  <span class=\"hljs-attr\">new<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      res.render(<span class=\"hljs-string\">'pages\/new'<\/span>);\n  }\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With that, we can run the app and see our form at <code>\/new<\/code>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/4y0DejI.png\" alt=\"Scotchgram\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>Of course our customers can not consume a form that is just staring back at them. They would want to fill it out and click the <code>Post<\/code> button. When the click what happens?<\/p>\n<p>On submission, the the form data is collected and sent to <code>\/create<\/code> endpoint which at the moment we are yet to create the route, so let\u2019s start doing something about that:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-keyword\">var<\/span> multipart = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'connect-multiparty'<\/span>);\n<span class=\"hljs-keyword\">var<\/span> multipartMiddleware = multipart();\n<span class=\"hljs-keyword\">var<\/span> controller = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'.\/app\/controller'<\/span>);\n\n<span class=\"hljs-built_in\">module<\/span>.exports = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">app<\/span>) <\/span>{\n    app.get(<span class=\"hljs-string\">'\/new'<\/span>, controller.new);\n    <span class=\"hljs-comment\">\/\/ Use middleware to handle uploaded files and access<\/span>\n    <span class=\"hljs-comment\">\/\/ uploaded files using req.file<\/span>\n    app.post(<span class=\"hljs-string\">'\/create'<\/span>, multipartMiddleware, controller.create);\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We have not just added a new route but also configured a middleware to help us process and get details about an uploaded file. With the <code>multipartMiddleware<\/code>, we could access any uploaded file from <code>req.file<\/code>.<\/p>\n<p>So we have a route, but routes point need logics to handle incoming requests. The controller\u2019s <code>create<\/code> action method is where this logic will live:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Dependencies<\/span>\n<span class=\"hljs-keyword\">var<\/span> cloudinary = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'cloudinary'<\/span>);\n<span class=\"hljs-comment\">\/\/ Mongoose Model<\/span>\n<span class=\"hljs-keyword\">var<\/span> Model = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'.\/model'<\/span>);\n\n<span class=\"hljs-comment\">\/\/ Configure Cloudinary<\/span>\n<span class=\"hljs-comment\">\/\/ with credentials available on<\/span>\n<span class=\"hljs-comment\">\/\/ your Cloudinary account dashboard<\/span>\ncloudinary.config({\n    <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'CLOUD_NAME'<\/span>,\n    <span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">'API_KEY'<\/span>,\n    <span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">'SECRET'<\/span>\n});\n\n<span class=\"hljs-built_in\">module<\/span>.exports = {\n  <span class=\"hljs-attr\">new<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      res.render(<span class=\"hljs-string\">'pages\/new'<\/span>);\n  },\n  <span class=\"hljs-attr\">create<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n\t  <span class=\"hljs-comment\">\/\/ Use Cloudinary uploader to upload to cloudinary sever<\/span>\n\t  <span class=\"hljs-comment\">\/\/ Access files uploaded from the browser using req.files<\/span>\n      cloudinary.uploader.upload(req.files.image.path, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">result<\/span>) <\/span>{\n\t      <span class=\"hljs-comment\">\/\/ Create a post model<\/span>\n\t      <span class=\"hljs-comment\">\/\/ by assembling all data as object<\/span>\n\t      <span class=\"hljs-comment\">\/\/ and passing to Model instance<\/span>\n          <span class=\"hljs-keyword\">var<\/span> post = <span class=\"hljs-keyword\">new<\/span> Model({\n              <span class=\"hljs-attr\">title<\/span>: req.body.title,\n              <span class=\"hljs-attr\">description<\/span>: req.body.description,\n              <span class=\"hljs-attr\">created_at<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(),\n              <span class=\"hljs-comment\">\/\/ Store the URL in a DB for future use<\/span>\n              <span class=\"hljs-attr\">image<\/span>: result.url\n              <span class=\"hljs-attr\">image_id<\/span>: result.public_id\n          });\n\t\t  <span class=\"hljs-comment\">\/\/ Persist by saving<\/span>\n          post.save(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">err<\/span>) <\/span>{\n              <span class=\"hljs-keyword\">if<\/span>(err){\n                  res.send(err)\n              }\n              <span class=\"hljs-comment\">\/\/ Redirect<\/span>\n              res.redirect(<span class=\"hljs-string\">'\/'<\/span>);\n          });\n      });\n  }\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>First we required <code>cloudinary<\/code> and our <code>mongoose<\/code> model (which we will create soon), then we configured Cloudinary using credentials available on the dashboard. Once the route is hit, we upload the file to Cloudinary server using the SDK\u2019s <code>uploader.upload<\/code> API and persist the post body including the URL returned from Cloudinary to the MongoDB using Mongoose.<\/p>\n<blockquote>\n<p>You can learn how to work with Mongo DB and Mongoose <a href=\"https:\/\/scotch.io\/tutorials\/using-mongoosejs-in-node-js-and-mongodb-applications\">here<\/a>.\u201c<\/p>\n<\/blockquote>\n<p>Once all that is successful, we return to the homepage, else, we send back an error to the browser about the failure.<\/p>\n<p>Let\u2019s add the model to complete the flow:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ app\/model.js<\/span>\n<span class=\"hljs-comment\">\/\/ Dependencies<\/span>\n<span class=\"hljs-keyword\">var<\/span> mongoose = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'mongoose'<\/span>);\n<span class=\"hljs-keyword\">var<\/span> Schema = mongoose.Schema;\n\n<span class=\"hljs-comment\">\/\/ create a schema<\/span>\n<span class=\"hljs-keyword\">var<\/span> postSchema = <span class=\"hljs-keyword\">new<\/span> Schema({\n    <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-built_in\">String<\/span>,\n    <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-built_in\">String<\/span>,\n    <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-built_in\">String<\/span>,\n    <span class=\"hljs-attr\">image_id<\/span>: <span class=\"hljs-built_in\">String<\/span>,\n    <span class=\"hljs-attr\">created_at<\/span>: <span class=\"hljs-built_in\">Date<\/span>\n});\n\n<span class=\"hljs-comment\">\/\/ the schema is useless so far<\/span>\n<span class=\"hljs-comment\">\/\/ we need to create a model using it<\/span>\n<span class=\"hljs-keyword\">var<\/span> Post = mongoose.model(<span class=\"hljs-string\">'Post'<\/span>, postSchema);\n\n<span class=\"hljs-comment\">\/\/ make this available to our users in our Node applications<\/span>\n<span class=\"hljs-built_in\">module<\/span>.exports = Post;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>To confirm that everything is working fine, I have installed Robomongo, a Mongo DB visualization tool. With Robomongo and can confirm that all my fields were persisted:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/DKaMTKN.png\" alt=\"RoboMongo\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"938\"\/><\/p>\n<h2>Widget Image Upload (Even Easier Uploads)<\/h2>\n<p>We can choose to make life easier for us by using the widget provided by Cloudinary. I left the this for later so you can appreciate the feature after going through the long process of the custom upload.<\/p>\n<p>To use Cloudinary\u2019s widget, include it in your script:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/partials\/scripts.ejs --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"\/\/widget.cloudinary.com\/global\/all.js\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With the widget script loaded, we can setup a handler for for that:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/partials\/scripts.ejs --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"\/\/widget.cloudinary.com\/global\/all.js\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">\n    <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"upload_widget_opener\"<\/span>).addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n        cloudinary.openUploadWidget({ <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'CLOUD_NAME'<\/span>, <span class=\"hljs-attr\">upload_preset<\/span>: <span class=\"hljs-string\">'UPLAOD_PRESET'<\/span>},\n                <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">error, result<\/span>) <\/span>{\n                    <span class=\"hljs-built_in\">console<\/span>.log(error, result)\n                    <span class=\"hljs-comment\">\/\/ Push URL into text input<\/span>\n                    <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'url_text'<\/span>).value = result&#91;<span class=\"hljs-number\">0<\/span>].url;\n                });\n\n    }, <span class=\"hljs-literal\">false<\/span>);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We attached a click event listener to button on the form (yet to be created). When this event occurs, we open the upload widget by calling <code>openUploadWidget<\/code> and passing in our cloud name and and upload preset.<\/p>\n<p>The upload preset is just a pre-configuration to what we could have been setting up via parameters in the REST URLs. We set this configuration and using a unique ID to differentiate them from each other. To set yours, go to <strong>Settings &gt;&gt; Upload Tab &gt;&gt; Upload Presets &gt;&gt; Enable<\/strong>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/sKFnbe6.png\" alt=\"Enabling Presets\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>The callback function for the upload gives us the result which we can play around with. What I have done is push the result into our text input so it can be sent to the server.<\/p>\n<p>Then, on the server, we can persist the URL to our database:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated<\/span>\n<span class=\"hljs-attr\">create<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      <span class=\"hljs-keyword\">var<\/span> post = <span class=\"hljs-keyword\">new<\/span> Model({\n          <span class=\"hljs-attr\">title<\/span>: req.body.title,\n          <span class=\"hljs-attr\">description<\/span>: req.body.description,\n          <span class=\"hljs-attr\">created_at<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(),\n          <span class=\"hljs-comment\">\/\/ Now we are requesting the image<\/span>\n          <span class=\"hljs-comment\">\/\/ from a form text input<\/span>\n          <span class=\"hljs-attr\">image<\/span>: req.body.image\n      });\n\n      post.save(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">err<\/span>) <\/span>{\n          <span class=\"hljs-keyword\">if<\/span>(err){\n              res.send(err)\n          }\n          res.redirect(<span class=\"hljs-string\">'\/'<\/span>);\n      });\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The form can now be updated to include the upload button and substitute the file input with a text input that stores the URL temporarily:<\/p>\n<blockquote>\n<p>NOTE: A more real life approach to this is using a hidden input<\/p>\n<\/blockquote>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/pages\/new.ejs --&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- Truncated --&gt;<\/span>\n <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"\/create\"<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span> <span class=\"hljs-attr\">enctype<\/span>=<span class=\"hljs-string\">\"multipart\/form-data\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui form\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Title<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Title\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Description<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span> <span class=\"hljs-attr\">rows<\/span>=<span class=\"hljs-string\">\"4\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Description\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">textarea<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Image<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Image URL\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"url_text\"<\/span>\/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui button\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"button\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"upload_widget_opener\"<\/span>&gt;<\/span>Upload with Widget<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui primary button\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Post<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Progress and Preview<\/h2>\n<p>Most times, it\u2019s a good UX practice to show progress of image upload or preview of images being uploaded. With Cloudinary\u2019s jQuery plugin, we can get going with this in few minutes.<\/p>\n<p>First thing to do as usual is load the required scripts\/dependencies:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- .\/views\/partials\/scripts.ejs --&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- Truncated for brevity --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/ajax.googleapis.com\/ajax\/libs\/jqueryui\/1.12.0\/jquery-ui.min.js\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/blueimp-file-upload\/9.12.5\/js\/jquery.iframe-transport.js'<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'text\/javascript'<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/blueimp-file-upload\/9.12.5\/js\/jquery.fileupload.js'<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'text\/javascript'<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">'https:\/\/cdn.jsdelivr.net\/jquery.cloudinary\/1.0.18\/jquery.cloudinary.min.js'<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'text\/javascript'<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The above is an arsenal of tools that will help us accomplish previewing images and showing a progress bar.<\/p>\n<p>Update the view to provide accommodation for the preview thumbnails and progress bar (with semantic):<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/pages\/new.ejs --&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- Truncated for brevity --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Image<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"upload_field\"<\/span>\/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-comment\">&lt;!-- Image thumbnails will be loaded here --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"thumbnails\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui teal progress\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"progress\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"bar\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We can now update our script logic to support preview and progress:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ Configure Cloudinary<\/span>\n$.cloudinary.config({ <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'CLOUD_NAME'<\/span>, <span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">'KEY'<\/span>})\n<span class=\"hljs-comment\">\/\/ Perform unsigned upload<\/span>\n$(<span class=\"hljs-string\">'.upload_field'<\/span>).unsigned_cloudinary_upload(<span class=\"hljs-string\">\"UPLOAD_PRESET\"<\/span>,\n            { <span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'CLOUD_NAME'<\/span>,},\n            { <span class=\"hljs-attr\">multiple<\/span>: <span class=\"hljs-literal\">true<\/span> }\n    )\n    .bind(<span class=\"hljs-string\">'cloudinarydone'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">e, data<\/span>) <\/span>{\n\t\t<span class=\"hljs-comment\">\/\/ Populate thumbnails when upload is finished<\/span>\n        $(<span class=\"hljs-string\">'.thumbnails'<\/span>).append($.cloudinary.image(data.result.public_id,\n                { <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'jpg'<\/span>, <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">150<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">100<\/span>,\n                    <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'thumb'<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'face'<\/span>, <span class=\"hljs-attr\">effect<\/span>: <span class=\"hljs-string\">'saturation:50'<\/span> } ))\n                    })\n       .bind(<span class=\"hljs-string\">'cloudinaryprogress'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">e, data<\/span>) <\/span>{\n\t\t<span class=\"hljs-comment\">\/\/ Update progress bar with upload progress<\/span>\n        $(<span class=\"hljs-string\">'.progress'<\/span>).progress({\n            <span class=\"hljs-attr\">percent<\/span>: <span class=\"hljs-built_in\">Math<\/span>.round((data.loaded * <span class=\"hljs-number\">100.0<\/span>) \/ data.total)\n        });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The idea is that the Cloudinary jQuery plugin provides special custom events which let\u2019s us hook into each stage of the upload process and do what ever pleases us.<\/p>\n<p>The <code>cloudinarydone<\/code> event is called once the upload is complete, giving us the power to grab uploaded data and append to the view.<\/p>\n<p>The <code>cloudinaryprogress<\/code> is called during the upload intervals making it easier for us to build a progress bar around the upload process.<\/p>\n<p>We can as well send the data returned on <code>cloudinarydone<\/code> back to the server if we wish to persist the URL as we have been doing in previous steps.<\/p>\n<h2>Incoming Transformation<\/h2>\n<p>Transformations in Cloudinary are like database rules or Express middleware. This is because, they can interfere upload, to transform uploaded content before sending to the cloud.<\/p>\n<p>Assuming we want a ration of 2:1 applied to our images with 1000 x 500 dimension before they are uploaded, we can apply this rule (transformation) in our upload logic:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated<\/span>\n<span class=\"hljs-attr\">create<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n     cloudinary.v2.uploader.upload(req.files.image.path,\n          <span class=\"hljs-comment\">\/\/Apply transformation<\/span>\n          { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">1000<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">500<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"limit\"<\/span> },\n          <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, result<\/span>) <\/span>{\n\t\t   <span class=\"hljs-comment\">\/\/ Handler here<\/span>\n      });\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We are using <code>v2<\/code> to support transformation.<\/p>\n<p>In a real project that handles a lot of this kind of request, it could be a good idea to use queues\/jobs (whichever your environment supports) to abstract this transaction and push it to a later time if the image being transformed is not going to be used instantly.<\/p>\n<p>One thing to keep in mind with this kind of transformation is that it does not save the original image which means that it transforms the image and then stores. To persist the original image, see the next section<\/p>\n<h2>Eager Transformation<\/h2>\n<p>This kind of transformation unlike the one we saw previously will store both the original image and the transformed. The transformed image can then be accessed with a different endpoint:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated<\/span>\n<span class=\"hljs-attr\">create<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n     cloudinary.v2.uploader.upload(req.files.image.path,\n          <span class=\"hljs-comment\">\/\/Apply transformation<\/span>\n          { <span class=\"hljs-attr\">eager<\/span>: &#91;\n\t        { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">2000<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">1000<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"pad\"<\/span> }, \n\t        { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">750<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">300<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"crop\"<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">\"north\"<\/span>} ]}, \n          <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, result<\/span>) <\/span>{\n\t\t   <span class=\"hljs-comment\">\/\/ Handler here<\/span>\n      });\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Upload Preset<\/h2>\n<p>Most times we pass in a lot of configuration to while trying to upload images especially transformation based configuration. If find yourself in a situation where you have to pass in the same configuration in more than one situations, then upload preset is for you.<\/p>\n<p>With upload preset, you can create configuration from your dashboard that can be re-used at different point in your application. The presets are assigned a unique ID and then you can tell the SDK which preset it should apply by passing it that ID.<\/p>\n<p>To configure a preset, first enable it via <strong>Settings &gt;&gt; Upload Tap &gt;&gt; Upload Presets &gt;&gt; Enable<\/strong> and then you can start adding new presets based on your taste. You will be provided with an already existing preset for default purposes but you can add more as you wish.<\/p>\n<p>When you have a preset configured and you have grabbed the ID, you can use it in your app:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated<\/span>\n<span class=\"hljs-attr\">create<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n     cloudinary.v2.uploader.upload(req.files.image.path,\n          <span class=\"hljs-comment\">\/\/Apply Upload preset<\/span>\n          { <span class=\"hljs-attr\">upload_preset<\/span>: <span class=\"hljs-string\">\"PRESET_ID\"<\/span> }, \n          <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, result<\/span>) <\/span>{\n\t\t   <span class=\"hljs-comment\">\/\/ Handler here<\/span>\n      });\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>To start performing admin tasks, let us first of all create a list of cards on the home page to show all our images.<\/p>\n<p>We have no route for <code>\/<\/code> yet and that is what we need right now. So let\u2019s add that to our existing routes configuration:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\napp.get(<span class=\"hljs-string\">'\/'<\/span>, controller.index);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Then we can go ahead to create an <code>index<\/code> action method in our controller:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\n<span class=\"hljs-attr\">index<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      Model.find({}, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">err, posts<\/span>) <\/span>{\n          <span class=\"hljs-keyword\">if<\/span>(err) res.send(err);\n\n          res.render(<span class=\"hljs-string\">'pages\/index'<\/span>, {<span class=\"hljs-attr\">posts<\/span>: posts});\n      });\n  }\n  <span class=\"hljs-comment\">\/\/...<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With the controller rendering a view with the post data, we need to create this view and present the data on the view:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/pages\/about.ejs --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">head<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">header<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui three column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">posts.forEach<\/span>(<span class=\"hljs-attr\">function<\/span>(<span class=\"hljs-attr\">post<\/span>, <span class=\"hljs-attr\">index<\/span>) { %&gt;<\/span>\n\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui card\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"image\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image %&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"max-height: 150px\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"content\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"header\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.title<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"meta\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"date\"<\/span>&gt;<\/span>\n                                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"calendar icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.created_at.getFullYear<\/span>() %&gt;<\/span>\n                                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span>&gt;<\/span>\n                                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"image icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.image_id<\/span> %&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"description\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.description<\/span> %&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                       <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"extra content\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"\/destroy\"<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"display: inline\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"destroy_form&lt;%= index %&gt;\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image_id\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image_id %&gt;\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">\"document.getElementById('destroy_form&lt;%= index %&gt;').submit(); return false;\"<\/span>&gt;<\/span>\n                                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"remove icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> Remove\n                                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/edit\/&lt;%= post.image_id %&gt;\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"edit icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> Update\n                            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> }); %&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">scripts<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/TyHhNDB.png\" alt=\"Image of Fleshed Homepage\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1127\"\/><\/p>\n<p>It\u2019s important to note that the remove link is submitting a parent form which just send the ID of the image we want to remove to the server while the update link takes us to an edit page (yet to be created).<\/p>\n<h2>Deleting Images<\/h2>\n<p>To delete images, we have to first delete from cloudinary server, wait for a response, and if successful remove from our database. The API for removing from the cloud using the SDK is <code>destroy<\/code>:<\/p>\n<p>As usual, we first create a route:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\napp.post(<span class=\"hljs-string\">'\/destroy'<\/span>, controller.destroy);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Then we create the action method in our controller:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\ndestroy: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(req, res)<\/span> <\/span>{\n      <span class=\"hljs-keyword\">var<\/span> imageId = req.body.image_id;\n      <span class=\"hljs-comment\">\/\/ The destroy method takes the image ID<\/span>\n      <span class=\"hljs-comment\">\/\/ which we need to remove<\/span>\n      cloudinary.v2.uploader.destroy(imageId, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(result)<\/span> <\/span>{\n\t\t      <span class=\"hljs-comment\">\/\/ We also delete this<\/span>\n\t\t      <span class=\"hljs-comment\">\/\/ image details from our database<\/span>\n              Model.findOneAndRemove({ image_id: imageId }, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span><span class=\"hljs-params\">(err)<\/span> <\/span>{\n                  <span class=\"hljs-keyword\">if<\/span> (err) res.send(err);\n\n                  res.redirect(<span class=\"hljs-string\">'\/'<\/span>);\n              });\n          });\n  }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We first remove the image by calling the <code>destroy<\/code> method and passing it the ID of the image we want to remove. When that is completed, we also remove the image details from our database.<\/p>\n<h2>Renaming Images<\/h2>\n<p>When an image is uploaded, it is assigned a random generated image ID. If for some reason, this image ID matters to you, we can change it to a real name. While doing so, we can use the opportunity to also update the image details.<\/p>\n<p>Let\u2019s make a route to render the <code>edit<\/code> form page which basically looks like that of <code>new<\/code> form but very few variation:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\napp.post(<span class=\"hljs-string\">'\/edit'<\/span>, controller.edit);\n<span class=\"hljs-comment\">\/\/ Handle submitted updates<\/span>\napp.post(<span class=\"hljs-string\">'\/update'<\/span>, controller.update);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We used the opportunity to add an extra route which will handle the update request from the edit form.<\/p>\n<p>Next we create the controller actions for the above routes, <code>edit<\/code> and <code>update<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated for brevity<\/span>\n  <span class=\"hljs-comment\">\/***\n  * Edit action method\n  ***\/<\/span>\nedit: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(req, res)<\/span> <\/span>{\n      Model.find({image_id: req.params.id}, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(err, posts)<\/span> <\/span>{\n          <span class=\"hljs-keyword\">if<\/span>(err) res.send(err);\n\t\t\t<span class=\"hljs-comment\">\/\/ Render edit form<\/span>\n\t\t\t<span class=\"hljs-comment\">\/\/with existing post<\/span>\n          res.render(<span class=\"hljs-string\">'pages\/edit'<\/span>, {post: posts&#91;<span class=\"hljs-number\">0<\/span>]});\n      });\n  },\n  <span class=\"hljs-comment\">\/***\n  * Update action method\n  ***\/<\/span>\n  update: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(req, res)<\/span> <\/span>{\n      <span class=\"hljs-keyword\">var<\/span> oldName = req.body.old_id\n      <span class=\"hljs-keyword\">var<\/span> newName = req.body.image_id;\n      cloudinary.v2.uploader.rename(oldName, newName,\n          <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span><span class=\"hljs-params\">(error, result)<\/span> <\/span>{\n              <span class=\"hljs-keyword\">if<\/span> (error) res.send(error);\n              Model.findOneAndUpdate({image_id: oldName}, \n                  Object.assign({}, req.body, {image: result.url}), \n                  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(err)<\/span> <\/span>{\n                  <span class=\"hljs-keyword\">if<\/span> (err) res.send(err);\n\n                  res.redirect(<span class=\"hljs-string\">'\/'<\/span>);\n              })\n          })\n\n  },\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We use Cloudinary\u2019s <code>rename<\/code> API method to update image name on the cloud. It takes the existing name (to find the image on the cloud), the <code>new name<\/code> and a callback as arguments.<\/p>\n<p>See how we are using <code>Object.assign<\/code> to update the model with <code>req.body<\/code> while updating the image property with the latest URL. This is because, after renaming an <code>image<\/code> on the cloud, the URL also changes because an image ID is part of the little pieces that composes an image URL.<\/p>\n<p>Now we can happily create the <code>edit<\/code> view and everything will just work:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/pages\/new.ejs --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">head<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">header<\/span> %&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui center aligned icon header\"<\/span>&gt;<\/span>\n        Edit: <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.title<\/span> %&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui two column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n\t            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image %&gt;\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"\/update\"<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span>  <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui form\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Title<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Title\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.title %&gt;\"<\/span>\/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Description<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span> <span class=\"hljs-attr\">rows<\/span>=<span class=\"hljs-string\">\"4\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Description\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.description<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">textarea<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Rename Image ID<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image_id\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"image_id\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image_id %&gt;\"<\/span>\/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image %&gt;\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.image_id %&gt;\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"old_id\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui primary button\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Post<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">scripts<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/JfkcWYP.png\" alt=\"Edit Image\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1127\"\/><\/p>\n<h2>Tagging<\/h2>\n<p>Just like tags in a blog post, we can categorize images by assigning tags to the. Thereafter, the images can be pulled up based on the assigned tag(s). Tagging improves organization of images and we can perform specific actions to a set of images identified by a tag.<\/p>\n<p>To tag images, we can either do that when uploading them or update the image at a later time with the tags. Let\u2019s play around by adding tags during upload.<\/p>\n<p>Update the <code>new<\/code> form view to have an extra field for submitting tags:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>Tags<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span><\/span>\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui fluid multiple search selection dropdown\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"tag\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"tags\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"dropdown icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"default text\"<\/span>&gt;<\/span>Tags<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"menu\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/span> <span class=\"hljs-attr\">data-value<\/span>=<span class=\"hljs-string\">\"puppy\"<\/span>&gt;<\/span>puppy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"item\"<\/span> <span class=\"hljs-attr\">data-value<\/span>=<span class=\"hljs-string\">\"kitten\"<\/span>&gt;<\/span>kitten<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We are using Semantic\u2019s dropdown multi-select widget and it can only work fine with a script:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"> $(<span class=\"hljs-string\">'#tag'<\/span>).dropdown({\n    <span class=\"hljs-attr\">allowAdditions<\/span>: <span class=\"hljs-literal\">true<\/span>\n });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now to the real thing \u2013 when the form is submitted, we would grab the input from <code>tags<\/code> input and pass it as an option (just like we did for transformation) to Cloudinary\u2019s <code>upload<\/code> API method in our controller\u2019s <code>create<\/code> action method:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.v2.uploader.upload(req.files.image.path,\n          {<span class=\"hljs-attr\">tags<\/span>: req.body.tags },\n<span class=\"hljs-comment\">\/\/... truncated          <\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>At the end, the form will look like this:\n<img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/ueGuG2f.png\" alt=\"Create Tag\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1127\"\/><\/p>\n<p>When the image is uploaded, from our management console we can see the tags:\n<img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/HHSBluh.png\" alt=\"Tags in management console\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<h2>Queries &amp; Search<\/h2>\n<p>We are already doing a great job and I would like to show off some of resource browsing features we can get with Cloudinary.<\/p>\n<p>It\u2019s a good thing we are storing data in a database but Cloudinary is generous enough to allow us to store additional information known as metadata. We can use Cloudinary\u2019s listing features to filter images and their metadata.<\/p>\n<p>A possible use case in our application is listing only the images in our server for administrative use. At the moment, we are only listing the images created by clients via browser uploads which there metadata are persisted in Mongo.<\/p>\n<p>We begin with an admin route which is just like every other ones we have seen:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes.js<\/span>\n<span class=\"hljs-comment\">\/*\n * Admin Routes\n *\n * *\/<\/span>\n app.get(<span class=\"hljs-string\">'\/admin'<\/span>, controller.admin.index);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Thereafter, we can create the <code>admin.index<\/code> action method which just list all images from the cloud:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/controller.js<\/span>\n<span class=\"hljs-attr\">admin<\/span>:{\n        <span class=\"hljs-attr\">index<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n            <span class=\"hljs-keyword\">var<\/span> q = req.query.q;\n            <span class=\"hljs-keyword\">var<\/span> callback = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">result<\/span>)<\/span>{\n\t            <span class=\"hljs-comment\">\/\/ This is value is used to<\/span>\n\t            <span class=\"hljs-comment\">\/\/ populate the search input box<\/span>\n                <span class=\"hljs-keyword\">var<\/span> searchValue = <span class=\"hljs-string\">''<\/span>;\n                <span class=\"hljs-keyword\">if<\/span>(q){\n                    searchValue = q;\n                }\n                res.render(<span class=\"hljs-string\">'admin\/index'<\/span>, {<span class=\"hljs-attr\">posts<\/span>: result.resources, <span class=\"hljs-attr\">searchValue<\/span>: searchValue});\n            };\n            <span class=\"hljs-keyword\">if<\/span>(q){\n\t            <span class=\"hljs-comment\">\/\/ Filter based on search input<\/span>\n\t            <span class=\"hljs-comment\">\/\/ if provided<\/span>\n                cloudinary.api.resources(callback,\n                    { <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'upload'<\/span>, <span class=\"hljs-attr\">prefix<\/span>: q });\n            } <span class=\"hljs-keyword\">else<\/span> {\n\t            <span class=\"hljs-comment\">\/\/ If no search input, list all<\/span>\n                cloudinary.api.resources(callback);\n            }\n        }\n    }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We also implemented a search functionality which is simple one. We use Cloudinary\u2019s <code>resources<\/code> API method to fetch all images, if a query parameter was passed in, we use the <code>prefix<\/code> option to search for it, else, we just spit all the images.<\/p>\n<p>Our view just looks like what we had in <code>pages\/index<\/code> but with different properties and a search box:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/admin\/index.ejs --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">head<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">header<\/span> %&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui center aligned icon header\"<\/span>&gt;<\/span>\n        Administrator\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui two column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui form\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"q\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Search\"<\/span>  <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= searchValue %&gt;\"<\/span>\/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui three column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">posts.forEach<\/span>(<span class=\"hljs-attr\">function<\/span>(<span class=\"hljs-attr\">post<\/span>, <span class=\"hljs-attr\">index<\/span>) { %&gt;<\/span>\n\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui card\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"image\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= post.url %&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"max-height: 150px\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"content\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"header\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.public_id<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"meta\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"date\"<\/span>&gt;<\/span>\n                                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"calendar icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.created_at<\/span> %&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"description\"<\/span>&gt;<\/span>\n                           Dimension: <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.width<\/span> %&gt;<\/span> X <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.height<\/span> %&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"extra content\"<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"\/admin\/destroy\"<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"display: inline\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"destroy_form&lt;%= index %&gt;\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image_id\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;%= post.public_id %&gt;\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">\"document.getElementById('destroy_form&lt;%= index %&gt;').submit(); return false;\"<\/span>&gt;<\/span>\n                                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"remove icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> Remove\n                            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/edit\/&lt;%= post.public_id %&gt;\"<\/span>&gt;<\/span>\n                            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"edit icon\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span> Edit\n                        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> }); %&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">scripts<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/if7rdCx.png\" alt=\"Search UI\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1127\"\/><\/p>\n<h2>Image Categorization<\/h2>\n<p>Remember when we added tags to images and we discussed that tags help us organize images? Image organization becomes possible because we are able to categorize them using tags.<\/p>\n<p>For the sake of simplicity, we won\u2019t add this feature into our existing app but for your consumption, you can create a tag cloud by fetching the list of tags available in your cloud from Cloudinary:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.api.tags(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">result<\/span>)<\/span>{\n\tres.render(<span class=\"hljs-string\">'pages\/index'<\/span>, {<span class=\"hljs-attr\">tags<\/span>: result.resources})\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With that, you can loop through all your tags and create a <code>tag<\/code> cloud with them. Cloudinary\u2019s tags API did the magic.<\/p>\n<p>You can also filter your images based on a specified tags. For instance:<\/p>\n<p><code>resources_by_tag<\/code> takes a tag name as one of it\u2019s argument which it uses to filter your images and return the ones that has the same tag that was passed in.<\/p>\n<h2>Moderating Image Uploads<\/h2>\n<p>In public facing systems that content quality and kind matters a lot, moderation becomes very important. With moderation feature, you can approve or decline uploaded images if they do not reach a particular requirement.<\/p>\n<p>An example of such requirement is when adding profile image at Upwork. During registration, the user adds his\/her photo and waits for few days for an admin to consider if the image is a head-shot before approving.<\/p>\n<p>Achieving moderation with Cloudinary is a breeze. You just switch the <code>moderation<\/code> option to <code>manual<\/code> when making uploads:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">create: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      cloudinary.v2.uploader.upload(req.files.image.path,\n          { <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">300<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">300<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"limit\"<\/span>, <span class=\"hljs-attr\">tags<\/span>: req.body.tags,\n          <span class=\"hljs-comment\">\/\/ Turn on moderation<\/span>\n           <span class=\"hljs-attr\">moderation<\/span>:<span class=\"hljs-string\">'manual'<\/span> },\n          <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">err, result<\/span>) <\/span>{\n              <span class=\"hljs-built_in\">console<\/span>.log(result);\n              <span class=\"hljs-comment\">\/\/... Brevity sake<\/span>\n <span class=\"hljs-comment\">\/\/... Brevity sake<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>When you open your media library, you can now see that as an admin, there is a button to accept or decline this image.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/zZOe8Io.png\" alt=\"Moderation\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<h2>Backing up Images<\/h2>\n<p>Backing up your web resources and any data in general has always been a recommended practice since day one. Cloudinary is no exception. Backing up in Cloudinary just entails turning on a switch.<\/p>\n<p>Backups as expected, takes extra space and for that reason, it is turned off by default so you can enable when you think you cloud deserves backups. Turning Automatic Backup ensures that images uploaded to your cloud are backed up.<\/p>\n<p>To turn Automatic Backup on, got the <a href=\"https:\/\/cloudinary.com\/blog\/automating_file_upload_and_sharing\">Upload Settings<\/a> from the management dashboard and change <strong>Automatic backup<\/strong> to <strong>Enabled<\/strong>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/FJw4LxN.png\" alt=\"Backing up\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>You can also explicitly specify that you want a given image to be backed up when it is being uploaded. To do so, set the <code>backup<\/code> option to true when making the upload as shown below:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.uploader.upload(req.files.image.path, \n\t<span class=\"hljs-comment\">\/\/ Backs up this particular image<\/span>\n\t{ <span class=\"hljs-attr\">backup<\/span>: <span class=\"hljs-literal\">true<\/span> },\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">result<\/span>) <\/span>{ \n\t\t<span class=\"hljs-built_in\">console<\/span>.log(result);\n\t });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Transforming Images<\/h2>\n<p>Let\u2019s see how we can combine everything we have seen including transformations to images while retrieving them from the cloud.<\/p>\n<p>We have been using the <code>.upload<\/code> method to send images and now to retrieve them, we use <code>.image<\/code> or <code>.url<\/code> method. The difference between <code>image<\/code> and <code>url<\/code> is, the former composes a HTML image tag while the latter generate the image\u2019s URL.<\/p>\n<h2>Embedding Images to Web Pages<\/h2>\n<p>We have already started embedding images to our web pages by getting the URL we stored in our database. What if we had no means of persisting data to a database and all we could afford is the image cloud? How do we get the images?<\/p>\n<p>The methods we saw above answers the question. It just takes in the image ID that we need to retrieve and an optional configuration object to transform the image.<\/p>\n<p>To demonstrate how we can embed images, let\u2019s add another feature to our app which displays a single post.<\/p>\n<p>Route first:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/routes<\/span>\n<span class=\"hljs-comment\">\/\/ Truncated<\/span>\napp.get(<span class=\"hljs-string\">'\/:id'<\/span>, controller.find);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Then the controller\u2019s action method, <code>find<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">find: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">req, res<\/span>) <\/span>{\n      <span class=\"hljs-keyword\">var<\/span> id = req.params.id;\n      Model.findOne({<span class=\"hljs-attr\">image_id<\/span>: id}, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">err, post<\/span>) <\/span>{\n          <span class=\"hljs-keyword\">if<\/span> (err) res.send(err);\n\n          res.render(<span class=\"hljs-string\">'pages\/single'<\/span>, {<span class=\"hljs-attr\">post<\/span>: post, <span class=\"hljs-attr\">image<\/span>: cloudinary.image, <span class=\"hljs-attr\">image_url<\/span>: cloudinary.url});\n      })\n  },\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We use Mongoose\u2019s <code>findOne<\/code> to retrieve a single post with the image ID which is passed in as param. When rendering, we are not just passing the post down to the view but also extracting the <code>image<\/code> and <code>url<\/code> methods from Cloudinary, <a href=\"https:\/\/cloudinary.com\/glossary\/image-aliasing\">aliasing<\/a> them and passing them to the view as well.<\/p>\n<p>Have a look at what the view now loos like:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-41\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- views\/pages\/single.ejs --&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">head<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">header<\/span> %&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui center aligned icon header\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.title<\/span> %&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui grid container\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui three column centered grid\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"column\"<\/span>&gt;<\/span>\n\t            <span class=\"hljs-comment\">&lt;!-- Use cloudinary.url to get image url --&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id) %&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"border: 7px solid lightgrey\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Title: <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.title<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Description: <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.description<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Public ID: <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span> <span class=\"hljs-attr\">post.image_id<\/span> %&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span> <span class=\"hljs-attr\">include<\/span> <span class=\"hljs-attr\">..<\/span>\/<span class=\"hljs-attr\">partials<\/span>\/<span class=\"hljs-attr\">scripts<\/span> %&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-41\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The new thing in the snippet above is that instead of using <code>post.image<\/code> to get the image as returned from our database, we use <code>cloudinary.url<\/code> which we already aliased as <code>image_url<\/code> to retrieve the image.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/cUemiFo.png\" alt=\"Single post\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<h2>Resizing &amp; Cropping Images<\/h2>\n<p>Remember I mentioned we can transform images both when uploading or retrieving when we discussed image transformation. Let\u2019s start seeing how we can transform images while fetching them:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-42\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 200, height 100}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-42\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>That will resize the image to 200 x 100 without considering the quality. We can also crop the image like so:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-43\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 200, height 100, crop: 'scale'}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-43\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The scale type of cropping will change the size of the image exactly to the given width and height without necessarily retaining the original aspect ratio. There are other cropping techniques and you can read more about them <a href=\"https:\/\/cloudinary.com\/documentation\/image_transformations#resizing_and_cropping_images\">here<\/a>.<\/p>\n<h2>Face Detection Cropping<\/h2>\n<p>With Cloudinary\u2019s face detection algorithm, we can crop an image based on where a face is positioned on the image.<\/p>\n<p>Assuming we have an image of a child in a very wide background of about 2000 X 1200 dimension and we need to get this down to about 100 x 100 with the face in the middle of the dimension, we can do this:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-44\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 100, height 100, crop: 'thumb', gravity: 'face'}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-44\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>gravity<\/code> property is used to target the face when cropping an image by specifying it\u2019s value to <code>face<\/code>.<\/p>\n<h2>Auto Cropping<\/h2>\n<p>Cloudinary just keeps getting amazing! We can use a technique known as automatic cropping to crop an image down to contain only the most important part and trimming down the redundant pieces. This is very much like face detection cropping but this time, we are not looking for a face but the important content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-45\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 100, height 100, gravity: \"<\/span>auto<span class=\"hljs-string\">\", crop: \"<\/span>fill<span class=\"hljs-string\">\"}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-45\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>By setting the gravity to <code>auto<\/code> instead of <code>face<\/code> as we saw previously, we are able to crop down the image in an intelligent manner leaving us with the most important portion of the image.<\/p>\n<h2>Automatic Format Selection &amp; Quality<\/h2>\n<p>Images are universally recognized by there popular formats (JPEG, PNG, etc) but some native environment have there own supported formats which is a more optimized approach.<\/p>\n<p>An example of such environment is Chrome (including the browser and other implementation like Electron or Node Webkit). Chrome supports a format type called <code>WebP<\/code> and it performs better than the universal formats.<\/p>\n<p>When fetching images, you can set the <code>fetch_format<\/code> property to <code>auto<\/code> so it can render the images based on  the native format rather than the universal. If no native format is supported, it defaults to universal.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-46\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 100, height 100, gravity: \"<\/span>auto<span class=\"hljs-string\">\", crop: \"<\/span>fill<span class=\"hljs-string\">\" fetch_format: \"<\/span>auto<span class=\"hljs-string\">\"}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-46\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Format selection is not the only automated process we can get from Cloudinary. Image quality is also achievable, automatically. By setting <code>quality<\/code> to <code>auto<\/code>,  Cloudinary will analyze a given image to find the best quality compression level and optimal encoding settings based on the image content and the viewing browser, in order to produce an image with good visual quality while minimizing the file size:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-47\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img \n\t<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"ui medium centered image\"<\/span> \n\tsrc=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, {width: 100, height 100, gravity: \"<\/span>auto<span class=\"hljs-string\">\", crop: \"<\/span>fill<span class=\"hljs-string\">\" quality: \"<\/span>auto<span class=\"hljs-string\">\"}) %&gt;\"<\/span>&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-47\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Image Shapes, Styles and Filters<\/h2>\n<p>What I have been doing is applying styles explicitly using CSS. Cloudinary is always trying to make our jobs and lives easy which us why we can change the shapes and styles of these images using the API.<\/p>\n<p>Let\u2019s see how we can change the shape of our images in the single post view to a rounded images and also add the grey borders using Cloudinary API:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-48\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> \n\t<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered \n\t\timage\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, \n\t\t\t\t{\n\t\t\t\t\twidth: 200, \n\t\t\t\t\theight: 200, \n\t\t\t\t\tradius: 100, \n\t\t\t\t\tcrop: \"<\/span><span class=\"hljs-attr\">fill<\/span>\", \n\t\t\t\t\t<span class=\"hljs-attr\">border:<\/span> \"<span class=\"hljs-attr\">10px_solid_grey<\/span>\"\n\t\t\t\t}\n\t\t\t) %&gt;<\/span>\" \/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-48\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>radius<\/code> is responsible for setting the image shape to circle and <code>border<\/code> adds the image borders. The <code>border<\/code> property is just like what we get with CSS but with the spaces replaced with an underscore (_).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/YxiKXYo.png\" alt=\"Shapes and Styles\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>There are lots more under image and shapes and you can visit Cloudinary to explore <a href=\"https:\/\/cloudinary.com\/documentation\/image_transformations#modify_image_shape_and_style\">your options<\/a><\/p>\n<p>Just like adding the styles and updating the shape, we can add fun filters to the image. My favourite is changing the image color to greyscale:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-49\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> \n\t<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered \n\t\timage\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, \n\t\t\t\t{\n\t\t\t\t\twidth: 200, \n\t\t\t\t\theight: 200, \n\t\t\t\t\tradius: 100, \n\t\t\t\t\tcrop: \"<\/span><span class=\"hljs-attr\">fill<\/span>\", \n\t\t\t\t\t<span class=\"hljs-attr\">border:<\/span> \"<span class=\"hljs-attr\">10px_solid_grey<\/span>\",\n\t\t\t\t\t<span class=\"hljs-attr\">effect:<\/span> \"<span class=\"hljs-attr\">grayscale<\/span>\"\n\t\t\t\t}\n\t\t\t) %&gt;<\/span>\" \/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-49\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Setting the <code>effect<\/code> property to <code>greyscale<\/code> gives us a black and white kinda thing:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_700,c_fill\/dpr_auto\/QwC4YQp_1.png\" alt=\"Greyscale Image\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"996\"\/><\/p>\n<p>Feel free to explore <a href=\"https:\/\/cloudinary.com\/documentation\/image_transformations#applying_image_effects_and_filters\">other options<\/a> including: hue, red, blue, green, negate, brightness, brightness_hsb, colorize, etc.<\/p>\n<h2>Making Images Responsive<\/h2>\n<p>If you are still building fixed width websites, then you may be out of touch with current trends. Fluid design and responsive content should be a primary focus for every developer because the web has gone from solely desktop systems to include mobile devices, such as smartphones and tablets of all sizes.<\/p>\n<p>It\u2019s difficult to build sites that adapt to a variety of device sizes. By default, text reflows to automatically fit the design, but other contents, particularly images, do not.<\/p>\n<p>Cloudinary provides a handful of options when it comes to making images responsive. Let\u2019s explore few of these options:<\/p>\n<p><strong>Automatic Responsive Images:<\/strong> This is achieved using Client Hints technology which allows web browsers to inform servers (or CDN layers) with the required dimensions and pixel densities of each specific image download request.<\/p>\n<p>With that kind of information, the server can then send a suitable image dimension for every giving device width.<\/p>\n<p>Chrome, Android and Opera are the browsers that support Client Hint and hints can be provided in the markup document via a meta tag:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-50\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">http-equiv<\/span>=<span class=\"hljs-string\">\"Accept-CH\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"DPR, Viewport-Width, Width\"<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-50\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>It is identified by the <code>http-equiv<\/code> which the value is set to <code>Accept-CH<\/code> (Accept Client Hint). The relevant hints are the DPR (Device Pixel Ratio) value, the Width available for the specific image in the responsive layout, and the Viewport-Width of the browser\u2019s window.<\/p>\n<p>Now that hints are being sent to the cloud, we can make request for our images and Cloudinary will be smart enough to send us the perfect image for our device width:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-51\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> \n\t<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered\n\t\timage\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id, \n\t\t\t\t{\n\t\t\t\t\tclient_hints: true, \n\t\t\t\t\ttransformation: &#91;\n\t\t\t\t\t  {\n\t\t\t\t\t\t  aspect_ratio: \"<\/span><span class=\"hljs-attr\">16:9<\/span>\", \n\t\t\t\t\t\t  <span class=\"hljs-attr\">crop:<\/span> \"<span class=\"hljs-attr\">fill<\/span>\"\n\t\t\t\t\t  },\n\t\t\t\t\t  {\n\t\t\t\t\t\t  <span class=\"hljs-attr\">width:<\/span> \"<span class=\"hljs-attr\">auto<\/span>\", \n\t\t\t\t\t\t  <span class=\"hljs-attr\">dpr:<\/span> \"<span class=\"hljs-attr\">auto<\/span>\", \n\t\t\t\t\t\t  <span class=\"hljs-attr\">crop:<\/span> \"<span class=\"hljs-attr\">scale<\/span>\"\n\t\t\t\t\t\t}\n\t\t\t\t  ]}\n\t\t\t) %&gt;<\/span>\" \/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-51\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><strong>JavaScript Based Detection<\/strong>: Another option which works better across browsers is using JavaScript to detect the viewport of a given client and then serve an image based on this information.<\/p>\n<p>Cloudinary\u2019s JavaScript library already implemented this approach internally so we do not have to write any complex logic to get going. We already have the library included in our demo project.<\/p>\n<p>We use <code>data-src<\/code> rather than <code>src<\/code> to request for the images. This enables Cloudinary to serve these images dynamically. You can set the <code>src<\/code> attribute to a placeholder image.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-52\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> \n\t<span class=\"hljs-attr\">data-src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url(post.image_id) %&gt;\"<\/span>\n\t<span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"http:\/\/placehold.it\/200x300\"<\/span> \/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-52\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, we call the JavaScript method to tell Cloudinary that we need to make use of the JavaScript-based detection:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-53\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span>&gt;<\/span><span class=\"javascript\">$.cloudinary.responsive()<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-53\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Remote Fetch<\/h2>\n<p>All the goodies we have seen in Cloudinary does not only apply to images that are stored in Cloudinary cloud. We can apply it to any other remote image making it possible for us to just use the Cloudinary SDK even when our content is not hosted by Cloudinary:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-54\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> \n\t<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui medium centered \n\t\timage\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"&lt;%= image_url('http:\/\/i.imgur.com\/67iZh9H.jpg', \n\t\t\t\t{\n\t\t\t\t\ttype: 'fetch'\n\t\t\t\t\twidth: 'auto', \n\t\t\t\t\tradius: 100, \n\t\t\t\t\tcrop: \"<\/span><span class=\"hljs-attr\">fill<\/span>\", \n\t\t\t\t\t<span class=\"hljs-attr\">border:<\/span> \"<span class=\"hljs-attr\">10px_solid_grey<\/span>\",\n\t\t\t\t\t<span class=\"hljs-attr\">effect:<\/span> \"<span class=\"hljs-attr\">grayscale<\/span>\"\n\t\t\t\t}\n\t\t\t) %&gt;<\/span>\" \/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-54\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>By specifying the <code>type<\/code> option as <code>fetch<\/code>, we can pass in a URL rather than a Cloudinary image ID.<\/p>\n<h2>Conclusion<\/h2>\n<p>There are some other solutions out there but from my experience and that of over 140k+ happy Cloudinary users, satisfaction is guaranteed. This article will not just serve as a tutorial but a reference to run back to when building your awesome apps with Cloudinary.<\/p>\n<p>Cloudinary documentations, blog posts and GitHub repos are also very helpful and most portion of this tutorial where gotten from them.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":22690,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[332,165,263],"class_list":["post-21576","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-api","tag-image-transformation","tag-sdk"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Build the Back-End For Your Own Instagram-style App with Cloudinary<\/title>\n<meta name=\"description\" content=\"Join me as we build our own Instagram-styled app.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build the Back-End For Your Own Instagram-style App with Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Join me as we build our own Instagram-styled app.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-16T18:32:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-08-12T17:35:51+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"600\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Build the Back-End For Your Own Instagram-style App with Cloudinary\",\"datePublished\":\"2017-08-16T18:32:21+00:00\",\"dateModified\":\"2023-08-12T17:35:51+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\"},\"wordCount\":10,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA\",\"keywords\":[\"API\",\"Image Transformation\",\"SDK\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2017\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\",\"name\":\"Build the Back-End For Your Own Instagram-style App with Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA\",\"datePublished\":\"2017-08-16T18:32:21+00:00\",\"dateModified\":\"2023-08-12T17:35:51+00:00\",\"description\":\"Join me as we build our own Instagram-styled app.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA\",\"width\":1200,\"height\":600},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build the Back-End For Your Own Instagram-style App with Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build the Back-End For Your Own Instagram-style App with Cloudinary","description":"Join me as we build our own Instagram-styled app.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","og_locale":"en_US","og_type":"article","og_title":"Build the Back-End For Your Own Instagram-style App with Cloudinary","og_description":"Join me as we build our own Instagram-styled app.","og_url":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2017-08-16T18:32:21+00:00","article_modified_time":"2023-08-12T17:35:51+00:00","og_image":[{"width":1200,"height":600,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend-png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary"},"author":{"name":"","@id":""},"headline":"Build the Back-End For Your Own Instagram-style App with Cloudinary","datePublished":"2017-08-16T18:32:21+00:00","dateModified":"2023-08-12T17:35:51+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary"},"wordCount":10,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA","keywords":["API","Image Transformation","SDK"],"inLanguage":"en-US","copyrightYear":"2017","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","url":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary","name":"Build the Back-End For Your Own Instagram-style App with Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA","datePublished":"2017-08-16T18:32:21+00:00","dateModified":"2023-08-12T17:35:51+00:00","description":"Join me as we build our own Instagram-styled app.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA","width":1200,"height":600},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/build_the_back_end_for_your_own_instagram_style_app_with_cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Build the Back-End For Your Own Instagram-style App with Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1645575639\/website-2021\/blog\/Instagram_backend\/Instagram_backend.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21576","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=21576"}],"version-history":[{"count":11,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21576\/revisions"}],"predecessor-version":[{"id":30898,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21576\/revisions\/30898"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/22690"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=21576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=21576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=21576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}