@amsom-habitat/nest-oracle
v2.0.0
Published
Oracle pour NestJS : pool Knex tracé, vues typées, fonctions stockées (binds natifs, CLOB/BLOB, contrat { data }), erreurs ORA- → HTTP.
Readme
@amsom-habitat/nest-oracle
Oracle pour NestJS : un pool Knex partagé et tracé, la lecture typée des vues, l'appel des fonctions
stockées et la traduction des erreurs ORA- en HTTP.
// app.module.ts (global par défaut ; le module initialise le client Oracle et ferme le pool à l'arrêt)
OracleModule.forRoot();
// lecture
const VIEW = view('AHVWS_CALLY_X', { schema: { booleans: ['EST_ACTIF'] }, keys });
constructor(@InjectKnex() private readonly db: Knex) {}
return VIEW.rows(await this.db(VIEW.name).select('*'));
// écriture
const { data } = await this.oracle.callFunction<Suivi>('AHWS_X', [id, pec]);
await this.oracle.callFunction('AHWS_MAIL', [id], { returns: 'clob' });Options (forRoot / forRootAsync)
Validées au démarrage : une valeur invalide ou manquante arrête l'application avec un message explicite.
| Option | Défaut |
|---|---|
| connection.{user,password,connectString} | ORACLE_USER, ORACLE_PASSWORD, ORACLE_CONNECT_STRING (obligatoires) |
| pool.min / pool.max | ORACLE_POOL_MIN (2) / ORACLE_POOL_MAX (10) |
| pool.acquireTimeoutMs | 60 000 |
| client.mode | 'thick' (échec bloquant) ; 'thin' n'initialise rien |
| client.libDir | ORACLE_CLIENT_LIB_DIR, sinon /opt/oracle/instantclient |
| functions.maxOutputBytes | 32 767 (plafond d'un VARCHAR2 PL/SQL) |
| functions.callTimeoutMs | aucun |
| functions.mapError | oracleErrorToHttpException |
| tracing.includeBindings | false (les valeurs bindées ne partent pas dans les traces) |
| tracing.maxBindingsLength | 2 000 |
| isGlobal | true |
Fonctions stockées : le contrat { data }
callFunction(name, params, { returns }) exécute BEGIN :result := name(:p0, …); END; en autoCommit :
toute atomicité se fait en PL/SQL, dans une seule fonction. Le nom est validé comme identifiant
Oracle, les paramètres sont bindés (jamais concaténés) :
boolean→ 1/0,undefined→null,Dateen natif ;Bufferou{ type: 'blob', value }→ BLOB,{ type: 'clob', value }→ CLOB ;- tout autre objet est refusé (
OracleDriverError) au lieu d'être écrit en[object Object]: un tableau ou un objet passe partoJsonParam(valeur)(JSON ; absent,{}et[]→NULL).
C'est la seule normalisation des valeurs envoyées à Oracle : le corps d'une requête reste brut jusque-là.
Inutile de convertir null en '' : Oracle ne distingue pas la chaîne vide de NULL.
Le retour est un succès si et seulement si le JSON rendu porte une clé data. Sinon,
OracleCallError (payload : corps parsé ou texte, raw : texte brut). Un échec du driver,
y compris pour emprunter une connexion, lève OracleDriverError (cause : l'erreur d'origine).
Les deux passent par mapError.
Un retour de plus de maxOutputBytes octets est signalé comme tel (500), sans être confondu avec un
paramètre invalide : appeler avec { returns: 'clob' }.
Erreurs ORA- → HTTP
oracleErrorToHttp(err) (aussi utilisé par le filtre d'exceptions du socle) :
| Code | HTTP | Message |
|---|---|---|
| ORA-20000 à ORA-20999 (RAISE_APPLICATION_ERROR) | 400 | message métier, première ligne |
| ORA-00001 (unicité) | 409 | message Oracle |
| 1722, 1858, 1843, 1400, 1407, 6502, 12899 | 400 | Paramètre invalide |
| autre ORA- | 500 | Erreur interne du serveur |
| pas de code ORA- | null | à traiter par l'appelant |
oracleErrorToHttpException (le mapError par défaut) : OracleCallError → OracleCallException, 400
dont le filtre de nest-socle renvoie le texte de la fonction tel quel ({"error": …}, ce que lisent les fronts) ; OracleDriverError → le statut ci-dessus, 500 à défaut.
Typage des vues
view(name, { schema, keys }) déclare une vue (le pendant de l'entité Doctrine v1) et l'inscrit dans
declaredViews(), avec le fichier qui la déclare (origin, lu par /coverage).
row(r): clés camelCase (columnToKey:ID_CONTRAT→idContrat), ou la tablekeys(colonne → clé, ordonnée), qui écarte aussi les colonnes non listées ;rows(rs): idem, puis dédoublonnage parid(première occurrence), comme l'identity map de Doctrine.
Le schema liste des noms de colonnes Oracle bruts ; une colonne déclarée dans deux listes est une
erreur de déclaration.
| Liste | Conversion (parité PHP/Doctrine) |
|---|---|
| booleans | (bool) : '' et '0' faux, le reste vrai, null conservé |
| integers | (int) : préfixe numérique ('12abc' → 12, '1e3' → 1000, 'abc' → 0) |
| decimals | floatval après '.' → '' et ',' → '.' : '1.234,56' → 1234.56, mais '12.5' → 125 |
| stringOrNumbers | is_numeric ? nombre : chaîne |
| strings | chaîne |
| json | JSON décodé, null si illisible |
| jsonOmittedIfNull | comme json, mais la clé est omise si null (propriété v1 array non nullable) |
Écriture : toOracleColumnValue(schema, column, value) applique la conversion inverse
(booléen → 0/1, JSON sérialisé, (int) PHP).
Fragments SQL (identifiants validés) : formattedDate(db, column, format = ORACLE_DATE_FORMAT)
(TO_CHAR, DD/MM/RR comme oci8), unixTimestamp(db, column) (AH_TIMESTAMP, fonction maison),
unaccentSql(column) (TRANSLATE sans accents), et removeAccents(text) côté JS.
@amsom-habitat/nest-oracle/casing expose columnToKey sans dépendance Nest ni Knex (générateur).
Migration 1.x → 2.0
| 1.x | 2.0 |
|---|---|
| initialiserClientOracle() dans main.ts | supprimé : OracleModule initialise le client (client) |
| OracleModule.forRoot({ connexion, pool, mapError }) | forRoot({ connection, pool, functions: { mapError } }) |
| OracleClobService.callFunction(n, p) | OracleService.callFunction(n, p, { returns: 'clob' }) |
| callFunction(n, p): Promise<unknown> | callFunction<T>(n, p?, options?): Promise<{ data: T }> |
| vue(nom, { typage, cles }) / Vue | view(name, { schema, keys }) / OracleView |
| .ligne() / .lignes() / .nom / .typage / .origine | .row() / .rows() / .name / .schema / .origin |
| Ligne, Cles | Row (Record<string, unknown>), KeyMap |
| vuesDeclarees() | declaredViews() |
| dateFormatee, dateTimestamp | formattedDate, unixTimestamp |
| oracleUnaccent | unaccentSql |
| FORMAT_DATE_ORACLE | ORACLE_DATE_FORMAT |
| toCamel(row) | columnToKey(column) (aussi via /casing) |
| versOracle (nest-rest) | toOracleColumnValue |
| omitNullJson(row, champs) | inchangé, ou schema.jsonOmittedIfNull |
| KnexModule, KNEX_TOKEN, creerKnex, tracerKnex, OracleFunctionCalling, fromOraclePool, fromKnex, ConnectionProvider, LoggerLike, mapRows, mapRowsCles, toCles, dedupById, toOracleJson, oracleValue, toOracleScalar, cleanOraMessage | retirés de l'API publique |
