CRUD Meeps

Create, read, update, delete

CRUD Àr de fyra grundlÀggande funktionera för att arbeta med data(create, read, update, delete). Det Àr inte kopplat till webben eller databas Àven om det Àr vÀldigt vanligt i det sammanhanget. Konceptet Àr att data ska kunna skapas, lÀsas, redigeras och tas bort.

Det som följer hÀr nedan Àr ett exempel pÄ CRUD. Exemplet Àr skapat i samma kod som anvÀnds för inloggning. Inloggningen Àr dock inget krav och det gÄr utmÀrkt att skapa detta utan en inloggning. Du behöver dÄ ersÀtta user fÀltet med anvÀndarens namn, eller skippa det helt.

Notera att koden i detta avsnitt kan krÀva att du Àndrar din databasmodell till att inte anvÀnda ..params

Router

Skapa en router för meeps, detta kommer hÄlla huvuddelen av arbetet. Detta kan implementeras vid sidan av login systemet, sedan kan funktionalitet flyttas till index eller sÄ.

Routern kommer att behöva följande delar, börjar med att skapa logiken direkt i routern, men det kommer bli ganska mycket kod sÄ det rekommenderas att du flyttar det du kan till en controller.

  • show, visa alla resurser

  • show/:id, visa en resurs med :id

  • create, skapa en resurs

    • GET visar ett formulĂ€r

    • POST skapar resursen

  • update/:id, uppdatera en resurs med :id

    • GET visar ett formulĂ€r

    • POST uppdaterar resursen

  • delete, ta bort en resurs

De router som lÄter anvÀndaren manipulera en resurs behöver verifiera att anvÀndaren Àr inloggad. Grunden till det finns i login systemet. För att förenkla inloggningskontrollen ska vi skapa en middleware för att sköta det.

Om du inte anvÀnder inloggningen sÄ behöver du inte heller verifiera en anvÀndare. Systemet Àr dÄ oskyddat, men det gÄr utmÀrkt att testa.

Verify, middleware

I nulÀget sÄ kontrolleras anvÀndarens session pÄ varje stÀlle dÀr inloggning krÀvs med en if sats, det Àr bÄde osÀkert, besvÀrligt och svÄrt att hantera. Koden för att bekrÀfta anvÀndarens session kan flyttas till en middleware(ett tillÀgg som kan kallas pÄ i express). Detta middleware, som vi kallar för verify, anropas sedan pÄ i de router som ska verifieras.

routes/home.route.js
// session kontroll med if sats i routen
router.get('/', (req, res, next) =>{
  if (req.session.loggedin) {
    res.render('home', {
      username: req.session.username
    });
  } else {
    res.redirect('/login');
  }
});

Read, GET routes

Ofta pÄbörjas arbetet med CRUD genom att kunna skriva ut en resurs, det vill sÀga att lÀsa den. DÄ fÄr resursen skapas manuellt i databasen. Designen för resursen i det hÀr fallet bygger pÄ tidigare kapitel i boken. Det krÀvs tvÄ tabeller för resurserna, meeps och users dÄ meeps innehÄller en relation till users tabellen.

Skapa tabellerna och testdata innan du gÄr vidare.

mysql> describe meeps;
+------------+-----------------+------+-----+---------+----------------+
| Field      | Type            | Null | Key | Default | Extra          |
+------------+-----------------+------+-----+---------+----------------+
| id         | bigint unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | bigint unsigned | NO   |     | NULL    |                |
| body       | text            | NO   |     | NULL    |                |
| created_at | timestamp       | YES  |     | NULL    |                |
| updated_at | timestamp       | YES  |     | NULL    |                |
+------------+-----------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Routerna för att lÀsa resurser ser ut som följer. Det handlar först och frÀmst om att hÀmta resurser med SQL. FrÄgan anvÀnder JOIN för att hÀmta anvÀndarens namn frÄn users tabellen. Utskriften behöver skapas med en template och kallas pÄ genom res.render.

Följande kodexempel anvÀnder query metoden frÄn SQL och Node kapitlet.

Skippar du anvÀndartabellen sÄ lÀgger du till author direkt i meeps. Du behöver dÄ inte anvÀnda JOIN.

routes/meeps.route.js
/* GET all meeps */
router.get('/',
  async (req, res, next) => {
    const sql = 'SELECT meeps.*, users.name AS author FROM meeps JOIN users ON meeps.user_id = users.id';
    result = await query(sql);
    res.send(result);
});

/* GET a meep */
router.get('/:id',
  param('id').isInt(),
  async (req, res, next) => {
    const sql = 'SELECT meeps.*, users.name AS author FROM meeps JOIN users ON meeps.user_id = users.id WHERE meeps.id = ?';
    result = await query(sql, req.params.id);
    res.send(result);
});

Glöm inte att registrera routen i appen.

app.js
...
const meepsRouter = require('./routes/meeps.route');
...
app.use('/meeps', meepsRouter);

Create, GET och POST routes

