HTTP image server without any npm package | TeloHub

HTTP image server without any npm package

Last updated on March 8, 2022. Created on March 7, 2022.


Table of Contents


Problem

  • We have an image
  • We want to build a server to share this file online
  • How can do that in NodeJS without any npm package

Pre-requisite

Folder structure

Our project should contain two files.

  1. index.js : this should house our application logic
  2. images/file.png: the file we want to serve should be named file.png and placed in images folder
images/:
file.png
index.js

💡 The code is a gradual build up to the final working code, you can keep track of the curent section by paying attention to the comments within the code

Create an HTTP server to listen for incoming request

Ok, we’d start by creating an http server. NodeJS has a build-in http module with methods for handling all things HTTP related, like in our case we would need the createServer method. The http.createServer method returns a *http.Server*object , we would store that in a variable server.

const http = require('http')
//create server
const server = http.createServer()

Start server

The server contains a listen method that we would call to start listening for incoming requests. We would pass the port we want our server to listen on to the listen method In our case we would choose port 5000

const http = require('http')
const server = http.createServer()
//start server
server.listen(5000)

Handle listening event.

Its often handy to log when your server has started listening, otherwise we might not be sure everything works as planned. You might also want to do something else when your server starts listening. Thankfully the server object is an event emitter, and we can listen for the listening event.

const http = require('http')
const server = http.createServer()
server.listen(5000)
//handle server listening
server.on('listening',()=>console.log('listening on 5000'))

Handle incoming requests

The http.createServer method accepts a callback function as parameter. The callback function we provide would be called whenever there is a new request.

The callback function would be called with two arguments.

  1. IncomingRequest object : this object contains information about the incoming request
  2. ServerReponse Object : this object contains properties and methods to structure a response.

In this case our callback would accept the IncomingRequest as req and the ServerReponse as res

const http = require('http')
const server = http.createServer((req,res)=>{
//handle incoming requests
})
server.listen(5000)
server.on('listening',()=>console.log('listening on 5000'))

Send Our Image as Response

To send our response to an incoming request, we need to first check if the request is asking for the information. We would inspect the request url to see if it matches our determined route. We would chose the "/" as our route. We can choose any other route like "/image" relative to our host address. But in this case we would go with "/" for simplicity. That is to say, assuming our host is localhost:5000. if a client visits http://localhost:5000 or http://localhost:5000/ we should respond with our image.

Let’s go through steps of how we would send our response if the url matches “/“

  1. set our content type to "image/png" by calling the res.setHeader method . Which takes in two parameters; header which in this case is "Content-Type" and value which we would like to be "image/png". This would help our client know what type of file we are sending.
  2. Create a Readable stream from our file by passing our file path to fs.createReadSteam method.
  3. call the pipe method of our file readable stream and pass the res object which is a writable stream as an argument. this should handle streaming the file to our client.
  4. Although not necessary, but we can listen for when we have finished streaming a file by listening to the end event of our file stream.
const http = require('http')
const fs = require('fs')

const server = http.createServer((req,res)=>{

//send image as response
if (req.url=="/"){
res.setHeader('Content-Type','image/png')
const file = fs.createReadStream('images/file.png')
file.pipe(res)
file.on('end',()=>console.log('image sent',new Date()))
}

})

server.listen(5000)

server.on('listening',()=>console.log('listening on 5000'))

Handle 404 (not found)

404 is the status code our server should respond with when a route a client requests for is not found, according to the HTTP protocol specification. Since this is a single image server, we would need Just one route to work, any other route should have the 404 response. Our chosen url to serve our image is the "/" route. That is to say, assuming our host is localhost:5000. if a client visits any route that is not http://localhost:5000 or http://localhost:5000/ . We should respond with a 404 error response.

We would handle the 404 error buy doing 3 things

  1. set our status code to 404 by assigning 404 to the res.statusCode property
  2. set our content type to "text/plain" by calling the res.setHeader method . Which takes in two parameters; header which in this case is "Content-Type" and value which we would like to be "text/plain"
  3. send the actual response by calling the res.end method and passing it our response body which is just a plain text "Not found!"
const http = require('http')
const fs = require('fs')

const server = http.createServer((req,res)=>{
if (req.url=="/"){
res.setHeader('Content-Type','image/png')
const file = fs.createReadStream('images/file.png')
file.pipe(res)
file.on('end',()=>console.log('image sent',new Date()))
}
else{
//Handle 404 error
console.error(req.url,'not found')
res.statusCode = 404
res.setHeader('Content-Type','text/plain')
res.end('Not Found!')
}

})

server.listen(5000)

server.on('listening',()=>console.log('listening on 5000'))

If you have trouble following this up, you most likely need to study up on http protocol

Handle Transfer Error

As we know. Sometimes things don’t go as expected. We don’t want the client waiting too long. We want to update the client if something goes wrong with his Download. To handle Transfer errors , we would listen to the error event on our file stream, and provide a callback function that would be expecting the error object.

We don’t want to send anything from the error object directly to the client. This might leak some information about how our server works to the client, and we don’t trust clients. Clients might be hackers.

We would construct a nice 500 error for the client to notify them that something went wrong on the server.

To send the 500 Error, We would:

  1. set our status code to 500 by assigning 500 to the res.statusCode property
  2. set our content type to "text/plain" by calling the res.setHeader method . Which takes in two parameters; header which in this case is "Content-Type" and value which we would like to be "text/plain"
  3. send the actual response by calling the res.end method and passing it our response body which is just a plain text "something went wrong"
const http = require('http')
const fs = require('fs')

const server = http.createServer((req,res)=>{

if (req.url=="/"){
res.setHeader('Content-Type','image/png')
const file = fs.createReadStream('images/file.png')
file.pipe(res)
file.on('end',()=>console.log('image sent',new Date()))

//handle tranfer error
file.on('error',(error)=>{
console.error(error)
res.statusCode = 500
res.setHeader('Content-Type','text/plain')
res.end('something went wrong')
})
}
else{
console.error(req.url,'not found')
res.statusCode = 404
res.setHeader('Content-Type','text/plain')
res.end('Not Found!')
}

})

server.listen(5000)

server.on('listening',()=>console.log('listening on 5000'))

How to run the code

Run node index.js command within the application directory to run the code.

Live demo

You can run the live demo on replit

Conclusion

So I hope this answers the question of how to build a server with any npm library.

I would still advice you use express or any other web server framework. as they will handles more issues that you won’t even think of and simplify your code alot. But this knowledge helps you know how to extend these libraries if you want to.

See you later. Bye 👋🏾

Hey 😁, did you enjoy this article?

Then you will enjoy our courses

See in on the other side, bye 👋🏾.

By using this site, you agree that you have read and understand its Privacy Policy.