How to upload files with Node.js + Hapi.js

Chai Phonbopit
Today I Learned ❤️Chai
4 min readMay 15, 2018

This post I just noted for myself what I learned after implemented handle files upload with Hapi.js (In the past, I used to handle file upload but with Express.js + Multer) In this post it’s different but very easier to implement.

Create Project

I initial project with yarn init and add dependencies:

yarn init
yarn add hapi inert
  • Hapi: Hapi library
  • Inert: Static file and directory handlers for Hapi.js

First step, I create a simple server with Hapi.js that running on port 2345

const Hapi = require('hapi')
const Inert = require('inert')
const server = Hapi.Server({ port: 2345 })
const init = async () => {
await server.register(Inert)
server.route({
path: '/',
method: 'GET',
handler: (req, h) => ({ message: 'Hello Hapi.js' })
})
await server.start()
}
init()

After that, verify that Hapi Server is running

nodemon server.js

We will see message like this

{
"message": "Hello Hapi.js"
}

Now, I know Hapi is working, next step is to create the endpoint for receiving a file, then I decided to create the endpoint with POST method like this:

server.route({
path: '/upload',
method: 'POST',
handler: (req, h) => {
const { payload } = req
return payload
}
})
I return the response with the same payload

Next, I upload an image with key file and use form-data to send a request to a server, the server responds with Buffer

{
"file": {
"type": "Buffer",
"data": []
}
}
Response file upload with Buffer

As you see, we received the response with Buffer, we able to write a file with fs module like this:

const fs = require('fs')fs.writeFile('filename.png', BUFFER_DATA, err => {
if (!err) {
console.log('Uploaded!')
}
})

I decided to create function to handle payload, I know only it’s Buffer but I don’t know a name of file then I hard code a filename to test.png 😄

const handleFileUpload = file => {
return new Promise((resolve, reject) => {
fs.writeFile('./upload/test.png', file, err => {
if (err) {
reject(err)
}
resolve({ message: 'Upload successfully!' })
})
})
}

Implement with routing

server.route({
path: '/upload',
method: 'POST',
handler: async (req, h) => {
const { payload } = req
const response = handleFileUpload(payload.file)
return response

}
})

And I test to upload my image, its working! 🎉

.
├── node_modules
├── package.json
├── server.js
├── upload
│ └── test.png
└── yarn.lock

Using Inert and Options

I already handle my image but overall it not good because I don’t know a file name. Lucky, Hapi.js provided an options for you I also added options to make response return as a stream.

server.route({
path: '/upload',
method: 'POST',
options: {
payload: {
output: 'stream',
}
},

handler: async (req, h) => {
const { payload } = req
const response = handleFileUpload(payload.file)
return response
}
})
Response as a stream, you can access _data, or hapi object to get a filename

As you see, response return stream, and contains information about filename, headers, then I updated my handleFileUpload function to write a file to

const handleFileUpload = file => {
return new Promise((resolve, reject) => {
const filename = file.hapi.filename
const data = file._data
fs.writeFile('./upload/' + filename, data, err => {
if (err) {
reject(err)
}
resolve({ message: 'Upload successfully!' })
})
})
}

I see a problem when I request a file path from URL http://localhost:2345/upload/test.png It’s 404 Not Found.

Time to implement Inert, I add more routing to serve a public folder, and after that It’s should show an image that upload

server.route({
method: 'GET',
path: '/upload/{file*}',
handler: {
directory: {
path: 'upload'
}
}

})

You can read more about Inert here : https://github.com/hapijs/inert

Now, I can view an image via request from URL

Setup NGINX

On production, sometimes its limit your file size, When I use NGINX, I change max file size to 50MB (or whatever you want) in nginx.conf

vi /etc/nginx/nginx.confclient_max_body_size 50M;

That’s all.

Happy Coding ❤️

--

--