afl-router
v2.2.2
Published
A minimalist HTTP router.
Downloads
11
Readme
About
Just a minimalist router to easily handle serving URLs in a HTTP server project. At this moment, this router can handle the serving of static files inside a defined public directory, HTTP request for any method, parsing url query string, body, and also supports route parameters in URL and a fallback resource in case no file or action is defined.
Installation
npm install afl-router
Usage
Table of Contents
- Setting up the router
- Defining the public directory (static files)
- Setting up routes
- Getting requests parameters
- Answering requests
Setting up the router
To setup the router, there are two options:
const http = require("http");
const aflRouter = require("afl-router");
var router = new aflRouter();
// Just pass the method `route()` to http's `createServer` callback.
var server = http.createServer(router.route()).listen(8080);
Or using the listen()
method inside the own router:
const aflRouter = require("afl-router");
var router = new aflRouter();
// The method `listen` will create the http server and return it.
var server = router.listen(8080);
Initial router configuration
An object can be passed to the router's constructor with some optional parameters.
const aflRouter = require("afl-router");
var router = new aflRouter({
publicDirectory: "app/dist",
defaultFilename: "index.html",
fallback: function(req) {
req.answer.text("Oops, resource not found.");
}
});
// The method `listen` will create the http server and return it.
var server = router.listen(8080);
Defining the public directory (static files)
To serve static files (html files, scripts, images, stylesheets, etc), it's necessary
to setup the public directory. It can be setup in the router constructor
or with the method setPublicDirectory()
after being created.
const aflRouter = require("afl-router");
var router = new aflRouter();
// Setting the value to "." will server any file inside the project's root.
router.setPublicDirectory(".");
// Point to a single directory:
router.setPublicDirectory("/app/dist");
var server = router.listen(8080);
All static files requested to the server, will be fetch realitve to the public directory defined. So, if the requested url is http://localhost:8080/scripts/mySript.js, and the public directory is defined to "/app/dist", the file that is going to be server should be in %project_root%/app/dist/scripts/myScript.js.
There is also setDefaultFilename()
method, that, if setup, will serve
that file in case none is specified (and no route is defined for that url).
const aflRouter = require("afl-router");
var router = new aflRouter();
router.setPublicDirectory("/app/dist");
router.setDefaultFilename("index.html");
var server = router.listen(8080);
If a public directory and a default filename are defined, when a URL is requested and no action is defined for that route, the router will look up for the default filename in the public directory. For example: http://localhost:8080/article will serve, in case it exists, the file index.html (default filename) inside the directory %project_root%/app/dist/article (public directory + url path).
Setting up routes
There are two ways of setting up routes. The first, is with the HTTP method shorthand:
const aflRouter = require("afl-router");
var router = new aflRouter();
// URL request "http://localhost:8080/items" will land here
router.get("/items", function(request) {
// do stuff
});
// POST to "http://localhost:8080/item/add" will land here
router.post("/item/add", function(request) {
// do stuff
});
var server = router.listen(8080);
Every HTTP method can be used this way (get, post, put, head, etc.).
An alternative way to setup a route is with an object, wich enables to define several method action for a single route in one invokation:
const aflRouter = require("afl-router");
var router = new aflRouter();
var userActions = {
get: function(request) {
// GET requests to http://localhost:8080/user land here
},
delete: function(request) {
// DELETE requests to http://localhost:8080/user land here
},
post: function(request) {
// POST requests to http://localhost:8080/user land here
}
};
router.newRoute("/user", userActions);
var server = router.listen(8080);
Route parameters
Named parameters can be setup in an URL, and retrieved
within the ParsedRequest
object passed in the callback.
const aflRouter = require("afl-router");
var router = new aflRouter();
// ie: http://localhost:8080/posts/2019/04
router.get("/posts/{year}/{month}", function(request) {
console.log(request.route.year); // ie: 2019
console.log(request.route.month); // ie: 04
});
// ie: http://localhost:8080/user/do_update/45
router.post("/user/do_{action}/{id}", function(request) {
console.log(request.route.action); // ie: 'update'
console.log(request.route.id); // ie: 45
});
// ie: http://localhost:8080/main.js
router.head("/{filename}.js", function(request) {
console.log(request.route.filename); // ie: 'main'
});
var server = router.listen(8080);
Getting requests parameters
Query string
To get query string parameters passed within the url, the
object query
inside the request
object received as
parameter in the route callback can be used:
const aflRouter = require("afl-router");
var router = new aflRouter();
// ie: http://localhost:8080/posts?month=10&year=2019
router.get("/posts", function(request) {
console.log(request.query.month);
console.log(request.query.year);
});
var server = router.listen(8080);
Query string parameters are parsed by the router and store inside
the query
object, and can be accessed by their name as properties.
Body content
Content of request's body will be parsed according to the
content-type declared, and is available through ParsedRequest.body
object:
const fs = require("fs");
const aflRouter = require("afl-router");
var router = new aflRouter();
// ie: http://localhost:8080/comments/new
router.post("/comments/new", function(request) {
console.log(request.body.user);
console.log(request.body.comment);
});
// Files uploaded with multipart/form-data are available in body object as well.
// In this example, the name of the file uploaded is 'photo':
router.post("/upload", function(request) {
let uploadedPhoto = request.body.photo;
// SaveSync moves the temp file to the new location with the given name
uploadedPhoto.saveSync(uploadedPhoto.filename);
request.answer.json({result: "Photo saved!"});
});
var server = router.listen(8080);
IMPORTANT!
Uploaded files that are not moved with save()
or saveSync()
should be deleted manually using it's tmpFile
property:
fs.unlinkSync(request.body.exampleFile.tmpFile)
.
Answering requests
Response shorthands (answer interface)
Inside the ParsedRequest
object, there's a interface answer
present to perform the request's response in a quick way.
There are 5 shorthand functions in ParsedRequest.answer
: json()
,
html()
, text()
, redirect()
and file()
. There's also a generic()
function that does not set up automatically the response's header Content-Type
.
This functions receive a first argument, data
(or filepath
in case of file()
, url
in case of redirect()
), and a second
optional argument, opts
. This second argument can contain statusCode
,
statusText
and an array of headers
that will be writen in the response.
If any of those is set, defaults will be overwriten.
const path = require("path");
const fs = require("fs");
const aflRouter = require("afl-router");
var router = new aflRouter();
router.get("/users", function(request) {
var users = [
{name: "Jack", last_seen: "2019-03-02"},
{name: "Peter", last_seen: "2018-11-27"}
];
// json() automatically stringify the object and set the
// response's Content-Type
request.answer.json(users);
});
router.get("/user/profile_photo/{id}", function(request) {
let userId = request.route.id;
let filepath = path.join("images", "users_profile", userId + ".jpg");
if(fs.existsSync(filepath)) {
// file() receives a path to file as parameter,
// if the file doesn't exists, will throw an error.
request.answer.file(filepath);
} else {
request.answer.text("File not found", {
statusCode: 404,
statusTest: "Not found"
}):
}
});
router.get("/redirect", request => request.answer.redirect("/users"));
var server = router.listen(8080);
As seen in the previous example, it is possible to change the defaults
statusCode
, statusText
and headers
on the response sent to the
client by this shorthand functions. Additionally, extra options download
and filename
can be passed to force client download the response file.
const aflRouter = require("afl-router");
var router = new aflRouter();
router.get("/somefile", function(request) {
// If `filename` option is not passed, the original filename will be used.
request.answer.file("/path/to/file.ext", {
download: true,
filename: "myFile.ext"
});
});
var server = router.listen(8080);
Using Node's http.ServerResponse
On every RouterCallback
, besides the ParsedRequest
object that
is passed as first argument to the callback, a second argument is given,
containing Node's raw http.ServerResponse
. This response can be used to
answer the request in the traditional way:
const aflRouter = require("afl-router");
var router = new aflRouter();
router.get("/users", function(request, response) {
response.writeHead(200, "OK", {"Content-Type": "text/html"});
response.write("<h1>Users page<h1>");
response.end();
});
var server = router.listen(8080);