From 69717ae4096dbd795c7bd91a4e8548ab426f87cc Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Tue, 15 Mar 2022 20:39:33 +0300 Subject: [PATCH] refactor: moved logic to multiple files --- db.js | 16 ++++++ env.js | 18 ++++++ handlers.js | 15 +++++ initSchema.js | 15 +++++ package.json | 3 +- preloads/index.js | 1 + preloads/routes.js | 8 +++ response.js | 29 ++++++++++ router.js | 30 ++++++++++ server.js | 130 ++++++++++---------------------------------- sql/schema_init.sql | 8 +++ 11 files changed, 170 insertions(+), 103 deletions(-) create mode 100644 db.js create mode 100644 env.js create mode 100644 handlers.js create mode 100644 initSchema.js create mode 100644 preloads/index.js create mode 100644 preloads/routes.js create mode 100644 response.js create mode 100644 router.js create mode 100644 sql/schema_init.sql diff --git a/db.js b/db.js new file mode 100644 index 0000000..2897c90 --- /dev/null +++ b/db.js @@ -0,0 +1,16 @@ +import pg from 'pg'; +import env from './env.js'; + +class Database { + constructor() { + this.client = new pg.Client({ + user: env.db.user, + host: env.db.host, + database: env.db.name, + password: env.db.password, + port: env.db.port, + }); + } +} + +export default new Database(); diff --git a/env.js b/env.js new file mode 100644 index 0000000..8e4aeec --- /dev/null +++ b/env.js @@ -0,0 +1,18 @@ +import dotenv from 'dotenv'; + +dotenv.config({ path: process.cwd() + '/.env' }); + +const env = { + app: { + port: parseInt(process.env.PORT, 10), + }, + db: { + user: process.env.PG_USER.toString(), + host: process.env.PG_HOST.toString(), + name: process.env.PG_DB_NAME.toString(), + password: process.env.PG_PASSWORD.toString(), + port: parseInt(process.env.PG_PORT, 10), + }, +}; + +export default env; diff --git a/handlers.js b/handlers.js new file mode 100644 index 0000000..adea050 --- /dev/null +++ b/handlers.js @@ -0,0 +1,15 @@ +import http from 'node:http'; +import Response from './response.js'; + +/** + * Get list of all artcles from database + * + * @param {http.ClientRequest} req + * @param {Response} res + */ +export const getArticlesHandler = async (req, res) => { + res.json([ + { id: 1, title: 'First artcile' }, + { id: 2, title: 'Second artcile' }, + ]); +}; diff --git a/initSchema.js b/initSchema.js new file mode 100644 index 0000000..2677a70 --- /dev/null +++ b/initSchema.js @@ -0,0 +1,15 @@ +import fs from 'node:fs'; +import db from './db.js'; + +async function run() { + const { client } = db; + client.connect(); + + const query = fs.readFileSync('sql/schema_init.sql').toString(); + + await client.query(query); + console.log(`SQL: ${query}`); + await client.end(); +} + +await run(); diff --git a/package.json b/package.json index 50d3dfa..5424134 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,11 @@ "name": "node-docker-example", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "server.js", "type": "module", "scripts": { "start": "node server.js", + "db:init": "node initSchema.js", "dev": "nodemon server.js" }, "keywords": [ diff --git a/preloads/index.js b/preloads/index.js new file mode 100644 index 0000000..b0b0f47 --- /dev/null +++ b/preloads/index.js @@ -0,0 +1 @@ +import './routes.js'; diff --git a/preloads/routes.js b/preloads/routes.js new file mode 100644 index 0000000..269845b --- /dev/null +++ b/preloads/routes.js @@ -0,0 +1,8 @@ +import router from '../router.js'; + +/** + * Handlers + */ +import { getArticlesHandler } from '../handlers.js'; + +router.define('/api/articles', getArticlesHandler); diff --git a/response.js b/response.js new file mode 100644 index 0000000..20d1ccf --- /dev/null +++ b/response.js @@ -0,0 +1,29 @@ +import http from 'node:http'; + +class Response { + /** + * Extends the http response object with this class + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + */ + constructor(req, res) { + this.req = req; + this.res = res; + } + + send(text) { + this.res.writeHead(200, { 'Content-Type': 'text/plain' }); + this.res.end(text); + } + + json(data, status = 200) { + this.res.writeHead(status, { 'Content-Type': 'application/json' }); + this.res.end(JSON.stringify(data)); + } + + notFound(data) { + this.json(data, 404); + } +} + +export default Response; diff --git a/router.js b/router.js new file mode 100644 index 0000000..56c1ebe --- /dev/null +++ b/router.js @@ -0,0 +1,30 @@ +import http from 'node:http'; + +class Router { + constructor() { + /** + * Store the current list of routes for out API + */ + this.routes = {}; + } + + /** + * + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + * @returns + */ + call(req, res) { + if (this.routes.hasOwnProperty(req.url)) { + this.routes[req.url](req, res); + return true; + } + return false; + } + + define(url, handler) { + this.routes[url] = handler; + } +} + +export default new Router(); diff --git a/server.js b/server.js index b0fe858..38e559e 100644 --- a/server.js +++ b/server.js @@ -1,95 +1,26 @@ import http from 'node:http'; -import dotenv from 'dotenv'; -import pg from 'pg'; - -class Database { - constructor() { - this.client = new pg.Client({ - user: process.env.PG_USER, - host: process.env.PG_HOST, - database: process.env.PG_DB_NAME, - password: process.env.PG_PASSWORD, - port: process.env.PG_PORT, - }); - } -} - -class Router { - constructor() { - /** - * Store the current list of routes for out API - */ - this.routes = {}; - } - - /** - * - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @returns - */ - call(req, res) { - if (this.routes.hasOwnProperty(req.url)) { - this.routes[req.url](req, res); - return true; - } - return false; - } - - define(url, handler) { - this.routes[url] = handler; - } -} - -class Response { - #req; - #res; - - /** - * Extends the http response object with this class - * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - */ - constructor(req, res) { - this.#req = req; - this.#res = res; - } - - send(text) { - this.#res.writeHead(200, { 'Content-Type': 'text/plain' }); - this.#res.end(text); - } - - json(data, status = 200) { - this.#res.writeHead(status, { 'Content-Type': 'application/json' }); - this.#res.end(JSON.stringify(data)); - } - - notFound(data) { - this.json(data, 404); - } -} +import env from './env.js'; +import db from './db.js'; +import router from './router.js'; +import Response from './response.js'; +import './preloads/index.js'; class Application { constructor() { this.booted = false; - - this.config = this.#setupConfig(); this.server = http.createServer(); - this.db = new Database(); - this.router = new Router(); - this.#connectDb(this.db.client).then(() => this.run()); + this.router = router; + this.db = db; } - run() { - this.#handle(); - this.booted = true; - } - - #setupConfig() { - return { - config: dotenv.config(), - }; + async run() { + try { + await this.#connectDb(this.db.client); + this.#handle(); + this.booted = true; + } catch (error) { + throw new Error(error); + } } /** @@ -97,11 +28,10 @@ class Application { * @param {pg.Client} client */ async #connectDb(client) { - try { - await client.connect(); - } catch (error) { + await client.connect().catch((error) => { console.error(error); - } + throw new Error(error); + }); } #handle() { @@ -127,24 +57,20 @@ class Application { */ response.send('Static Page.'); }); - this.server.listen(process.env.PORT, () => - console.log(`Server is running on port "${process.env.PORT}"`) + this.server.listen(env.app.port, () => + console.log(`[LOG]: Server is running on port "${env.app.port}"`) ); } } const app = new Application(); -/** - * - * @param {http.ClientRequest} req - * @param {Response} res - */ -const getArticlesHandler = async (req, res) => { - res.json([ - { id: 1, title: 'First artcile' }, - { id: 2, title: 'Second artcile' }, - ]); -}; +app.run().catch((error) => { + console.error('[ERR]: Error on startup the application.', error); +}); -app.router.define('/api/articles', getArticlesHandler); +process.on('SIGINT', () => { + console.log(`[LOG]: Graceefully stopping the server...`); + app.server.close(); + process.exit(); +}); diff --git a/sql/schema_init.sql b/sql/schema_init.sql new file mode 100644 index 0000000..2e31506 --- /dev/null +++ b/sql/schema_init.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS "articles"( + "id" INTEGER PRIMARY KEY NOT NULL, + "title" VARCHAR(255) NOT NULL, + "description" VARCHAR(255) NOT NULL, + "body" TEXT NOT NULL, + "created_at" TIMESTAMP(0) WITH TIME zone NOT NULL, + "updated_at" TIMESTAMP(0) WITH TIME zone NOT NULL +); \ No newline at end of file