Skip to content

sanchezzzhak/node-moleculer-web

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

39 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

node-moleculer-web

NPM Version License

High-performance web server integration for Moleculer based on uWebSockets.js.

πŸ“¦ Features

  • Built on top of ultra-fast uWebSockets.js.
  • Fully compatible with Moleculer services.
  • Support for controllers and middleware.
  • Request and response helpers.
  • Cookie and body parsing.
  • Easy redirect handling.
  • TypeScript support via index.d.ts.

πŸš€ Installation

npm install node-moleculer-web

Uses

1 Create controller in folder controllers/home.js

const {AbstractController} = require('node-moleculer-web');
class HomeController extends AbstractController {
  async index() {
    return `helo world`;
  }
}
module.exports = HomeController

2 Create service in folder services/app.service.js

const {UwsServer} = require('node-moleculer-web');
const {Service} = require('moleculer');

const HomeController = require('../controllers/home');
  
class AppService extends Service {
  constructor(broker) {
    super(broker);
    this.parseServiceSchema({
      name: 'app',
      settings: {
          // base port for server
          port: process.evn.SERVER_PORT ?? 3101,
          // on what ip to listen
          ip: process.evn.SERVER_IP ?? '127.0.0.1',
          portSchema: process.evn.SERVER_SCHEMA ?? 'node',         
          // if statics are not needed, just remove next parameters  
          publicDir: __dirname + '/../public',
          publicIndex: 'index.html', // or false
          staticCompress: true,      // support compresion gzip, br, deflate
          staticLastModified: true,  // send last modified header for static files
          // list of controllers
          controllers: {
             home: HomeController
          }
      },
      // added mixin UwsServer to current services
      mixins: [
          UwsServer
      ],
      created: this.createService
    })
  }

  createService() {
    // register routing where home is the controller and index is the method	
    this.createRoute('get / #c:home.index')
  }
}
module.exports = AppService

Router path

  • <request type> / #c:<controller name>.<action>
  • <request type> / #s:<service name>.<action>
  • allowed request types:
  • any - HTTP ALL
  • connect - HTTP CONNECT
  • del - HTTP DELETE
  • get - HTTP GET
  • head - HTTP HEAD
  • options - HTTP OPTIONS
  • patch - HTTP PATCH
  • post - HTTP POST
  • put - HTTP PUT
  • trace - HTTP TRACE

Router example

  • optional id param
  • <request type> /articles/:id? #c:article.actionIndex
  • regex id param
  • <request type> /articles/:id(\d+) #c:article.actionView

get id param value in controller

  this.req.getParameter('id') // or  this.req.getParameter('0')
  // or
  // this.inidReuqstData();
  // this.requestData.parameters.id

Router Options

  • cache - second http cache
  • onBefore(route, req, res) - Function before call for controller or service
  • onAfter(route, req, res, data) - Function after call for controller or service

Example options for createRoute

this.createRoute('get / #c:home.index', {cache: 5});

πŸ“Ά AbstractController properties:

property description
requestData read request data
cookieData read/write cookie
redirectType "header" | "meta" | "js" (default meta)
format default response content type default html
statusCode default response http code number 200
statusCodeText default response http code string 200 OK
broker link ServiceBroker molecular.js
req link HttpRequest uWebsocket.js
res link HttpResponse uWebsocket.js

requestData or cookieData (The property objects are available after executing the this.initRequest() method inside the controller method)

πŸ•’ Performance Tracking

  • .timer
    • An instance of Timer used to measure request processing duration.
    • .start() β€” Starts the timer.
    • .stop() β€” Stops the timer. and Returns elapsed time in milliseconds.

AbstractController methods:

πŸ” JWT Methods:

  • .initJWT(key, iat = false)
    • Initializes the JWT utility with a secret key and whether to include issued-at time (iat).
  • .getJWT()
    • Returns the initialized JWT instance. Throws an error if not initialized.
  • .createJwtToken(payload = {})
    • Creates a JWT token from the provided payload.
  • .extractJwtToken(token)
    • Extracts and returns the decoded payload from a given JWT token.

πŸ“₯ Request Handling:

  • .initRequest()
    • Initializes requestData and cookieData objects for accessing request data and cookies. Also sets client-hints headers if enabled.
  • .readBody()
    • Reads the request body asynchronously. Returns a Promise.

