<- Tillbaks

Vilka steg för att komma igång

// Importera express
const express = require("express");
var cookieParser = require('cookie-parser');

// Skapa en express app
const app = express();

// Använda moduler
app.use(cookieParser());

Node.js

Node.js är en JavaScript runtime byggd på Chrome V8 motorn. Det kör JavaScript kod på server-sida. Det är non-blocking, asynkron-arkitektur och körs på singel threaded. Det kan ha tillgång till filer och operativsystem datavaser som kan inte göras i webbläsaren. Det håller koll på sessions och cookies.

Moduler

Att använda moduler importera och exportera funktioner och variabler mellan olika filer. Det är ett sätt att organisera koden och göra den mer läsbar och underhållbar.

// Importera moduler
const express = require("express");
const fs = require("fs");
const path = require("path");
//...

Routrar

Routrar används is den kontext som vi lärde att dela upp funktionalitet för olika URL endpoints. (ex. localhost:xxxx/api/hello_world, /, /wow ect).

Att komma igång behövs det express.

// index.js

// Importera express modul
const express = require("express");

// Importera routrar
const helloWorldRouter = require("./routes/hello_world");

// Skapa en express app
const app = express();

// Använda routrar (localhost:xxxx/api/hello_world)
app.use("/api/hello_world", helloWorldRouter);

Router fil ligger i en mapp kallad routers och filen själv heter hello_world.js.

// hello_world.js

// Importera express modul
const express = require("express");

// Skapa router
const router = express.Router();

// Skapa en endpoint för /api/hello_world

// Request och response objekt är inbyggda i express och används för att hantera inkommande request och skicka svar tillbaka till klienten.
router.get("/", (req, res) => {
		res.send("Hello World!");
});

// Exportera routern så index.js kan använda den router
module.exports = router;

HTTP metoder

Det finns olika HTTP metoder som används för att skicka och ta emot data mellan klient och server. De vanligaste metoderna är:

Masterframe konsept

Iställe för att ha en stor statisk HTML dokument man kan ha mindre delar som kombineras i en router. Det är bra att omanvända delar och ha lättare vis att ändra delar över hela sidan. Man kan ha alla HTML delar i en mapp kallad masterframe. Vi kan laga en funktion i en module som returnerar html filen

// readHTML.js
var fs = require('fs');

function readHTML(htmlfile) {
	try {
		htmltext = fs.readFileSync(htmlfile, 'utf8');
	} catch (err) {
		console.error(err);
	}
	return htmltext;
}

module.exports = readHTML;
// Exempel på en router där det används masterframe
const express = require("express");
const router = express.Router();

const fs = require("fs");
var path = require("path");
const readHTML = require('./readHTML');

var htmlPart = readHTML('./masterframe/hello_world.html');
var htmlPart2 = readHTML('./masterframe/hello_world2.html');

router.get("/", (req, res) => {
		res.write(htmlPart);

		res.write("<p>Hello World!</p>");

		res.write(htmlPart2);
		res.end();
});

Request Processing Pipeline

Databas koppling

En exempel för att koppla till en databas från klient-sida kan vara:

  1. Checking server up-status (Ok/Error)
  2. Loading connection bootstrap (Ok/Error)
  3. Starting encryption engine (Ok/Error)
  4. Creating VPN-tunnel (Ok/Error)
  5. Checking credentials (Ok/Error)
  6. Loading database handshake (Ok/Error)
  7. Try/catch connecting (Ok/Error)
  8. Checking connection status (Ok/Error)

Your are in!

Det kan implementeras med Promise, Callback eller Async/Await för att ha asynkronitet för processen att kopla till en databas.

// Exempel på en Promise som kopplar till en databas
function connectToDatabase() {
	return new Promise((resolve, reject) => {
		// Simulera en databasanslutning
		setTimeout(() => {
			const success = true; // Simulera en lyckad anslutning
			if (success) {
				resolve("Connected to database");
			} else {
				reject("Failed to connect to database");
			}
		}, 1000); // Simulera en fördröjning på 1 sekund
	});
}

// Använda Promise
connectToDatabase()
	.then((message) => {
		console.log(message); // "Connected to database"
	})
	.catch((error) => {
		console.error(error); // "Failed to connect to database"
	});
// Exempel på en Async/Await som kopplar till en databas
async function connectToDatabase() {
	try {
		// Simulera en databasanslutning
		setTimeout(() => {
			const success = true; // Simulera en lyckad anslutning
			if (success) {
				resolve("Connected to database");
			} else {
				reject("Failed to connect to database");
			}
		}, 1000); // Simulera en fördröjning på 1 sekund

		console.log(message); // "Connected to database"
	} catch (error) {
		console.error(error); // "Failed to connect to database"
	}
}
connectToDatabase();
// Exempel på en Callback som kopplar till en databas
function connectToDatabase(callback) {
	// Simulera en databasanslutning
	setTimeout(() => {
		const success = true; // Simulera en lyckad anslutning
		if (success) {
			callback(null, "Connected to database");
		} else {
			callback("Failed to connect to database", null);
		}
	}, 1000); // Simulera en fördröjning på 1 sekund
}

