{"id":1665,"date":"2022-05-16T13:22:01","date_gmt":"2022-05-16T13:22:01","guid":{"rendered":"https:\/\/codevoweb.com\/?p=1665"},"modified":"2023-05-05T08:35:58","modified_gmt":"2023-05-05T08:35:58","slug":"node-postgresql-upload-resize-multiple-images","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/node-postgresql-upload-resize-multiple-images\/","title":{"rendered":"Node.js and PostgreSQL: Upload and Resize Multiple Images"},"content":{"rendered":"\n<p>In this article, you&#8217;ll learn how to upload and resize single and multiple images with Node.js, TypeScript, Multer, and Sharp.<\/p>\n\n\n\n<p>Related Post: Backend<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"\/api-node-postgresql-typeorm-project-setup\">API with Node.js + PostgreSQL + TypeORM: Project Setup<\/a><\/li>\n\n\n\n<li><a href=\"\/api-node-postgresql-typeorm-jwt-authentication\">API with Node.js + PostgreSQL + TypeORM: JWT Authentication<\/a><\/li>\n\n\n\n<li><a href=\"\/api-node-postgresql-typeorm-send-emails\">API with Node.js + PostgreSQL + TypeORM: Send Emails<\/a><\/li>\n\n\n\n<li><a href=\"\/node-express-typeorm-postgresql-rest-api\">Node.js, Express, TypeORM, PostgreSQL: CRUD Rest API<\/a><\/li>\n<\/ol>\n\n\n\n<p>Related Post: Frontend<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"\/react-rtk-query-react-hook-form-and-material-ui-multipart-formdata\">React, RTK Query, React Hook Form and Material UI &#8211; Image Upload<\/a><\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"450\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images.webp\" alt=\"Node.js and PostgreSQL Upload and Resize Multiple Images\" class=\"wp-image-1690\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images.webp 800w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images-300x169.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images-768x432.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images-100x56.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images-700x394.webp 700w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/Node.js-and-PostgreSQL-Upload-and-Resize-Multiple-Images-600x338.webp 600w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-content-wrap{padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-contents-title-wrap{padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-content-wrap .kb-table-of-content-list{color:#ffffff;font-weight:regular;font-style:normal;margin-top:10px;margin-right:0px;margin-bottom:0px;margin-left:-5px;}@media all and (max-width: 1024px){.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}@media all and (max-width: 767px){.kb-table-of-content-nav.kb-table-of-content-id_2c4ccd-48 .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}<\/style>\n\n\n<h2 class=\"wp-block-heading\">Upload a Single Image With Multer<\/h2>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<p>Before we start writing the image upload logic, let&#8217;s create a utility function to generate a UUID (<strong>Universally Unique Identifier<\/strong>) which we will use as a random string in the filenames.<\/p>\n\n\n\n<p><strong>src\/utils\/uuid.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nfunction uuid() {\n  const head = Date.now().toString(32);\n  const tail = Math.random().toString(32).substring(2);\n\n  return head + tail;\n}\n\nexport default uuid;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using Multer to Upload a Single Image<\/h3>\n\n\n\n<p>Create an upload folder in the <code>src<\/code> directory and within the <code>upload<\/code> folder create a <code>single-upload-disk.ts<\/code> file.<\/p>\n\n\n\n<p>Within the <code>single-upload-disk.ts<\/code> file, we&#8217;ll write the code to upload and save a single image to the disk using <code>multer.diskStorage()<\/code> method.<\/p>\n\n\n\n<p><strong>src\/upload\/single-upload-disk.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nimport { Request } from 'express';\nimport multer from 'multer';\nimport uuid from '..\/utils\/uuid';\n\nconst multerStorage = multer.diskStorage({\n  destination: function (req: Request, file: Express.Multer.File, cb) {\n    cb(null, `${__dirname}\/..\/..\/public\/posts\/single`);\n  },\n  filename: function (req: Request, file: Express.Multer.File, cb) {\n    const ext = file.mimetype.split('\/')[1];\n    const filename = `post-${uuid()}-${Date.now()}.${ext}`;\n    req.body.image = filename;\n    req.body.images = [];\n    cb(null, filename);\n  },\n});\n\nconst multerFilter = (\n  req: Request,\n  file: Express.Multer.File,\n  cb: multer.FileFilterCallback\n) =&gt; {\n  if (!file.mimetype.startsWith('image')) {\n    return cb(new multer.MulterError('LIMIT_UNEXPECTED_FILE'));\n  }\n\n  cb(null, true);\n};\n\nconst upload = multer({\n  storage: multerStorage,\n  fileFilter: multerFilter,\n  limits: { fileSize: 1024 * 1024 * 5, files: 1 },\n});\n\nexport const uploadPostImageDisk = upload.single('image');\n\n<\/code>\n<\/pre>\n\n\n\n<p>Here is an overview of what I did above:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First I imported the <code><a href=\"https:\/\/www.npmjs.com\/package\/multer\" target=\"_blank\" rel=\"noreferrer noopener\">multer<\/a><\/code> package, our custom <code>uuid<\/code> function and the Express <code>Request<\/code> interface.<\/li>\n\n\n\n<li>Then I called the <code>multer.diskStorage()<\/code> method with a configuration object.<br><br>The <code>multer.diskStorage()<\/code> gives us absolute control over how we store files on the disk. <br><br>The configuration object passed to <code>multer.diskStorage({})<\/code> takes two properties (<strong>filename<\/strong> and <strong>destination<\/strong>).<br><br>The <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">destination<\/mark> property can be a string or function that specifies the destination path of the uploaded files. If you provide a function then you must manually create the destination folder.<br><br>The <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">filename<\/mark> property is a function that determines the uploaded file&#8217;s name.<br><br>Also, in the filename function, I attached the filename to <code>req.body.image<\/code> to make it available to the other controllers.<\/li>\n\n\n\n<li>Next, I defined a filter function that will be called for every processed file to determine which type of files should be uploaded.<\/li>\n\n\n\n<li> Next, I evoked the Multer function to return an instance that provides several methods for generating middleware that processes files uploaded in&nbsp;<code>multipart\/form-data<\/code>&nbsp;format.<\/li>\n\n\n\n<li>Lastly, I called the <code>upload.single('image')<\/code> to return a middleware for processing a single file.<br><\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Create a public folder in the root directory and within the public folder create a folder named posts.<br>In the posts folder create two additional folders named single and multiple.<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code>public\/\n\u2514\u2500\u2500 posts\/\n  \u251c\u2500\u2500 multiple\/\n  \u2514\u2500\u2500 single\/<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Route Handler Functions<\/h3>\n\n\n\n<p>Now, it&#8217;s time to add the <code>uploadPostImageDisk<\/code> middleware to the middleware stack of the<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\"> PATCH<\/mark> and <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">POST<\/mark> methods.<\/p>\n\n\n\n<p><strong>Note:<\/strong> You must call the <code>uploadPostImageDisk<\/code> middleware before the <code>validate(updatePostSchema)<\/code> middleware so that the <code>req.body.image<\/code> and <code>req.body.images<\/code> will be available during the validation phase.<\/p>\n\n\n\n<p><strong>src\/routes\/post.routes.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nrouter\n  .route('\/')\n  .post(\n    uploadPostImageDisk,\n    validate(createPostSchema),\n    createPostHandler\n  )\n  .get(getPostsHandler);\n\nrouter\n  .route('\/:postId')\n  .get(validate(getPostSchema), getPostHandler)\n  .patch(\n    uploadPostImageDisk,\n    validate(updatePostSchema),\n    updatePostHandler\n  )\n  .delete(validate(deletePostSchema), deletePostHandler);\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Upload a Single Image with Multer and Sharp<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Using Multer to Upload a Single Image<\/h3>\n\n\n\n<p>Now let&#8217;s take it a step further by resizing the single image before saving it on the disk.<\/p>\n\n\n\n<p>To do that you need to install the <code><a href=\"https:\/\/www.npmjs.com\/package\/sharp\" target=\"_blank\" rel=\"noreferrer noopener\">sharp<\/a><\/code> package which will allow us to process the uploaded image before storing it on the disk.<\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nyarn add sharp &amp;&amp; yarn add -D @types\/sharp\n<\/code>\n<\/pre>\n\n\n\n<p>In the previous example, we used <code>multer.diskStorage()<\/code> to immediately save the uploaded image on the disk.<\/p>\n\n\n\n<p>To process the uploaded image before saving it to the disk, we need to use <code>multer.memoryStorage()<\/code> to read the image to memory as a buffer object.<\/p>\n\n\n\n<p><strong>src\/upload\/multi-upload-sharp.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nimport { NextFunction, Request, Response } from 'express';\nimport multer from 'multer';\nimport sharp from 'sharp';\nimport uuid from '..\/utils\/uuid';\n\nconst multerStorage = multer.memoryStorage();\n\nconst multerFilter = (\n  req: Request,\n  file: Express.Multer.File,\n  cb: multer.FileFilterCallback\n) =&gt; {\n  if (!file.mimetype.startsWith('image')) {\n    return cb(new multer.MulterError('LIMIT_UNEXPECTED_FILE'));\n  }\n\n  cb(null, true);\n};\n\nconst upload = multer({\n  storage: multerStorage,\n  fileFilter: multerFilter,\n  limits: { fileSize: 5000000, files: 1 },\n});\n\nexport const uploadPostImage = upload.single('image');\n<\/code>\n<\/pre>\n\n\n\n<p>Next, we need to call the <code>single()<\/code> method on the <a href=\"https:\/\/www.npmjs.com\/package\/multer\" target=\"_blank\" rel=\"noreferrer noopener\">Multer<\/a> instance to populate <code>req.file<\/code> with the buffer object.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using Sharp to Resize a Single Image<\/h3>\n\n\n\n<p>Now the <code>req.file.buffer<\/code> object will be available in the <code>resizePostImage<\/code> middleware ready for processing.<\/p>\n\n\n\n<p>In order to avoid running into errors when no file was uploaded, we need to check if the Request object has a file property before proceeding with the processing logic.<\/p>\n\n\n\n<p><strong>src\/upload\/multi-upload-sharp.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nexport const resizePostImage = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) =&gt; {\n  try {\n    const file = req.file;\n    if (!file) return next();\n\n    const fileName = `post-${uuid()}-${Date.now()}.jpeg`;\n    await sharp(req.file?.buffer)\n      .resize(800, 450)\n      .toFormat('jpeg')\n      .jpeg({ quality: 90 })\n      .toFile(`${__dirname}\/..\/..\/public\/posts\/single\/${fileName}`);\n\n    req.body.image = fileName;\n\n    next();\n  } catch (err: any) {\n    next(err);\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>Here is a breakdown of what I did above:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, I constructed the filename with the custom <code>uuid()<\/code> function and <code>Date.now()<\/code>. Also, I added the <code>.jpeg<\/code> extension because I know I&#8217;ll process the image to <strong>JPEG<\/strong> before saving it to the disk.<\/li>\n\n\n\n<li>Next, I resized the image, changed it to a <strong>JPEG<\/strong>, reduced the quality, and called <code>.toFile()<\/code> to save it to the disk.<\/li>\n\n\n\n<li>Lastly, I assigned the filename to <code>req.body.image<\/code> to make it available for the schema validation middleware.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Route Handlers<\/h2>\n\n\n\n<p>In the <code>src\/routes\/post.routes.ts<\/code> file, import the <code>uploadPostImage<\/code> and <code>resizePostImage<\/code> then add them to the middleware stack of the <strong>POST<\/strong> and <strong>PATCH<\/strong> routes.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note: <\/strong>The order of the middleware matters in the middleware stack.<\/p>\n<\/blockquote>\n\n\n\n<p><strong>src\/routes\/post.routes.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nrouter\n  .route('\/')\n  .post(\n    uploadPostImage,\n    resizePostImage,\n    validate(createPostSchema),\n    createPostHandler\n  )\n  .get(getPostsHandler);\n\nrouter\n  .route('\/:postId')\n  .get(validate(getPostSchema), getPostHandler)\n  .patch(\n    uploadPostImage,\n    resizePostImage,\n    validate(updatePostSchema),\n    updatePostHandler\n  )\n  .delete(validate(deletePostSchema), deletePostHandler);\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Upload Multiple Images with Multer and Sharp<\/h2>\n\n\n\n<p>To upload multiple images, <a href=\"https:\/\/www.npmjs.com\/package\/multer\" target=\"_blank\" rel=\"noreferrer noopener\">Multer<\/a> gives us two functions <code>.arrays(fieldname[, max_count])<\/code> and <code><em>.fields<\/em>([{ name: fieldname, [,maxCount: maxCount]}])<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using Multer to Upload Multiple Images<\/h3>\n\n\n\n<p>I decided to design the multiple-image upload logic with different field names to show the different options available with Multer.<\/p>\n\n\n\n<p>Here the <code>.fields()<\/code> method accepts an array of objects. Within the individual objects, you need to provide the field name and the maximum number of files the field should accept.<\/p>\n\n\n\n<p><strong>src\/upload\/multi-upload-sharp.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nimport { NextFunction, Request, Response } from 'express';\nimport multer, { FileFilterCallback } from 'multer';\nimport sharp from 'sharp';\nimport uuid from '..\/utils\/uuid';\n\nconst multerStorage = multer.memoryStorage();\n\nconst multerFilter = (\n  req: Request,\n  file: Express.Multer.File,\n  cb: FileFilterCallback\n) =&gt; {\n  if (!file.mimetype.startsWith('image')) {\n    return cb(new multer.MulterError('LIMIT_UNEXPECTED_FILE'));\n  }\n  cb(null, true);\n};\n\nconst upload = multer({\n  storage: multerStorage,\n  fileFilter: multerFilter,\n  limits: { fileSize: 5 * 1024 * 1024 },\n});\n\nexport const uploadPostImages = upload.fields([\n  { name: 'image', maxCount: 1 },\n  { name: 'images', maxCount: 3 },\n]);\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using Sharp to Resize Multiple Images<\/h3>\n\n\n\n<p>Multer will then populate the&nbsp;<code>req.files<\/code>&nbsp;object with the field names and each field name will map to an array of the associated file information objects.<\/p>\n\n\n\n<p><strong>src\/upload\/multi-upload-sharp.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nexport const resizePostImages = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) =&gt; {\n  try {\n    if (!req.files) return next();\n\n    \/\/ resize imageCover\n    \/\/ @ts-ignore\n    if (req.files?.image) {\n      const filename = `post-${uuid()}-${Date.now()}.jpeg`;\n      req.body.image = filename;\n      \/\/ @ts-ignore\n      await sharp(req.files?.image[0]?.buffer)\n        .resize(800, 450)\n        .toFormat('jpeg')\n        .jpeg({ quality: 90 })\n        .toFile(filename);\n    }\n\n    \/\/ resize images\n    \/\/ @ts-ignore\n    if (req.files.images) {\n      req.body.images = [];\n      await Promise.all(\n        \/\/ @ts-ignore\n        req?.files?.images.map((file, i) =&gt; {\n          const filename = `post-${uuid()}-${Date.now()}-${i + 1}.jpeg`;\n\n          req.body.images.push(filename);\n          return sharp(file.buffer)\n            .resize(800, 450)\n            .toFormat('jpeg')\n            .jpeg({ quality: 90 })\n            .toFile(`${__dirname}\/..\/..\/public\/posts\/multiple\/${filename}`);\n        })\n      );\n    }\n\n    next();\n  } catch (err: any) {\n    next(err);\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Processing a single file<\/h4>\n\n\n\n<p>The <code>req.file.image<\/code> will now be an array and since we used a <code>maxCount: 1<\/code> . It will only have one buffer object.<\/p>\n\n\n\n<p>You can access the buffer object with <code>req.files?.image[0]?.buffer<\/code> and pass it to the Sharp function.<\/p>\n\n\n\n<p>Lastly, you can perform the necessary operations on the buffer by appending the appropriate methods before saving it to the disk.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Processing multiple files<\/h4>\n\n\n\n<p>Here comes the hard part. The <code>req?.files?.images<\/code> will be an array containing at least one buffer object depending on how many images the user uploaded.<\/p>\n\n\n\n<p>The only way to process the individual buffer objects in the array is to map over the array.<\/p>\n\n\n\n<p>The Sharp function returns a promise so we need to explicitly return the individual promise in an array with the <code>map()<\/code> method and use <code>Promise.all()<\/code> to execute them.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note:<\/strong> you need to use <code>await Promise.all()<\/code> to give <code>Promise.all()<\/code> some time to execute the individual Promises before moving to the next middleware.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Routes Handler Function<\/h3>\n\n\n\n<p>Now import the <code>uploadPostImages<\/code> and <code>resizePostImages<\/code> middleware into <code>src\/routes\/post.routes.ts<\/code> file and include them in the <strong>POST<\/strong> and <strong>PATCH<\/strong> route middleware stack.<\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\nrouter\n  .route('\/')\n  .post(\n    uploadPostImages,\n    resizePostImages,\n    validate(createPostSchema),\n    createPostHandler\n  )\n  .get(getPostsHandler);\n\nrouter\n  .route('\/:postId')\n  .get(validate(getPostSchema), getPostHandler)\n  .patch(\n    uploadPostImages,\n    resizePostImages,\n    validate(updatePostSchema),\n    updatePostHandler\n  )\n  .delete(validate(deletePostSchema), deletePostHandler);\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Frontend for Uploading Either Single or Multiple Images<\/h2>\n\n\n\n<p>Below is the custom image upload component I created with <a href=\"\/react-rtk-query-react-hook-form-and-material-ui-multipart-formdata\">React, Material-UI, and React Hook Form to upload single and multiple images<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"608\" height=\"512\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png\" alt=\"multipart formData single image upload with React, RTQ Query, React hook form and TypeScript\" class=\"wp-image-1131\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png 608w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-300x253.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-100x84.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-534x450.png 534w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-single-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-600x505.png 600w\" sizes=\"auto, (max-width: 608px) 100vw, 608px\" \/><\/figure>\n\n\n\n<p>Upload multiple images with React.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"646\" height=\"730\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png\" alt=\"multipart formData multiple image upload with React, RTQ Query, React hook form and TypeScript\" class=\"wp-image-1130\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png 646w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-265x300.png 265w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-88x100.png 88w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-398x450.png 398w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-multiple-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-600x678.png 600w\" sizes=\"auto, (max-width: 646px) 100vw, 646px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"690\" height=\"918\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png\" alt=\"multipart formData image upload with React, RTQ Query, React hook form and TypeScript\" class=\"wp-image-1129\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript.png 690w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-225x300.png 225w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-75x100.png 75w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-338x450.png 338w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/04\/multipart-formData-image-upload-with-React-RTQ-Query-React-hook-form-and-TypeScript-600x798.png 600w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Congratulation on reaching the end. Please leave a comment below if you learned something new from this article.<\/p>\n\n\n\n<p>In this article, you learned how to upload and resize single and multiple images with Node.js, Multer, Shape, and PostgreSQL.<\/p>\n\n\n\n<p>Check out the source codes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/wpcodevo\/Blog_MUI_React-hook-form.git\" target=\"_blank\" rel=\"noreferrer noopener\">Frontend built with React, MUI, and RTK Query Upload Images<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/wpcodevo\/node_typeorm\/tree\/node_typeorm_image_upload_local\" target=\"_blank\" rel=\"noreferrer noopener\">Backend built with Node.js, TypeORM, and Postgres<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you&#8217;ll learn how to upload and resize single and multiple images with Node.js, TypeScript, Multer, and Sharp. Related Post: Backend Related Post:&#8230;<\/p>\n","protected":false},"author":1,"featured_media":1690,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[47],"tags":[42],"class_list":["post-1665","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-nodejs","tag-nodejs-api"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/1665","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/comments?post=1665"}],"version-history":[{"count":1,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/1665\/revisions"}],"predecessor-version":[{"id":11226,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/1665\/revisions\/11226"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/1690"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=1665"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=1665"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=1665"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}