backend | autentikaatio

Notes-demo 2 (1/2)

Projekti

Tarkoituksena on tehdä backend joka toimii frontendin ja tietokannan välissä. Käytetään node/express-kirjastoa webbiserversin toteutukseen. Lisämateriaalia löytyy Full Stack open-kurssin osasta 3.

Tee notesdemo-kansion alle uusi kansio notesback, suorita siellä npm init. Asenna tämän jälkeen tarvittavat kirjastot:

npm install express --save
npm install mysql2 --save
npm install knex --save    

Lisää .gitignore jossa

node_modules/
.env
dist/
build/    

Tee notesback-kansion juureen tiedosto index.js:

const express = require('express')
const app = express()

app.get('/', (req, res) => {
  res.send('<h1>Hello World!</h1>')
})

const PORT = 3001
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})    

Kokeile käynnistää webbiserveri:

node index.js

Nyt osoitteessa http://localhost:3001 toimii backend. Jos kokeilet tehdä muutoksia Hello World-tekstiin se ei päivity. Jotta päivitykset tulisivat automaattisesti käyttöön lisää kirjasto nodemon:

npm install nodemon --save-dev

Käynnistä palvelin uudestaan:

npx nodemon index.js

Lisää käynnistyskomento package.json-tiedostoon jotta voit käynnistää sen jatkossa helposti komennolla npm start.

"start": "npx nodemon index.js",

Tietokantayhteys

Aikaisemmin on tehty tietokanta Docker-ympäristöön. Käynnistä Docker ja varmista että tietokanta on olemassa. index.js-tiedostossa määritellään yhteys tietokantaan knex:n avulla:

const options = {
    client: 'mysql2',
    connection: {
        host: '127.0.0.1',
        user: 'root',
        password: 'mypass123',
        database: 'notesdemo_db'
    }
}
const knex = require('knex')(options);    

Lisää index.js-tiedoston loppuun /GET -kysely jolla haetaan kaikki musitiinpanot:

app.get('/notes', (req, res) => {
    knex('notes').select('*')
        .then((rows) => {
            console.log(rows);
            res.json(rows);
        })
        .catch((err) => {
            console.log('SELECT * NOTES failed')
            res.status(500).json(
                { error: err }
            )
        })
})    

Testaa näkyvätkö muistiinpanot osoitteessa http://localhost:3001/notes.

Lisää kansio tests REST-testeille. Tee tiedosto notes_get.http joka sisällöksi seuraava HTTP-pyyntö:

GET http://localhost:3001/notes HTTP/1.1

env

Ensimmäisessä esimerkissä tietokantayhteys on määritelty suoraan index.js-tiedostossa. Yhteyden tiedot riippuvat ympäristöstä ja ne tulee erottaa .env-tiedostoon. Luo backendin juureen uusi tiedosto .env:

DB_HOST = localhost
DB_USER = root
DB_PASS = mypass123
DB_DATABASE = notesdemo_db
DB_TYPE = mysql2
DB_PORT = 3306
SECRET = tosisalainensalasanainen
PORT = 3001    

Kannattaa kopioida .env myös tiedostoon .env-local koska käytössä oleva env-tiedosto ei tallennu GitHub-repoosi.

Asenna käyttöön dotenv

npm install dotenv --save

Luo alikansio utils ja sinne tiedosto config.js. Tiedostossa luetaan .env ja haetaan tiedot muuttujiin. SECRET tulee käyttöön vasta myöhemmin autentikaation myötä.

require('dotenv').config()

let PORT = process.env.PORT
let SECRET = process.env.SECRET

let DATABASE_OPTIONS = {
    client: process.env.DB_TYPE,
    connection: {
        host: process.env.DB_HOST,
        user: process.env.DB_USER,
        password: process.env.DB_PASS,
        database: process.env.DB_DATABASE
    }
}

module.exports = {
    DATABASE_OPTIONS,
    PORT,
    SECRET
}    

index.js-tiedostossa poista kovakoodattu tietokantayhteys sekä portti. Käytetään config.js-tiedoston lukemia tietoja:

