fuhttp-ts
TypeScript icon, indicating that this package has built-in type declarations

1.2.1 • Public • Published

fuhttp-ts

Build Status

fuhttp

Web-server for nodejs written in TypeScript, highly inspired by this blog post. The server is built on top of the http package in nodejs.

Why another web-server package for nodejs? because I can (and because I didn't need all features provided by e.g. express)

Setup

Install the package

npm install fuhttp-ts

fuhttp-ts can now be included within your nodejs project using:

var fuhttp = require('fuhttp-ts');

For TypeScript:

import * as fuhttp from 'fuhttp-ts';

Manual Installation

Manual installation requires a clone of the repo.

Install dependencies defined in package.json

npm install

Run the rebuild script, as defined in packages.json. This will delete the build/ folder, compile all .ts files, create build/ folder where all .js files will reside, and append the license banner to each output file.

Documentation

Server

The Server class are used for handling HTTP requests and responses.

Constructor

Creating a new http-server:

constructor(portnumber, hoststring = null)

Where port is the tcp/ip-port which the server will listen for.

If host is set, only connections from host will be accepted.

var server = new fuhttp.Server(8080, 'localhost');

Here, connections made from client with origin-ip localhost connecting to the server on port 8080 is only accepted. If the HTTP-server should be made publicly available, hostname should be ignored.

Add Route

Adding a Route to match with an incoming HTTP-request:

Where route is a set of url-patterns. See Route for more information

add(pathstring, routeRoute)void

path is the parent sub-url, where route is accessible from.

let route = new Route('api');
... // Setup routes
 
server.add('/api', route);
server.add('/test', route);

The server can now be accesses the defined route object, both from /api and /test

Route lookup

Routing is implemented in a linked list inspired manner. The server only contains a parent route, which has a reference to its sub-routes. The routing uses this implementation to support faster lookup of routes for parsing, as opposed needing to loop through all added routes to find a matching route.

Add Middleware

A middleware is a set of instructions run before each request in handled by the Routes. See the Middleware section for more information about the available Middlewares

use(middlewareIMiddleware)void

Error Handling

The server may throw errors, e.g. if a route is not found. This is where the Error Functions come into play. The Server comes with some predefined error-handling functions, these can however be overwritten, which they also probably should.

Status Code Type Event name Method Name
400 Request Error request onRequestError(error, response)
444 Response Error response onResponseError(error, response)
404 Route Not Found notfound onNotFoundError(response)
405 Method Not Allowed notallowed onMethodNotAllowed(supportedMethods, response)
413 Request Data Too Large overflow onOverflowError(response)
NA Client Emits Error clientError onClientError(error, socket)
NA Server Closes close onClose()
NA Client Request A HTTP Upgrade upgrade onUpgrade()
NA Exception thrown from server method exception onException()

Each error-handler can be set using the method as described in Method Name, or through the more general method:

on(eventstring, funcFunction)boolean

Where event is the Event Name as defined in the table above.

The provided example is both equivalent, defining a function to run when there's an error with a HTTP-request

server.onRequestError = function(error: Error, response: http.ServerResponse) {
    ...
};
 
server.on('request', function(error: Error, response: http.ServerResponse) {
    ...
});

Start Server

For the server to start accepting incoming requests, the server needs to be started:

listen()void 

Your HTTP-server is now able to handle incoming HTTP-requests, as configured in your constructor. If no routes are added at this point, the application will throw an error.

server.listen();

Route

The Route class for parsing and matching incoming HTTP-requests based on an URL

Constructor

constructor(routeNamestring = null)

Where routeName is the parent-pattern that will be matched against all incoming requests.

let route = new Route('api');

If the server is running locally, the route will be accessible from the url http://localhost/api. All incoming requests will be split on the first / (after the hostname), and a matching route will be searched for within that route.

let r1 = new Route('user');
let r2 = new Route('company');

When the server receives a HTTP-request: [hostname]/user, the r1 route will search for a match from one of its registered patterns, and ignore r2. This would prevent the HTTP-server from searching all the defined routes in both r1 and r2.

Add Url-Patterns

Requests against the HTTP-server can be done using these common HTTP-methods:

  • GET
  • POST
  • DELETE
  • PUT

What method to use when is really up to you, but you should however try to follow the convention. For a simple guide, visit restapitutorial.com

The format of each function is defined as following:

requestUrl: string, func: (req: http.IncomingMessage, res: http.ServerResponse, ...params: any[]) => void

requestUrl is the pattern which the route should match against.

Parameters are defined using the : notation infront of a keyword.

func is the function to call when a HTTP-request url matches the defined pattern in requestUrl. When a route matches, the first two parameters will always be:

  • req - The actual request made by a user, see the documentation on nodejs.org
  • res - The response to send to the user, see the documentation on nodejs.org

An undefined number of parameters is passed to func depending on the defined pattern in requestUrl. If no parameters is defined in requestUrl, only the first two parameters will be set.

route.get('api/:id', function(req, res, id) {
    // HTTP/GET
    // Variable id corresponds to data passed in :id of the requestUrl, e.g. api/1 => id = 1
    ...
    
});
 
route.get('api/name/:name', function(req, res, name) {
    // HTTP/GET
    // api/name/hello => name = hello
    ...
});
 
route.get('user/:id', function(req, res, id, optional) {
    // HTTP/GET
    // user/1 => id = 1
    ...
});
 
route.get('hello/world', function(req, res) {
 
});
 

route.post(...) , route.delete(...), route.put(...) can be replaced with route.get(...) in the provided example.

Sub Routing

Sub-routing is a way of combining or creating nested routes, and is possible through

add(path: string, route: Route): void
let route = new Route();
let userRoute = new Route();
let baseRoute = new Route();
 
route.add('/user', user);
route.add('/base', base);

Here, the userRoute will be accessible through /user, and baseRoute through base/

NOTE: If both route and baseRoute has a set of colliding route-patterns, only the parent-route will be matched, in this case route.

Middleware

Each Route can run a middleware before a matched route is called. As opposed to a server middleware, which is run on each request, a route middleware is only run for a single route. Use-cases could be to lock-down certain routes for authentication.

use(middlewareIMiddleware)void

More information about middlewares, and the different types can be found in the middleware section below

Middlewares

A Middleware is a set of instructions to run before a matching route is searched for. Middlewares are used for altering the HTTP-request and HTTP-response objects before they are passed to a matching route function.

Multiple middlewares are included in this package:

CORS

Enables cross-origin requests on the server.

...
let cors = new Cors({...}); // See documentatino for options below
server.use(cors);
Options
  • cookies: boolean
    • default = false
    • Whether the allow-credentials header should be set, supporting the use of cookies with CORS (Access-Control-Allow-Credentials)
  • methods: HTTP_METHODS[]
    • default = null => uses HTTP-method used in HTTP-request
    • Supported http-methods for a route, defaults to current request method (Access-Control-Request-Method)
  • maxage: number
    • default = 1
    • How many seconds a response to be cached for CORS (Access-Control-Max-Age)

The CORS middleware are implemented following the guide(s) found at enable-cors.org

BodyJsonParse

Parsing of body-data to json, using querystring or JSON.parse.

...
server.use(new BodyJsonParse());

This middleware exposes a .body variable as an object type to the http.ServerResponse parameter.

function(request, response) {
    let bodyData:{} = request.body;
    
    response.write('post id: ' + bodyData.id);
}

JsonResponse

Generates a json response from an input js object. Exposes a method json() to the response object, and automatically ends the response.

function(request, response) {
    response.json({
        id: 12,
        description: "lorem ipsum"
    });
}

HTML response:

{
    "id": 12,
    "description": "lorem ipsum"
}

Custom

Each middleware is implementing the IMiddleware interface, which requires the alter(...) method to be implemented.

alter(request: http.IncomingMessage, response: http.ServerResponse): boolean;

All middlewares is required to return a boolean value. If a middleware is returning false, the request will stop executing.

import {IMiddleware} from 'fuhttp';
 
class MyCustomMiddleware implements IMiddleware {
    public alter(req, res): boolean {
        ... // Do some tweaks on the req / res objects here
        return true; // return false if you want to stop execution
    }
}

Examples

Multiple examples are found in the examples/ folder.

var fuhttp=require('fuhttp-ts'); // Load package
 
var myRoute = new fuhttp.Route();
 
myRoute.get("hello/:name", function(req, res, name) {
    res.write("Hello, your name is " + name);
    res.end();
});
 
var server = new fuhttp.Server(5000); // Defined the port number which the http-server should accept connections
 
server.add("api", myRoute); // Add the myRoute for parsing request URIs and call appropriate route
server.listen(); // Start listening for incoming requests

TODOs

  • Support for connect middlewares
    • As the connect middlewares expectes an input parameter next() this idea was scrapped, as I'm not a fan of this design structure
  • Test middlewares?
  • Performance testing, and comparison of other libraries

Release History

See the changelog

License

© Fosen-Utvikling AS, 2016 - 2018. Licensed under a MIT license

Readme

Keywords

Package Sidebar

Install

npm i fuhttp-ts

Weekly Downloads

1

Version

1.2.1

License

MIT

Unpacked Size

172 kB

Total Files

42

Last publish

Collaborators

  • jonaskirkemyr