Handling File Uploads in Node.js with Express and Multer

Handling File Uploads in Node.js with Express and Multer

Forms, File Uploads and Security with Node.js and Express.How to manage storing and handling files uploaded via forms, in Express. ... Then include it in your Node.js file: const express

If you’re building a web application, you’re likely to encounter the need to build HTML forms on day one. They’re a big part of the web experience, and they can be complicated.

Typically the form handling process involves:

  • displaying an empty HTML form in response to an initial GET request
  • user submitting the form with data in a POST request
  • validation on both the client and the server
  • re-displaying the form populated with escaped data and error messages if invalid
  • doing something with the sanitized data on the server if it’s all valid
  • redirecting the user or showing a success message after data is processed.

Handling form data also comes with extra security considerations.

We’ll go through all of these and explain how to build them with Node.js and Express — the most popular web framework for Node. First, we’ll build a simple contact form where people can send a message and email address securely and then take a look what’s involved in processing file uploads.

Setup

Make sure you’ve got a recent version of Node.js installed; node -v should return 8.9.0 or higher.

Download the starting code from here with git:

git clone https://github.com/sitepoint-editors/node-forms.git
cd node-forms
npm install
npm start

There’s not too much code in there. It’s just a bare-bones Express setup with EJS templates and error handlers:

// server.js
const path = require('path')
const express = require('express')
const layout = require('express-layout')
const routes = require('./routes')
const app = express()

app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')

const middleware = [
  layout(),
  express.static(path.join(__dirname, 'public')),
]
app.use(middleware)

app.use('/', routes)

app.use((req, res, next) => {
  res.status(404).send("Sorry can't find that!")
})

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

app.listen(3000, () => {
  console.log(`App running at http://localhost:3000`)
})

The root url / simply renders the index.ejs view.

// routes.js
const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.render('index')
})

module.exports = router

Displaying the Form

When people make a GET request to /contact, we want to render a new view contact.ejs:

// routes.js
router.get('/contact', (req, res) => {
  res.render('contact')
})

The contact form will let them send us a message and their email address:

<!-- views/contact.ejs -->
<div class="form-header">
  <h2>Send us a message</h2>
</div>
<form method="post" action="/contact" novalidate>
  <div class="form-field">
    <label for="message">Message</label>
    <textarea class="input" id="message" name="message" rows="4" autofocus></textarea>
  </div>
  <div class="form-field">
    <label for="email">Email</label>
    <input class="input" id="email" name="email" type="email" value="" />
  </div>
  <div class="form-actions">
    <button class="btn" type="submit">Send</button>
  </div>
</form>

See what it looks like at http://localhost:3000/contact.

Form Submission

To receive POST values in Express, you first need to include the body-parser middleware, which exposes submitted form values on req.body in your route handlers. Add it to the end of the middlewares array:

// server.js
const bodyParser = require('body-parser')

const middlewares = [
  // ...
  bodyParser.urlencoded()
]

It’s a common convention for forms to POST data back to the same URL as was used in the initial GET request. Let’s do that here and handle POST /contact to process the user input.

Let’s look at the invalid submission first. If invalid, we need to pass back the submitted values to the view so they don’t need to re-enter them along with any error messages we want to display:

router.get('/contact', (req, res) => {
  res.render('contact', {
    data: {},
    errors: {}
  })
})

router.post('/contact', (req, res) => {
  res.render('contact', {
    data: req.body, // { message, email }
    errors: {
      message: {
        msg: 'A message is required'
      },
      email: {
        msg: 'That email doesn‘t look right'
      }
    }
  })
})

If there are any validation errors, we’ll do the following:

  • display the errors at the top of the form
  • set the input values to what was submitted to the server
  • display inline errors below the inputs
  • add a form-field-invalid class to the fields with errors.
<!-- views/contact.ejs -->
<div class="form-header">
  <% if (Object.keys(errors).length === 0) { %>
    <h2>Send us a message</h2>
  <% } else { %>
    <h2 class="errors-heading">Oops, please correct the following:</h2>
    <ul class="errors-list">
      <% Object.values(errors).forEach(error => { %>
        <li><%= error.msg %></li>
      <% }) %>
    </ul>
  <% } %>
</div>

<form method="post" action="/contact" novalidate>
  <div class="form-field <%= errors.message ? 'form-field-invalid' : '' %>">
    <label for="message">Message</label>
    <textarea class="input" id="message" name="message" rows="4" autofocus><%= data.message %></textarea>
    <% if (errors.message) { %>
      <div class="error"><%= errors.message.msg %></div>
    <% } %>
  </div>
  <div class="form-field <%= errors.email ? 'form-field-invalid' : '' %>">
    <label for="email">Email</label>
    <input class="input" id="email" name="email" type="email" value="<%= data.email %>" />
    <% if (errors.email) { %>
      <div class="error"><%= errors.email.msg %></div>
    <% } %>
  </div>
  <div class="form-actions">
    <button class="btn" type="submit">Send</button>
  </div>
