🔬Backend Topics - marcdroger/blok-tech-team Wiki

Inhoud

Research Validator met Express

Research en documentatie topic van Judith Koelewijn

Het is belangrijk om server-side validatie uit te voeren bij het bouwen van applicaties. De reden: je kan nooit alleen op de invoer van de gebruiker vertrouwen, omdat de invoer soms verkeerd of valse gegevens bevat.

Client-side validatie is een goede manier om door de meeste input heen te komen, maar toch heb je ook server-side validatie nodig voor volledige dekking en zekerheid.

Installeren Express Validator:

npm install --save express-validator

Standaard validatieregels met Express Validator

Om kennis te maken met Express Validator doorloop ik eerst eenvoudige voorbeelden. Als eerste een stuk code om te controleren of de ingevoerde waarde in het email veld een geldige email is of niet. De volgende stap is het afdwingen van een 'sterk' wachtwoord door het ten minste 6 karakters te laten bevatten.

De eerste stap is het toevoegen van middleware op de route waar het formulier is (in het geval van dit voorbeeld /login)

const { body, validationResult } = require('express-validator');

app.post('/login',
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({
        min: 6
    }),
    (req, res) => {
        const errors = validationResult(req);

        if (!errors.isEmpty()) {
            return res.status(400).json({
                success: false,
                errors: errors.array()
            });
        }

        res.status(200).json({
            success: true,
            message: 'Login successful',
        })
    });

In de bovenstaande snippet maken we gebruik van twee validatie methoden:

isEmail() - Deze validator functie controleert of de inkomende string een geldig emailadres is.

isLength() - Deze validator controleert of de lengte van een string binnen een opgegeven bereik valt. In dit geval is het opgegeven bereik een minimum van 6 tekens.

Omdat het in dit teamproject bij onze applicatie alleen gaat om het inloggen ligt de focus op het valideren van het e-mailadres. Je wilt niet hebben dat er valse niet bestaande e-mailadressen worden ingevuld. Vooral omdat je als student graag de uitwisseling wilt, en dan blij bent iemand gevonden te hebben, en deze dan vervolgens niet kan bereiken. Dit geeft veel ruimte voor valse hoop én daarbij teleurstelling.

Andere methodes binnen Express Validator

Los van de bovenstaande twee voorbeelden kan Express Validator nog meer valideren. Ik heb ze doorgenomen en de belangrijkste op een rijtje gezet:

isNumeric() - Controleert of de invoer numeriek is

bevat() - Controleert of de invoer een bepaalde waarde bevat

isBoolean() - Controleert of de invoer een boolean waarde is

isCurrency() - Controleert of de invoer in valuta is geformatteerd

isJSON() - Controleert of de invoer JSON is

isMobilePhone() - Controleert of de invoer een geldig mobiel telefoonnummer is

isPostalCode() - Controleert of de invoer een geldige postcode is

isBefore() en isAfter() - Controleert of een datum voor of na een andere datum valt

Ik heb ervoor gekozen de email te checken en te formatten. Verder heb ik een minimum en maximum aan karakters bij de input fields gedaan (zodat de gebruiker niet snel een letter intypt om het formulier maar in te kunnen vullen)

Implementatie binnen ons project

De bovenstaande voorbeelden vanuit de express validator guide stuurden alleen een json string als antwoord, met daarin de error message. Dit heb ik werkend gekregen, maar dit was voor de client-side niet de passende oplossing. Ik heb het als volgt verder uitgewerkt:

Ik heb op de volgende manier de validator uitgewerkt binnen ons project. Eerst check ik of er errors zijn, en als deze er zijn redirect ik weer naar het formulier, omdat het niet verzenden kan worden. Dan geef ik in de res.render het alert mee (wat een array is van de error). Bij de else (wanneer het wel goed gaat, wordt het formulier gestuurd en gaat de gebruiker verder).

