📜 ⬆️ ⬇️

node.js reference abbreviation


Hi, Habr! In this article, I will step by step consider creating a simple web application - abbreviation of links to node.js, using mysql-libmysqlclient, MooTools on the server and jQuery on the client. The article assumes that the reader has already passed the “Hello world” exercise and figured out the very basics of node.js.

Immediately warn you - I do not consider myself an expert in node.js. I propose to discuss the code and get closer to the ideal together. Perhaps together we will make a great example for newbies. Therefore - constructive criticism is welcomed.

Server MooTools


I do not want to holivarit on the topic-need-unnecessary, I chose to use, you can decide otherwise.
It was not possible to connect MooTools right away - you can’t just take, download the server file and make require ('./ MooTools').
I was helped by MooTools tweet from July 12th and the link davidwalsh.name/mootools-nodejs . As a result, the library was connected as follows:
require('./Lib/MooTools').apply(GLOBAL); 


Mysql


Any NoSQL database is more suitable for this application, but my goal was to learn how to work with MySQL in node.js.
I took out a topic about Node-mysql-libmysqlclient from the bookmarks, took all the necessary information and gathered the client into the manuals without problems.
I threw the script itself into the Lib folder in the project, so it is easily initiated like this:
 var conn = require('./Lib/mysql/mysql-libmysqlclient').createConnectionSync(); conn.connectSync('localhost', 'NAME', 'PASS', 'nodejs'); 

')

Extending the ServerResponse


To make it easier to set headers in the application and redirect, I decided to extend the ServerResponse prototype (I think this approach is in line with the JavaScript ideology) using the MooTools implements method:
 var http = require('http'); http.ServerResponse.implement({ // code -    .   ,  200 (Ok)  404 (Not Found) // plain -     true,  - html header : function (code, plain) { this.writeHead(code, { 'Content-Type': plain ? 'text/plain' : 'text/html' }); }, // response.redirect('http://example.com')      redirect : function (url, status) { this.writeHead(status || 302, { 'Content-Type' : 'text/plain', 'Location' : url }); this.write('Redirecting to ' + url); this.end(); } }); 


Application framework


 http.createServer(function (req, res) { // code will be here }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/'); 


Links, file Link.js


So, the link we have three properties - id in the database, the url to which it leads and the code that is shown in our abbreviation.
We will not write code separately into the database, but will receive it on the basis of id, translating it into a 36-number system ([0-9a-z]).
 Link.getCode = function (id) { return id ? id.toString(36) : null; }; Link.fromCode = function (code) { return code ? parseInt(code, 36) : null; }; 


Based on this, we build the class reference itself, which we place in front of the static methods:
 Link = new Class({ initialize : function (obj) { //      -      this.setId(obj.id).id || this.setCode(obj.code); this.setUrl(obj.url); }, setId : function (id) { // id    int this.id = (isNaN(id) || id <= 0) ? null : parseInt(id); return this; }, getId : function () { return this.id; }, setUrl : function (url) { this.url = url || null; return this; }, getUrl : function () { return this.url; }, setCode : function (code) { this.id = Link.fromCode(code); return this; }, getCode : function () { return Link.getCode(this.id); } }); 


In the database we will create a simple table and write a model for getting and inserting the link:
 CREATE TABLE `shortLinks` ( `id` int(11) NOT NULL AUTO_INCREMENT, `url` varchar(512) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 


 Model = new Class({ initialize : function (conn) { //       this.conn = conn; }, create : function (args) { //     ,     return new Link(args); }, get : function (link, fn) { //     if (!link.getId()) throw 'EmptyId'; //  .  ,  link.id    int var q = 'SELECT * FROM `shortLinks` WHERE `id` = ' + link.getId(); //    .   . this.conn.query(q, function (err, res) { if (err) throw err; res.fetchAll(function (err, rows) { if (err) throw err; //     -,    fn(rows.length ? link.setUrl(rows[0].url) : null); res.freeSync(); }); }); }, put : function (link, fn) { // url - ,  !!    var q = 'INSERT INTO `shortLinks` (`id`, `url`) ' + 'VALUES (NULL , "' + this.conn.escapeSync(link.getUrl()) + '");' this.conn.query(q, function (err, res) { if (err) throw err; //       fn(link.setId( //       this.conn.lastInsertIdSync() )); }.bind(this)); } }); //     exports.Link = Link; exports.Model = Model; 


You can add the following line to initialization:
 var linkModel = new (require('./Link').Model)(conn); 

Note that the brackets around require('./Link').Model are required, otherwise it will try to create a require object.

Renderer, Renderer.js file



 //        require('./Lib/MooTools').apply(GLOBAL); //     var url = require('url'); //      var fs = require('fs'); exports.Renderer = new Class({ initialize : function (linkModel) { this.link = linkModel; } }); 


Renderer, like Link.Model, will be created only once during application initialization and each request is used the same, due to which we do not lose precious milliseconds.
The basis will be the run method:

 run : function (req, res) { var path = url.parse(req.url, true); if (path.query && 'add' in path.query) { var addUrl = path.query.add; //      , , "example.com" //        ,   default- if (!url.parse(addUrl).protocol) { addUrl = 'http://' + addUrl; } //    this.add(res, addUrl); //     ( url/!abc12 ) } else if (path.pathname.test(/^\/![0-9a-z]+$/)) { //      (/!)    this.send(res, path.pathname.substr(2)); } else { //        this.index(res); } }, 


Before we forget, we ./init.js edit a little ./init.js
 var linkModel = new (require('./Link').Model)(conn); var renderer = new (require('./Renderer').Renderer)(linkModel); http.createServer(function (req, res) { renderer.run(req, res); }).listen(8124, "127.0.0.1"); 


Because Links will be added by agiaks, all we need is to write the link to the database and return its code:
 add : function (res, url) { res.header(200); this.link.put( this.link.create({ url : url }), function (link) { res.end(link.getCode()); } ); }, 


If the user wants to go through an abbreviated link - send it where necessary, or inform that there is no such link
 send : function (res, code) { this.link.get( this.link.create({ code : code }), function (link) { if (link) { res.redirect(link.getUrl()); } else { res.header(404, true); res.end('There is not such url'); } } ); }, 


For the main page - just display the file index.html. Please note, I recommend to always indicate the path with __dirname first, because otherwise you might run into trouble
 index : function (res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) throw err; res.header(200); res.end(data); }) } 


Home page


On the main page, we will proceed simply - we will connect google jikvery from the reps, add an input field and when the user wants to shorten the link - we have received the code for the new link with Ajax, we will nicely display it under the input field.
 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>   node.js</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script> 

 $(function () { $('input[type=submit]').click(function() { var input = $('input[type=text]'); var url = input.val(); input.val(''); url && $.ajax({ url : './', data: ({ add : url }), success : function (data) { var result = location.protocol + '//' + location.host + '/!' + data; $('#url') .prepend( $('<dd>').append( $('<a>') .text(result) .attr('href', result) ) .hide() .fadeIn() ) .prepend( $('<dt>') .text(url) .hide() .fadeIn() ); } }); }) }); 

  </script> </head> <body> <div id="form"> <input type="text" /> <input type="submit" /> </div> <dl id="url"></dl> </body> </html> 


Everything.

Result


Immediately I will warn you - I do not guarantee the safety of your links, the service is exclusively demonstrative.

All code in one place on pastebin

Source: https://habr.com/ru/post/105691/


All Articles