</form>

Submit the form at http://localhost:3000/contact to see this in action. That’s everything we need on the view side.

Validation and Sanitization

There is a handy middleware express-validator for validating and sanitizing data using the validator.js library, let’s include it in our middlewares array:

// server.js
const validator = require('express-validator')

const middlewares = [
  // ...
  validator()
]

Validation

With the validators provided we can easily check that a message and a valid email was provided:

// routes.js
const { check, validationResult } = require('express-validator/check')

router.post('/contact', [
  check('message')
    .isLength({ min: 1 })
    .withMessage('Message is required'),
  check('email')
    .isEmail()
    .withMessage('That email doesn‘t look right')
], (req, res) => {
  const errors = validationResult(req)
  res.render('contact', {
    data: req.body,
    errors: errors.mapped()
  })
})

Sanitization

With the sanitizers provided we can trim whitespace from the start and end of the values, and normalize the email into a consistent pattern. This can help remove duplicate contacts being created by slightly different inputs. For example, ' [email protected]' and '[email protected] ' would both be sanitized into '[email protected]'.

Sanitizers can simply be chained onto the end of the validators:

const { matchedData } = require('express-validator/filter')

router.post('/contact', [
  check('message')
    .isLength({ min: 1 })
    .withMessage('Message is required')
    .trim(),
  check('email')
    .isEmail()
    .withMessage('That email doesn‘t look right')
    .trim()
    .normalizeEmail()
], (req, res) => {
  const errors = validationResult(req)
  res.render('contact', {
    data: req.body,
    errors: errors.mapped()
  })

  const data = matchedData(req)
  console.log('Sanitized:', data)
})

The matchedData function returns the output of the sanitizers on our input.

The Valid Form

If there are errors we need to re-render the view. If not, we need to do something useful with the data and then show that the submission was successful. Typically, the person is redirected to a success page and shown a message.

HTTP is stateless, so you can’t redirect to another page and pass messages along without the help of a session cookie to persist that message between HTTP requests. A “flash message” is the name given to this kind of one-time-only message we want to persist across a redirect and then disappear.

There are three middlewares we need to include to wire this up:

const cookieParser = require('cookie-parser')
const session = require('express-session')
const flash = require('express-flash')

const middlewares = [
  // ...
  cookieParser(),
  session({
    secret: 'super-secret-key',
    key: 'super-secret-cookie',
    resave: false,
    saveUninitialized: false,
    cookie: { maxAge: 60000 }
  }),
  flash()
]

The express-flash middleware adds req.flash(type, message) which we can use in our route handlers:

// routes
router.post('/contact', [
  // validation ...
], (req, res) => {
  const errors = validationResult(req)
  if (!errors.isEmpty()) {
    return res.render('contact', {
      data: req.body,
      errors: errors.mapped()
    })
  }

  const data = matchedData(req)
  console.log('Sanitized: ', data)
  // Homework: send sanitized data in an email or persist in a db

  req.flash('success', 'Thanks for the message! I‘ll be in touch :)')
  res.redirect('/')
})

The express-flash middleware adds messages to req.locals which all views have access to:

<!-- views/index.ejs -->
<% if (messages.success) { %>
  <div class="flash flash-success"><%= messages.success %></div>
<% } %>

<h1>Working With Forms in Node.js</h1>

You should now be redirected to index view and see a success message when the form is submitted with valid data. Huzzah! We can now deploy this to production and be sent messages by the prince of Nigeria.

Security considerations

If you’re working with forms and sessions on the Internet, you need to be aware of common security holes in web applications. The best security advice I’ve been given is “Never trust the client!”

TLS over HTTPS

Always use TLS encryption over https:// when working with forms so that the submitted data is encrypted when it’s sent across the Internet. If you send form data over http://, it’s sent in plain text and can be visible to anyone eavesdropping on those packets as they journey across the Internet.

Wear Your Helmet

There’s a neat little middleware called helmet that adds some security from HTTP headers. It’s best to include right at the top of your middlewares and is super easy to include:

// server.js
const helmet = require('helmet')

middlewares = [
  helmet()
  // ...
]

Cross-site Request Forgery (CSRF)

You can protect yourself against cross-site request forgery by generating a unique token when the user is presented with a form and then validating that token before the POST data is processed. There’s a middleware to help you out here as well:

