@@ -78,6 +78,97 @@ function isModelClass(
7878 ) ;
7979}
8080
81+ import { DatabaseDriverError } from '../errors' ;
82+
83+ function handleDatabaseDriverError (
84+ this : DefaultCrudRepository < Entity , unknown , AnyObject > ,
85+ err : unknown ,
86+ ) : never {
87+ const error = err as AnyObject ;
88+ if ( err === null || err === undefined ) {
89+ throw new Error ( 'An unknown database execution error occurred.' ) ;
90+ }
91+
92+ // Handling existing already mapped errors
93+ if ( error . statusCode && error . statusCode >= 400 && error . statusCode < 500 ) {
94+ throw error ;
95+ }
96+
97+ const parsedCode = Number ( error . code ) ;
98+ const rawCode = ! isNaN ( parsedCode ) ? error . code : error . errno ; // error.code for posgres while errno for mysql/mongodb
99+
100+ const codeStr = String ( rawCode ) ;
101+
102+ // Initialize with default values
103+ let statusCode = 500 ;
104+ let errorCode = 'DATABASE_ERROR' ;
105+ let message = error . message || 'An unexpected database error occurred.' ;
106+
107+ // Evaluate database signatures and re-map properties dynamically
108+ switch ( codeStr ) {
109+ // 1. Unique Key / Duplicate Entries
110+ case '23505' : // Postgres
111+ case '1062' : // MySQL
112+ case '11000' : // MongoDB
113+ case '11001' :
114+ statusCode = 409 ;
115+ errorCode = 'DB_UNIQUE_CONSTRAINT_VIOLATION' ;
116+ message =
117+ 'The operation conflicts with an existing record unique constraint.' ;
118+ break ;
119+ // 2. Foreign Key Constraints (Missing Parents / Existing Children)
120+ case '23503' : // Postgres
121+ case '1216' : // MySQL
122+ case '1217' :
123+ case '1451' :
124+ case '1452' :
125+ statusCode = 422 ;
126+ errorCode = 'DB_FOREIGN_KEY_VIOLATION' ;
127+ message =
128+ 'Relational integrity validation failed. Referenced parent record not found.' ;
129+ break ;
130+ // 3. Null / Required Fields Omissions
131+ case '23502' : // Postgres
132+ case '1048' : // MySQL
133+ case '1364' :
134+ case '121' : // MongoDB Document Validation Failed
135+ statusCode = 400 ;
136+ errorCode = 'DB_NOT_NULL_VIOLATION' ;
137+ message = 'Required database schema properties are missing or null.' ;
138+ break ;
139+ // 4. Bad Casts / Truncation / Data Type Mismatch
140+ case '22P02' : // Postgres Invalid Text Representation (e.g. Bad UUID format)
141+ case '22001' : // Postgres String Data Right Truncation
142+ case '1265' : // MySQL Data Truncated
143+ case '1366' :
144+ statusCode = 400 ;
145+ errorCode = 'DB_DATA_TYPE_MISMATCH' ;
146+ message =
147+ 'The query properties contain unexpected formatting types or overflows.' ;
148+ break ;
149+ // 5. Generated Column Violations
150+ case '3105' : // MySQL Server Generated column value ignored/disallowed
151+ case '1906' : // MariaDB Generated column value ignored/disallowed
152+ statusCode = 400 ;
153+ errorCode = 'DB_GENERATED_COLUMN_VIOLATION' ;
154+ message =
155+ 'Cannot manually assign or update values on a database-generated computed column.' ;
156+ break ;
157+ }
158+
159+ // If we matched a standard driver rule, throw our clean uniform class
160+ if ( statusCode !== 500 ) {
161+ throw new DatabaseDriverError ( this . entityClass , message , {
162+ code : errorCode ,
163+ statusCode : statusCode ,
164+ nativeCode : rawCode ,
165+ } ) ;
166+ }
167+
168+ // Otherwise, bubble up the original error safely to protect core connection strings/etc.
169+ throw err ;
170+ }
171+
81172/**
82173 * This is a bridge to the legacy DAO class. The function mixes DAO methods
83174 * into a model class and attach it to a given data source
@@ -488,7 +579,9 @@ export class DefaultCrudRepository<
488579 async create ( entity : DataObject < T > , options ?: Options ) : Promise < T > {
489580 // perform persist hook
490581 const data = await this . entityToData ( entity , options ) ;
491- const model = await ensurePromise ( this . modelClass . create ( data , options ) ) ;
582+ const model = await ensurePromise (
583+ this . modelClass . create ( data , options ) ,
584+ ) . catch ( handleDatabaseDriverError ) ;
492585 return this . toEntity ( model ) ;
493586 }
494587
@@ -499,7 +592,7 @@ export class DefaultCrudRepository<
499592 ) ;
500593 const models = await ensurePromise (
501594 this . modelClass . createAll ( data , options ) ,
502- ) ;
595+ ) . catch ( handleDatabaseDriverError ) ;
503596 return this . toEntities ( models ) ;
504597 }
505598
@@ -520,7 +613,7 @@ export class DefaultCrudRepository<
520613 const include = filter ?. include ;
521614 const models = await ensurePromise (
522615 this . modelClass . find ( this . normalizeFilter ( filter ) , options ) ,
523- ) ;
616+ ) . catch ( handleDatabaseDriverError ) ;
524617 const entities = this . toEntities ( models ) ;
525618 return this . includeRelatedModels ( entities , include , options ) ;
526619 }
@@ -531,7 +624,7 @@ export class DefaultCrudRepository<
531624 ) : Promise < ( T & Relations ) | null > {
532625 const model = await ensurePromise (
533626 this . modelClass . findOne ( this . normalizeFilter ( filter ) , options ) ,
534- ) ;
627+ ) . catch ( handleDatabaseDriverError ) ;
535628 if ( ! model ) return null ;
536629 const entity = this . toEntity ( model ) ;
537630 const include = filter ?. include ;
@@ -551,7 +644,7 @@ export class DefaultCrudRepository<
551644 const include = filter ?. include ;
552645 const model = await ensurePromise (
553646 this . modelClass . findById ( id , this . normalizeFilter ( filter ) , options ) ,
554- ) ;
647+ ) . catch ( handleDatabaseDriverError ) ;
555648 if ( ! model ) {
556649 throw new EntityNotFoundError ( this . entityClass , id ) ;
557650 }
@@ -583,7 +676,7 @@ export class DefaultCrudRepository<
583676 const persistedData = await this . entityToData ( data , options ) ;
584677 const result = await ensurePromise (
585678 this . modelClass . updateAll ( where , persistedData , options ) ,
586- ) ;
679+ ) . catch ( handleDatabaseDriverError ) ;
587680 return { count : result . count } ;
588681 }
589682
@@ -614,7 +707,9 @@ export class DefaultCrudRepository<
614707 ) : Promise < void > {
615708 try {
616709 const payload = await this . entityToData ( data , options ) ;
617- await ensurePromise ( this . modelClass . replaceById ( id , payload , options ) ) ;
710+ await ensurePromise (
711+ this . modelClass . replaceById ( id , payload , options ) ,
712+ ) . catch ( handleDatabaseDriverError ) ;
618713 } catch ( err ) {
619714 if ( err . statusCode === 404 ) {
620715 throw new EntityNotFoundError ( this . entityClass , id ) ;
@@ -626,24 +721,30 @@ export class DefaultCrudRepository<
626721 async deleteAll ( where ?: Where < T > , options ?: Options ) : Promise < Count > {
627722 const result = await ensurePromise (
628723 this . modelClass . deleteAll ( where , options ) ,
629- ) ;
724+ ) . catch ( handleDatabaseDriverError ) ;
630725 return { count : result . count } ;
631726 }
632727
633728 async deleteById ( id : ID , options ?: Options ) : Promise < void > {
634- const result = await ensurePromise ( this . modelClass . deleteById ( id , options ) ) ;
729+ const result = await ensurePromise (
730+ this . modelClass . deleteById ( id , options ) ,
731+ ) . catch ( handleDatabaseDriverError ) ;
635732 if ( result . count === 0 ) {
636733 throw new EntityNotFoundError ( this . entityClass , id ) ;
637734 }
638735 }
639736
640737 async count ( where ?: Where < T > , options ?: Options ) : Promise < Count > {
641- const result = await ensurePromise ( this . modelClass . count ( where , options ) ) ;
738+ const result = await ensurePromise (
739+ this . modelClass . count ( where , options ) ,
740+ ) . catch ( handleDatabaseDriverError ) ;
642741 return { count : result } ;
643742 }
644743
645744 exists ( id : ID , options ?: Options ) : Promise < boolean > {
646- return ensurePromise ( this . modelClass . exists ( id , options ) ) ;
745+ return ensurePromise ( this . modelClass . exists ( id , options ) ) . catch (
746+ handleDatabaseDriverError ,
747+ ) ;
647748 }
648749
649750 /**
0 commit comments