This is an old revision of the document!
Scopul sedintei:
Puteti pleca de la urmatorul schelet de cod. Sunt implementate urmatoarele:
Un REST API poate fi structurat, cel mai des, in doua moduri:
Noi am ales o impartire bazata pe context. Fiecare context din cadrul serviciului are propriul lui folder (de exemplu, folderul authors). O impartire pe functionalitate implica foldere dedicate functionaltiatilor (de exemplu, folder controllers, folder services, etc…).
In scriptul de start se initializeaza serverul. Deoarece express se bazeaza pe conceptul de middlewares, adica lanturi de functii, initializarea nu inseamna nimic mai mult decat mai multe functii ale unor pachete puse impreuna, la inceput.
const express = require('express'); // pachet folosit pentru partea de REST API server const morgan = require('morgan'); // pachet folosit pentru formatarea log-urilor const helmet = require('helmet'); // pachet folosit pentru adaugarea unor headere de securitate const createError = require('http-errors'); // pachet folosit pentru trimiterea de erori require('express-async-errors'); // pachet folosit pentru captarea erorilor pe rute require('log-timestamp'); // pachet folosit pentru injectarea timpului in momenul in care se da console.log() const routes = require('./routes.js'); // fisierul (scris de noi) care are configurate toate rutele const app = express(); // instantierea serverului efectiv app.use(helmet()); // adaugarea primului middleware, cel oferit de pachetul helmet app.use(morgan(':remote-addr - :remote-user [:date[web]] ":method :url HTTP/:http-version" :status :res[content-length]')); // adaugarea celui de-al doilea middleware, cel oferit de pachetul morgan app.use(express.json()); // adaugarea celui de-al treilea middleware, cel care extrage obiecte JSON din corpul cererilor. Util pentru POST si PUT app.use(express.urlencoded({ extended: false })); // adaugarea celui de-al patrulea middleware, cel care extrage obiecte x-www-urlencoded din corpul cererilor. Util pentru POST si PUT, daca nu se foloseste JSON app.use('/api', routes); // adaugarea rutelor configurate de noi in lantul de rute, cu radacina /api app.use((err, req, res, next) => { console.error(err); let status = 500; let message = 'Something Bad Happened'; if (err.httpStatus) { status = err.httpStatus; message = err.message; } return next(createError(status, message)); }); // adaugarea unui middleware ce intercepteaza erorile si foloseste createError pentru incapsularea lor const port = process.env.PORT || 80; // stabilirea portului pe care va rula serverul in functie de variabila de mediu. Daca nu exista, este implicit 80 app.listen(port, () => { console.log(`App is listening on ${port} and running on ${process.env.NODE_ENV} mode`); }); // deschiderea serverului pe portul stabilit
Acest fisier are rol de a centraliza toate controllerele folosite in aplicatie si de a le servi sub forma unui obiect Router care este, apoi, consumat in start.js.
const Router = require('express').Router(); // instantierea obiectului Router din biblioteca express. Ajuta la rutare modulara const authorsControllers = require('./authors/controllers.js'); // controllerul folosit pentru autori Router.use('/authors', authorsControllers); // adaugarea controllerului de autori in rutele obiectului Router, cu radacina /authors module.exports = Router; // expunerea obiectului Router pentru a putea fi folosit de alte fisiere. Bineinteles, dupa cum ati vazut, este folosit in start.js
Baza de date este PostgreSQL, asa ca, pentru interactiune, am utilizat un driver facut pentru NodeJS, care se numeste pg. PG ofera posibilitatea scrierii de cereri SQL ad-hoc. Se puteau folosi si alte pachete, care abstractizeaza acest proces, precum Knex.js, insa, pentru simplitate, am optat pe SQL “de mana”.
Scopul acestui fisier este de a initializa conexiunea cu baza de date si de a oferi, catre public, o functie (query) prin care se pot trimite cereri SQL catre baza de date.
const { Pool } = require('pg'); // preluarea obiectului Pool din pachetul pg care initializeaza conexiunea cu baza de date const { getSecret } = require('docker-secret'); // folosit mai tarziu, o sa vedeti ;) const options = { host: process.env.PGHOST, database: process.env.PGDATABASE, port: process.env.PGPORT, user: process.env.NODE_ENV === 'development' ? process.env.PGUSER : getSecret(process.env.PGUSER_FILE), password: process.env.NODE_ENV === 'development' ? process.env.PGPASSWORD : getSecret(process.env.PGPASSWORD_FILE) } // preluarea informatiilor de conectare din variabilele de mediu const pool = new Pool(options); // realizarea conexiunii folosind informatiile de conectare si obiectul Pool const query = async (text, params) => { const { rows, } = await pool.query(text, params); return rows; }; // crearea functiei de interactiune cu baza de date. Functia are doi parametri, un string, care reprezinta cererea SQL si un vector de valori, din care sunt preluate valorile si injectate in cerere. module.exports = { query, }; // expunerea functiei