πŸ“€ Response Methods:

  • .asJson(obj, httpCode = 200)

    • Sends a JSON response with optional HTTP status code.
  • .renderRaw({ view, httpCode, format })

    • Renders raw content (string or HTML) with proper MIME type and HTTP status.
  • .render({ template, params, httpCode, format })

    • Renders an EJS template with given parameters and sends it as a response.
  • .setStatus(httpCode)

    • Sets the HTTP status code and its text representation.
  • .writeHeader(key, value)

    • Writes a custom header to the response.
  • .setCorsHeaders()

    • Sets CORS-related headers for cross-origin requests.

πŸ” Redirect Methods:

  • .redirect(location, httpCode = 301)
    • Performs a redirect using one of the following strategies:
    • You can set the strategy in the redirectType property of the controller.
    • REDIRECT_TYPE_META: Meta refresh redirect (HTML-based).
    • REDIRECT_TYPE_JS: JavaScript-based redirect.
    • REDIRECT_TYPE_HEADER: Standard HTTP Location header redirect.

Example Controllers

response json object

class Home extends AbstractController {
  async index() {
   return this.asJson({}, 200);
  }
}

response redirect to other url

class Home extends AbstractController {
  async index() {
    return this.redirect('https://youdomain.dev', 301 /* optional*/ , "meta" /* optional*/ );
  }
}

response ejs template

class Home extends AbstractController {
  async index() {
    return this.render({
      template, params, httpCode: 200, format: 'html'
    });
  }
}

response raw

class Home extends AbstractController {
  async index() {
    return this.renderRaw({view: 'string', httpCode: 200, format: 'html'});
  }
}

or

class Home extends AbstractController {
  async index() {
    return 'Hello World'
  }
}

πŸͺ Cookie Usage Example β€” Reading and Writing Cookies

const { AbstractController } = require('node-moleculer-web');

module.exports = class Home extends AbstractController {
  /**
   * Initialize request data and cookie handler
   */
  async index() {
    // Initialize requestData and cookieData
    this.initRequest();
    // πŸ” Read a cookie value, with fallback
    const cookieValue = this.cookieData.get('my_cookievalue', String(Date.now() /* or 1*new Date()  */   ));
    // ✍️ Set/update the cookie with a new value
    this.cookieData.set('my_cookievalue', cookieValue);
    // πŸ“€ Return current cookie value as response
    return `Current cookie value: ${cookieValue}`;
  }
};

πŸ“₯ Get Request Data Example

const { AbstractController } = require('node-moleculer-web');

module.exports = class Home extends AbstractController {
  /**
   * Handle GET request and return parsed request data
   */
  async index() {
    // Initialize request and cookie utilities
    this.initRequest();
    // Extract request data
    const headers = this.requestData.headers;       // All request headers
    const ip = this.requestData.ip;                 // Client IP address
    const query = this.requestData.query ?? {};     // Query parameters
    const referer = this.requestData.referer;       // Referrer URL
    const currentUrl = this.requestData.url;        // Current request URL
    const userAgent = this.requestData.userAgent;   // User-Agent string
  
    // Return all data as JSON response
    return this.asJson({
      headers,
      ip,
      query,
      referer,
      currentUrl,
      userAgent
    }, 200);
  }
};

πŸ”„ Call Another Microservice from Controller

const { AbstractController } = require('node-moleculer-web');

module.exports = class Home extends AbstractController {
  /**
   * Calls another microservice and returns the result as JSON
   */
  async index() {
    try {
      const data = await this.broker.call('service-name.action', {
        email: 'test@example.com'
      }) ?? {};
      return this.asJson(data, 200);
    } catch (error) {
      return this.asJson({
        error: error.message,
        code: error.code || 500
      }, error.code || 500);
    }
  }
};

πŸ“₯ Read POST Request Body Example

const { AbstractController } = require('node-moleculer-web');

module.exports = class Home extends AbstractController {
  /**
   * Reads the request body and returns it as JSON
   */
  async index() {
    try {
      const body = await this.readBody();
      return this.asJson({ body }, 200);
  
    } catch (error) {
      return this.asJson({
        error: 'Failed to read request body',
        message: error.message
      }, 400);
    }
  }
};

πŸ” JWT Usage Example β€” Create and Verify Tokens

class Home extends AbstractController {
  // create token
  async index() {
    this.initJWT('mykey');
    const payload = {userId: 0, status: true};
    const token = this.getJWT().create(payload)
    return this.asJson({token}, 200);
  }
  // extract payload for token + validation (is payload not null then token valid)
  async test() {
   this.initRequest()
   const token = this.requestData.query.token ?? '';
   this.initJWT('mykey', false);
   const payload = this.getJWT().extract(token)
   return this.asJson({payload}, 200);
  }
}