// server.js
const csrf = require('csurf')

middlewares = [
  // ...
  csrf({ cookie: true })
]

In the GET request we generate a token:

// routes.js
router.get('/contact', (req, res) => {
  res.render('contact', {
    data: {},
    errors: {},
    csrfToken: req.csrfToken()
  })
})

And also in the validation errors response:

router.post('/contact', [
  // validations ...
], (req, res) => {
  const errors = validationResult(req)
  if (!errors.isEmpty()) {
    return res.render('contact', {
      data: req.body,
      errors: errors.mapped(),
      csrfToken: req.csrfToken()
    })
  }

  // ...
})

Then we just need include the token in a hidden input:

<!-- view/contact.ejs -->
<form method="post" action="/contact" novalidate>
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <!-- ... -->
</form>

That’s all that’s required.

We don’t need to modify our POST request handler as all POST requests will now require a valid token by the csurf middleware. If a valid CSRF token isn’t provided, a ForbiddenError error will be thrown, which can be handled by the error handler defined at the end of server.js.

You can test this out yourself by editing or removing the token from the form with your browser’s developer tools and submitting.

Cross-site Scripting (XSS)

You need to take care when displaying user-submitted data in an HTML view as it can open you up to cross-site scripting(XSS). All template languages provide different methods for outputting values. The EJS <%= value %> outputs the HTML escaped value to protect you from XSS, whereas <%- value %> outputs a raw string.

Always use the escaped output <%= value %> when dealing with user submitted values. Only use raw outputs when you’re sure that’s is safe to do so.

File Uploads

Uploading files in HTML forms is a special case that requires an encoding type of "multipart/form-data". See MDN’s guide to sending form data for more detail about what happens with multipart form submissions.

You’ll need additional middleware to handle multipart uploads. There’s an Express package named multer that we’ll use here:

// routes.js
const multer = require('multer')
const upload = multer({ storage: multer.memoryStorage() })

router.post('/contact', upload.single('photo'), [
  // validation ...
], (req, res) => {
  // error handling ...

  if (req.file) {
    console.log('Uploaded: ', req.file)
    // Homework: Upload file to S3
  }

  req.flash('success', 'Thanks for the message! I’ll be in touch :)')
  res.redirect('/')
})

This code instructs multer to upload the file in the “photo” field into memory and exposes the File object in req.file which we can inspect or process further.

The last thing we need is to add the enctype attribute and our file input:

<form method="post" action="/contact?_csrf=<%= csrfToken %>" novalidate enctype="multipart/form-data">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <div class="form-field <%= errors.message ? 'form-field-invalid' : '' %>">
    <label for="message">Message</label>
    <textarea class="input" id="message" name="message" rows="4" autofocus><%= data.message %></textarea>
    <% if (errors.message) { %>
      <div class="error"><%= errors.message.msg %></div>
    <% } %>
  </div>
  <div class="form-field <%= errors.email ? 'form-field-invalid' : '' %>">
    <label for="email">Email</label>
    <input class="input" id="email" name="email" type="email" value="<%= data.email %>" />
    <% if (errors.email) { %>
      <div class="error"><%= errors.email.msg %></div>
    <% } %>
  </div>
  <div class="form-field">
    <label for="photo">Photo</label>
    <input class="input" id="photo" name="photo" type="file" />
  </div>
  <div class="form-actions">
    <button class="btn" type="submit">Send</button>
  </div>
</form>

Unfortunately, we also needed to include _csrf as a GET param so that the csurf middleware plays ball and doesn’t lose track of our token during multipart submissions.

Try uploading a file, you should see the File objects logged in the console.

Populating File Inputs

In case of validation errors, we can’t re-populate file inputs like we did for the text inputs. A common approach to solving this problem involves these steps:

  • uploading the file to a temporary location on the server
  • showing a thumbnail and filename of the attached file
  • adding JavaScript to the form to allow people to remove the selected file or upload a new one
  • moving the file to a permanent location when everything is valid.

Because of the additional complexities of working with multipart and file uploads, they’re often kept in separate forms.

Thanks For Reading

I hope you enjoyed learning about HTML forms and how to work with them in Express and Node.js. Here’s a quick recap of what we’ve covered:

  • displaying an empty Form in response to a GET request
  • processing the submitted POST data
  • displaying a list of errors, inline errors and submitted data
  • checking submitted data with validators
  • cleaning up submitted data with sanitizers
  • passing messages across redirects with a flash message
  • protecting yourself against attacks like CSRF and XSS
  • processing file uploads in multipart form submissions.

