2.0.5 • Published 8 months ago

http-server-simple v2.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

http-server-simple is an http server written in pure javascript for NodeJS. It has no dependencies. The syntaxe was inspired by the Express library.

The following methods are supported: GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE.

Usage

const {Server} = require("http-server-simple");
const server = new Server();

/* GET 127.0.0.1:5000/text */
server.get("/text", (req,res) => {
    return res.text("Sending a text/plain message");
})
/* GET 127.0.0.1:5000/json */
.get("/json", (req,res) => {
    return res.json({message: "Sending an application/json message"});
})
/* GET 127.0.0.1:5000/html */
.get("/html", (req,res) => {
    return res.html("<h1>Sending a text/html message</h1>");
})
/* POST 127.0.0.1:5000/ */
.post("/", (req,res) => {
    return res.text("A post request to /");
})
.listen(5000, "127.0.0.1");

Router

Server level routing done in the previous example will implicitly create a router with the "/" root, you can create your own router with a different root

const {Server, Router} = require("http-server-simple");
const server = new Server();
const router = new Router();
const users = require("./data/users");

/* GET 127.0.0.1:5000/user */
router.get("/", (req,res) => {
    return res.json(users);
})
/* GET 127.0.0.1:5000/user/:id */
.get("/:id", (req,res) => {
    const id = req.params.id;
    return res.json(user.id);
})
/* DELETE 127.0.0.1:5000/user/:id */
.delete("/:id", (req,res) => {
    const id = req.params.id;
    delete user.id;
    return res.end();
});

server.router("/user",router).listen(5000,"127.0.0.1");

Router prefix

In order to create routes within the router with the same prefix without having to repeat the prefix in each route, you can use Router.prefix()

const {Server, Router} = require("http-server-simple");
const server = new Server();
const router = new Router();
const users = require("./data/users");
const posts = require("./data/posts");

const userRouter = Router.prefix("/user", router => {
    /* GET 127.0.0.1:5000/api/v1/user */
    router.get("/", (req,res) => res.json(users))

    /* GET 127.0.0.1:5000/api/v1/user/:id */
    .get("/:id", (req,res) => res.json(users[req.params.id]));
});

const postRouter = Router.prefix("/post", router => {
    /* GET 127.0.0.1:5000/api/v1/post */
    router.get("/", (req,res) => res.json(posts))

    /* GET 127.0.0.1:5000/api/v1/post/:id */
    .get("/:id", (req,res) => res.json(posts[req.params.id]));
});


server.router("/api/v1",[userRouter, postRouter])
    .listen(5000,"127.0.0.1");

Middleware

Route handlers are considered middlewares too. The only difference between a middleware and a handler is the use of the next function. Handlers do not use the next function, middlewares do. Not calling next in the middle of the middleware chain will hang the server. To exit a middleware chain you have to return res.text(), res.html(), res.json() or res.end()

const {Server, Router} = require("http-server-simple");
const server = new Server();
const router = new Router();

server.router("/router",router,(req,res,next) => {
    console.log("Router middlewares will execute before routes middlewares.");
    next();
}).listen(5000,"127.0.0.1");

/* 127.0.0.1:5000/router/get */
router.get("/get",
(req,res,next) => {
    console.log("Route middlewares execute after the router middlewares.");

    if(Math.random() < 0.5) {
        console.log("Call the next middleware.");
        return next();
    } 

    console.log("Otherwise terminate the chain.");
    return res.end();
    
},
(req,res) => {
    console.log("This is the route handler, it's last in the middleware chain and doesn't use the next function.");
    return res.end();
});

/* GET 127.0.0.1:5000/ */
server.get("/", (req,res,next) => {
    console.log("Server level routing also supports middlewares.");
    next();
},
(req,res) => {
    console.log("End of the chain = route handler.");
    return res.end();
});

Query Parameters

Use the req.query function to get the query parameters from the url

const {Server} = require("http-server-simple");
const server = new Server();

/* GET 127.0.0.1:5000/?a=1&b=2 */
server.get("/", (req,res) => {
    const queries = req.query(); // returns {a: "1", b: "2"}
    const a = req.query("a"); //returns "1"
    const c = req.query("c"); //returns null
    return res.end();
})
.listen(5000,"127.0.0.1");

Route parameters

Access the dictionary of route parameters with the req.params attribute

const {Server} = require("http-server-simple");
const server = new Server();

/* GET 127.0.0.1:5000/user/:id */
server.get("/user/:id", (req,res) => {
    const id = req.params.id;

    return res.html(`<h1>This is user ${id}</h1>`);
})
.listen(5000,"127.0.0.1");

