Skip to content

v0.9.0

Compare
Choose a tag to compare
@icebob icebob released this 12 Feb 09:08
· 221 commits to master since this release

Breaking changes

Drop Node 6 & 8 support

Due to Node 6 & 8 LTS end of life, the minimum Node version is 10.

Changed mappingPolicy default value

In the previous version the mappingPolicy default value was all which means, you can call
any services via API Gateway which accepted by whitelist. This setting is not too secure.
From this version, the default value is restrict if at least one alias is defined in route options.
If there are not aliases & mappingPolicy defined, the behaviour will be the old one.

Use server property instead of middleware

We have removed the middleware service setting because it was not straightforward. Therefore, we have created a new server setting.
If server: true (which is the default value), API Gateway will create a HTTP(s) server. If server: false, it won't create a HTTP server, so you can use API Gateway as an Express middleware.

Migration guide

Before

const ApiGateway = require("moleculer-web");

module.exports = {
    mixins: [ApiGateway],
    settings: {
        middleware: true
    }
}

After

const ApiGateway = require("moleculer-web");

module.exports = {
    mixins: [ApiGateway],
    settings: {
        server: false
    }
}

Other low-level breaking changes

  • sendResponse signature is changed to this.sendResponse(req, res, data)

New

File upload aliases

API Gateway has implemented file uploads. You can upload files as a multipart form data (thanks for busboy library) or as a raw request body. In both cases, the file is transferred to an action as a Stream. In multipart form data mode you can upload multiple files, as well.

Please note, you have to disable other body parsers in order to accept files.

Example

const ApiGateway = require("moleculer-web");

module.exports = {
    mixins: [ApiGateway],
    settings: {
        path: "/upload",

        routes: [
            {
                path: "",

                // You should disable body parsers
                bodyParsers: {
                    json: false,
                    urlencoded: false
                },

                aliases: {
                    // File upload from HTML multipart form
                    "POST /": "multipart:file.save",
                    
                    // File upload from AJAX or cURL
                    "PUT /": "stream:file.save",

                    // File upload from HTML form and overwrite busboy config
                    "POST /multi": {
                        type: "multipart",
                        // Action level busboy config
                        busboyConfig: {
                            limits: {
                                files: 3
                            }
                        },
                        action: "file.save"
                    }
                },

                // Route level busboy config.
                // More info: https://github.com/mscdex/busboy#busboy-methods
                busboyConfig: {
                    limits: {
                        files: 1
                    }
                    // Can be defined limit event handlers
                    // `onPartsLimit`, `onFilesLimit` or `onFieldsLimit`
                },

                mappingPolicy: "restrict"
            }
        ]
    }
});

HTTP2 server

HTTP2 experimental server has been implemented into API Gateway. You can turn it on with http2: true service setting.

Example

const ApiGateway = require("moleculer-web");

module.exports = {
    mixins: [ApiGateway],
    settings: {
        port: 8443,

        // HTTPS server with certificate
        https: {
            key: fs.readFileSync("key.pem"),
            cert: fs.readFileSync("cert.pem")
        },

        // Use HTTP2 server
        http2: true
    }
});

Dynamic routing

The this.addRoute(opts, toBottom = true) new service method is added to add/replace routes. You can call it from your mixins to define new routes (e.g. swagger route, graphql route...etc).
The function detects that the route is defined early. In this case, it will replace the previous route configuration with the new one.

To remove a route, use the this.removeRoute("/admin") method. It removes the route by path.

ETag supporting

Thank to tiaod for ETag implementation. PR: #92

Example

const ApiGateway = require("moleculer-web");

module.exports = {
    mixins: [ApiGateway],
    settings: {
        // Service-level option
        etag: false,

        routes: [
            {
                path: "/",

                // Route-level option.
                etag: true
            }
        ]
    }
}

The etag option value can be false, true, weak, strong, or a custom Function.

Custom ETag generator function

module.exports = {
    mixins: [ApiGateway],
    settings: {
        // Service-level option
        etag: (body) => generateHash(body)
    }
}

Please note, it doesn't work with stream responses. In this case, you should generate the etag by yourself.

Example

module.exports = {
    name: "export",
    actions: {
        // Download response as a file in the browser
        downloadCSV(ctx) {
            ctx.meta.$responseType = "text/csv";
            ctx.meta.$responseHeaders = {
                "Content-Disposition": `attachment; filename="data-${ctx.params.id}.csv"`,
                "ETag": '<your etag here>'
            };
            return csvFileStream;
        }
    }
}

Auto-aliasing feature

The auto-aliasing means you don't have to add all service aliases to the routes, the Gateway can generate it from service schema. If a new service is entered or leaved, Gateway regenerate aliases.

To configure which services are used in route use the whitelist.

Example

// api.service.js
module.exports = {
    mixins: [ApiGateway],

    settings: {
        routes: [
            {
                path: "/api",

                whitelist: [
                    "posts.*",
                    "test.*"
                ],

                aliases: {
                    "GET /hi": "test.hello"
                },

                autoAliases: true
            }
        ]
    }
};
// posts.service.js
module.exports = {
    name: "posts",
    version: 2,

    settings: {
        // Base path
        rest: "posts/"
    },

    actions: {
        list: {
            // Expose as "/api/v2/posts/"
            rest: "GET /",
            handler(ctx) {}
        },

        get: {
            // Expose as "/api/v2/posts/:id"
            rest: "GET /:id",
            handler(ctx) {}
        },

        create: {
            rest: "POST /",
            handler(ctx) {}
        },

        update: {
            rest: "PUT /:id",
            handler(ctx) {}
        },

        remove: {
            rest: "DELETE /:id",
            handler(ctx) {}
        }
    }
};

The generated aliases

   GET /api/hi             => test.hello
   GET /api/v2/posts       => v2.posts.list
   GET /api/v2/posts/:id   => v2.posts.get
  POST /api/v2/posts       => v2.posts.create
   PUT /api/v2/posts/:id   => v2.posts.update
DELETE /api/v2/posts/:id   => v2.posts.remove

Example to define full path alias

// posts.service.js
module.exports = {
    name: "posts",
    version: 2,

    settings: {
        // Base path
        rest: "posts/"
    },

    actions: {
        tags: {
            // Expose as "/tags/" instead of "/api/v2/posts/tags"
            rest: {
                method: "GET",
                fullPath: "/tags"
            },
            handler(ctx) {}
        }
    }
};

Changes

  • new optimizeOrder: true setting in order to optimize route & alias paths (deeper first). Default: true.
  • new logging route option to disable request logging. It can be useful for health check routes. Default: true.
  • tilde (~) replace issue fixed. #98
  • throw 503 - ServiceUnavailableError when a service defined in aliases but not available. Ref: #27
  • new internalServiceSpecialChar service setting to override special char for internal services (~)
  • new httpServerTimeout setting to overwrite the default HTTP server timeout. #126
  • add reformatError method to change the response error object (remove or add fields, localize error message...etc).
  • new listAliases action to get all registered route aliases.
  • remove bluebird dependency & using native Promise & async/await.