I have been having a hard time finding a markdown editor that has all the features that I want, also I want one that I can use in any operating system environment that I can get node.js installed on. So for today’s express example why not a markdown editor, after all if you want a job done right sometimes you have to do it yourself.
1 - Express Markdown Editor Example, and what to know first
This express example requires at least some background knowledge with express, node.js and javaScript in general.
1.1 - Setup
This project involves just the use of express and marked when it comes to node.js dependences. All other code is just pure vanilla js code.
1
2
3
4
5
6
7
8
9
10
11
$ mkdir express-example-markdown-editor
$ cd express-example-markdown-editor
$ npm init
$ npm install express@4.16.4 --save
$ npm install marked@0.6.2 --save
$ mkdir middleware
$ mkdir public
$ mkdir _posts
$ cd public
$ mkdir html
$ mkdir js
2 - The Main app.js file
In the main app.js file I creates the main express app object instance, and add some application settings with the app.set method. These settings have to do with what port to listen on, as well at the current working directory in which mark down files are to be found.
Here I am also using the express static built in middleware as well as a way to host all static assets for the client system. I am also using the built in body parser middleware as a way to parse incoming post request body’s from the client system as well.
app.listen(app.get('port'), () => console.log('example markdown editor is up on port: ' + app.get('port')));
In addition to built in middleware I am also using much of my own middleware methods for reading the current file at the current directory, parsing the markdown to html and sending it to the client.
2 - The middleware folder.
In this exmpress example I am using my own external express middleware methods for preforming all kinds of tasks that I want to accomplish via node.js rather than the browser. To keep my main app.js file from becoming a long monolithic block of code that is hard to follow in a blog post I have broken things down into many external files, and placed them in a middleware folder. In this section I will be going over these including the one the parses the mark down to plain html.
2.1 - The /middleware/md_html.js file
Here Is the middleware that I worked out that is used to parse the current mark down file to plain old html. In this middleware I am using marked.js, the only other dependency for this express example to do so. There is much more to write about when it comes to using marked.js, but I have all ready wrote a post on marked, so I will not be getting into detail about that here.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let express = require('express'),
path = require('path'),
fs = require('fs'),
marked = require('marked'),
router = module.exports = express.Router();
// send html of current markdown file
router.get('*', (req, res) => {
let fn = req.app.get('fn'),
dir = req.app.get('dir'),
dir_md = path.join(dir, fn);
// read current markdown file
fs.readFile(dir_md, 'utf8', (e, md) => {
if (e) {
res.status(500).send(e.message);
} else {
// used marked to send html of markdown
res.set('Content-Type', 'text/html');
let html = '<head> <link rel="stylesheet" type="text/css" href="edit.css"><\/head>' +
'<body>' + marked(md) + '<\/body>';
res.status(200).send(html);
}
});
});
Beyond the use of marked I am just using the node.js built in file system module to read the current markdown file, there are other options for doing so as well such as fs-extra, but I do not want to pull to much focus away from the fact that this is an express example, and not an example on other node.js dependencies.
2.2 - The /middleware/body_check.js file
Here I have a middleware that is the first of many when it comes to handing post requests that are made from the client system. Here I create an object that will untamitly be the response for the post request, I also check for a post body, and if it has an action property.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
let express = require('express'),
router = module.exports = express.Router();
router.use([
// create reply object
(req, res, next) => {
// Create reply object
res.reply = {
success: false,
mess: 'no body object populated.'
};
next();
},
// check body
(req, res, next) => {
// check for body or next
if (!req.body) {
res.status(400).json(res.reply);
} else {
// body must have an action property
if (!req.body.action) {
res.mess = 'body must have an action property';
res.status(400).json(res.reply);
} else {
// we are good
next();
}
}
},
// update settings
(req, res, next) => {
let app = res.app;
// sync server side fn and dir settings to any settings given by client
app.set('fn', req.body.fn || app.get('fn'));
app.set('dir', req.body.dir || app.get('dir'));
res.reply.fn = app.get('fn');
res.reply.dir = app.get('dir');
next();
}
]);
I also update application settings with any values that may be present in the object as well.
2.3 - The /middleware/action.js file
This middleware calls another middleware method depending on the action property value. If the middleware method does not exist then as one would expect the method responds to the request with a 400 http status.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let express = require('express'),
path = require('path'),
router = module.exports = express.Router();
router.use((req, res, next) => {
try {
let action = require(path.join(res.app.get('dir_mw'), './action_' + req.body.action + '.js'));
action(req, res, next);
} catch (e) {
res.reply.mess = e.message;
res.status(400).send(res.reply)
}
});
this way in order to add more actions I just have to add the file to the middleware folder, and then update my client system to make use of that feature. I tend to prefer something like this compared to having each middleware hard coded into the source code of the project. If the file is there it will make use of it, if not it will shoot out an error.
2.5 - The /middleware/action_open.js file
This action can be used to just send back the raw text of the current file. The encoding is one of the application settings set in the main app.js file, there is no way to change it as of yet, but that might change at some point maybe.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let express = require('express'),
path = require('path'),
fs = require('fs');
module.exports = (req, res, next) => {
// try to open the current filename at the current dir
Actions can be simple lik this one or far more complex, it just so happens that I do want at least some like this that are just read that current file and give me that text.
2.6 - The /middleware/action_save.js file
Here I have the middleware that will save the current file with the data that is given from the client system.