@@ -78,6 +78,90 @@ 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 rawCode = error . code || error . sqlState || '' ;
98+ const codeStr = String ( rawCode ) ;
99+
100+ // Initialize with default values
101+ let statusCode = 500 ;
102+ let frameworkCode = 'DATABASE_ERROR' ;
103+ let cleanMessage = error . message || 'An unexpected database error occurred.' ;
104+
105+ // Evaluate database signatures and re-map properties dynamically
106+ switch ( codeStr ) {
107+ // 1. Unique Key / Duplicate Entries
108+ case '23505' : // Postgres
109+ case '1062' : // MySQL
110+ case '11000' : // MongoDB
111+ case '11001' :
112+ statusCode = 409 ;
113+ frameworkCode = 'DB_UNIQUE_CONSTRAINT_VIOLATION' ;
114+ cleanMessage =
115+ 'The operation conflicts with an existing record unique constraint.' ;
116+ break ;
117+
118+ // 2. Foreign Key Constraints (Missing Parents / Existing Children)
119+ case '23503' : // Postgres
120+ case '1216' : // MySQL
121+ case '1217' :
122+ case '1451' :
123+ case '1452' :
124+ statusCode = 422 ;
125+ frameworkCode = 'DB_FOREIGN_KEY_VIOLATION' ;
126+ cleanMessage =
127+ 'Relational integrity validation failed. Referenced parent record not found.' ;
128+ break ;
129+
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+ frameworkCode = 'DB_NOT_NULL_VIOLATION' ;
137+ cleanMessage = 'Required database schema properties are missing or null.' ;
138+ break ;
139+
140+ // 4. Bad Casts / Truncation / Data Type Mismatch
141+ case '22P02' : // Postgres Invalid Text Representation (e.g. Bad UUID format)
142+ case '22001' : // Postgres String Data Right Truncation
143+ case '1265' : // MySQL Data Truncated
144+ case '1366' :
145+ statusCode = 400 ;
146+ frameworkCode = 'DB_DATA_TYPE_MISMATCH' ;
147+ cleanMessage =
148+ 'The query properties contain unexpected formatting types or overflows.' ;
149+ break ;
150+ }
151+
152+ // If we matched a standard driver rule, throw our clean uniform class
153+ if ( statusCode !== 500 ) {
154+ throw new DatabaseDriverError ( this . entityClass , cleanMessage , {
155+ code : frameworkCode ,
156+ statusCode : statusCode ,
157+ nativeCode : rawCode ,
158+ } ) ;
159+ }
160+
161+ // Otherwise, bubble up the original error safely to protect core connection strings/etc.
162+ throw err ;
163+ }
164+
81165/**
82166 * This is a bridge to the legacy DAO class. The function mixes DAO methods
83167 * into a model class and attach it to a given data source
@@ -488,7 +572,9 @@ export class DefaultCrudRepository<
488572 async create ( entity : DataObject < T > , options ?: Options ) : Promise < T > {
489573 // perform persist hook
490574 const data = await this . entityToData ( entity , options ) ;
491- const model = await ensurePromise ( this . modelClass . create ( data , options ) ) ;
575+ const model = await ensurePromise (
576+ this . modelClass . create ( data , options ) ,
577+ ) . catch ( handleDatabaseDriverError ) ;
492578 return this . toEntity ( model ) ;
493579 }
494580
@@ -499,7 +585,7 @@ export class DefaultCrudRepository<
499585 ) ;
500586 const models = await ensurePromise (
501587 this . modelClass . createAll ( data , options ) ,
502- ) ;
588+ ) . catch ( handleDatabaseDriverError ) ;
503589 return this . toEntities ( models ) ;
504590 }
505591
@@ -520,7 +606,7 @@ export class DefaultCrudRepository<
520606 const include = filter ?. include ;
521607 const models = await ensurePromise (
522608 this . modelClass . find ( this . normalizeFilter ( filter ) , options ) ,
523- ) ;
609+ ) . catch ( handleDatabaseDriverError ) ;
524610 const entities = this . toEntities ( models ) ;
525611 return this . includeRelatedModels ( entities , include , options ) ;
526612 }
@@ -531,7 +617,7 @@ export class DefaultCrudRepository<
531617 ) : Promise < ( T & Relations ) | null > {
532618 const model = await ensurePromise (
533619 this . modelClass . findOne ( this . normalizeFilter ( filter ) , options ) ,
534- ) ;
620+ ) . catch ( handleDatabaseDriverError ) ;
535621 if ( ! model ) return null ;
536622 const entity = this . toEntity ( model ) ;
537623 const include = filter ?. include ;
@@ -551,7 +637,7 @@ export class DefaultCrudRepository<
551637 const include = filter ?. include ;
552638 const model = await ensurePromise (
553639 this . modelClass . findById ( id , this . normalizeFilter ( filter ) , options ) ,
554- ) ;
640+ ) . catch ( handleDatabaseDriverError ) ;
555641 if ( ! model ) {
556642 throw new EntityNotFoundError ( this . entityClass , id ) ;
557643 }
@@ -583,7 +669,7 @@ export class DefaultCrudRepository<
583669 const persistedData = await this . entityToData ( data , options ) ;
584670 const result = await ensurePromise (
585671 this . modelClass . updateAll ( where , persistedData , options ) ,
586- ) ;
672+ ) . catch ( handleDatabaseDriverError ) ;
587673 return { count : result . count } ;
588674 }
589675
@@ -614,7 +700,9 @@ export class DefaultCrudRepository<
614700 ) : Promise < void > {
615701 try {
616702 const payload = await this . entityToData ( data , options ) ;
617- await ensurePromise ( this . modelClass . replaceById ( id , payload , options ) ) ;
703+ await ensurePromise (
704+ this . modelClass . replaceById ( id , payload , options ) ,
705+ ) . catch ( handleDatabaseDriverError ) ;
618706 } catch ( err ) {
619707 if ( err . statusCode === 404 ) {
620708 throw new EntityNotFoundError ( this . entityClass , id ) ;
@@ -626,24 +714,30 @@ export class DefaultCrudRepository<
626714 async deleteAll ( where ?: Where < T > , options ?: Options ) : Promise < Count > {
627715 const result = await ensurePromise (
628716 this . modelClass . deleteAll ( where , options ) ,
629- ) ;
717+ ) . catch ( handleDatabaseDriverError ) ;
630718 return { count : result . count } ;
631719 }
632720
633721 async deleteById ( id : ID , options ?: Options ) : Promise < void > {
634- const result = await ensurePromise ( this . modelClass . deleteById ( id , options ) ) ;
722+ const result = await ensurePromise (
723+ this . modelClass . deleteById ( id , options ) ,
724+ ) . catch ( handleDatabaseDriverError ) ;
635725 if ( result . count === 0 ) {
636726 throw new EntityNotFoundError ( this . entityClass , id ) ;
637727 }
638728 }
639729
640730 async count ( where ?: Where < T > , options ?: Options ) : Promise < Count > {
641- const result = await ensurePromise ( this . modelClass . count ( where , options ) ) ;
731+ const result = await ensurePromise (
732+ this . modelClass . count ( where , options ) ,
733+ ) . catch ( handleDatabaseDriverError ) ;
642734 return { count : result } ;
643735 }
644736
645737 exists ( id : ID , options ?: Options ) : Promise < boolean > {
646- return ensurePromise ( this . modelClass . exists ( id , options ) ) ;
738+ return ensurePromise ( this . modelClass . exists ( id , options ) ) . catch (
739+ handleDatabaseDriverError ,
740+ ) ;
647741 }
648742
649743 /**
0 commit comments