Let us know how you got on in the comments!

Node.js for Beginners - Learn Node.js from Scratch (Step by Step)

Node.js for Beginners - Learn Node.js from Scratch (Step by Step)

Node.js for Beginners - Learn Node.js from Scratch (Step by Step) - Learn the basics of Node.js. This Node.js tutorial will guide you step by step so that you will learn basics and theory of every part. Learn to use Node.js like a professional. You’ll learn: Basic Of Node, Modules, NPM In Node, Event, Email, Uploading File, Advance Of Node.

Node.js for Beginners

Learn Node.js from Scratch (Step by Step)

Welcome to my course "Node.js for Beginners - Learn Node.js from Scratch". This course will guide you step by step so that you will learn basics and theory of every part. This course contain hands on example so that you can understand coding in Node.js better. If you have no previous knowledge or experience in Node.js, you will like that the course begins with Node.js basics. otherwise if you have few experience in programming in Node.js, this course can help you learn some new information . This course contain hands on practical examples without neglecting theory and basics. Learn to use Node.js like a professional. This comprehensive course will allow to work on the real world as an expert!
What you’ll learn:

  • Basic Of Node
  • Modules
  • NPM In Node
  • Event
  • Email
  • Uploading File
  • Advance Of Node

Top 7 Most Popular Node.js Frameworks You Should Know

Top 7 Most Popular Node.js Frameworks You Should Know

Node.js is an open-source, cross-platform, runtime environment that allows developers to run JavaScript outside of a browser. In this post, you'll see top 7 of the most popular Node frameworks at this point in time (ranked from high to low by GitHub stars).

Node.js is an open-source, cross-platform, runtime environment that allows developers to run JavaScript outside of a browser.

One of the main advantages of Node is that it enables developers to use JavaScript on both the front-end and the back-end of an application. This not only makes the source code of any app cleaner and more consistent, but it significantly speeds up app development too, as developers only need to use one language.

Node is fast, scalable, and easy to get started with. Its default package manager is npm, which means it also sports the largest ecosystem of open-source libraries. Node is used by companies such as NASA, Uber, Netflix, and Walmart.

But Node doesn't come alone. It comes with a plethora of frameworks. A Node framework can be pictured as the external scaffolding that you can build your app in. These frameworks are built on top of Node and extend the technology's functionality, mostly by making apps easier to prototype and develop, while also making them faster and more scalable.

Below are 7of the most popular Node frameworks at this point in time (ranked from high to low by GitHub stars).

Express

With over 43,000 GitHub stars, Express is the most popular Node framework. It brands itself as a fast, unopinionated, and minimalist framework. Express acts as middleware: it helps set up and configure routes to send and receive requests between the front-end and the database of an app.

Express provides lightweight, powerful tools for HTTP servers. It's a great framework for single-page apps, websites, hybrids, or public HTTP APIs. It supports over fourteen different template engines, so developers aren't forced into any specific ORM.

Meteor

Meteor is a full-stack JavaScript platform. It allows developers to build real-time web apps, i.e. apps where code changes are pushed to all browsers and devices in real-time. Additionally, servers send data over the wire, instead of HTML. The client renders the data.

The project has over 41,000 GitHub stars and is built to power large projects. Meteor is used by companies such as Mazda, Honeywell, Qualcomm, and IKEA. It has excellent documentation and a strong community behind it.

Koa

Koa is built by the same team that built Express. It uses ES6 methods that allow developers to work without callbacks. Developers also have more control over error-handling. Koa has no middleware within its core, which means that developers have more control over configuration, but which means that traditional Node middleware (e.g. req, res, next) won't work with Koa.

Koa already has over 26,000 GitHub stars. The Express developers built Koa because they wanted a lighter framework that was more expressive and more robust than Express. You can find out more about the differences between Koa and Express here.

Sails

Sails is a real-time, MVC framework for Node that's built on Express. It supports auto-generated REST APIs and comes with an easy WebSocket integration.

The project has over 20,000 stars on GitHub and is compatible with almost all databases (MySQL, MongoDB, PostgreSQL, Redis). It's also compatible with most front-end technologies (Angular, iOS, Android, React, and even Windows Phone).

Nest

Nest has over 15,000 GitHub stars. It uses progressive JavaScript and is built with TypeScript, which means it comes with strong typing. It combines elements of object-oriented programming, functional programming, and functional reactive programming.

Nest is packaged in such a way it serves as a complete development kit for writing enterprise-level apps. The framework uses Express, but is compatible with a wide range of other libraries.

LoopBack