Body parsing

Access the body of a POST,PUT or PATCH request with req.body. The body will be parsed depending on the Content-Type header. If the parsing failed, the Content-Type is invalid or no Content-Type was specified, req.body will be the raw data with the specified encoding.

For all other methods, req.body returns undefined

SERVER SIDE

const {Server} = require("http-server-simple");
const server = new Server();
const users = require("./data/users");

/* PUT 127.0.0.1:5000/user/:id */
server.put("/user/:id", (req,res) => {
    const id = req.params.id;

    // {name: "bob", age: 10}
    const newData = req.body;

    users[id] = {...users[id], ...newData};

    return res.json(users[id]);
})
.listen(5000,"127.0.0.1");

CLIENT SIDE (using urlencoded)

fetch("http://127.0.0.1:5000/user/1", {
    method: "PUT",
    body: "name=bob&age=10",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    }
});

Unhandled exceptions

A status code 500 and no content will be sent as a response if an uncaught exception happens in one of the middlewares or handlers

SERVER SIDE

const {Server} = require("http-server-simple");
const server = new Server();

/* PUT 127.0.0.1:5000/exception */
server.get("/exception", (req,res) => {
    res.nonExistantFunction();
})
.listen(5000,"127.0.0.1");

CLIENT SIDE

const res = await fetch("http://127.0.0.1:5000/exception");

//true
console.log(res.status === 500);

Download

Downloading a file from the disk can be done in two ways. the "low level" way is with the res.download method and the "high level" way is with the Download class helper.

res.download takes as argument a File object and, optionally, an object containing the start and end bytes that you would like to download.

SERVER SIDE

const {Server, File} = require("http-server-simple");
const server = new Server();

/* PUT 127.0.0.1:5000/download */
server.get("/download", (req,res) => {
    //content of file: 0123456789
    const file = new File("path/to/a/file/that/exists");

    opts = {};
    if(req.query("start")) {
        opts.start = parseInt(req.query("start"));
    }
    if(req.query("end")) {
        opts.end = parseInt(req.query("end"));
    }

    return res.download(file, opts);
})
.listen(5000,"127.0.0.1");

CLIENT SIDE

let res = await fetch("http://127.0.0.1:5000/download");
let text = await res.text();

//0123456789, or it will download the file on a browser that respect the Content-Disposition header
console.log(text);
console.log(res.status); //200

res = await fetch("http://127.0.0.1:5000/download?start=2");
text = await res.text();

console.log(text); //23456789
console.log(res.status); //206

res = await fetch("http://127.0.0.1:5000/download?end=5");
text = await res.text();

console.log(text); //012345
console.log(res.status); //206

res = await fetch("http://127.0.0.1:5000/download?start=2&end=5");
text = await res.text();

console.log(text); //23456
console.log(res.status); //206

res = await fetch("http://127.0.0.1:5000/download?start=100");

//416, invalid range
console.log(res.status);

the Download class however only takes a File object or a string representing its path as argument. It looks at the Range header to determine what bytes to send.

SERVER SIDE

const {Server, Download} = require("http-server-simple");
const server = new Server();

/* PUT 127.0.0.1:5000/download */
server.get("/download", (req,res) => {
    //content of file: 0123456789
    const file = "path/to/a/file/that/exists";
    const downloader = new Download(req,res);

    return downloader.download(file);
})
.get("/resumableDownload", (req,res) => {
    const file = "path/to/a/file/that/exists";
    const downloader = new Download(req,res);

    return downloader.resumableDownload(file);
})
.listen(5000,"127.0.0.1");

CLIENT SIDE

let res = await fetch("http://127.0.0.1:5000/download");
let text = await res.text();

console.log(text); //0123456789
console.log(res.status); //200

res = await fetch("http://127.0.0.1:5000/download", {
    headers: {
        "Range": "bytes=2-"
    }
});
text = await res.text();

 //0123456789, the Range header was ignored because downloader.download() does not support resuming therefore it downloads the whole file.
console.log(text);
console.log(res.status); //200

res = await fetch("http://127.0.0.1:5000/resumableDownload", {
    headers: {
        "Range": "bytes=2-"
    }
});
text = await res.text();

console.log(text); //23456789
console.log(res.status) //206
2.0.3

8 months ago

2.0.2

8 months ago

2.0.5

8 months ago

2.0.4

8 months ago

2.0.1

8 months ago

2.0.0

8 months ago

1.3.2

2 years ago

1.3.1

2 years ago

1.3.0

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.2.0

2 years ago

1.1.0

2 years ago

1.0.0

2 years ago