Extend rest service

  • ! you still need to create an app server to use this example:
const {HttpService} = require('node-moleculer-web');
const {Service} = require('moleculer');
class RestService extends Service {
  constructor(broker) {
    super(broker);
    this.parseServiceSchema({
      name: 'rest',
      settings: {},
      mixins: [
				HttpService  // add HttpService mixin
      ],
      actions: {
        hello: {
          rest: 'GET /hello',
          handler(ctx) {
            return 'Hello1 API Gateway!'
          }
        },
        hello2: {
          rest: 'GET /hello2/:test1/:test2',
          handler(ctx) {
            // read request data for meta object
            console.log('requestData', ctx.meta.requestData)
            // read cookie
            ctx.meta.cookieData.get('test', 0);
            // write cookie 
            ctx.meta.cookieData.set('test', 2);
            // write header
            ctx.meta.headers['my-custom-header'] = 'lama';
            return 'Hello2 API Gateway!'
          }
        },
        
        testJson: {
          rest: 'GET /hello3/test/json',
          handler(ctx) {
            return this.asJson(ctx, {
              myvalue: 111
            })
          }
        },
        
        testRedirect: {
          rest: 'GET /hello3/test/redirect',
          handler(ctx) {
            return this.redirect(ctx, 'https://google.com', 301, 'meta')
          }
        }
        
      },
    });
  }
}

NGINX config if request proxying is used for once instance

server {
  listen 80;
  listen 443 ssl;
  listen [::]:80;
  listen [::]:443;

  server_name domain.com;
  
  location / {
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3001;
    proxy_redirect off;
  }
}

NGNIX config if clustering is used

Run locally

npx moleculer-runner -i 4 services

or

node node_modules/.bin/moleculer-runner -i 4 services
  • The config itself
  • The ports must match from your chosen strategy. node or ( list added version 1.2.1)
upstream node_apps {
  server 127.0.0.1:3001;
  server 127.0.0.1:3002;
  server 127.0.0.1:3003;
  server 127.0.0.1:3004;
}

server {
  listen 80;
  listen 443 ssl;
  listen [::]:80;
  listen [::]:443;

  server_name domain.com;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://node_apps;
    proxy_redirect off;
  }
}

cluster config moleculer.config.js

module.exports = {
  nodeID: 'DEMO',
  transporter: "TCP",
  registry: {
    // type of call strategy for microservices
    strategy: "RoundRobin",
    // If set to true, then each webserver will use only its own micro-services
    preferLocal: false,      
  },
  logger: console
};

BZT Benchmark (install view docs https://gettaurus.org/)

run app

node tests/services/local.js

run strestest minimal logic

bzt tests/bzt-config.yaml

bzt-test.png

22:41:17 INFO: Test duration: 0:06:03
22:41:17 INFO: Samples count: 12909515, 0.00% failures
22:41:17 INFO: Average times: total 0.008, latency 0.008, connect 0.000
22:41:17 INFO: Percentiles:
+---------------+---------------+
| Percentile, % | Resp. Time, s |
+---------------+---------------+
|           0.0 |           0.0 |
|          50.0 |         0.006 |
|          90.0 |         0.016 |
|          95.0 |         0.018 |
|          99.0 |         0.025 |
|          99.9 |         0.038 |
|         100.0 |         0.058 |
+---------------+---------------+
22:41:17 INFO: Request label stats:
+---------------------------------------------------------------------------+--------+---------+--------+-------+
| label                                                                     | status |    succ | avg_rt | error |
+---------------------------------------------------------------------------+--------+---------+--------+-------+
| http://localhost:8080/bzt?id=39641&&hash=104bdfd40acf8d03e6b485e11b681fd4 |   OK   | 100.00% |  0.016 |       |
| http://localhost:8080/bzt?id=39641&&hash=104bdfd40acf8d03e6b485e11b681fd5 |   OK   | 100.00% |  0.006 |       |
| http://localhost:8080/bzt?id=39641&&hash=104bdfd40acf8d03e6b485e11b681fd6 |   OK   | 100.00% |  0.005 |       |
| http://localhost:8080/bzt?id=39641&&hash=104bdfd40acf8d03e6b485e11b681fd7 |   OK   | 100.00% |  0.005 |       |
| http://localhost:8080/bzt?id=39641&&hash=104bdfd40acf8d03e6b485e11b681fd8 |   OK   | 100.00% |  0.006 |       |
+---------------------------------------------------------------------------+--------+---------+--------+-------+

About

create moleculer-web service for uWebsoketJs lib

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy