ForgeTotal.js 5
nl

Total.js 5

Een volledige REST-resource bouwen met acties

Total.js 5 · total5@0.0.19 actionscrudrestnosql Uitgevoerd met Total.js 5

Een REST-resource bestaat uit zes bewerkingen: lijst (query), read, insert, update (vervangen), patch (enkele velden wijzigen) en remove. In Total.js is elk daarvan een actie met een eigen validatie, zodat de routetabel een eenvoudige koppeling van URL naar actie blijft. params valideert de id uit de URL, query de filters van de lijst en input de body. Binnen een actie zijn $.params, $.query en de gevalideerde body (tweede argument van action) al schone waarden.

Meld een ontbrekend record met $.invalid(404): het antwoordt met de statuscode en een JSON-fout, voor elke actie op dezelfde manier. $.success() antwoordt met { "success": true } en $.callback(value) met een waarde, die bovendien door de output van de actie wordt gefilterd. UID() maakt de id van een nieuw record.

patch heeft partial: true nodig. Zonder die optie wordt een veld dat niet is verzonden toch gevuld met de standaardwaarde van zijn type ('' voor een String), en update() zou de opgeslagen waarde met die lege tekst overschrijven. Met partial: true bereiken alleen de velden die in het verzoek staan de actie.

actions/products.js
NEWACTION('Products/query', {
	name: 'List products',
	query: 'page:Number, search:String',
	output: 'items:[id:UID, name:String, price:Number], page:Number',
	action: function($) {
		var page = $.query.page || 1;
		var builder = NOSQL('products').find().sort('name').take(10).skip((page - 1) * 10);
		if ($.query.search)
			builder.search('name', $.query.search);
		builder.callback((err, items) => $.callback({ items: items, page: page }));
	}
});

NEWACTION('Products/read', {
	name: 'Read a product',
	params: '*id:UID',
	output: 'id:UID, name:String, price:Number',
	action: function($) {
		NOSQL('products').read().where('id', $.params.id).callback(function(err, item) {
			if (item)
				$.callback(item);
			else
				$.invalid(404);
		});
	}
});

NEWACTION('Products/insert', {
	name: 'Create a product',
	input: '*name:String, *price:Number',
	action: function($, model) {
		model.id = UID();
		NOSQL('products').insert(model).callback(() => $.callback({ id: model.id }));
	}
});

NEWACTION('Products/update', {
	name: 'Replace a product',
	params: '*id:UID',
	input: '*name:String, *price:Number',
	action: function($, model) {
		NOSQL('products').update(model).where('id', $.params.id).callback(function(err, count) {
			if (count)
				$.success();
			else
				$.invalid(404);
		});
	}
});

NEWACTION('Products/patch', {
	name: 'Change some fields of a product',
	partial: true,
	params: '*id:UID',
	input: 'name:String, price:Number',
	action: function($, model) {
		NOSQL('products').update(model).where('id', $.params.id).callback(function(err, count) {
			if (count)
				$.success();
			else
				$.invalid(404);
		});
	}
});

NEWACTION('Products/remove', {
	name: 'Remove a product',
	params: '*id:UID',
	action: function($) {
		NOSQL('products').remove().where('id', $.params.id).callback(function(err, count) {
			if (count)
				$.success();
			else
				$.invalid(404);
		});
	}
});
controllers/products.js
exports.install = function() {
	ROUTE('GET    /products/            --> *Products/query');
	ROUTE('GET    /products/{id}/       --> *Products/read');
	ROUTE('POST   /products/            --> *Products/insert');
	ROUTE('PUT    /products/{id}/       --> *Products/update');
	ROUTE('PATCH  /products/{id}/       --> *Products/patch');
	ROUTE('DELETE /products/{id}/       --> *Products/remove');
};