mirror of
https://github.com/sergeyyarkov/node-docker-example.git
synced 2026-10-11 18:29:34 +03:00
feat: added new article handler & route handling by regexp
This commit is contained in:
parent
69717ae409
commit
e17386a782
35
handlers.js
35
handlers.js
@ -1,5 +1,6 @@
|
|||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
import Response from './response.js';
|
import Response from './response.js';
|
||||||
|
import db from './db.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of all artcles from database
|
* Get list of all artcles from database
|
||||||
@ -8,8 +9,34 @@ import Response from './response.js';
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
export const getArticlesHandler = async (req, res) => {
|
export const getArticlesHandler = async (req, res) => {
|
||||||
res.json([
|
const data = await db.client.query('SELECT * FROM "articles"');
|
||||||
{ id: 1, title: 'First artcile' },
|
res.json({ data: data.rows });
|
||||||
{ id: 2, title: 'Second artcile' },
|
};
|
||||||
]);
|
|
||||||
|
/**
|
||||||
|
* Get article by ID
|
||||||
|
*
|
||||||
|
* @param {http.ClientRequest} req
|
||||||
|
* @param {Response} res
|
||||||
|
*/
|
||||||
|
export const getArticleHandler = async (req, res) => {
|
||||||
|
/**
|
||||||
|
* Validate url `id` param
|
||||||
|
*/
|
||||||
|
if (!/^\d+$/.test(req.params.id)) {
|
||||||
|
res.json({ data: {}, error: 'Parameters is not valid!' }, 422);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await db.client.query(
|
||||||
|
'SELECT * from "articles" WHERE "id" = $1',
|
||||||
|
[req.params.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.rows.length === 0) {
|
||||||
|
res.json({ data: {}, error: 'Article not found.' }, 404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ data: data.rows });
|
||||||
};
|
};
|
||||||
|
|||||||
41
package-lock.json
generated
41
package-lock.json
generated
@ -393,6 +393,18 @@
|
|||||||
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
|
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"handlebars": {
|
||||||
|
"version": "4.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||||
|
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"neo-async": "^2.6.0",
|
||||||
|
"source-map": "^0.6.1",
|
||||||
|
"uglify-js": "^3.1.4",
|
||||||
|
"wordwrap": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
@ -594,8 +606,7 @@
|
|||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
@ -603,6 +614,11 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"neo-async": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
|
},
|
||||||
"nodemon": {
|
"nodemon": {
|
||||||
"version": "2.0.15",
|
"version": "2.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz",
|
||||||
@ -682,6 +698,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||||
},
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg=="
|
||||||
|
},
|
||||||
"pg": {
|
"pg": {
|
||||||
"version": "8.7.3",
|
"version": "8.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz",
|
||||||
@ -881,6 +902,11 @@
|
|||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||||
|
},
|
||||||
"split2": {
|
"split2": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
|
||||||
@ -960,6 +986,12 @@
|
|||||||
"is-typedarray": "^1.0.0"
|
"is-typedarray": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uglify-js": {
|
||||||
|
"version": "3.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz",
|
||||||
|
"integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"undefsafe": {
|
"undefsafe": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
@ -1026,6 +1058,11 @@
|
|||||||
"string-width": "^4.0.0"
|
"string-width": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wordwrap": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||||
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
|||||||
@ -21,6 +21,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
|
"handlebars": "^4.7.7",
|
||||||
|
"path-to-regexp": "^6.2.0",
|
||||||
"pg": "^8.7.3"
|
"pg": "^8.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import router from '../router.js';
|
|||||||
/**
|
/**
|
||||||
* Handlers
|
* Handlers
|
||||||
*/
|
*/
|
||||||
import { getArticlesHandler } from '../handlers.js';
|
import { getArticleHandler, getArticlesHandler } from '../handlers.js';
|
||||||
|
|
||||||
router.define('/api/articles', getArticlesHandler);
|
router.define('/api/articles', getArticlesHandler);
|
||||||
|
router.define('/api/article/:id', getArticleHandler);
|
||||||
|
|||||||
@ -11,8 +11,8 @@ class Response {
|
|||||||
this.res = res;
|
this.res = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(text) {
|
send(text, status = 200) {
|
||||||
this.res.writeHead(200, { 'Content-Type': 'text/plain' });
|
this.res.writeHead(status, { 'Content-Type': 'text/plain' });
|
||||||
this.res.end(text);
|
this.res.end(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,8 +21,8 @@ class Response {
|
|||||||
this.res.end(JSON.stringify(data));
|
this.res.end(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
notFound(data) {
|
notFound(text) {
|
||||||
this.json(data, 404);
|
this.send(text, 404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
56
router.js
56
router.js
@ -1,29 +1,77 @@
|
|||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
|
import { pathToRegexp } from 'path-to-regexp';
|
||||||
|
|
||||||
class Router {
|
class Router {
|
||||||
constructor() {
|
constructor() {
|
||||||
/**
|
/**
|
||||||
* Store the current list of routes for out API
|
* Store the current list of routes
|
||||||
*/
|
*/
|
||||||
this.routes = {};
|
this.routes = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This function checks for the existence of a route and calls its handler
|
||||||
*
|
*
|
||||||
* @param {http.ClientRequest} req
|
* @param {http.ClientRequest} req
|
||||||
* @param {http.ServerResponse} res
|
* @param {http.ServerResponse} res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
call(req, res) {
|
call(req, res) {
|
||||||
|
/**
|
||||||
|
* Find the exact route
|
||||||
|
*/
|
||||||
if (this.routes.hasOwnProperty(req.url)) {
|
if (this.routes.hasOwnProperty(req.url)) {
|
||||||
this.routes[req.url](req, res);
|
this.routes[req.url].handler(req, res);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find route by regular expression
|
||||||
|
*/
|
||||||
|
const keys = Object.keys(this.routes);
|
||||||
|
let i = keys.length;
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
const route = this.routes[keys[i]];
|
||||||
|
if (!route.options.exact && route.options.regexp !== undefined) {
|
||||||
|
const match = req.url.match(route.options.regexp);
|
||||||
|
if (match) {
|
||||||
|
if (route.options.params !== undefined) {
|
||||||
|
req.params = {};
|
||||||
|
route.options.params.forEach((param) => {
|
||||||
|
req.params[param.name] = match[1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
route.handler(req, res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
define(url, handler) {
|
/**
|
||||||
this.routes[url] = handler;
|
* Add more routes to router store
|
||||||
|
*
|
||||||
|
* @param {string} url
|
||||||
|
* @param {function} handler
|
||||||
|
* @param {boolean} exact
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
url,
|
||||||
|
handler,
|
||||||
|
options = { exact: true, regexp: undefined, params: undefined }
|
||||||
|
) {
|
||||||
|
if (url.includes(':')) {
|
||||||
|
options.params = [];
|
||||||
|
options.exact = false;
|
||||||
|
options.regexp = pathToRegexp(url, options.params);
|
||||||
|
}
|
||||||
|
this.routes[url] = {
|
||||||
|
handler,
|
||||||
|
options,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user