// express validator: checks email, invalid numbers and symbols, length of input
router.post('/add', 
body('email').isEmail().normalizeEmail().withMessage('Must be a valid email address, try again'),
body('education').isLength({ min: 2, max: 60 }).withMessage('Education has a minimum of 2 characters, and a maximum of 60'),
body('school').isLength({ min: 4, max: 60 }).withMessage('Current school has a minimum of 4 characters, and a maximum of 60'),


 async(req, res) => { 
  const errors = validationResult(req)

  if(!errors.isEmpty()) {
    const alert = errors.array()
    res.render('add', {
      alert
    }) 

} else {

Vervolgens heb ik binnen Pug de alert array ingeladen. Hier maak ik eerst een if statement om te checken of er inderdaad een error is, vervolgens doe ik voor elke error in het alert de error.msg tonen.

  if alert != undefined 
     each error in alert
      h2= `${error.msg}`
           

Hier werkt het als volgt: als de alert array niet leeg is (en er dus een error is), moet er voor elke error de message getoond worden.

Screenshot 2022-06-22 at 09 04 57

Als laatste heb ik in de sass styling toegevoegd die past bij een error message (combinatie geel met bruin).

Reflecterend hierop was het best een uitdaging het werkend te krijgen. De voorbeelden van express-validator zagen er goed te doen uit, maar eenmaal zelf implementeren was best pittig. Toen ik het eenmaal werkend had als JSON, moest ik nog een manier vinden het als pug te kunnen laden. Ik kijk er dus positief op terug dat dit allemaal gelukt is, vooral omdat ik pug ook nog niet kende. Ik heb hier dus veel van geleerd, en het implementeren van je topic binnen de hele scope vond ik een leuke bijdrage.

Bronnen:

Contributor, G. (2021, 3 februari). Form Data Validation in Node.js with express-validator. Stack Abuse. Geraadpleegd op 16 juni 2022, van https://stackabuse.com/form-data-validation-in-nodejs-with-express-validator/

Topic: Node Mailer

Topic Nigel Fijnheer

Node Mailer word gebruik om emails te sturen naar gebruikers. Dit kunnen emails zijn zoals een bevestiging als je een account hebt aangemaakt, of iets hebt veranderd. Het is een goede manier van feedback geven aan de gebruiker als iets gelukt is.

Installeren van Node Mailer

npm install nodemailer

Om nodemailer te gebruiker moet je require gebruiken:

const nodemailer = require("nodemailer")

Transporter

Ook heb je een transporter nodig. Dit is waar je de mail vandaan stuurt. Je kan je eigen domein email gebruiken als je dat hebt, of iets zoals Gmail, Hotmail of Outlook. In de documentatie van Nodemailer stond dat het makkelijk is om Gmail te gebruiken. Dit is wat ik eerst ging proberen, maar je kan vanaf Gmail geen emails meer sturen vanaf minder beveiligde apps. Daarna ben ik gaan kijken of ik Outlook kan gaan gebruiken. Ik heb een nieuw test account aangemaakt bij Outlook. Eerst kreeg ik een authentication error, dit kon ik oplossen door een instelling te wijzigen zodat andere app emails kunnen sturen. Uiteindelijk was dit allemaal gelukt, en kon ik nu emails versturen.

Code voor nodemailer

async function main() {
    let transporter = nodemailer.createTransport({
      host: "smtp-mail.outlook.com", // Host van je email
      port: 587, // Port
      secure: false, 
      auth: {
        user: "Je email", // Je email
        pass: "Je eigen wachtwoord", // Wachtwoord van je email
      },
      tls:{
        ciphers:'SSLv3',
        rejectUnauthorized:false
      }
    });
  
    let info = await transporter.sendMail({
      from: '"Tech team 3" <[email protected]>', // van wie de email komt
      to: req.body.email, // Waar gaat de email heen.
      subject: "Welcome", // Onderwerp
      text: "Your account have been created.",
      html: "<b>Hello world?</b>",  
    });
  
    console.log("Message sent: %s", info.messageId);
  }
  
  main().catch(console.error);

Nodemailer geeft je dus de optie om je eigen mail te maken, en deze te sturen. Ik verstuur deze email als er een account word aangemaakt. Ik gebruik dus de functie main() in de post request van de add pagina

router.post('/add', async(req, res) => {
 // Nodemailer functie
}

Nadat je een gebruiker hebt aangemaakt via het add scherm krijg je nu een email. In deze mail staat de account informatie die je hebt ingevuld in het form. Dat ziet er dan zo uit:

Schermafbeelding 2022-06-19 om 17 32 30

Alternatieven

Om mails te versturen via NodeJS heb ik niet echt goede alternatieven opties kunnen vinden. Nodemailer wordt het meest gebruikt, en geeft je eigenlijk alle opties die je nodig hebt. Ook is het erg flexibel. Je kan je eigen email opzetten via HTML, en je eigen email gebruiken om de mails te versturen. Eigenlijk alles wat je nodig hebt.

Bronnen:

Mongoose

Research topic van Allyssa

Mongoose is een Object Data Modeling library (ODM) voor MongoDB en Node.js. Met Mongoose kan je de relaties beheren tussen data, biedt Mongoose schema validatie en wordt gebruikt om te vertalen tussen objecten in code en de objecten in MongoDB.

Database connectie met mongoose

const mongoose = require('mongoose');

const url = process.env.DB_URI;
const databaseName = process.env.DB_DATABASE;

const connectDB = async () => {
  try {
    mongoose.connect(
      url,{
        dbName: databaseName,
        useNewUrlParser: true,
        useUnifiedTopology: true
      },
    );
    console.log('Connection with database established.');
  }catch (error) {
    console.log(`an error occured: ${error}`)
  }
}

module.exports = connectDB

Mongoose schema vs model

Een Mongoose-model is een wrapper op het Mongoose-schema. Een Mongoose-schema definieert de structuur van het document, waarden etc. terwijl een Mongoose model een interface biedt naar de database voor het maken, opvragen, updaten en verwijderen van data.

Schema definieren

Een schema definieert document properties via een object waarbij de key name overeenkomt met de eigenschap naam in de collection.

const mongoose = require('mongoose')

const studentSchema = new mongoose.Schema(
  {
    firstname: String,
    lastname: String,
    education: String,
    currentSchool: String,
    countryPreference: String,
    email: String,
  }
);

module.exports = studentSchema;

Hierboven defineren we een property genaamd firstname met schema type String die wordt toegewezen aan een interne validator die wordt geactiveerd wanneer het model in de database wordt opgeslagen. Het zal mislukken als het gegevenstype van de waarde geen String is.

De volgende Schema types zijn toegestaan:

  • Array
  • Boolean
  • Buffer
  • Date
  • Mixed (generiek/flexibel datatype)
  • Number
  • ObjectId
  • String

Model

Nu moet de model constructor op de Mongoose instance aanroepen en deze de naam van de collection en verwijzing naar de schema definitie doorgeven. Dat zou kunnen op een aantal manieren kunnen. Het bovenstaande module.exports zou kunnen worden aangepast naar:

module.exports = mongoose.model('students', studentSchema)

In dit project heb ik iets anders gedaan. De bovenste manier is het simpelst omdat je Mongoose dan niet bij elke modules file hoeft op te roepen. Als we misschien wat meer tijd hadden gehad, had ik dit nog kunnen refactoren.

Voor het updaten van een student ziet de module hiervan er zo uit:

const studentSchema = require('../schema/student')
const mongoose = require('mongoose')
const student = mongoose.model('students', studentSchema)

async function updateStudent(userId, newData) {
  try {
    await student.findOneAndUpdate(userId, newData)
    console.log('userId updated')
    return true
  } catch (error) {
    console.warn('updating user failed',error)
    return false
  }
}

module.exports = updateStudent

In de mainControllor wordt de bovenstaande functie opgeroepen bij de router.post('/update')

router.post('/update', async (req, res) => {
  updateStudent({_id: req.body.id}, req.body) ? res.redirect('/') : res.redirect('/404')
});

Bronnen

freeCodeCamp.org. (2019, 7 juni). Introduction to Mongoose for MongoDB. Geraadpleegd op 14 juni 2022, van https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/#:%7E:text=Mongoose%20is%20an%20Object%20Data,of%20those%20objects%20in%20MongoDB.&text=MongoDB%20is%20a%20schema%2Dless%20NoSQL%20document%20database.

Mongoose v6.4.0: Schemas. (z.d.). Mongoose. Geraadpleegd op 14 juni 2022, van https://mongoosejs.com/docs/guide.html

Sessions

Aangezien we geen login systeem hebben vanwege de korte tijd hebben we gekozen om een gebruiker te selecteren bij de index pagina. Netzoals bij het inloggen gebruiken we dan sessions om bij te houden in de applicatie welke gebruiker ingelogd/gekozen is. Ik ging uitzoeken wat ik nodig had om in onze applicatie sessions te gebruiken.

Aangezien we met express werken ontdekte ik al gauw express-session. Een npm package die je helpt met het aanmaken en beheren van sessions in je express applicatie. Met een simpele npm install express-session hebben we express session in het project.

Hoe werkt een session?

Wanneer de client een login-aanvraag doet bij de server, maakt de server een sessie aan en slaat die op aan de server-kant. Wanneer de server antwoordt naar de client, stuurt hij een cookie. Deze cookie bevat de unieke id van de sessie die op de server is opgeslagen, en die nu op de client wordt opgeslagen.

We gebruiken deze sessie-ID en zoeken de sessie op die is opgeslagen in de database of de sessie-winkel om een één-op-één-overeenkomst te handhaven tussen een sessie en een cookie.

Hoe gebruik je een session?

const oneDay = 1000 * 60 * 60 * 24;
app.use(sessions({
    secret: "thisismysecrctekeyfhrgfgrfrty84fwir767",
    saveUninitialized:true,
    cookie: { maxAge: oneDay },
    resave: false 
}));

secret - a random unique string key used to authenticate a session. It is stored in an environment variable and can’t be exposed to the public.
resave - takes a Boolean value. It enables the session to be stored back to the session store, even if the session was never modified during the request.
saveUninitialized - this allows any uninitialized session to be sent to the store.
cookie: { maxAge: oneDay } - this sets the cookie expiry time. The browser will delete the cookie after the set duration elapses.

Het opzetten van de basis

Om de sessie te initialiseren, stellen we de sessie middleware in binnen de routes van de individuele HTTP verzoeken.

Wanneer een client een request stuurt, zal de server een session ID instellen en de cookie gelijk aan die session ID zetten. De cookie wordt dan opgeslagen in de set cookie HTTP header in de browser. Telkens wanneer de browser (client) zich vernieuwt, zal de opgeslagen cookie deel uitmaken van dat verzoek.

stap 1: Node.js libraries inladen + initialiseren + session opties

Importeren

const express = require('express');
const cookieParser = require("cookie-parser");
const sessions = require('express-session');

Initializeren

const app = express();
const PORT = 4000;

Express-sessions opties toevoegen

// creating 24 hours from milliseconds
const oneDay = 1000 * 60 * 60 * 24;

//session middleware
app.use(sessions({
    secret: "thisismysecrctekeyfhrgfgrfrty84fwir767",
    saveUninitialized:true,
    cookie: { maxAge: oneDay },
    resave: false
}));

Authenticatie credentials toevoegen

//username and password
const myusername = 'user1'
const mypassword = 'mypassword'

// a variable to save a session
var session;

Het toevoegen van de endpoints

Nu de basis staat checken we of de credentials valid zijn of niet. Dit kan op de volgende manier:

app.get('/',(req,res) => {
    session=req.session;
    if(session.userid){
        res.send("Welcome User <a href=\'/logout'>click to logout</a>");
    }else
    res.sendFile('views/index.html',{root:__dirname})
});

**Als de credentials valid zijn: **

  • Gebruiker krijgt toegang
  • Server maakt tijdelijke session aan met random string (user ID)
  • Server stuurt cookie naar client's browser. Hier zit het session ID in. Wanneer de gebruiker's browser de cookie opslaat komt deze bij elke request naar de server weer mee.

https://www.section.io/engineering-education/session-management-in-nodejs-using-expressjs-and-express-session/

https://www.npmjs.com/package/express-session

⚠️ **GitHub.com Fallback** ⚠️