Hello, everyone!

In this post, we’ll look at how to upload files in a Node.js application using Express, Multer, and TypeScript. We’ll go through the prerequisites, setting up your Node.js app with TypeScript, adding controllers and routes, integrating Multer to handle file uploads, and finally, connecting it all to a template.

Note: The GitHub repository for this project, including all the code, will be provided below.

Let’s get started!


Prerequisites

For this tutorial, make sure you have the following set up:

Note: Familiarity with Multer for handling file uploads and TypeScript for adding static types to JavaScript is recommended.

Setting Up the App

In this section, we’ll create the initial folder structure for our application and set up our project dependencies. We’ll be using npm to manage dependencies, but you can also use Yarn if you prefer.

  1. Create the project folderFirst, open your terminal, navigate to your desired directory, and run the following commands to create and enter the project folder:
    mkdir file-upload-app cd file-upload-app
  2. Initialize the projectNext, initialize the project with npm (or Yarn if that’s your preference). This will create a package.json file to manage dependencies for our project. To initialize with npm, run:
    npm init -y

    This command will create a package.json file with default settings, which we’ll modify as we add more dependencies.

  3. Install project dependenciesNow, let’s install the required dependencies: Express, Multer, and their corresponding types for TypeScript. Run the following command:
    npm install express multer npm install --save-dev @types/express @types/multer typescript ts-node

    This will install Express and Multer as regular dependencies, and the type definitions for Express, Multer, and TypeScript as dev dependencies.

Project Structure

To give you an overview of how the app will be structured, here’s what our folder layout will look like:

└── file-upload-app/ │ ├── src/ │ ├── index.ts │ ├── routes/ │ │ └── fileRoute.ts │ └── controllers/ │ └── fileController.ts │ ├── node_modules/ ├── package.json ├── tsconfig.json └── package-lock.json

In this structure:

Setting Up TypeScript

Now, let’s set up TypeScript for the project. To do this, we need to configure the tsconfig.json file, which will define how TypeScript compiles our code.

  1. Create the tsconfig.json fileRun the following command to create the TypeScript configuration file:
    npx tsc --init
  2. Configure the tsconfig.jsonOpen the generated tsconfig.json file and update it to the following settings to properly handle the TypeScript compilation for this project:
    { "compilerOptions": { "target": "ES6", "module": "commonjs", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], "exclude": ["node_modules"] }
    • "target": "ES6" ensures that the JavaScript output is compatible with modern environments.
    • "module": "commonjs" is the module system used by Node.js.
    • "outDir": "./dist" specifies the folder where compiled JavaScript files will be stored.
    • "rootDir": "./src" defines where the TypeScript source files are located.

Optional Bonus: Setting Up Nodemon

To make the development process easier, you can set up Nodemon to automatically restart the server whenever there are changes to the source files.

  1. Install Nodemon as a dev dependency:
    npm install --save-dev nodemon
  2. Update package.json scriptsAdd a script to the package.json file to run the app with Nodemon. Modify the "scripts" section as follows:
     "scripts": { "dev": "nodemon src/index.ts", "start": "ts-node src/index.ts" }
  3. Running the app with NodemonNow, you can run the app with:
    npm run dev

    This will start the app and automatically restart the server whenever you modify the TypeScript files.

     

GitHub console launching file upload app with Express, Multer, TypeScript Node.js
GitHub console upload app Express Multer TypeScript

Setting Up the Folder Structure and Files

In this part, we’re going to set up the folder structure and create the necessary files. We’ll configure the main index.ts file, set up routes in the fileRoute.ts file, and handle the upload logic in the fileController.ts file. Here’s how each file should look:


1. src/controllers/fileController.ts

This controller will handle the logic for file uploads. We’ll create a function uploadFile to manage file uploads.

