Have ideas to improve npm?Join in the discussion! »

    @amphibian/server

    2.2.0 • Public • Published

    @amphibian/server

    build status coverage report

    A bare bones node.js server based on Koa that makes setup and maintaining a breeze.

    import createServer from '@amphibian/server';
     
    const server = createServer();
     
    async function helloWorldHandler(context, next) {
        context.status = 200;
        context.body = {
            message: 'Hello World!',
            something: await operation()
        };
    }
     
    server.registerRouteHandler(helloWorldHandler, {
        method: 'get',
        path: '/hello-world'
    });

    Handlers

    There are two methods for setting up handlers: registerHandler and registerRouteHandler. The first method, registerHandler, does not bind to any specific route and method, while the second, registerRouteHandler, does.

    You will have to call await next() yourself if the handler should be skipped.

    registerHandler

    function normalHandler(context, next) {
        if (context.method.toLowerCase() === 'get') {
            context.body = {message: 'It works!'};
            return;
        }
     
        await next();
    }
     
    server.registerHandler(normalHandler);

    registerRouteHandler

    Takes three arguments: name, handler and options. name can be omitted. options takes two properties: method and path.

    registerRouteHandler takes care of calling await next() for you.

    function routeHandler(context) {
        context.body = {message: 'It works!'}
    }
     
    server.registerRouteHandler(routeHandler, {
        method: 'get',
        path: '/hello'
    });

    Middleware

    Creating middleware is as easy as it gets. Just call next, the second handler argument, to pass the request onto the next handler in the list.

    async function logTimeMiddleware(context, next) {
        console.time('request')
        await next();
        console.timeEnd('request');
    }
     
    server.registerMiddleware(logTimeMiddleware);

    Note that registerMiddleware is exactly the same function as registerHandler – using it is syntactic sugar that logs “Registered middleware” instead of “Registered handler”.

    Advanced routing

    For reference, the provided route (whether it's a RegExp or a String) is placed in context.routePath. This might be useful for measuring the performance of each route.

    async function logHandler(context) {
        console.log(context.routePath); // > /^\/message\/([^\/]+)$/
    }
     
    server.registerRouteHandler(logHandler, {
        method: 'get',
        path: /^\/message\/([^\/]+)$/
    });

    String with parameters

    Provide path as a String containing parameters prefixed by :.

    async function advancedStringHandler(context) {
        const {yourMessage} = context.args;
        context.status = 200;
        context.body = {message: `Your message is: ${yourMessage}`};
    }
     
    server.registerRouteHandler(advancedStringHandler, {
        method: 'get',
        path: '/message/:yourMessage'
    });

    Path parameter matches are placed as property of the context.args Object.

    RegExp

    To take advantage of RegExp routes using the built in router utility, simply send a regular expression instead of a String in the path key of the options object:

    async function advancedRegExpHandler(context) {
        const [message] = context.args;
        context.status = 200;
        context.body = {message: `Your message is: ${message}`};
    }
     
    server.registerRouteHandler(advancedRegExpHandler, {
        method: 'get',
        path: /^\/message\/([^\/]+)$/
    });

    Any RegExp matches are placed as an Array in context.args.

    Options object

    An options object can be passed as an argument in createServer.

    options.port (number)

    Sets the port the server will listen on. Default: 4000

    options.logging (boolean|'errors')

    Enables or disables logging. Set to String errors to only log errors. Default: true

    options.listen (boolean)

    Useful when using @amphibian/server in conjunction with socket.io. Read more here. Default: true

    Error handling

    @amphibian/server will automatically handle errors by serving an error object to the body. The error object has two properties: code and message.

    {
        "error": {
            "code": "unknown_error",
            "message": "Something went wrong"
        }
    }

    error.code will default to unknown_error if no code is set on the Error instance. The response.status will default to 500 unless the Error instance has a status property. You can safely throw errors from a handler:

    async function throwingHandler(context) {
        const error = new Error('Something went wrong');
        error.status = 400;
        error.code = 'my_error_code';
     
        throw error;
    }
     
    server.registerRouteHandler(throwingHandler, {
        method: 'get',
        path: '/error'
    });

    Navigating to /error will yield the following response.body, with 400 as response.status:

    {
        "error": {
            "code": "my_error_code",
            "message": "Something went wrong"
        }
    }

    Error boundaries

    To do handle the errors before @amphibian/server spits them out, set up an error boundary:

    async function errorBoundary(context, next) {
        try {
            await next();
        } catch (error) {
            if (error.code === 'fatal_error') {
                console.log(error.stack);
                throw new Error('camouflaged error');
            }
     
            throw error;
        }
    }
     
    server.registerMiddleware(errorBoundary);

    Ensure the errorBoundary is the first middleware/handler you register. You won't be able to catch errors from those that come before it.

    Prevent logging an error

    To prevent @amphiban/server from logging an error, give the error a log property set to false.

    const error = new Error('some_error');
    error.log = false;
    throw error;

    Graceful shutdown

    To avoid dropping active requests when receiving a SIGTERM signal, you should implement a SIGTERM signal handler on the node process. This will allow the server to finish active requests before shutting down.

    The @amphibian/server server Object has a Function close that returns a Promise:

    import createServer from '@amphibian/server';
     
    const server = createServer();
     
    process.on('SIGTERM', async () => {
        await server.close();
        process.exit();
    });

    New server requests that are received during shutdown will be refused as per net.server.close().

    Usage with socket.io (as a koa callback)

    import http from 'http';
    import io from 'socket.io';
    import createServer from '@amphibian/server';
     
    const server = createServer({listen: false});
    const httpServer = http.createServer(app.callback());
    const socket = io(httpServer);
     
    io.on('connection', /* ... */);
    httpServer.listen(3000);

    Keywords

    none

    Install

    npm i @amphibian/server

    DownloadsWeekly Downloads

    61

    Version

    2.2.0

    License

    ISC

    Unpacked Size

    200 kB

    Total Files

    12

    Last publish

    Collaborators

    • avatar