const config = require('./utils/config')
const options = config.DATABASE_OPTIONS;
const PORT = config.PORT;

Debuggausta varten voit kehitysvaiheessa lisätä index.js-tiedostoon rivin knex.on('query', console.log);.

POST

Uuden muistiinpanon lisääminen tapahtuu kutsumalla app.post-metodia:

app.post('/notes', (req, res) => {
   const note = req.body;
   console.log(note);
})    

Lisää tests-kansioon REST-testi muistiinpanon lisäämiselle:

POST http://localhost:3001/notes HTTP/1.1
content-type: application/json

{
    "content": "REST-testi",
    "date": "2026-01-10T17:30:31.098Z",
    "important": false
}    

Muistiinpano tulee json-muodossa. Lisaä app.use(express.json()); backendin index.js-tiedostoon. Kun ajat nyt testin niin muistiinpano tulostuu console.log:n avulla konsoliin:

Muistiinpano pitäisi seuraavaksi tallettaa tietokantaan.

const newNote = {
    content: note.content,
    important: note.important,
    date: new Date(note.date),
    user_id: 1 /* TÄMÄ KORJATAAN KIRJAUTUMISEN JÄLKEEN */
}

knex('notes').insert(newNote)
    .then(id_arr => {
        console.log(id_arr);
        newNote.id = id_arr[0];
        res.json(newNote);
    })    

Suorita testi ja varmista onnistuuko muistiinpanon tallennus.

Lisää catch-haara .then-osion jälkeen, katso mallia muistiinpanojen hakemisesta. Yleinen virhetilanne ovat puuuttuvat tiedot. Ennen lisäämistä varmistetaan että note sisältää oikeat tiedot. Virhetilanteessa palautetaan 400-virhe.

if (note.content === undefined || note.date === undefined || note.important === undefined) {
    return res.status(400).json(
        { error: "check json-data" }
    )
}    

DELETE

Poistaminen tapahtuu app.delete-metodin avulla. Poistettava id saadaan parametrina.

app.delete('/notes/:id', (req, res) => {
    const id = req.params.id;
    console.log(id);
})    

Lisää REST-testi joka poistaa muistiinpanon.

DELETE http://localhost:3001/notes/4 HTTP/1.1

Varmista tuleeko konsoliin oikea id. Tämän jälkeen lisätään varsinainen poistaminen funktiolle:

knex('notes').where('id', '=', id).del()
    .then(status => {
        console.log("delete ok")
        res.status(204).end();
    })    

Lisää jälleen catch-haara.

PUT

Muistiinpanon muokkaaminen tapahtuu app.put-metodilla. Pyyntö saa json-tietona päivitettävän muistiinpanon sekä parametrina id:n.

app.put('/notes/:id', (req, res) => {
    const id = req.params.id;
    const note = req.body;

    const updatedNote = {
      content: note.content,
      important: note.important,
      date: new Date(note.date)
    }

    knex('notes').update(updatedNote).where('id', '=', id)
        .then((response) => {
            console.log(response)
            res.status(204).end();
        })
})    

Tee REST-testi put_notes.http joka muokkaa muistiinpanoa.

PUT http://localhost:3001/notes/5 HTTP/1.1
Content-Type: application/json

{
    "content": "Muutettu muistiinpano",
    "date": "2026-01-10T17:30:31.098Z",
    "important": true
}    

Lisää jälleen catch-haara sekä varmista että note sisältää oikeat tiedot.

Frontend

Voit käynnistää nyt notesdemo:n aiemmin tehdyn frontendin. JSON-serverin sijaan tarkoitus on käyttää backendin välittämiä tietoja. Saat kuitenkin konsoliin CORS-virheilmoituksen (Cross-origin resource sharing):

Ongelma on se, että selaimen JavaScript on portissa 5173 ja tietokannan portti on 3001, niiden Origin on eri. Lisätietoa: Fullstack MOOC: CORS.

Otetaan backendissä käyttöön Noden cors-middleware:

npm install cors --save

Lisää index.js-tiedostoon

const cors = require('cors')
app.use(cors())    

Seuraavaksi toteutetaan autentikaatio notesdemoon.