TP Express CRUD

De $1

Introduction

Pour le moment, on a pas de base de données, donc on va juste simuler... Mais on prépare le routage, la page d'accueil etc. J'ai copié un TP de Peter Sander qu'il donne dans le cadre du cours "Programmable Web server-side" à Polytech (je donne le cours "client-side").

Dans cet exemple on utilise un générateur d'appli qui font du CRUD à l'aide du module express-generator. Par ailleurs, nous n'allons pas utiliser app.update et app.delete pour les opérations de suppression et mise à jour, car pour tester, il nous faudrait écrire un client Ajax. Pour le moment on fera cela avec du GET, par la suite il suffira de changer app.get en app.update, lorsque nous aurons un client Ajax.

 

Simple example of CRUD routing

Here, we're concerned with how browser URLs get mapped to our application code. For example, when we type localhost:3000 into the browser address bar, we want our application to display the project home page in the browser. When we type  localhost:3000/read we'd like everything in the database to display. Typing localhost:3000/read/foo would read and display the specific item indexed by foo. And so on.

To simplify and to concentrate just on the routing

  • we're not actually getting items from the database, the server is just going to make stuff up
  • we're just doing straight hard-wired HTML; no templates
  • we're using forms, meaning that we're getting new pages when we need to change what's displayed (we'll do a single-page display with AJAX later).

Tools

We'll assume you've already got npm, Node.js and express installed. You'll also need

  • sudo npm install -g express-generator
  • sudo npm install -g body-parser

Getting started

  1. Pick a directory to install the project, eg, routing. Then let express generate the project skeleton code
    • express routing

This should spew out some lines indicating that it's creating files and then the routing directory should contain: app.js  bin  package.json  public  routes  views

  1. Install the necessary modules
    • cd routing
    • npm install

This should spew out some lines indicating what modules it's getting. There should now be a new directory node-modules with the necessary modules stored locally to the project. Storing the modules locally is generally a good idea to prevent updates to the global modules from breaking a running application that might rely on older versions.


Actually, there was too much automatically-generated functionality. We're only going to be modifying the file routes/index.js. But we'll leave the un-needed stuff in place rather than deleting it; it doesn't get in the way.

We're ready to roll. To start up the server

  • npm start

We need to restart the server with the above line after every code change in the stuff below.

Read with GET

We're going to get information from the server (which really should be getting it from the database) to the client browser.

Home page

The first modification to app.js is to get rid of the template and just hard-wire some HTML for the home page. Make it look like this

var express = require('express');
var router = express();

/* GET home page. */

router.get('/', function (req, res, next) {
var html = '<p>got home page</p>';
res.send(html);
});

module.exports = router;

Pretty trivial: localhost:3000 just displays a header saying what's up.

NB: All further additions to app.js in what follows go before the module.exports = router; line. This is important.

All items

The idea now is to get all items from the database. Adding

/* Read stuff. */

/* GET everything */

router.get('/read', function (req, res, next) {
// get all entities from db
var html = '<p>read everything from db</p>';
res.send(html);
});

and browsing to localhost:3000/read in the address bar should do it. The line // get all entities from db is where the code to actually access the database would go.

Specific item

Now we'd like to get just one specific item from the database. Adding

/* GET something specific. */
router.get('/read/:id', function (req, res, next) {
// get indicated entity from db
var html = '<p>read ' + req.params.id + ' from db</p>';
res.send(html);
});

and browsing to localhost:3000/read/foo should do it.

Well, that was pretty dull.

Create with POST

Now comes the wildly exciting part - getting information from the browser to the server. This is a multi-step process

  1. we first get a form from the server to the client
  2. fill out the form and send it back to the server
  3. parse the form and do what's needed (generally stuff the information from the form into the database)
  4. send a new display to the browser.

The first modification to app.js is to get the modules needed to parse the form. The start of the file should look like this

var express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
router.use(bodyParser.json());

Then add

/* Create stuff. */

/* A browser address bar can only issue GET requests.

GET form to fill in with new object details. */
router.get('/create', function (req, res, next) {
var html = '<form method="post" action="/created">' +
'<div><input type="text" name="something" placeholder="something"></div>' +
'<div><input type="submit" value="go"></div>' +
'</form>';
res.send(html);
});

/* POST to parse form and create something specific. */

router.post('/created', function (req, res, next) {
// parse form, create new entity, insert new entity into db
var html = '<p>created new ' + req.body.something + ' in db</p>';
res.send(html);
});

to the end of the file. The first block puts up a very simple form on the browser when the URL localhost:3000/create is typed into the address bar The second block parses the form when it's submitted to the server, and then in reality would talk to the database.

Take a moment to catch your breath.

Delete with...GET

Specific item via URL

Huh? How come this isn't titled Delete with DELETE? Well, the browser address bar can only cope with GET, so we have to improvise and DELETE with GET. Fortunately that's not too hard. Add the following

/* Delete stuff.
A browser address bar can only issue GET requests.
A form can only issue GET and POST requests.
But we can improvise. */

/* DELETE something specific from the URL. */

router.get('/delete/:id', function (req, res, next) {
if (req.path == '/delete') {
next();
}
// remove indicated entity from db
var html = '<p>deleted ' + req.params.id + ' from db</p>';
res.send(html);
});

Now localhost:3000/delete/foo in the browser address bar will remove the item foo from the database.

Specific item via form

We can also send a form where the information on what to delete is to be filled in. Note that the form is POSTed back to the server; forms don't do PUT.

/* DELETE something specific by posting a form.
GET form to fill in with details of object to delete.
POST it back to the server */

router.get('/delete', function (req, res, next) {
var html = '<form method="post" action="/deleted">' +
'<div><input type="text" name="something" placeholder="delete something"></div>' +
'<div><input type="submit" value="go"></div>' +
'</form>';
res.send(html);
});

router.post('/deleted', function (req, res, next) {

// parse form and remove indicated entity from db
var html = '<p>deleted ' + req.body.something + ' from db</p>';
res.send(html);
});

Then just localhost:3000/delete will bring up the form.

Update with...GET

Specific item via form

Same deal as with DELETE - the address bar can only send GET, and a form can only GET and POST

/* Update stuff.
A browser address bar can only issue GET requests.
A form can only issue GET and POST requests.
But we can improvise. */

/* UPDATE something specific from the URL. */

router.get('/update/:id', function (req, res, next) {
var html = '<form method="post" action="/updated">' +
'<div>' + req.params.id +
': <input type="text" name="something" placeholder="something"></div>' +

'<input type="hidden" name="id" value="' + req.params.id + '">' +
'<div><input type="submit" value="go"></div>' +
'</form>';
res.send(html);
});

/* POST to parse form and update something specific. */

router.post('/updated', function (req, res, next) {
// parse form and insert updated entity into db
var html = '<p>edited and updated ' + req.body.id + ':
' + req.body.something + ' in db</p>';

res.send(html);
});

Summary

URL in browser address bar code action
localhost:3000/ router.get('/',... gets home page
localhost:3000/read router.get('/read',... gets all entities
localhost:3000/read/foo router.get('/read/:id',... gets entity foo
localhost:3000/create router.get('/create',... gets form to create new entity
  router.post('/created',... creates new entity as per form
localhost:3000/delete/foo router.get('/delete/:id',... deletes entity foo
localhost:3000/delete router.get('/delete',... gets form to delete entity
  router.post('/deleted',... deletes entity as per form
localhost:3000/update/foo router.get('/update/:id gets form to update entity foo
  router.post('/updated',... updates entity as per form

Caveat

This is all very simplified, but it gets the idea. In any non-trivial application the routes would not be all on the same level.