jasongauvin / api-knex

simple api crud for my webdoc : knex + postgres + node + express

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

API REST WITH KNEX (https://youtu.be/xFsaRVNLtxI)

Ne pas oublier "body-parser"

PREREQUIS :

  • node et gitignore

     brew install node
    
      npm install gitignore -g
    
  • postgres

      brew install postgres
    
      brew services start postgres
      
      createdb
    
  • Express generator

     npm install -g express-generator
    
  • knex install

     npm install -g knex
    

1 - Créer un dossier serveur :

mkdir api-crud
mkdir server
	express
	git init
	gitignore node

2 - Créer la db :

	createdb cjs-web-store

3 - Initialiser le project knex :

	npm install --save knex pg

	knex init
	
	knexfile.js --> //transformer en

		module.exports  =  {
			development:  {
				client:  'pg',
				connection:  'postgres://localhost/cjs-xeb-store'
				},
		};

4 - Créer les tables migrations :

	knex migration:make create-sticker

migration (dossier migration) :

exports.up  =  function(knex, Promise)  {

	return knex.schema.createTable('sticker', (table)=>  {

		table.increment();

		table.text('title');

		table.text('description');

		table.float('rating');

		table.text('url');

	})

};

  

exports.down  =  function(knex, Promise)  {

	return knex.schema.dropTable('sticker');
};

Puis :

	knex migrate:latest

Accéder à la db "cjs-web-store" postgres crée plus haut :

	pcql cjs-web-store

Puis pour afficher les tables :

	\dt

Puis pour afficher la table "sticker" :

	\d sticker

5 - Préparer fixtures/echantillons :

	knex seed:make 01_sticker

Puis les envoyer :

	knex seed:run

Afficher le contenu de la db "sticker":

	select * from sticker;
	// \x pour affichage élargi
	
	// :x pour sortir

6 - Convertir app express en JSON API

Dans app.js supprimer :

	// view engine setup
	app.set('views', path.join(__dirname, 'views'));
	app.set('view engine', 'jade');
	// Puis supprimer le dossier "view".

Dans app.js supprimer :

	app.use(express.static(path.join(__dirname, 'public')));
	// Puis supprimer le dossier "routes"

Dans app.js supprimer :

	var indexRouter =  require('./routes/index');
	var usersRouter =  require('./routes/users');
	// et donc aussi
	app.use('/', indexRouter);
	app.use('/users', usersRouter);
	// Puis supprimer le dossier "public"

Dans app.js modifier :

	 // error handler
	app.use(function(err, req, res, next)  {
	// set locals, only providing error in development
		res.locals.message  = err.message;
		res.locals.error  = req.app.get('env')  ===  'development'  ? err :  {};
	
	// render the error page
	res.status(err.status  ||  500);
	res.render('error');
	});

En :

	app.use(function(err, req, res, next)  {
		res.status(err.status  ||  500);
		res.json({
			message: err.message,
			error: req.app.get('env')  ===  'development'  ? err :  {}
		});
	});

Faire :

	npm uninstall jade
	npm i

Puis vérifier le fonctionnement du serveur :

	npm start
	// localhost:3000/# doit renvoyer
	// {"message":"Not Found","error":{"message":"Not Found"}}

7 - Créer le dossier API et le router

	mkdir api
	touch stickers.js

Dans stickers.js, déclarer le router et créer la première route :

	const express =  require('express');
	const router = express.Router();
	router.get('/', (req, res)  =>  {
		res.json({
			message:  ':D it\'s ok'
			})
		})

	  

	module.exports  = router;

Dans app.js, modifier tout les "var" en "const" puis déclarer le router "stickers.js" du dossier api :

	const stickers =  require('./api/stickers');
	// puis rajouter la ligne suivant pour utiliser la route
	app.use('/api/v1/stickers', stickers);

Puis installer "nodemon" :

	npm install nodemon --save-dev

Modifier le fichier "packages.json" :

	"scripts": {
		"start":  "node ./bin/www",
		"dev":  "nodemon"
		},

Puis faire :

	npm run dev

8 - Créer connexion db et le fichier de requète

Créer le dossier db et le ficher knex.js :

	mkdir db
	touch knex.js

Dans knex.js :

	const environment = process.env.NODE_ENV  ||'development';
	const config =  require('../knexfile');
	const knex =  require('knex');
	const environmentConfig = config[environment];
	const connection =  knex(environmentConfig);

	module.exports  = connection;

Puis creer le fichier queries.js dans le dossier db :

	touch queries.js 

Dans queries.js :

	const knex =  require('./knex');  // la connection

	module.exports  =  {
		getAll()  {
			// 'sticker' car c'est le nom de la table a retourner de la db
			return  knex('sticker');
		}
	}

9 - Lister le contenu de la table "sticker" avec GET (getAll() )

Dans le dossier api, sur le fichier stickers.js modifier comme ceci :

	const express =  require('express');
	const router = express.Router();  
	
	// on appelle queries
	const queries =  require('../db/queries');
	
	router.get('/', (req, res)  =>  {
	// après le then() stickers c'est le nom du paramètre
		// getAll() récupère le résultat de la fonction getAll de queries.js et .then() lui dans le nom de stickers
		queries.getAll().then(stickers =>  {
		// et stickers le nom de la variable de la fonction then()
			res.json(stickers);
		})
	})

	module.exports  = router;

10 - Tests unitaire avec mocha, chai et supertest

Commencez par installer :

	npm install --save-dev mocha chai supertest

Puis modifier le fichier knexfile.js en ajoutant le lien vers la nouvelle db "text-cjs-web-store" :

	// Update with your config settings.
	// listing des db

	module.exports  =  {

		development:  {
			client:  'pg',
			connection:  'postgres://localhost/cjs-web-store'
		},
		test:  {
			client:  'pg',
			connection:  'postgres://localhost/test-cjs-web-store'
		}
	};

Créer le dossier test et le fichier app.test.js :

	mkdir test
	touch test/app.test.js

Dans app.test.js écrire :

	const knex = require('../db/knex');
	
	describe('CRUD stickers', ()  =>  {
	});

Dans pachage.json modifier "scripts" comme ceci : On va supprimer la db puis en créer une nouvelle !

	"scripts": {
		"start":  "node ./bin/www",
		"dev":  "nodemon",
		"test":  "(dropdb --if-exist test-cjs-web-store && createdb test-cjs-web-store) && NODE_ENV=test mocha"
	},
	...

Ensuite on va tester les migrations et les seeds dans notre app.test.js comme ceci :

	const knex =  require('../db/knex');

	describe('CRUD stickers', ()  =>  {
		before(()  =>  {
			// run migrations
			knex.migrate.latest()
				.then(()  =>  {
				// run seeds
				return knex.seed.run();
				});
		});
	});

Pour lancer les tests :

	npm test

11 - Vérifier que les tests fonctionnent :

Modifier le fichier app.test.js comme ceci :

	...
	before((done)  =>  {
		// run migrations
		knex.migrate.latest()
			.then(()  =>  {
			// run seeds
			return knex.seed.run();
		}).then(()  =>  done()  );
	});

	it('works...', function()  {
		console.log('It\'s working!');
	})
	...

Maintenant, nous allons tester la route /api/v1/stickers avec supertest. Modifier app.test.js comme ceci :

	const request =  require('supertest');
	const knex =  require('../db/knex');

	const app =  require('../app');
	const fixtures =  require('./fixtures');

	describe('CRUD stickers', ()  =>  {
		before((done)  =>  {
			// run migrations
			knex.migrate.latest()
				.then(()  =>  {
				// run seeds
				return knex.seed.run();
		}).then(()  =>  done()  );
	});

	it('List All Records', (done)  =>  {
		request(app)
			.get('/api/v1/stickers')
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.then((response)  =>  {
				expect(response.body).to.be.a('array');
				expect(response.body).to.deep.equal(fixtures.stickers);
				done();
			});
		console.log('It\'s okay ! Next step')
		})
	});

Voilà ! Faites :

	npm test

12 - Lister un seul contenu de la table "sticker" avec GET sur /api/v1/stickers/:id

Sur le fichier api/stickers.js, on crée une fonction qui vérifie si notre id est valide ou non :

	function  isValidId(req, res,next){
		if(!isNaN(req.params.id)){
			return  next();
		}  else  {
			next(new  Error('invalid ID'))
		}
	}

On crée une route :id avec une id valide :

	router.get('/:id', isValidId, (req, res)  =>  {
		res.json({
			message: "helloooooo!"
		})
	})

On met la route:id sur la db :

Dans api/stickers.js on modifie :

	...
	router.get('/:id', isValidId, (req, res, next)  =>  {
		queries.getOne(req.params.id).then(sticker =>  {
			if(sticker) {
				res.json(sticker)
			} else {
				// ici next() fait la gestion des erreur, voir app.js.
				next();
			}
		})
	})
	...

Et dans queries.js on rajoute la requète getOne() qui va chercher le contenu spécifique à l'id:

	...
	getOne(id)  {
		return  knex('sticker').where('id', id).first();
	}
	...

Maintenant on va rajouter des test unitaires sur l'id 1 et 5. Modifier le fichier app.test.js en rajoutant les tests :

	...
	it('Show one records by id', (done)  =>  {
		request(app)
			// on spécifie la route à tester
			.get('/api/v1/stickers/1')
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.expect(200)
			.then((response)  =>  {	
				// ici un object car on va chercher un seul record (sticker) dans l'array (stickers)
				expect(response.body).to.be.a('object');
				// on indique quel numéro dans le tableau (qui part de 0 bien entendu !)
				expect(response.body).to.deep.equal(fixtures.stickers[0]);
				done();
			});
	})

	it('Show one records by id', (done)  =>  {
		request(app)
			.get('/api/v1/stickers/5')
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.expect(200)
			.then((response)  =>  {	
				expect(response.body).to.be.a('object');
				expect(response.body).to.deep.equal(fixtures.stickers[4]);
				done();
			});
	})
	...

13 - Créer un contenu avec POST sur /api/v1/stickers

Déjà on commence par créer la route POST dans api/stickers.js :

	router.post('/', (req, res, next)  =>  {
	
	});

Après on créé la fonction qui validera notre contenu à inserer en db :

	function  validSticker(sticker)  {
		// on vérifie que le type est bien une string puis que le contenu n'est pas égal à null
		const hasTitle =  typeof sticker.title  ==  'string'  && sticker.title.trim()  !=  '';
		const hasURL =  typeof sticker.url  ==  'string'  && sticker.url.trim()  !=  '';
		const hasDescription =  typeof sticker.description  ==  'string'  && sticker.description.trim()  !=  '';
		const hasRating =  !isNaN(sticker.rating);
		
		return hasTitle && hasURL && hasDescription && hasRating;
	}

On rempli la route POST :

	router.post('/', (req, res, next)  =>  {
		if(validSticker(req.body)){
			//insert into db
			// On passe en requète le corps de l'autocollant
			queries.create(req.body).then(stickers =>  {
				res.json(stickers[0]);
			})
		}  else  {
			next(new  Error('Invalid sticker'));
		}
	});

Ensuite on rajoute dans queries.js la requète correspondante :

	create(sticker)  {
		return  knex('sticker').insert(sticker, '*');
	}

14 - Créer un test d'enregistrement

Dans app.test.js, a la suite écrire :

	it('Create a records', (done)  =>  {
		request(app)
			.post('/api/v1/stickers')
			.send(fixtures.sticker)
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.expect(200)
			.then((response)  =>  {	
				expect(response.body).to.be.a('object');
				fixtures.sticker.id  = response.body.id;
				expect(response.body).to.deep.equal(fixtures.sticker);
				done();
			});
	});

15 - Update un contenu avec PUT sur /api/v1/stickers/id

On commence par créer la route put et on vérifie que l'id est valide :

	router.put('/:id', isValidId, (req, res, next)  =>  {
		// on utilise la function validSticjer pour vérifier le contenu
		if(validSticker(req.body)){
			// update the sticker
			queries.update(req.params.id, req.body).then(stickers =>  {
			res.json(stickers[0]);
			})
		}  else  {
			next(new  Error('Invalid sticker'));
		}
	});

Ensuite on rajoute dans queries.js la requète correspondante :

	...
	update(id, sticker)  {
		return  knex('sticker').where('id', id).update(sticker, '*');
	},
	...

16 - Créer un test d'update

	...
	it('Update a records', (done)  =>  {
		fixtures.sticker.rating  =  5
		request(app)
			.put('/api/v1/stickers/10')
			.send(fixtures.sticker)
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.expect(200)
				.then((response)  =>  {
				expect(response.body).to.be.a('object');
				expect(response.body).to.deep.equal(fixtures.sticker);
				done();
			});
	});
	...

17 - Delete un content avec DELETE sur api/v1/stickers/id

On commence par créer la route et utiliser la function isValidId pour vérifier que l'id est bien valide (pour changer) :

	router.delete('/:id', isValidId, (req, res)  =>  {
		// delete a record
		queries.delete(req.params.id).then(()  =>  {			
			res.json({
				deleted:  true
			});
		});			
	});

Ensuite on rajoute dans queries.js la requète correspondante :

	delete(id)  {
		return  knex('sticker').where('id', id).del();
	}

Puis on crée le test correspondant :

	it('Delete a records', (done)  =>  {
		request(app)
			.delete('/api/v1/stickers/10')
			.set('Accept', 'application/json')
			.expect('Content-type', /json/)
			.expect(200)
			.then((response)  =>  {
				expect(response.body).to.be.a('object');
				expect(response.body).to.deep.equal({
				deleted:  true
				});
				done();
			});
	});

About

simple api crud for my webdoc : knex + postgres + node + express


Languages

Language:JavaScript 100.0%