plugdo-mvc
A module that implements mvc
, webapi
and realtime
web software architecture. It help you focus in the implementation of the business logic, instance of writing express routing each time you need a new section.
Compatibility: Node.js version 6+ and Express version 4+
Install with npm
:
npm install @dellasera/plugdo-mvc
IMPORTANT Directory Structure
In order to found the components, a directory structure must be created as follow:
--content
- source
--source/controllers
--source/dependencies
--source/webapi
--source/realtime
--views
Directories definitions:
content: you will add the css, js, font files.
source: the MVC logic will be added here.
source/controllers: You can create directories and js files, the js files will have the controller logic.
source/dependencies: You can create directories and js files, the js files will have the services, utilities, classes, helper, etc.
source/webapi: You can create directories and js files, the js files will have the web api logic.
source/realtime: You can create directories and js files, the js files will have the realtime channel logic.
views: the EJS views will be added here.
IMPORTANT ERROR and 404 Not Found
The error and 404 not found are returned in XML format, because the PLUGDO MVC is a mix between Web Page and Web API, the general response for erros and 404 not found was changed to XML format.
PLUGDO MVC
Controllers
Create a new controller is easy, just add a new file in source/controllers, plugdo MVC will load the new file after initialization, the loading is not automatic. You must restart the nodejs execution.
First, start the plugdo-mvc server. (use nodemon for development is recommended)
const mvc = require("@dellasera/plugdo-mvc").mvc();
const path = require("path");
const port = process.env.PORT === undefined ? 3000 : process.env.PORT;
mvc.start(port, path.resolve(__dirname)); // The port and root path is required
Create a controller, adding a file in source/controllers. The controller must return a json model. An example is:
mvc.controller({
name: "customer",
action: "index"
}, function (req) {
return {
title: "Customer Home Page",
message: "Welcome to our web page!"
};
});
The expected URL will be:
http://domain.com/customer/index
You can also replace the default URL format as name/action, just including the path: "/new-path". Also a dependency injection simple way is available to pass the dependencies previously defined.
mvc.controller({
name: "customer",
action: "index",
path: "/new-path"
}, "customerService", function (req, customerService) {
return customerService.getAll();
});
The expected URL will be:
http://domain.com/new-path
The default view to be required is:
views/customer/index.ejs
If you need to use another file, skiping the default, just add the view option as follow:
mvc.controller({
name: "customer",
action: "index",
view: "customer-index.ejs" // Also a directory, view: "customer/index/home.ejs"
}, function (req) {
return {
title: "Customer Home Page",
message: "Welcome to our web page!"
};
});
Dependencies
Create a new dependency is easy, just add a new file in source/dependencies, plugdo MVC will load the new file after initialization, the loading is not automatic. You must restart the nodejs execution.
mvc.dependencies.customerService = {
"getAll": function () {
return [
{
name: "Marco A. Castillo",
country: "Panama"
},
{
name: "Rafael Della Sera",
country: "Panama"
}
]
}
};
Added in version 1.0.31 the mvc.service() fnction. This new function allow you to add services or collect services. The same example above:
mvc.service("customerService", {
"getAll": function () {
return [
{
name: "Marco A. Castillo",
country: "Panama"
},
{
name: "Rafael Della Sera",
country: "Panama"
}
]
}
});
Web API
Create a new webapi is easy, just add a new file in source/webapi, plugdo MVC will load the new file after initialization, the loading is not automatic. You must restart the nodejs execution.
mvc.api({
name: "customer",
action: "all"
}, function (req) {
return [
{
name: "Marco A. Castillo",
country: "Panama"
},
{
name: "Rafael Della Sera",
country: "Panama"
}
];
});
The expected URL for XML format will be:
http://domain.com/api/customer/all/xml
The expected URL for JSON format will be:
http://domain.com/api/customer/all/json
You can also replace the default URL format as name/action, just including the path: "/new-path". Also a dependency injection simple way is available to pass the dependencies previously defined.
mvc.api({
name: "customer",
action: "all",
path: "/service/customer/get/all"
}, "customerService", function (req, customerService) {
return customerService.getAll();
});
The expected URL for XML format will be:
http://domain.com/api/service/customer/get/all/xml
The expected URL for JSON format will be:
http://domain.com/api/service/customer/get/all/json
Real Time
Create a new realtime channel is easy, just add a new file in source/realtime, plugdo MVC will load the new file after initialization, the loading is not automatic. You must restart the nodejs execution.
mvc.realtime("channel1");
Using the realtime channel throught the Web Page
Create a new ejs view file, including the required socket.io files and publish the new view creating a Controller.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Realtime Example</title>
<link rel="stylesheet" href="/css/enterpriseui.min.css"/>
<script src="/socket.io/socket.io.js"></script>
<script src="/js/plugdo-dependency.min.js"></script>
<script src="/js/plugdoeui.min.js"></script>
</head>
<body id="eui">
<script>
var realtime = new PlugdoRealtime("http://localhost:3000");
function PlugdoRealtime (host) {
var self = this;
self.io = io.connect(host);
this.register = function (name, callback) {
self.io.on("client" + name, function (data) {
callback(data);
});
};
this.send = function (name, data) {
self.io.emit("server" + name, data);
}
}
load(function () {
plugdo.ready(function() {
realtime.send("channel1", { date: Date.now(), author: "Marco Castillo"} );
realtime.register("channel1", function (data) {
console.log(data);
});
});
}, true);
</script>
</body>
</html>
Using the realtime channel from the server side
Create a new Controller or Web API.
Calling the realtime
mvc.broadcast.publish("channel1", { date: Date.now(), author: "Marco Castillo"});
mvc.api({
name: "customer",
action: "new"
}, "customerService", function (req, customerService) {
// Create the customer model
let customer = {
date: Date.now(),
author: req.params.name
};
// Insert the new customer and collect the response back from the service
let responseModel = customerService.createCustomer(customer);
if(responseModel.success) {
// Send the new customer to the realtime channel if the customer was created successfully
mvc.broadcast.publish("channel1", customer);
}
return responseModel;
});
Upload Files
mvc.upload("/upload");
Adding this path using the method mvc.upload before the mvc.start is executed, allow you to accept files through form multipart. the default behavior is to response a json object. The /upload path become a Web API. If you want to avoid this default behavior, you must pass options to the mvc.upload.
The following next=true options, will upload the file and create a json model injected in the req.files. The mvc.upload accept multiple files by default so the req.files is an array. With the next=true option, you can create a controller or a web api. Now you can execute your on logic, like, uploading a file and adding the file in the database.
mvc.upload("/upload", { next: true });
You can register to the realtime channel "plugdo-mvc-upload-in-progress" to collect the progress of the file. The model sent is:
let realtimeModel = {
path,
file: {
field,
name,
path,
encoding,
type,
size
}
}
PLUGDO MS (Micro-service)
Create a new micro-service is easy, just implement the ms.start() framework to start working with RabbitMQ task. You can create an application that just open a connection with RabbitMQ to consume or emit message to the queue.
Consume a Queue
Let's see the following example to open a connection with RabbitMQ waiting for a payload message in JSON format. (Just JSON payload is supported).
// index.js
const ms = require("./modules/plugdo-mvc").ms();
// "localhost" is the host used in this example
ms.start("localhost").then(() => {
// Queue channel "PLUGDO_MS"
// ms.consume() function register this queue as listener
ms.consume("PLUGDO_MS", function (payload, end) {
// Add your own logic here to process the payload JSON object
end(payload); // Always end the process
});
});
Also you can emit message. This node app will end as soon as the message is sent. The connection remain open when you consume a queue, otherwise the app will close.
// index.js
const ms = require("./modules/plugdo-mvc").ms();
// "localhost" is the host used in this example
ms.start("localhost").then(() => {
// Queue channel "PLUGDO_MS"
// Emit a message to this queue
let payload = {
id: 1,
message: "Payload Message"
};
ms.emit("PLUGDO_MS", payload);
});
The following demo consume a queue, and also emit a message to that queue. You will be able to log the payload recieved in the consume method.
// index.js
const ms = require("./modules/plugdo-mvc").ms();
// "localhost" is the host used in this example
ms.start("localhost").then(() => {
// Queue channel "PLUGDO_MS"
// ms.consume() function register this queue as listener
ms.consume("PLUGDO_MS", function (payload, end) {
// Add your own logic here to process the payload JSON object
console.log(payload);
end(payload); // Always end the process
}).emit("PLUGDO_MS", {
id: 1,
message: "Payload Message"
});
});
Release Notes
- v1.2.11: Adding the confiuration mvc.forceJSON = true before the server starts, you are able to avoid the response type json or xml in the api path because It is force to be JSON. Using http://localhost/api/name/action without json or xml at the end. Also the global error response in XML format is replace for json format as follow: { response: { success: false, errorCode: 500, error: "error message here (stack value)" } }
Quick Notes
The req parameter injected in the controller is the express object, if you need more details about the properties available, just go to expressjs.com documentation.