Dessa routes kommer att anvÀnda verify middlewaren för att autentisera anvÀndaren. Routerna kommer Àven att behöva validera och tvÀtta input för att sÀkra systemet.

För att skapa nya resurser anvÀnds ett webbformulÀr, routen för att nÄ formulÀret kan till exempel vara /new. Det Àr viktigt att denna route kommer före :id routen(i koden) för att hÀmta enskilda resurser, annars kommer det inte att fungera.

Router som lÀser parametrar med :PARAM behöver komma efter de andra i ordningen. Det som sker annars Àr att /new hamnar i /:id parametern.

SjÀlva formulÀret kan med fördel skrivas som en komponent och inkluderas pÄ andra sidor för att skapa nya resurser.

routes/meeps.route.js
/* GET a form for posting a new meep  */
router.get('/new',
  verify,
  (req, res, next) => {
    res.render('meepsform');
});

FormulÀret kommer att posta resursen till /meeps dÀr POST routen hanterar det.

Koden Àr osÀker och hanterar inte XSS, testa genom att skriva html kod i en meep.

routes/meeps.route.js
/* POST a new meep */
router.post('/',
  body('body').notEmpty(),
  verify,
  async (req, res, next) => {
    const sql = 'INSERT INTO meeps (user_id, body, created_at, updated_at) VALUES (?, ?, now(), now())';
    const result = await query(sql, [req.session.userid, req.body.body]);
    if (result.insertId > 0) {
      res.redirect('/meeps/' + result.insertId);
    }
});

För att kontrollera att en resurs skapats sÄ kontrollerar vi att SQL frÄgans resultat innehÄller ett nytt id. NÀr resursen skapats sÄ skickas anvÀndaren vidare till GET routen för att visa en resurs med :id.

Utan user_id med en author sÄ sparar du inte det i databasen utan author. Du anvÀnder inte heller dÄ req.session.userid

Update, GET och POST routes

För att uppdatera en resurs anvÀnds resursens id för att hitta den. Det finns ett antal sÀtt att skicka med id vÀrdet pÄ. I det hÀr exemplet kommer formulÀret innehÄlla resursens id, vÀrdet skickas i en input med typen hidden.

För att komma Ät id vÀrdet skapas en GET update route som tar id vÀrdet som en parameter. VÀrdet lÀses in och skrivs ut i formulÀret.

Resursens id anvÀnds för att hÀmta resursen frÄn databasen. Resursen vÀrden behövs sÄ att den faktiskt kan uppdateras, dessa skickas med res.render för att kunna anvÀndas pÄ formulÀrsidan.

routes/meeps.route.js
/* GET form for updating a meep  */
router.get('/update/:id',
  param('id').isInt(),
  verify,
  async (req, res, next) => {
    const sql = 'SELECT meeps.*, users.name FROM meeps JOIN users ON meeps.user_id = users.id WHERE meeps.id = ?';
    result = await query(sql, req.params.id);
    res.render('meepsform', { meep: result[0] });
});

Nedan följer den sista delen, post routen till update som hanterar den faktiska uppdateringen av resursen. Routen behöver fortfarande sÀkras, validera och tvÀtta data samt hantera fel.

routes/meeps.route.js
/* POST a new meep */
router.post('/update',
  body('meepid').isInt(),
  body('body').notEmpty(),
  verify,
  async (req, res, next) => {
    const sql = 'UPDATE meeps SET body = ?, updated_at = now() WHERE id = ?';
    const result = await query(sql, [req.body.body, req.body.meepid]);
    if (result.changedRows > 0) {
      res.redirect('/meeps/' + req.body.meepid);
    }
});

DELETE, POST route

HÀr presenteras enbart en POST route för att ta bort en resurs. Denna kan lÀnkas frÄn en lista av resurserna eller liknande. Det kan sÄklart vara bra att lÄta anvÀndaren bekrÀfta borttagningen av en resurs, dÄ kan javascript anvÀnds pÄ klient-sidan eller sÄ skapas en GET route för delete, som i sin tur pekar till POST routen.

Alternativt Àr en GET route till /delete/:id ocksÄ en möjlighet för att ta bort resurser.

routes/meeps.route.js
/* POST to delete a meep */
router.post('/delete/',
  body('meepid').isInt(),
  verify,
  async (req, res, next) => {
    const sql = 'DELETE FROM meeps WHERE id = ?';
    const result = await query(sql, req.body.meepid);
    if (result.affectedRows > 0) {
      res.redirect('/meeps');
    }
  });

Summering

Ovan finns alla delarna för att utföra CRUD operationer pÄ en resurs, i detta fall meeps. Systemet krÀver fortfarande mycket arbete, med bÄde sÀkerhet och anvÀndbarhet, men grunden Àr dÀr.

Den kod som presenteras pÄ sidan hör till största del till routes filen för resursen. Den koden bör landa pÄ ett 100-tal rader utan sÀkerhet och felhantering. Det Àr hanterbart, men du bör definitivt flytta delar till en controller om du bygger vidare pÄ systemet.

Last updated