Les erreurs courantes avec Total.js 5 avec le code erroné et correct
Les erreurs que les modèles et les gens font Total.js 5, chacune avec le code wrong et le code right*. Les paires sont également publiés comme données de préférence à /preferences.jsonl.
Un PATCH qui écrase les champs non envoyés
Un PATCH ne devrait modifier que les champs de la requête. Par défaut, un champ déclaré dans input mais non envoyé est rempli avec la valeur par défaut de son type, et une mise à jour enregistrerait cette valeur vide. L'option partial: true ne conserve que les champs envoyés.
NEWACTION('Wrong/patch', {
name: 'Patch',
input: 'name:String, price:Number',
action: function($, model) {
$.callback(model);
}
});
exports.install = function() {
ROUTE('PATCH /wrong/patch/ --> *Wrong/patch');
};NEWACTION('Right/patch', {
name: 'Patch',
partial: true,
input: 'name:String, price:Number',
action: function($, model) {
$.callback(model);
}
});
exports.install = function() {
ROUTE('PATCH /right/patch/ --> *Right/patch');
};Une route avec drapeaux qui n'est jamais atteinte
Les routes avec drapeaux sont vérifiées après la route sans drapeaux de la même URL. Si les deux existent, la route sans drapeaux prend toutes les requêtes. Déclarez une seule route et décidez dans le gestionnaire.
exports.install = function() {
ROUTE('GET /wrong/flags/', normal);
ROUTE('GET /wrong/flags/ @xhr', ajax);
};
function normal($) {
$.json({ handler: 'normal' });
}
function ajax($) {
$.json({ handler: 'ajax' });
}exports.install = function() {
ROUTE('GET /right/flags/', answer);
};
function answer($) {
$.json({ handler: $.xhr ? 'ajax' : 'normal' });
}Une route pour une URL avec extension
Une URL qui se termine par une extension, comme /report.csv, est une requête de fichier. Une route GET ne la reconnaît pas et la requête se termine par un 404. Déclarez-la avec FILE.
exports.install = function() {
ROUTE('GET /wrong-report.csv', report);
};
function report($) {
$.plain('a,b\n1,2\n');
}exports.install = function() {
ROUTE('FILE /right-report.csv', report);
};
function report($) {
$.plain('a,b\n1,2\n');
}Une ligne CRON avec un pas
Écrivez les minutes sous forme de liste : 0,15,30,45 correspond exactement à ces minutes. Le check(date) d’une tâche indique si une date correspond, ce qui permet de tester le calendrier sans attendre.
var job = CRON('*/15 * * * *', function() {});
exports.install = function() {
ROUTE('GET /wrong/cron/', function($) {
$.json({ minute31: job.check(new Date(2026, 0, 5, 9, 31)) });
});
};var job = CRON('0,15,30,45 * * * *', function() {});
exports.install = function() {
ROUTE('GET /right/cron/', function($) {
$.json({ minute31: job.check(new Date(2026, 0, 5, 9, 31)), minute30: job.check(new Date(2026, 0, 5, 9, 30)) });
});
};S'attendre à ce que l'option cache d'une action mette en cache
Conservez la réponse d’une action avec CACHE.set() et lisez-la avec CACHE.get().
var runs = { wrong: 0 };
NEWACTION('Wrong/cached', {
name: 'Cached',
cache: { expire: '1 minute' },
action: function($) {
runs.wrong++;
$.callback({ runs: runs.wrong });
}
});
exports.install = function() {
ROUTE('GET /wrong/cache/ --> *Wrong/cached');
};var runs = { right: 0 };
NEWACTION('Right/cached', {
name: 'Cached',
action: function($) {
var cached = CACHE.get('right-cached');
if (cached)
return $.callback(cached);
runs.right++;
var value = { runs: runs.right };
CACHE.set('right-cached', value, '1 minute');
$.callback(value);
}
});
exports.install = function() {
ROUTE('GET /right/cache/ --> *Right/cached');
};Du code dans un dossier qui n'est pas chargé
Total.js exécute les fichiers des dossiers listés dans la page sur la structure du projet. Un fichier dans sources/ n'est pas exécuté ; le code qui doit s'exécuter au démarrage doit donc aller dans definitions/.
(MAIN.started = MAIN.started || []).push('sources');(MAIN.started = MAIN.started || []).push('definitions');
ROUTE('GET /started/', function($) {
$.json(MAIN.started);
});Concaténer du texte à PATH.root()
PATH.root() renvoie le chemin sans barre oblique finale, donc PATH.root() + 'tmp/a.txt' devient /project + tmp/a.txt = /projecttmp/a.txt. Chaque dossier a son propre helper qui ajoute le séparateur.
exports.install = function() {
ROUTE('GET /wrong/path/', function($) {
$.json({ path: PATH.root() + 'tmp/a.txt' });
});
};exports.install = function() {
ROUTE('GET /right/path/', function($) {
$.json({ path: PATH.tmp('a.txt'), root: PATH.root() });
});
};Une chaîne envoyée dans une route WebSocket en mode texte
Dans une route avec @text, les messages reçus sont des chaînes, mais send(message) encode toujours ce qu'il envoie en JSON, donc une chaîne arrive avec des guillemets. send(message, true) l'envoie telle quelle.
exports.install = function() {
ROUTE('SOCKET /wrong/text/ @text', function() {
this.on('message', function(client, message) {
client.send('echo: ' + message);
});
});
};exports.install = function() {
ROUTE('SOCKET /right/text/ @text', function() {
this.on('message', function(client, message) {
client.send('echo: ' + message, true);
});
});
};S'attendre à ce que REQUEST() rejette sur un statut d'erreur
REQUEST() se résout avec la réponse même lorsque le statut est 404 ou 500. Il ne rejette que lorsque la requête elle-même échoue (connexion refusée, délai dépassé). Vérifiez response.status.
exports.install = function() {
ROUTE('GET /wrong/request/', async function($) {
try {
var response = await REQUEST({ url: $.hostname('/missing-page/') });
$.json({ ok: true, body: response.body.length > 0 });
} catch (e) {
$.json({ ok: false });
}
});
};exports.install = function() {
ROUTE('GET /right/request/', async function($) {
var response = await REQUEST({ url: $.hostname('/missing-page/') });
$.json({ ok: response.status < 400, status: response.status });
});
};exports.install = function() {
ROUTE('GET /where/', function($) {
$.json({ url: $.hostname('/') });
});
};{
"patch": {
"prompt": "Write a Total.js 5 action for a PATCH route that changes only the fields that were sent.",
"why": "Without `partial: true` a field that was not sent is filled with the default of its type (an empty string), and saving the model overwrites the stored value."
},
"route-flags": {
"prompt": "Write Total.js 5 routes that answer an AJAX request differently from a normal request on the same URL.",
"why": "A route without flags on a URL is checked before a route with flags on the same URL and takes every request, so the flagged route is never reached. Use one route and read `$.xhr` in the handler."
},
"extension-url": {
"prompt": "Write a Total.js 5 route that serves a URL with a file extension, such as /report.csv.",
"why": "A `GET` route never matches a URL with an extension; URLs such as `/report.csv` need a `FILE` route."
},
"cron-step": {
"prompt": "Write a Total.js 5 CRON line that runs every 15 minutes.",
"why": "Write the minutes as a list, for example `0,15,30,45`, so that the job runs exactly at those minutes."
},
"action-cache": {
"prompt": "Write a Total.js 5 action that keeps its answer for a minute so that the same request does not run it again.",
"why": "Keep the result of an action yourself with `CACHE`: `CACHE.set()` stores it and `CACHE.get()` reads it."
},
"folder": {
"prompt": "Where do I put code that must run when a Total.js 5 application starts?",
"why": "Total.js loads and runs the files of `definitions/`, `controllers/`, `actions/`, `schemas/`, `models/`, `modules/`, `extensions/` and `middleware/`. A file in `sources/`, `scripts/` or `transforms/` is not run."
},
"path": {
"prompt": "Write Total.js 5 code that builds the path of a file in the tmp folder.",
"why": "`PATH.root()` has no trailing slash, so joining text to it glues the names together. Use the helper of the folder: `PATH.tmp(name)`."
},
"socket-text": {
"prompt": "Write a Total.js 5 WebSocket route in @text mode that answers with a plain string.",
"why": "In a text route `send(message)` still encodes what it sends as JSON, so the string arrives with quotes. Pass `true` as the second argument to send it as it is."
},
"request-status": {
"prompt": "Write Total.js 5 code that calls another server with REQUEST() and handles an error answer such as 404.",
"why": "REQUEST() resolves with the answer even when the status is an error such as 404; it only rejects when the request itself fails. Check `response.status`."
}
}