Releases: moleculerjs/moleculer-web
v0.9.1
v0.9.0
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 tothis.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.
v0.8.5
v0.8.4
v0.8.3
0.8.2
New authenticate
method.
This authenticate
method is similar to authorize
. You have access to req
, res
and route
objects and you can authenticate the user from the request.
The returned data is saved to the ctx.meta.user
. To enable this logic set authentication: true
in route options.
Example
module.exports = {
name: "api",
mixins: [ApiGatewayService],
settings: {
routes: [
{
// Enable authentication
authentication: true
}
]
},
methods: {
authenticate(ctx, route, req, res) {
let accessToken = req.query["access_token"];
if (accessToken) {
if (accessToken === "12345") {
return Promise.resolve({ id: 1, username: "john.doe", name: "John Doe" });
} else {
return Promise.reject();
}
} else {
return Promise.resolve(null);
}
}
}
});
Changes
- update dependencies.
- added
.npmignore
v0.8.1
v0.8.0
Breaking changes
The onAfterCall
hook has changed
In previous versions of Moleculer Web, you couldn't manipulate the data
in onAfterCall
. Now you can, but you must always return the new or original data
.
Modify only headers
broker.createService(ApiGatewayService, {
settings: {
routes: [{
onAfterCall(ctx, route, req, res, data) {
res.setHeader("X-Custom-Header", "123456");
// Must return the original `data`
return data;
}
}]
}
});
Modify (wrap) the original data
broker.createService(ApiGatewayService, {
settings: {
routes: [{
onAfterCall(ctx, route, req, res, data) {
// Wrap the original data to a new object
return {
other: "things",
data: data
};
}
}]
}
});
Custom alias hooks
The onBeforeCall
and authorize
hooks are called before custom alias functions too.
And you have access to Context as req.$ctx
or res.$ctx
New
Response header data from ctx.meta
Since Moleculer v0.12, you can use ctx.meta
to send back response headers to the Moleculer Web.
The old method is deprecated but works.
Available meta fields:
ctx.meta.$statusCode
- setres.statusCode
.ctx.meta.$statusMessage
- setres.statusMessage
.ctx.meta.$responseType
- setContent-Type
in header.ctx.meta.$responseHeaders
- set all keys in header.ctx.meta.$location
- setLocation
key in header for redirects.
Old method
module.exports = {
name: "export",
actions: {
downloadCSV:
responseType: "text/csv",
responseHeaders: {
"Content-Disposition": "attachment; filename=\"data.csv\"",
},
handler() {
return "...";
}
}
}
}
New method
module.exports = {
name: "export",
actions: {
// Download a file in the browser
downloadCSV(ctx) {
ctx.meta.$responseType = "text/csv";
ctx.meta.$responseHeaders = {
"Content-Disposition": `attachment; filename="data-${ctx.params.id}.csv"`
};
return "...";
}
// Redirect the request
redirectSample(ctx) {
ctx.meta.$statusCode = 302;
ctx.meta.$location = "/test/hello";
}
}
}
Support array & nested objects in query
Thanks for @hwuethrich, Moleculer Web supports arrays & nested objects in querystring.
GET /api/opt-test?a=1&a=2
a: ["1", "2"]
GET /api/opt-test?foo[bar]=a&foo[bar]=b&foo[baz]=c
foo: {
bar: ["a", "b"],
baz: "c"
}
Support error-handler middlewares
There is support to use error-handler middlewares in the API Gateway. So if you pass an Error
to the next(err)
function, it will call error handler middlewares which have signature as (err, req, res, next)
.
broker.createService({
mixins: [ApiService],
settings: {
// Global middlewares. Applied to all routes.
use: [
cookieParser(),
helmet()
],
routes: [
{
path: "/",
// Route-level middlewares.
use: [
compression(),
passport.initialize(),
passport.session(),
function(err, req, res, next) {
this.logger.error("Error is occured in middlewares!");
this.sendError(req, res, err);
}
],
Changes
preValidate
has been removed.- fix multiple CORS origin handling. Thanks for @felipegcampos
- if
X-Correlation-Id
is in the request header, it is used asrequestID
inContext
. - types in errors have been changed (removed
ERR_
prefix) path-to-regexp
is updated to v2.x.x