LoopBack is a framework that allows developers to quickly create REST APIs. It has an easy-to-use CLI wizard and allows developers to create models either on their schema or dynamically. It also has a built-in API explorer.

LoopBack has over 12,000 GitHub stars and is used by companies such as GoDaddy, Symantec, and the Bank of America. It's compatible with many REST services and a wide variety of databases (MongoDB, Oracle, MySQL, PostgreSQL).

Hapi

Similar to Express, hapi serves data by intermediating between server-side and client-side. As such, it's can serve as a substitute for Express. Hapi allows developers to focus on writing reusable app logic in a modular and prescriptive fashion.

The project has over 11,000 GitHub stars. It has built-in support for input validation, caching, authentication, and more. Hapi was originally developed to handle all of Walmart's mobile traffic during Black Friday.

Node JS Full Course Learn Node.js in 7 Hours

Node JS Full Course Learn Node.js in 7 Hours

This Edureka Node.js Full Course video will help you in learn Node.js along with practical demonstration. This Node.js Tutorial for Beginners is ideal for both beginners as well as professionals who want to master the most prominently used javascript backend framework.

This Edureka Node.js Full Course video will help you in learn Node.js along with practical demonstration. This Node.js Tutorial for Beginners is ideal for both beginners as well as professionals who want to master the most prominently used javascript backend framework. Below are the topics covered in this node.js tutorial video:
2:32 What is Node.js?
3:22 Client-Server Architecture
4:12 Multi-Threaded Model
6:13 Single-Threaded Model
7:43 Multi-Threaded vs Event-Driven
9:45 Uber Old Architecture
11:10 Uber New Architecture
12:30 What is Node.js?
13:05 Sucess Stories
14:20 Node.js Trend
14:40 Node.js Features
16:25 Node.js Installation
16:50 Node.js First Example
17:30 Blocking vs Non-blocking
18:50 Demo
23:50 Node.js Modules
23:50 NPM
25:10 Global Objects
26:55 File System
30:30 Callbacks
31:45 Event
33:05 HTTP
34:50 Hands On
1:09:45 Node.js Tutorial
1:10:45 What is Node.js?
1:12:10 Features of Node.js
1:13:00 Node.js Architecture
1:14:55 NPM(Node Package Manager)
1:16:20 Node.js Modules
1:16:30 Node.js Modules Types
1:16:35 Core Modules
1:16:55 Local Modules
1:17:10 3rd Party Modules
1:18:35 JSON File
1:23:30 Data Types
1:25:35 Variables
1:26:40 Operators
1:27:45 Functions
1:29:10 Objects
1:29:55 File Systems
1:33:50 Events
1:34:20 HTTP Module
1:40:02 Events
1:44:37 HTTP Module
1:45:27 Creating a Web Server using Node.js
1:45:42 Express.js
1:46:57 Demo
1:58:37 Node.js NPM Tutorial
1:59:37 What is NPM?
2:03:12 Main Functions of NPM
2:04:27 Need For NPM
2:08:07 NPM Packages
2:17:42 NPM Installation
2:18:12 JSON File
2:31:32 Node.js Express Tutorial
2:32:02 Introduction to Express.js
2:32:32 Features of Express.js
2:35:27 Getting Started with Express.js
2:39:42 Routing Methods
2:44:57 Hands-On
2:48:12 Building RESTful API with Node.js
2:48:27 What is REST API?
2:49:42 Features of REST API
2:51:12 Principles of REST API
2:56:37 Methods of REST API
2:59:52 Building REST API with Node.js
3:24:07 Node.js MySQL Tutorial
3:24:32 What is MySQL?
3:25:13 Advantages of Using MySQL with Node.js
3:27:38 MySQL Installation
3:44:23 Node.js MongoDB Tutorial
3:44:58 What is NoSQL?
3:47:53 NoSQL Databases
3:48:38 Introduction to MongoDB
3:52:48 Features of MongoDB
3:53:03 MongoDB Installation
4:36:08 Node.js Docker Tutorial
4:36:38 What is Docker?
4:39:13 Docker Working
4:41:43 Docker Basics
4:41:48 DockerFile
4:42:03 Docker Images
4:42:23 Docker Container
4:44:38 Why use Node.js with Docker?
4:45:18 Demo: Node.js with Docker
4:58:38 MEAN Stack Application Tutorial
4:59:18 What is MEAN Application?
4:59:53 MongoDB
5:00:28 Express
5:01:13 Angular
5:01:23 Node.js
5:02:17 RESTful API
5:03:02 Contact List MEAN App
6:17:57 Node.js Interview Questions

Got a question on the topic? Please share it in the comment section below and our experts will answer it for you.