{"id":28253,"date":"2022-03-23T22:01:12","date_gmt":"2022-03-23T22:01:12","guid":{"rendered":"http:\/\/build-serverless-upload-with-cloudinary"},"modified":"2025-06-26T11:56:39","modified_gmt":"2025-06-26T18:56:39","slug":"build-serverless-upload-with-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/","title":{"rendered":"Building Serverless Upload for Images"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Static sites (or Jamstack sites) are great in many ways, from performance to scalability. Instead of creating and maintaining a massive monolith web server, we now can easily split it to directly dealing with microservices APIs from the client-side only. While it is more straightforward working with APIs, there is one challenge &#8211; security. Since there is no back-end support for front-end needed, everything, including sensitive information such as authentication credentials, will be exposed to the HTTPS request for a specific API call, either on the request body or in the code itself. For example, using Cloudinary signed upload service for Node.js. The authentication for a signed (secured) upload is a secure protocol created based on user\u2019s API key, API secret key, and cloud name. And you\u2019re not supposed to expose these on the client-side.<\/p>\n<p>To solve that, we use a serverless function.<\/p>\n<h2>What is a Serverless Function?<\/h2>\n<p>A serverless function is a method that we can trigger using an HTTPS request and does not tie specifically to any web servers. It is hosted and managed by any cloud computing companies (hosting platforms) such as Digital Ocean, Netlify, Vercel, Google Cloud, AWS, etc\u2026<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1602365807\/mediajams\/serverless-function.png\" alt=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/v1602365807\/mediajams\/serverless-function.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"975\" height=\"413\"\/><\/p>\n<p>Depending on the <a href=\"https:\/\/cloudinary.com\/glossary\/hosting\">hosting<\/a> platform you are using, you can write a server-function in Node.js, Golang, Java, and so on.<\/p>\n<p>This tutorial will create a secured upload application with a serverless function using Cloudinary, Vercel, and Nuxtjs.<\/p>\n<\/div>\n\n<div class=\"wp-block-cloudinary-markdown \"><h2>Setting Up<\/h2>\n<p>The first step is to make sure Vercel CLI is installed. <a href=\"https:\/\/vercel.com\/docs\/cli\">Vercel CLI<\/a> allows us to develop and test our serverless functions locally.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">i<\/span> <span class=\"hljs-selector-tag\">-g<\/span> <span class=\"hljs-selector-tag\">vercel<\/span> <span class=\"hljs-selector-id\">#or<\/span> <span class=\"hljs-selector-tag\">yarn<\/span> <span class=\"hljs-selector-tag\">global<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-tag\">vercel<\/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\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, let\u2019s put together a basic Nuxt application scaffold by executing the following command on your terminal:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-id\">#Using<\/span> <span class=\"hljs-selector-tag\">npm<\/span> (<span class=\"hljs-selector-tag\">v6<\/span><span class=\"hljs-selector-class\">.1<\/span> <span class=\"hljs-selector-tag\">onwards<\/span>)\n<span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">init<\/span> <span class=\"hljs-selector-tag\">nuxt-app<\/span> <span class=\"hljs-selector-tag\">cloudinary-upload-app<\/span>\n\n\n\n<span class=\"hljs-selector-id\">#Or<\/span> <span class=\"hljs-selector-tag\">using<\/span> <span class=\"hljs-selector-tag\">YARN<\/span>\n<span class=\"hljs-selector-tag\">yarn<\/span> <span class=\"hljs-selector-tag\">create<\/span> <span class=\"hljs-selector-tag\">nuxt-app<\/span> <span class=\"hljs-selector-tag\">cloudinary-upload-app<\/span>\n\n\n\n<span class=\"hljs-selector-id\">#Or<\/span> <span class=\"hljs-selector-tag\">using<\/span> <span class=\"hljs-selector-tag\">npx<\/span>\n<span class=\"hljs-selector-tag\">npx<\/span> <span class=\"hljs-selector-tag\">create-nuxt-app<\/span> <span class=\"hljs-selector-tag\">cloudinary-upload-app<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>And select the configuration options accordingly. You can choose anything to suit your needs, but I recommend the following for our starter application:<\/p>\n<ul>\n<li>UI framework: Tailwind CSS &#8211; we use TailwindCSS to provide a basic beautiful CSS setup without extra configuration.<\/li>\n<li>Axios &#8211; we use this module to send API requests to the serverless function.<\/li>\n<li>Linting tools: Prettier, Lint staged files.<\/li>\n<li>Rendering mode: Universal (SSR\/SSG) &#8211; we want Nuxt to act as a Static site generator for our application during development. This mode is required to turn on the static deployment static.<\/li>\n<li>Deployment target: Static &#8211; this is important as we want to deploy our application as static site.<\/li>\n<\/ul>\n<p>Once it\u2019s ready, we can navigate to the project directory and have it connected to Vercel using:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">cd cloudinary-upload-app &amp;&amp; vercel\n<\/code><\/span><\/pre>\n<p>and set up your project linking accordingly. See <a href=\"https:\/\/vercel.com\/docs\/cli#commands\/overview\/framework-detection\">Vercel\u2019s project settings<\/a> for more information.<\/p>\n<p>\u26a0\ufe0f You may need to login using <code>vercel login<\/code> to connect to your Vercel account.<\/p>\n<p>Then run the following to have the app up and running:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">vercel dev\n<\/code><\/span><\/pre>\n<p>You can see how the app looks on the browser by holding CMD (Mac) or Ctrl and clicking on the port link displayed in the terminal:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1602232056\/mediajams\/get_port.png\" alt=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/v1602232056\/mediajams\/get_port.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"455\" height=\"244\"\/><\/p>\n<p>Now we can start to create our upload component.<\/p>\n<h2>Upload Component<\/h2>\n<p>In the app directory, create a new file <code>Upload.vue<\/code> under <code>components<\/code> directory with the following skeleton code:<\/p>\n<p>This is our Upload component.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1602232056\/mediajams\/component_upload.png\" alt=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/v1602232056\/mediajams\/component_upload.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"315\" height=\"133\"\/><\/p>\n<p>Next we will create the component skeleton by adding the following 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-tag\">&lt;<span class=\"hljs-name\">template<\/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\">\"p-4 w-full\"<\/span>&gt;<\/span>\n     <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"text-xl font-semibold text-blue-700 text-center\"<\/span>&gt;<\/span>\n     Upload with Cloudinary Demo\n     <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/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\">\"my-4 mx-auto text-center\"<\/span>&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!--This is our upload component--&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">upload<\/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\">template<\/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<h3>Receive User Input<\/h3>\n<p>To allow user to select a local file for uploading, we use <code>input<\/code> element with the following attributes:<\/p>\n<ul>\n<li>\n<code>type=&quot;file&quot;<\/code> &#8211; indicate this input field is meant to let users choose a file from their device storage.<\/li>\n<li>\n<code>accept=&quot;.jpeg,.jpg,.png,image\/png,image\/png,.svg,.gif&quot;<\/code> &#8211; indicate the target file type the input element should accept. By setting it to accept only image types, we make sure users will only select the right file format for our component to handle.<\/li>\n<li>A bit of CSS styling by assigning Tailwind class names to <code>class<\/code>\n<\/li>\n<\/ul>\n<p>In <code>template<\/code> section of <code>Upload.vue<\/code>, our code looks like:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;input\ntype=<span class=\"hljs-string\">\"file\"<\/span>\naccept=<span class=\"hljs-string\">\".jpeg,.jpg,.png,image\/jpeg,image\/png,.svg,.gif\"<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"p-3 border cursor-pointer\"<\/span>\naria-label=<span class=\"hljs-string\">\"upload image button\"<\/span>\n\/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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 output in browser becomes:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/f_auto,q_auto\/c_limit,w_2000\/f_auto\/q_auto\/v1604922949\/mediajams\/upload_comp\" alt=\"How Upload component looks\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1278\" height=\"368\"\/><\/p>\n<p>Now we need to declare a method <code>selectFile<\/code> in <code>methods<\/code> fields of the component\u2019s JavaScript logic located in <code>&lt;script&gt;<\/code> section:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">export<\/span> <span class=\"hljs-selector-tag\">default<\/span> {\n<span class=\"hljs-attribute\">methods<\/span>: {\n<span class=\"hljs-built_in\">selectFile<\/span>(e){}\n}\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Then bind that method as a listener to <code>change<\/code> event in the input field<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;input\ntype=<span class=\"hljs-string\">\"file\"<\/span>\naccept=<span class=\"hljs-string\">\".jpeg,.jpg,.png,image\/jpeg,image\/png,.svg,.gif\"<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"p-3 border cursor-pointer\"<\/span>\n@change=<span class=\"hljs-string\">\"selectFile\"<\/span>\n\/&gt;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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>Once a user clicks on <code>Choose File<\/code> and selects a file, <code>selectFile<\/code> will be triggered with <code>e<\/code> representing the event <code>change<\/code>\u2019s data as a parameter.<\/p>\n<h3>Handling the User Input<\/h3>\n<p>We can get the list of selected files by accessing <code>e.target.files<\/code>, which contains <code>File<\/code> objects. Since we allow users to upload a single file, we only need to care about the first file on the list &#8211; <code>files[0]<\/code><\/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-keyword\">const<\/span> file = e.target.files&#91;<span class=\"hljs-number\">0<\/span>]\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>To upload using HTTPs, we first need to read and convert the received file\u2019s data to base64 format. For that, we use the built-in <code>FileReader<\/code> of the File API for the web and perform the following:<\/p>\n<ul>\n<li>Load the file data using <code>FileReader<\/code>\n<\/li>\n<li>Translate its data as base64 using <code>FileReader.readAsDataURL<\/code> method<\/li>\n<\/ul>\n<p>Since these processes are asynchronous, we will use Promise API to wrap around them:<\/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-keyword\">const<\/span> readData = <span class=\"hljs-function\">(<span class=\"hljs-params\">f<\/span>) =&gt;<\/span>\n<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve<\/span>) =&gt;<\/span> {\n<span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader()\nreader.onloadend = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> resolve(reader.result)\nreader.readAsDataURL(f)\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>And use <code>await\/async<\/code> to make sure we get the output data before moving on to the next step.<\/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-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> readData(file)\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>To this point, we get our data ready to be sent and upload. And our implementation code for <code>selectFile()<\/code> becomes:<\/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-keyword\">async<\/span> selectFile(e) {\n<span class=\"hljs-keyword\">const<\/span> file = e.target.files&#91;<span class=\"hljs-number\">0<\/span>]\n\n<span class=\"hljs-comment\">\/* Make sure file exists *\/<\/span>\n<span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> readData = <span class=\"hljs-function\">(<span class=\"hljs-params\">f<\/span>) =&gt;<\/span>\n<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve<\/span>) =&gt;<\/span> {\n<span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader()\nreader.onloadend = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> resolve(reader.result)\nreader.readAsDataURL(f)\n})\n<span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> readData(file)\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>So far, so good? The next step is to build a serverless function for uploading.<\/p>\n<h2>Add a Serverless Upload Function<\/h2>\n<p><a href=\"https:\/\/vercel.com\/docs\/serverless-functions\/introduction\">Vercel provides excellent support for serverless functions<\/a> written in different backend languages. In our demo app, we use Node.js and Cloudinary Node.js SDK to write our upload serverless function.<\/p>\n<p>We add the <code>api<\/code> folder to the root of the project. By default, Vercel will detect the serverless functions based on this directory and deploy them accordingly.<\/p>\n<p>Let\u2019s add an <code>upload.js<\/code> file to the <code>api<\/code> folder, with the following content:<\/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-built_in\">module<\/span>.exports = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n<span class=\"hljs-comment\">\/* logic implementation here *\/<\/span>\n}\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>Great. Now we need to install Cloudinary Node.js SDK as project dependency and start using it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">i<\/span> <span class=\"hljs-selector-tag\">cloudinary<\/span> <span class=\"hljs-selector-id\">#OR<\/span> <span class=\"hljs-selector-tag\">yarn<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-tag\">cloudinary<\/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\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Setting Up Cloudinary Account<\/h3>\n<p>To have the Cloudinary upload service working, we need to config it with <code>cloudName<\/code>, <code>api_key<\/code> and <code>api_Secret<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> sdk = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'cloudinary'<\/span>).v2\n\n\n\nsdk.config({\n<span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'your-cloud-name'<\/span>,\n<span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">'your-api-key'<\/span>,\n<span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">'your-api-secret'<\/span>,\n})\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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 find these keys at the your <a href=\"https:\/\/cloudinary.com\/console\">Cloudinary Dashboard page<\/a> or Settings page:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1602239746\/mediajams\/dashboard.png\" alt=\"https:\/\/res.cloudinary.com\/mayashavin\/image\/upload\/v1602239746\/mediajams\/dashboard.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1097\" height=\"279\"\/><\/p>\n<p>Then we can execute an upload under the registered Cloudinary account based on the <code>req.body.file<\/code> received from the HTTPs request:<\/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-built_in\">module<\/span>.exports = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n<span class=\"hljs-keyword\">const<\/span> body = req.body\n<span class=\"hljs-keyword\">const<\/span> uploader = sdk.uploader\n\n\n\n<span class=\"hljs-keyword\">const<\/span> resource = <span class=\"hljs-keyword\">await<\/span> uploader.upload(body.file)\n\n<span class=\"hljs-keyword\">return<\/span> res.json(resource)\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>Our complete code for <code>upload.js<\/code> will be:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> sdk = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'cloudinary'<\/span>).v2\n\n\n\nsdk.config({\n<span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-string\">'your-cloud-name'<\/span>,\n<span class=\"hljs-attr\">api_key<\/span>: <span class=\"hljs-string\">'your-api-key'<\/span>,\n<span class=\"hljs-attr\">api_secret<\/span>: <span class=\"hljs-string\">'your-api-secret'<\/span>,\n})\n\n\n\n<span class=\"hljs-built_in\">module<\/span>.exports = <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n<span class=\"hljs-keyword\">const<\/span> body = req.body\n<span class=\"hljs-keyword\">const<\/span> uploader = sdk.uploader\n\n\n\n<span class=\"hljs-keyword\">const<\/span> resource = <span class=\"hljs-keyword\">await<\/span> uploader.upload(body.file)\n\n<span class=\"hljs-keyword\">return<\/span> res.json(resource)\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><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\u2019s it. Let\u2019s go back to our <code>Upload<\/code> component and connect its <code>selectFile<\/code> to this serverless function, shall we?<\/p>\n<h3>Calling the Upload Serverless Function<\/h3>\n<p>Inside <code>selectFile()<\/code> implementation of <code>Upload.vue<\/code>, we call the serverless function through a HTTPS POST request using <code>$axios<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> resource = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">this<\/span>.$axios.$post(<span class=\"hljs-string\">'\/api\/upload'<\/span>, {\n<span class=\"hljs-attr\">file<\/span>: data\n})\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><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>And if the upload is completed successfully, we will receive a JSON object containing all relevant data for the uploaded image. Otherwise, it will return a JSON object containing <code>error<\/code> with <code>msg<\/code> field indicating the error.<\/p>\n<h3>Making the UI Informative<\/h3>\n<p>To make our UI be more informative during the uploading process, let\u2019s add a <code>loading<\/code> , <code>image<\/code> and <code>error<\/code> variables to <code>data<\/code> section of the component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">data() {\n<span class=\"hljs-keyword\">return<\/span> {\n<span class=\"hljs-attr\">loading<\/span>: <span class=\"hljs-literal\">false<\/span>,\n<span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-comment\">\/\/contain the uploaded image<\/span>\n}\n},\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><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>And assign values them accordingly during <code>selectFile()<\/code> execution:<\/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-keyword\">async<\/span> selectFile(e) {\n<span class=\"hljs-keyword\">const<\/span> file = e.target.files&#91;<span class=\"hljs-number\">0<\/span>]\n\n<span class=\"hljs-comment\">\/* Make sure file exists *\/<\/span>\n<span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n\n\n\n<span class=\"hljs-keyword\">this<\/span>.loading = <span class=\"hljs-literal\">true<\/span> <span class=\"hljs-comment\">\/\/Start loading<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> readData = <span class=\"hljs-function\">(<span class=\"hljs-params\">f<\/span>) =&gt;<\/span>\n<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve<\/span>) =&gt;<\/span> {\n<span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader()\nreader.onloadend = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> resolve(reader.result)\nreader.readAsDataURL(f)\n})\n<span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> readData(file)\n<span class=\"hljs-keyword\">const<\/span> resource = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">this<\/span>.$axios.$post(<span class=\"hljs-string\">'\/api\/upload'<\/span>, {\n<span class=\"hljs-attr\">file<\/span>: data\n})\n\n<span class=\"hljs-keyword\">this<\/span>.loading = <span class=\"hljs-literal\">false<\/span> <span class=\"hljs-comment\">\/\/End loading<\/span>\n\n<span class=\"hljs-keyword\">if<\/span> (resource.error) {\n<span class=\"hljs-keyword\">this<\/span>.error = resource.error\n<span class=\"hljs-keyword\">return<\/span>\n}\n\n<span class=\"hljs-keyword\">this<\/span>.image = resource\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>Then add the HTML code to handle the following:<\/p>\n<ul>\n<li>Disable the input field when an upload is going on.<\/li>\n<li>Display an \u201cUploading\u2026\u201d message when an upload is going on<\/li>\n<li>Display an error message when something is wrong.<\/li>\n<li>Display the uploaded image otherwise.<\/li>\n<\/ul>\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\">&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"upload-comp\"<\/span>&gt;\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span>\n<span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\".jpeg,.jpg,.png,image\/jpeg,image\/png,.svg,.gif\"<\/span>\n<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"p-3 border cursor-pointer\"<\/span>\n@<span class=\"hljs-attr\">change<\/span>=<span class=\"hljs-string\">\"selectFile\"<\/span>\n<span class=\"hljs-attr\">:disabled<\/span>=<span class=\"hljs-string\">\"loading\"<\/span>\n\/&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\">\"m-5 text-lg font-thin italic\"<\/span> <span class=\"hljs-attr\">v-show<\/span>=<span class=\"hljs-string\">\"loading\"<\/span>&gt;<\/span>\nUploading...\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">v-if<\/span>=<span class=\"hljs-string\">\"error &amp;&amp; !loading\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"m-5\"<\/span>&gt;<\/span>{{ error.msg }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n<span class=\"hljs-attr\">v-if<\/span>=<span class=\"hljs-string\">\"!loading &amp;&amp; image\"<\/span>\n<span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"image.secure_url\"<\/span>\n<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"m-5\"<\/span>\n&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-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>Now our complete code for <code>Upload.vue<\/code> is:<\/p>\n<ul>\n<li>\n<code>template<\/code> section:<\/li>\n<\/ul>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-20\" 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\">template<\/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\">\"upload-comp\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span>\n<span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\".jpeg,.jpg,.png,image\/jpeg,image\/png,.svg,.gif\"<\/span>\n<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"p-3 border cursor-pointer\"<\/span>\n@<span class=\"hljs-attr\">change<\/span>=<span class=\"hljs-string\">\"selectFile\"<\/span>\n<span class=\"hljs-attr\">:disabled<\/span>=<span class=\"hljs-string\">\"loading\"<\/span>\n\/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"m-5 text-lg font-thin italic\"<\/span> <span class=\"hljs-attr\">v-show<\/span>=<span class=\"hljs-string\">\"loading\"<\/span>&gt;<\/span>\nUploading...\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\">v-if<\/span>=<span class=\"hljs-string\">\"error &amp;&amp; !loading\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"m-5\"<\/span>&gt;<\/span>{{ error.msg }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n<span class=\"hljs-attr\">v-if<\/span>=<span class=\"hljs-string\">\"!loading &amp;&amp; image\"<\/span>\n<span class=\"hljs-attr\">:src<\/span>=<span class=\"hljs-string\">\"image.secure_url\"<\/span>\n<span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"m-5\"<\/span>\n&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\">template<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><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<ul>\n<li>\n<code>script<\/code> section<\/li>\n<\/ul>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-21\" 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>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> {\ndata() {\n<span class=\"hljs-keyword\">return<\/span> {\n<span class=\"hljs-attr\">loading<\/span>: <span class=\"hljs-literal\">false<\/span>,\n<span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-literal\">null<\/span>,\n}\n},\n<span class=\"hljs-attr\">methods<\/span>: {\n<span class=\"hljs-keyword\">async<\/span> selectFile(e) {\n<span class=\"hljs-keyword\">const<\/span> file = e.target.files&#91;<span class=\"hljs-number\">0<\/span>]\n\n<span class=\"hljs-comment\">\/* Make sure file exists *\/<\/span>\n<span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n\n\n\n<span class=\"hljs-keyword\">this<\/span>.loading = <span class=\"hljs-literal\">true<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> readData = <span class=\"hljs-function\">(<span class=\"hljs-params\">f<\/span>) =&gt;<\/span>\n<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve<\/span>) =&gt;<\/span> {\n<span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader()\nreader.onloadend = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> resolve(reader.result)\nreader.readAsDataURL(f)\n})\n<span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> readData(file)\n<span class=\"hljs-keyword\">const<\/span> resource = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">this<\/span>.$axios.$post(<span class=\"hljs-string\">'\/api\/upload'<\/span>, {\n<span class=\"hljs-attr\">file<\/span>: data\n})\n\n<span class=\"hljs-keyword\">this<\/span>.loading = <span class=\"hljs-literal\">false<\/span>\n\n\n\n<span class=\"hljs-keyword\">if<\/span> (resource.error) {\n<span class=\"hljs-keyword\">this<\/span>.error = resource.error\n<span class=\"hljs-keyword\">return<\/span>\n}\n\n<span class=\"hljs-keyword\">this<\/span>.image = resource\n}\n}\n}\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-21\"><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>And finally, you can simply run the following to have your application deploy to Vercel on production.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">vercel --prod\n<\/code><\/span><\/pre>\n<h2>Summary<\/h2>\n<p>At this point you have a functional application from front-end to back-end to securely upload an asset with the least set up required. The next step is to play around with the upload configurations from Cloudinary to pre-generate transformations, or getting smart color detection, and so on. And then using the <a href=\"https:\/\/cloudinary.nuxtjs.org\/\">Cloudinary module for Nuxt<\/a> to dynamically display the uploaded asset with optimization on the client-side.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28254,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,212,380,371,373,303],"class_list":["post-28253","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-next-js","tag-serverless","tag-under-review","tag-upload","tag-video"],"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>Building Serverless Upload for Images<\/title>\n<meta name=\"description\" content=\"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.\" \/>\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\/guest_post\/build-serverless-upload-with-cloudinary\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building Serverless Upload for Images\" \/>\n<meta property=\"og:description\" content=\"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-23T22:01:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-26T18:56:39+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\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\/guest_post\/build-serverless-upload-with-cloudinary\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Building Serverless Upload for Images\",\"datePublished\":\"2022-03-23T22:01:12+00:00\",\"dateModified\":\"2025-06-26T18:56:39+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\"},\"wordCount\":5,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"Next.js\",\"Serverless\",\"Under Review\",\"Upload\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\",\"name\":\"Building Serverless Upload for Images\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA\",\"datePublished\":\"2022-03-23T22:01:12+00:00\",\"dateModified\":\"2025-06-26T18:56:39+00:00\",\"description\":\"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA\",\"width\":1024,\"height\":512},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building Serverless Upload for Images\"}]},{\"@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":"Building Serverless Upload for Images","description":"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.","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\/guest_post\/build-serverless-upload-with-cloudinary\/","og_locale":"en_US","og_type":"article","og_title":"Building Serverless Upload for Images","og_description":"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-03-23T22:01:12+00:00","article_modified_time":"2025-06-26T18:56:39+00:00","og_image":[{"width":1024,"height":512,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/"},"author":{"name":"","@id":""},"headline":"Building Serverless Upload for Images","datePublished":"2022-03-23T22:01:12+00:00","dateModified":"2025-06-26T18:56:39+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/"},"wordCount":5,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA","keywords":["Guest Post","Image","Next.js","Serverless","Under Review","Upload","Video"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/","name":"Building Serverless Upload for Images","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA","datePublished":"2022-03-23T22:01:12+00:00","dateModified":"2025-06-26T18:56:39+00:00","description":"This tutorial will explore how to create a secured upload component with Cloudinary, and enhance the power of serverless in your Jamstack application.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA","width":1024,"height":512},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-serverless-upload-with-cloudinary\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Building Serverless Upload for Images"}]},{"@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\/v1681925016\/Web_Assets\/blog\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507\/797318a61ec5cf80a537e7ecc765edefde4378a2-1024x512-1_2825473507.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28253","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=28253"}],"version-history":[{"count":3,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28253\/revisions"}],"predecessor-version":[{"id":37835,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28253\/revisions\/37835"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28254"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}