// Använda Callback i parametern
connectToDatabase((error, message) => {
	if (error) {
		console.error(error); // "Failed to connect to database"
	} else {
		console.log(message); // "Connected to database"
	}
});

// Kan visas också
connectToDatabase((error, message) => { if (error) { console.error(error); } else { console log(message); } });


// BE AWARE OF CALLBACK HELL! Att man inte har massa callbacks för att det blir svårt att läsa koden

Databas koppling (ADODB)

ADODB är en databas koppling som används för att koppla till en mdb databas. Det är en del av Microsofts ActiveX Data Objects (ADO) och används för att hämta och manipulera data i databaser.

Exempel på hur man använder med express router:

// Importera express modul och skapa router
const express = require("express");
const router = express.Router();
// Importera ADODB modul
const ADODB = require("node-adodb");

// Skapa en ADODB databas koppling
router.get('/:id', (request, response) => {
	// Id som man har satt i URL:en
	const id = request.params.id;
	// Koppla till databasen som ligger i /data/mdb/ mappen
	const connection = ADODB.open('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=./data/mdb/somedatabase.mdb;');

	// En asynkron funktion som hämtar data från databasen
	async function sqlQuery() {
		const result = await connection.query(`SELECT * FROM somedatabase WHERE id = ${id}`);

		// Testar om resultatet är tomt
		if (result.length === 0) {
			response.send("No data found");
			return;
		}

		// Ta ut datan från resultatet
		const date = result[0]['date'];
		const text = result[0]['text'];

		response.send(date);
		response.write('<br>');
		response.send(text);
	}

	// Anropa funktionen 
	sqlQuery();

});

Callstack

Callstack är en struktur som används för att hålla reda på vilka funktioner som har anropats och i vilken ordning. När en funktion anropas läggs den till i callstacken, och när den är klar tas den bort från callstacken. Det är viktigt att förstå hur callstacken fungerar för att kunna hantera asynkronitet och undvika problem som “callback hell”.

// Exempel på hur callstacken fungerar
function firstFunction() {
		console.log("First function called");
		secondFunction();
}

function secondFunction() {
		console.log("Second function called");
		thirdFunction();
}

function thirdFunction() {
		console.log("Third function called");
}

Det här exemplet visar hur callstacken fungerar. När firstFunction anropas läggs den till i callstacken, och när den är klar anropas secondFunction, som också läggs till i callstacken. När secondFunction är klar anropas thirdFunction, som också läggs till i callstacken. När thirdFunction är klar tas den bort från callstacken, och så vidare.

Ajax (Asynchronous JavaScript and XML)

Ajax tillåter interaction med server efter klient-sidan har laddat in. Det gör att man kan updatera sidan utan att behöva ladda om sidan.

Hur man använder:

// Exempel att hämta filen ajax_info.txt med Ajax och sätta in i elementet med id "demo"
function loadDoc() {
	// Skapa en XMLHttpRequest objekt
  const xhttp = new XMLHttpRequest();
	// Sätta en callback funktion som körs när requesten är klar
  xhttp.onload = function() {
		// Sätta in innehållet i elementet med id "demo"
    document.getElementById("demo").innerHTML = this.responseText;
    }
	// Öppna en GET request till filen ajax_info.txt
  xhttp.open("GET", "ajax_info.txt", true);
	// Skicka requesten
  xhttp.send();
}

I modärn tid kan man använda Fetch API istället också.

Sessions och Cookies

Sessions används för att hålla reda på användarens status och information under en period. Det är viktigt för att kunna hantera inloggningar och andra användarspecifika funktioner på server-sidan. Sessions lagras på servern och en session ID skickas till klienten som en cookie. När klienten gör en begäran till servern skickas session ID:t med i begäran så att servern kan identifiera användaren.

Cookies är små textfiler som lagras på klientens dator och används för att lagra information om användaren. Cookies kan användas för att lagra sessions ID, inloggningsinformation och andra användarspecifika inställningar. Cookies har en utgångstid och kan raderas av användaren när som helst.

Exempel node express som använder sessions och cookies:

// Importera express modul och skapa router
const express = require("express");
const app = express();

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

app.use(cookieParser());
app.use(session({secret: 'this-is-a-secret-token', resave: false, saveUninitialized: true}));

app.get('/', (req, res) => {
	// Kolla om man är loggad in i session
	if (req.session.loggedin) {
		res.send('Welcome back!');
	} else {
		res.send('Please login to see this page!');
	}


	
});