import { Request, Response } from 'express'; import fs from 'fs'; import path from 'path'; export const uploadFile = (req: Request, res: Response): void => { if (!req.file) { res.status(400).send('No file uploaded.'); return; } const filePath = path.join(__dirname, '../../', req.file.path); // Set headers for file streaming res.setHeader('Content-Type', req.file.mimetype); res.setHeader('Content-Disposition', `attachment; filename="${req.file.originalname}"`); // Stream the file to the response const fileStream = fs.createReadStream(filePath); fileStream.pipe(res); fileStream.on('error', (error) => { console.error('File streaming error:', error); res.status(500).send('Error streaming the file.'); }); };

Explanation:

Testing the App

To test the app at this stage, you can use Postman or other API testing tools like Insomnia or Hoppscotch.

  1. Clone the GitHub repositoryYou can find the complete project setup on GitHub: GitHub Repository.
    git clone https://github.com/dev-luckymhz/upload-file-node.js-typescript.git cd upload-file-node.js-typescript npm install npm run dev
  2. Testing with Postman (or alternative tools)
    • Open Postman or your preferred API testing tool.
    • Send a POST request to http://localhost:3000/api/upload with form data.
    • Add a file with the key file and choose any file to upload.

If the setup is correct, the app should respond with the file data, or return it as a stream if you configured it that way in the controller.

Postman, server testing, file upload testing
Postman file upload testing Express Multer server

 

Integrating EJS (Optional)

To add a template to your app using EJS, you need to configure Express to use EJS as the view engine and set up the appropriate folder for views. Here’s how you can do that:

  1. Install EJS:
    npm install ejs
  2. Update src/index.ts to include EJS:Here’s how you set EJS as the view engine in your index.ts file:
    import express, { Application } from 'express'; import path from 'path'; import fileRoute from './routes/fileRoute'; const app: Application = express(); const PORT = process.env.PORT || 3000; // Set EJS as the view engine app.set("view engine", "ejs"); app.set('views', path.join(__dirname, '/views')); app.use(express.json()); app.use('/api', fileRoute); // Serve the upload form app.get('/', (req, res) => { res.render('uploadForm'); }); app.listen(PORT, () => { console.log(`Server is running on <http://localhost>:${PORT}`); });

Updated Project Structure

Your updated project structure with the views folder for the EJS templates will look like this:

file-upload-app/ │ ├── src/ │ ├── index.ts │ ├── routes/ │ │ └── fileRoute.ts │ ├── controllers/ │ │ └── fileController.ts │ └── views/ │ └── uploadForm.ejs │ ├── node_modules/ ├── package.json ├── tsconfig.json └── package-lock.json

Creating the EJS Form

In the views folder, create a file called uploadForm.ejs with the following content. This will be a Bootstrap-style form for uploading files:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Upload File</title> <link href="<https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css>" rel="stylesheet"> </head> <body> <div class="container mt-5"> <div class="card"> <div class="card-header"> <h3>Upload a File</h3> </div> <div class="card-body"> <form id="uploadForm" enctype="multipart/form-data"> <div class="mb-3"> <label for="file" class="form-label">Choose File</label> <input type="file" class="form-control" id="file" name="file" required> </div> <button type="submit" class="btn btn-primary">Upload</button> </form> </div> </div> </div> <!-- Modal to display the uploaded file --> <div class="modal fade" id="fileModal" tabindex="-1" aria-labelledby="fileModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="fileModalLabel">Uploaded File</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <iframe id="fileFrame" width="100%" height="400px"></iframe> </div> </div> </div> </div> <script src="<https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js>"></script> <script> document.getElementById('uploadForm').addEventListener('submit', async function(event) { event.preventDefault(); const formData = new FormData(); formData.append('file', document.getElementById('file').files[0]); const response = await fetch('/api/upload', { method: 'POST', body: formData }); if (response.ok) { const fileBlob = await response.blob(); const fileURL = URL.createObjectURL(fileBlob); // Show the uploaded file in the modal const fileFrame = document.getElementById('fileFrame'); fileFrame.src = fileURL; const fileModal = new bootstrap.Modal(document.getElementById('fileModal')); fileModal.show(); } else { alert('File upload failed!'); } }); </script> </body> </html>

Explanation:

Leave a Reply

Your email address will not be published. Required fields are marked *