A tiny wrapper around got HTTP client that allows Moleculer services to communicate with REST APIs. Why got? Because reasons.
- Make HTTP requests from Actions and Events
- Stream data
- Cache data
- Customizable log messages and errors
npm install moleculer-http-client --save
Use httpClient
field to configure your got client.
module.exports = {
name: "http",
/**
* Moleculer settings
*/
settings: {
// HTTP client settings
httpClient: {
// Boolean value indicating whether request should be logged or not
logging: true,
// Log request function
logOutgoingRequest: logOutgoingRequest,
// Log response function
logIncomingResponse: logIncomingResponse,
// Format the Response
responseFormatter: "body", // one of "body", "headers", "status", "raw"
// Format the Errors
errorFormatter: errorFormatter,
// Got Client options
defaultOptions: {
// Put here any Got available option that can be used to extend Got client
}
}
},
};
HTTP GET action
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
Type: Promise
, Stream
HTTP POST action
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Note: When streaming use
ctx.meta
to passurl
andopt
andctx.params
to pass stream data
Type: Promise
HTTP PUT action
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Note: When streaming use
ctx.meta
to passurl
andopt
andctx.params
to pass stream data
Type: Promise
HTTP PATCH action
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Note: When streaming use
ctx.meta
to passurl
andopt
andctx.params
to pass stream data
Type: Promise
HTTP DELETE action
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Note: When streaming use
ctx.meta
to passurl
andopt
andctx.params
to pass stream data
Type: Promise
HTTP GET method
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
Type: Promise
, Stream
HTTP POST method
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Type: Promise
HTTP PUT method
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Type: Promise
HTTP PATCH method
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
streamPayload |
Stream |
optional | Stream payload |
Type: Promise
HTTP DELETE method
Property | Type | Default | Description |
---|---|---|---|
url |
String |
required | URL |
opt |
Object |
optional | Request options |
Type: Promise
Action Example
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
// Create broker
let broker = new ServiceBroker();
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService]
});
// Start the broker
broker.start().then(() => {
broker
// Make a HTTP GET request
.call("http.get", {
url: "https://httpbin.org/json",
opt: { responseType: "json" }
})
.then(res => broker.logger.info(res))
.catch(error => broker.logger.error(error));
});
Result
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/json"
INFO http-client/HTTP: <= HTTP GET to "https://httpbin.org/json" returned with status code 200
INFO http-client/BROKER: { slideshow: { author: 'Yours Truly', date: 'date of publication', slides: [ [Object], [Object] ], title: 'Sample Slide Show' } }
Event Example
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
// Create broker
let broker = new ServiceBroker();
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService],
events: {
// Register an event listener
async "some.Event"(request) {
this.logger.info("Caught an Event");
// Use service method to make a request
const res = await this._get(request.url, request.opt);
this.logger.info("Printing Payload");
// Print the response data
this.logger.info(res.body);
}
}
});
// Start the broker
broker.start().then(() => {
broker
// Emit some event
.emit("some.Event", {
url: "https://httpbin.org/json",
opt: { responseType: "json" }
});
});
Result
INFO http-client/HTTP: Caught an Event
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/json"
INFO http-client/HTTP: <= HTTP GET to "https://httpbin.org/json" returned with status code 200
INFO http-client/HTTP: Printing Payload
INFO http-client/HTTP: { slideshow: { author: 'Yours Truly', date: 'date of publication', slides: [ [Object], [Object] ], title: 'Sample Slide Show' } }
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
const fs = require("fs");
// Create broker
let broker = new ServiceBroker({
nodeID: "http-client"
});
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService]
});
// Start the broker
broker.start().then(() => {
broker
// Make a HTTP GET request
.call("http.get", {
url: "https://sindresorhus.com/",
opt: { isStream: true }
})
.then(res => {
const filePath = "./examples/stream-get/file.md";
res.pipe(fs.createWriteStream(filePath, { encoding: "utf8" }));
res.on("response", response => {
broker.logger.info(response.statusCode);
});
})
.catch(error => broker.logger.error(error));
});
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
const ApiGateway = require("moleculer-web");
const fs = require("fs");
// Create broker
let broker = new ServiceBroker({
nodeID: "http-client"
});
// Create a HTTP Client Service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService]
});
// Create HTTP Server Services
broker.createService({
name: "api",
mixins: [ApiGateway],
settings: {
port: 4000,
routes: [
{
path: "/stream",
bodyParsers: { json: false, urlencoded: false },
aliases: { "POST /": "stream:api.postStream" }
}
]
},
actions: {
postStream(ctx) {
return new Promise((resolve, reject) => {
const stream = fs.createWriteStream(
"./examples/stream-post/stored-data.md"
);
stream.on("close", async () => {
resolve({ fileName: `file.md`, method: "POST" });
});
// Return error to the user
stream.on("error", err => {
reject(err);
});
// Pipe the data
ctx.params.pipe(stream);
});
}
}
});
// Start the broker
broker.start().then(() => {
const streamFile = "./examples/stream-post/data-to-stream.md";
const stream = fs.createReadStream(streamFile, { encoding: "utf8" });
// Pass stream as ctx.params
// Pass URL and options in ctx.meta
const req = broker.call("http.post", stream, {
meta: { url: "http://localhost:4000/stream", isStream: true }
});
req.then(res => {
broker.logger.info(res.statusCode);
});
});
If you are using actions to make HTTP requests then you can use Moleculer's cache to cache responses.
Please note that when using Moleculer's cache you will be ignoring
Cache-Control
header field. If you care aboutCache-Control
then you should use Got's cache.
Example of Moleculer Cache
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
// Create broker
let broker = new ServiceBroker({
nodeID: "http-client",
// Enable Moleculer Cache
cacher: "Memory"
});
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService],
actions: {
get: {
// Enable cache for GET action
// More info: https://moleculer.services/docs/0.13/caching.html
cache: true
}
}
});
// Start the broker
broker.start().then(() => {
broker
// Make a HTTP GET request
.call("http.get", {
url: "https://httpbin.org/json",
opt: { responseType: "json" }
})
.then(res => broker.logger.info(res.body))
.then(() =>
broker.call("http.get", {
url: "https://httpbin.org/json",
opt: { responseType: "json" }
})
)
.then(res => broker.logger.info(res.body))
.catch(error => broker.logger.error(error));
});
Result
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/json"
INFO http-client/HTTP: <= HTTP GET to "https://httpbin.org/json" returned with status code 200
Request -> INFO http-client/BROKER: { slideshow: { author: 'Yours Truly', date: 'date of publication', slides: [ [Object], [Object] ], title: 'Sample Slide Show' } }
Cache -> INFO http-client/BROKER: { slideshow: { author: 'Yours Truly', date: 'date of publication', slides: [ [Object], [Object] ], title: 'Sample Slide Show' } }
If you are using methods or you care about Cache-Control
header option then you should use Got's cache.
Example of Got cache
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
// Using JS Map as cache
const cacheMap = new Map();
// Create broker
let broker = new ServiceBroker({
nodeID: "http-client"
});
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService],
settings: {
httpClient: {
defaultOptions: {
// Set Got's built-in cache
// More info: https://www.npmjs.com/package/got#cache-1
cache: cacheMap
}
}
}
});
// Start the broker
broker.start().then(() => {
broker
// Make a HTTP GET request
.call("http.get", {
url: "https://httpbin.org/cache/150",
opt: { responseType: "json" }
})
.then(res => broker.logger.info(res.isFromCache))
.then(() =>
broker.call("http.get", {
url: "https://httpbin.org/cache/150",
opt: { responseType: "json" }
})
)
.then(res => broker.logger.info(res.isFromCache))
.catch(error => broker.logger.error(error));
});
Result
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/cache/150"
INFO http-client/HTTP: <= HTTP GET to "https://httpbin.org/cache/150" returned with status code 200
INFO http-client/BROKER: false
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/cache/150"
INFO http-client/HTTP: **CACHED** HTTP GET to "https://httpbin.org/cache/150" returned with status code 200
INFO http-client/BROKER: true
If you need to do some fancy request (e.g., HEAD
, TRACE
, OPTIONS
) you can directly call the got client available at _client
.
const { ServiceBroker } = require("moleculer");
const HTTPClientService = require("moleculer-http-client");
// Create broker
let broker = new ServiceBroker({
nodeID: "http-client"
});
// Create a service
broker.createService({
name: "http",
// Load HTTP Client Service
mixins: [HTTPClientService],
actions: {
async fancyRequest(ctx) {
try {
// Direct call to Got Client
// Can be any Got supported HTTP Method
return await this._client(ctx.params.url, ctx.params.opt);
} catch (error) {
throw error;
}
}
}
});
// Start the broker
broker.start().then(() => {
broker
// Make a fancy request
.call("http.fancyRequest", {
url: "https://httpbin.org/json",
opt: { method: "GET", responseType: "json" }
})
.then(res => broker.logger.info(res.body))
.catch(error => broker.logger.error(error));
});
INFO http-client/HTTP: => HTTP GET to "https://httpbin.org/json"
INFO http-client/HTTP: <= HTTP GET to "https://httpbin.org/json" returned with status code 200
INFO http-client/BROKER: { slideshow: { author: 'Yours Truly', date: 'date of publication', slides: [ [Object], [Object] ], title: 'Sample Slide Show' } }
const service = broker.createService({
name: "http",
mixins: [MoleculerHTTP],
settings: {
httpClient: {
// Input is Got's options object. More info: https://www.npmjs.com/package/got#options
logOutgoingRequest: options => {
console.log(`-----> Request ${options.href}`);
},
// Input is Got's response object: More info: https://www.npmjs.com/package/got#response
logIncomingResponse: response => {
console.log(`<----- Response Status Code ${response.statusCode}`);
}
}
}
});
const service = broker.createService({
name: "http",
mixins: [MoleculerHTTP],
settings: {
httpClient: {
// Custom error handler function
// Input error is Got's error. More info: https://www.npmjs.com/package/got#errors
errorFormatter: error => {
return new Error("Custom Error");
}
}
}
});
$ npm test
Please send pull requests improving the usage and fixing bugs, improving documentation and providing better examples.
The project is available under the MIT license.
Copyright (c) 2016-2020 MoleculerJS