Asynkronitet och blocking code

Synchronus

Varje kodrad körs efter att föregående kodrad är avslutad. Man kan läsa koden uppifrån och ned och få en känsla för den ordning som koden exekveras i.

I synkrom programmering så blockar programmet när det väntar på input från användaren, eller när programmet läser en fil. Programexekveringen slutar tills det kommer input, från användaren eller tills dess filen är inläst.

Asynchronus

En central event loop styr exekveringen. Så fort en operation, en kodrad, blockar länas exekveringskontrollen över till event loopen. Exempel när vi låter användaren mata in något från tangentbordet, eller när vi läser och skriver till filer. Dessa är blockande operationer. Tanken är att event loopen tar över så fort någon exekvering blockar. Event loopen kan då lämna över exekveringen till en annan del i programmet som inte väntar, och på det sättet exekveras hela programmet snabbare.

Så fort något blockar så lämnas kontrollen över till de programdelar som inte blockar och kan exekveras.

Callback

Man kan ha funktioner som in-parameter. Den praktiken kalls “callback”. Det är en funktion som skickas som argument till en annan funktion. Denna funktion kan anropas när den andra funktionen är klar med sin uppgift. Det är ett sätt att hantera asynkronitet i JavaScript.

function functionOne(name, callback) {
		callback(name);
}

function functionTwo(name) {
		console.log("Hello " + name);
}

functionOne("World", functionTwo);

Man kan förkorta det att ha funktionen direkt som parameter.

function functionOne(name, callback) {
		callback(name);
}

//Kallar functionOne -- Sätter function two direkt i parameter 
functionOne("World", functionTwo(name) { console.log("Hello " + name);} );

Promises

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Promises representerar är asynchronous operationer som kan göras i framtiden. Det är snyggare vis att göra än callbacks.

const myPromise = new Promise((resolve, reject) => {
		// Gör något asynkront
		const success = true; // Simulera en lyckad operation
		if (success) {
				resolve("Operation successful");
		} else {
				reject("Operation failed");
		}
});

myPromise
  .then((result) => {
    console.log(result); // "Operation successful!"
    return "Next step";
  })
  .then((newResult) => {
    console.log(newResult); // "Next step"
  })
  .catch((error) => {
    console.error(error); // Error handling
  })
  .finally(() => {
    console.log("Promise completed"); // Runs regardless of success/failure
  });

Man kan också göra Promise.all()

Promise.all([
		promise1,
		promise2,
		promise3
])
	.then((results) => {
				// Alla promises har lyckats
				console.log(results);
		})
	.catch((error) => {
				// Någon promise misslyckades
				console.error(error);
		});

Promise.race() kan användas för att köra flera promises och få resultatet av den första som lyckas.

Promise.race([
		promise1,
		promise2,
		promise3
])
	.then((result) => {
				// Den första som lyckades
				console.log(result);
		})
	.catch((error) => {
				// Någon promise misslyckades
				console.error(error);
		});

Async/Await

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Async/Await är en syntaktisk socker för att hantera asynkron kod. Det gör koden mer läsbar och lättare att förstå. Det är ett sätt att skriva asynkron kod som ser ut som synkron kod.

async function myAsyncFunction() {
		try {
				const result = await myPromise; // Väntar på att promise ska uppfyllas
				console.log(result); // "Operation successful!"
		} catch (error) {
				console.error(error); // Error handling
		} finally {
				console.log("Async function completed"); // Runs regardless of success/failure
		}
}

DOM (Document Object Model)

DHTML (Dynamic HTML)

https://en.wikipedia.org/wiki/Dynamic_HTML

DHTML tillåter utvecklare att göra saker med DOM med att kombinera HTML, CSS och JavaScript.

DHTML är inte vanlig att använda i konversation för det har blivit standard.

DHTML har inga interaktioner med servern efter det är laddat på klient-sidan.

Att få en element i DOM’en kan göras med document.getElementById("id") och document.getElementsByClassName("class"). Och om man vill få attribut, innehåll eller utsende används .value, .innerHTML och .style.

Man kan ha event listeners för element att ge olika funktionalitet för en element

element.addEventListener("event", function() {
		// do something
});

// Eller

element.onclick = function() {
		// do something
}

Man kan ha olika events som onclick, onmouseover, onmouseout, onchange, onfocus, onblur, onsubmit och onload. Dessa kan användas för att ge olika funktionalitet till element.

Man kan ersätta function orded med en arrow.


function functionOne(name) {
		console.log("Hello " + name);
}

// Förkortad version med arrow function
functionOne = (name) => {
		console.log("Hello " + name);
}

Det finns olika events för keybindings.

window.addEventListener("keydown", function(event) {
		alert("Key pressed: " + event.key);
});

Former

När man försöker skicka filer i en form måste form element ha attribut enctype="multipart/form-data"