@@ -50,6 +50,9 @@ export type Options = {
5050     * it should be included in the charset. 
5151     */ 
5252    urlSegmentCharset ?: string ; 
53+ 
54+     modelNameMapping ?: Record < string ,  string > ; 
55+     prefix ?: string ; 
5356} ; 
5457
5558type  RelationshipInfo  =  { 
@@ -65,6 +68,20 @@ type ModelInfo = {
6568    relationships : Record < string ,  RelationshipInfo > ; 
6669} ; 
6770
71+ type  Match  =  { 
72+     type : string ; 
73+     id : string ; 
74+     relationship : string ; 
75+     prefix : string ; 
76+ } ; 
77+ 
78+ enum  UrlPatterns  { 
79+     SINGLE  =  'single' , 
80+     FETCH_RELATIONSHIP  =  'fetchRelationship' , 
81+     RELATIONSHIP  =  'relationship' , 
82+     COLLECTION  =  'collection' , 
83+ } 
84+ 
6885class  InvalidValueError  extends  Error  { 
6986    constructor ( public  readonly  message : string )  { 
7087        super ( message ) ; 
@@ -220,29 +237,60 @@ class RequestHandler extends APIHandlerBase {
220237    // divider used to separate compound ID fields 
221238    private  idDivider ; 
222239
223-     private  urlPatterns ; 
240+     private  urlPatternMap : Record < UrlPatterns ,  UrlPattern > ; 
241+     private  modelNameMapping : Record < string ,  string > ; 
242+     private  reverseModelNameMapping : Record < string ,  string > ; 
243+     private  prefix : string  |  undefined ; 
224244
225245    constructor ( private  readonly  options : Options )  { 
226246        super ( ) ; 
227247        this . idDivider  =  options . idDivider  ??  prismaIdDivider ; 
228248        const  segmentCharset  =  options . urlSegmentCharset  ??  'a-zA-Z0-9-_~ %' ; 
229-         this . urlPatterns  =  this . buildUrlPatterns ( this . idDivider ,  segmentCharset ) ; 
249+ 
250+         this . prefix  =  options . prefix ; 
251+         this . modelNameMapping  =  options . modelNameMapping  ??  { } ; 
252+         this . reverseModelNameMapping  =  Object . fromEntries ( 
253+             Object . entries ( this . modelNameMapping ) . map ( ( [ k ,  v ] )  =>  [ v ,  k ] ) 
254+         ) ; 
255+         this . urlPatternMap  =  this . buildUrlPatternMap ( segmentCharset ) ; 
230256    } 
231257
232-     buildUrlPatterns ( idDivider :  string ,   urlSegmentNameCharset : string )  { 
258+     private   buildUrlPatternMap ( urlSegmentNameCharset : string ) :  Record < UrlPatterns ,   UrlPattern >  { 
233259        const  options  =  {  segmentValueCharset : urlSegmentNameCharset  } ; 
260+ 
261+         const  buildPath  =  ( segments : string [ ] )  =>  { 
262+             return  this . prefix  +  '/'  +  segments . join ( '/' ) ; 
263+         } ; 
264+ 
234265        return  { 
235-             // collection operations 
236-             collection : new  UrlPattern ( '/:type' ,  options ) , 
237-             // single resource operations 
238-             single : new  UrlPattern ( '/:type/:id' ,  options ) , 
239-             // related entity fetching 
240-             fetchRelationship : new  UrlPattern ( '/:type/:id/:relationship' ,  options ) , 
241-             // relationship operations 
242-             relationship : new  UrlPattern ( '/:type/:id/relationships/:relationship' ,  options ) , 
266+             [ UrlPatterns . SINGLE ] : new  UrlPattern ( buildPath ( [ ':type' ,  ':id' ] ) ,  options ) , 
267+             [ UrlPatterns . FETCH_RELATIONSHIP ] : new  UrlPattern ( buildPath ( [ ':type' ,  ':id' ,  ':relationship' ] ) ,  options ) , 
268+             [ UrlPatterns . RELATIONSHIP ] : new  UrlPattern ( 
269+                 buildPath ( [ ':type' ,  ':id' ,  'relationships' ,  ':relationship' ] ) , 
270+                 options 
271+             ) , 
272+             [ UrlPatterns . COLLECTION ] : new  UrlPattern ( buildPath ( [ ':type' ] ) ,  options ) , 
243273        } ; 
244274    } 
245275
276+     private  reverseModelNameMap ( type : string ) : string  { 
277+         return  this . reverseModelNameMapping [ type ]  ??  type ; 
278+     } 
279+ 
280+     private  matchUrlPattern ( path : string ,  routeType : UrlPatterns ) : Match  { 
281+         const  pattern  =  this . urlPatternMap [ routeType ] ; 
282+         if  ( ! pattern )  { 
283+             throw  new  InvalidValueError ( `Unknown route type: ${ routeType }  ) ; 
284+         } 
285+ 
286+         const  match  =  pattern . match ( path ) ; 
287+         if  ( match )  { 
288+             match . type  =  this . modelNameMapping [ match . type ]  ??  match . type ; 
289+             match . relationship  =  this . modelNameMapping [ match . relationship ]  ??  match . relationship ; 
290+         } 
291+         return  match ; 
292+     } 
293+ 
246294    async  handleRequest ( { 
247295        prisma, 
248296        method, 
@@ -274,19 +322,18 @@ class RequestHandler extends APIHandlerBase {
274322        try  { 
275323            switch  ( method )  { 
276324                case  'GET' : { 
277-                     let  match  =  this . urlPatterns . single . match ( path ) ; 
325+                     let  match  =  this . matchUrlPattern ( path ,   UrlPatterns . SINGLE ) ; 
278326                    if  ( match )  { 
279327                        // single resource read 
280328                        return  await  this . processSingleRead ( prisma ,  match . type ,  match . id ,  query ) ; 
281329                    } 
282- 
283-                     match  =  this . urlPatterns . fetchRelationship . match ( path ) ; 
330+                     match  =  this . matchUrlPattern ( path ,  UrlPatterns . FETCH_RELATIONSHIP ) ; 
284331                    if  ( match )  { 
285332                        // fetch related resource(s) 
286333                        return  await  this . processFetchRelated ( prisma ,  match . type ,  match . id ,  match . relationship ,  query ) ; 
287334                    } 
288335
289-                     match  =  this . urlPatterns . relationship . match ( path ) ; 
336+                     match  =  this . matchUrlPattern ( path ,   UrlPatterns . RELATIONSHIP ) ; 
290337                    if  ( match )  { 
291338                        // read relationship 
292339                        return  await  this . processReadRelationship ( 
@@ -298,7 +345,7 @@ class RequestHandler extends APIHandlerBase {
298345                        ) ; 
299346                    } 
300347
301-                     match  =  this . urlPatterns . collection . match ( path ) ; 
348+                     match  =  this . matchUrlPattern ( path ,   UrlPatterns . COLLECTION ) ; 
302349                    if  ( match )  { 
303350                        // collection read 
304351                        return  await  this . processCollectionRead ( prisma ,  match . type ,  query ) ; 
@@ -311,8 +358,7 @@ class RequestHandler extends APIHandlerBase {
311358                    if  ( ! requestBody )  { 
312359                        return  this . makeError ( 'invalidPayload' ) ; 
313360                    } 
314- 
315-                     let  match  =  this . urlPatterns . collection . match ( path ) ; 
361+                     let  match  =  this . matchUrlPattern ( path ,  UrlPatterns . COLLECTION ) ; 
316362                    if  ( match )  { 
317363                        const  body  =  requestBody  as  any ; 
318364                        const  upsertMeta  =  this . upsertMetaSchema . safeParse ( body ) ; 
@@ -338,8 +384,7 @@ class RequestHandler extends APIHandlerBase {
338384                            ) ; 
339385                        } 
340386                    } 
341- 
342-                     match  =  this . urlPatterns . relationship . match ( path ) ; 
387+                     match  =  this . matchUrlPattern ( path ,  UrlPatterns . RELATIONSHIP ) ; 
343388                    if  ( match )  { 
344389                        // relationship creation (collection relationship only) 
345390                        return  await  this . processRelationshipCRUD ( 
@@ -362,8 +407,7 @@ class RequestHandler extends APIHandlerBase {
362407                    if  ( ! requestBody )  { 
363408                        return  this . makeError ( 'invalidPayload' ) ; 
364409                    } 
365- 
366-                     let  match  =  this . urlPatterns . single . match ( path ) ; 
410+                     let  match  =  this . matchUrlPattern ( path ,  UrlPatterns . SINGLE ) ; 
367411                    if  ( match )  { 
368412                        // resource update 
369413                        return  await  this . processUpdate ( 
@@ -376,8 +420,7 @@ class RequestHandler extends APIHandlerBase {
376420                            zodSchemas 
377421                        ) ; 
378422                    } 
379- 
380-                     match  =  this . urlPatterns . relationship . match ( path ) ; 
423+                     match  =  this . matchUrlPattern ( path ,  UrlPatterns . RELATIONSHIP ) ; 
381424                    if  ( match )  { 
382425                        // relationship update 
383426                        return  await  this . processRelationshipCRUD ( 
@@ -395,13 +438,13 @@ class RequestHandler extends APIHandlerBase {
395438                } 
396439
397440                case  'DELETE' : { 
398-                     let  match  =  this . urlPatterns . single . match ( path ) ; 
441+                     let  match  =  this . matchUrlPattern ( path ,   UrlPatterns . SINGLE ) ; 
399442                    if  ( match )  { 
400443                        // resource deletion 
401444                        return  await  this . processDelete ( prisma ,  match . type ,  match . id ) ; 
402445                    } 
403446
404-                     match  =  this . urlPatterns . relationship . match ( path ) ; 
447+                     match  =  this . matchUrlPattern ( path ,   UrlPatterns . RELATIONSHIP ) ; 
405448                    if  ( match )  { 
406449                        // relationship deletion (collection relationship only) 
407450                        return  await  this . processRelationshipCRUD ( 
@@ -531,11 +574,13 @@ class RequestHandler extends APIHandlerBase {
531574        } 
532575
533576        if  ( entity ?. [ relationship ] )  { 
577+             const  mappedType  =  this . reverseModelNameMap ( type ) ; 
578+             const  mappedRelationship  =  this . reverseModelNameMap ( relationship ) ; 
534579            return  { 
535580                status : 200 , 
536581                body : await  this . serializeItems ( relationInfo . type ,  entity [ relationship ] ,  { 
537582                    linkers : { 
538-                         document : new  Linker ( ( )  =>  this . makeLinkUrl ( `/${ type } ${ resourceId } ${ relationship }  ) ) , 
583+                         document : new  Linker ( ( )  =>  this . makeLinkUrl ( `/${ mappedType } ${ resourceId } ${ mappedRelationship }  ) ) , 
539584                        paginator, 
540585                    } , 
541586                    include, 
@@ -582,11 +627,13 @@ class RequestHandler extends APIHandlerBase {
582627        } 
583628
584629        const  entity : any  =  await  prisma [ type ] . findUnique ( args ) ; 
630+         const  mappedType  =  this . reverseModelNameMap ( type ) ; 
631+         const  mappedRelationship  =  this . reverseModelNameMap ( relationship ) ; 
585632
586633        if  ( entity ?. _count ?. [ relationship ]  !==  undefined )  { 
587634            // build up paginator 
588635            const  total  =  entity ?. _count ?. [ relationship ]  as  number ; 
589-             const  url  =  this . makeNormalizedUrl ( `/${ type } ${ resourceId } ${ relationship }  ,  query ) ; 
636+             const  url  =  this . makeNormalizedUrl ( `/${ mappedType } ${ resourceId } ${ mappedRelationship }  ,  query ) ; 
590637            const  {  offset,  limit }  =  this . getPagination ( query ) ; 
591638            paginator  =  this . makePaginator ( url ,  offset ,  limit ,  total ) ; 
592639        } 
@@ -595,7 +642,7 @@ class RequestHandler extends APIHandlerBase {
595642            const  serialized : any  =  await  this . serializeItems ( relationInfo . type ,  entity [ relationship ] ,  { 
596643                linkers : { 
597644                    document : new  Linker ( ( )  => 
598-                         this . makeLinkUrl ( `/${ type } ${ resourceId } ${ relationship }  ) 
645+                         this . makeLinkUrl ( `/${ mappedType } ${ resourceId } ${ mappedRelationship }  ) 
599646                    ) , 
600647                    paginator, 
601648                } , 
@@ -680,7 +727,8 @@ class RequestHandler extends APIHandlerBase {
680727            ] ) ; 
681728            const  total  =  count  as  number ; 
682729
683-             const  url  =  this . makeNormalizedUrl ( `/${ type }  ,  query ) ; 
730+             const  mappedType  =  this . reverseModelNameMap ( type ) ; 
731+             const  url  =  this . makeNormalizedUrl ( `/${ mappedType }  ,  query ) ; 
684732            const  options : Partial < SerializerOptions >  =  { 
685733                include, 
686734                linkers : { 
@@ -1009,9 +1057,12 @@ class RequestHandler extends APIHandlerBase {
10091057
10101058        const  entity : any  =  await  prisma [ type ] . update ( updateArgs ) ; 
10111059
1060+         const  mappedType  =  this . reverseModelNameMap ( type ) ; 
1061+         const  mappedRelationship  =  this . reverseModelNameMap ( relationship ) ; 
1062+ 
10121063        const  serialized : any  =  await  this . serializeItems ( relationInfo . type ,  entity [ relationship ] ,  { 
10131064            linkers : { 
1014-                 document : new  Linker ( ( )  =>  this . makeLinkUrl ( `/${ type } ${ resourceId } ${ relationship }  ) ) , 
1065+                 document : new  Linker ( ( )  =>  this . makeLinkUrl ( `/${ mappedType } ${ resourceId } ${ mappedRelationship }  ) ) , 
10151066            } , 
10161067            onlyIdentifier : true , 
10171068        } ) ; 
@@ -1147,7 +1198,7 @@ class RequestHandler extends APIHandlerBase {
11471198    } 
11481199
11491200    private  makeLinkUrl ( path : string )  { 
1150-         return  `${ this . options . endpoint } ${ path }  ; 
1201+         return  `${ this . options . endpoint } ${ this . prefix } ${ path }  ; 
11511202    } 
11521203
11531204    private  buildSerializers ( modelMeta : ModelMeta )  { 
@@ -1156,15 +1207,16 @@ class RequestHandler extends APIHandlerBase {
11561207
11571208        for  ( const  model  of  Object . keys ( modelMeta . models ) )  { 
11581209            const  ids  =  getIdFields ( modelMeta ,  model ) ; 
1210+             const  mappedModel  =  this . reverseModelNameMap ( model ) ; 
11591211
11601212            if  ( ids . length  <  1 )  { 
11611213                continue ; 
11621214            } 
11631215
11641216            const  linker  =  new  Linker ( ( items )  => 
11651217                Array . isArray ( items ) 
1166-                     ? this . makeLinkUrl ( `/${ model }  ) 
1167-                     : this . makeLinkUrl ( `/${ model } ${ this . getId ( model ,  items ,  modelMeta ) }  ) 
1218+                     ? this . makeLinkUrl ( `/${ mappedModel }  ) 
1219+                     : this . makeLinkUrl ( `/${ mappedModel } ${ this . getId ( model ,  items ,  modelMeta ) }  ) 
11681220            ) ; 
11691221            linkers [ model ]  =  linker ; 
11701222
@@ -1208,6 +1260,9 @@ class RequestHandler extends APIHandlerBase {
12081260                } 
12091261                const  fieldIds  =  getIdFields ( modelMeta ,  fieldMeta . type ) ; 
12101262                if  ( fieldIds . length  >  0 )  { 
1263+                     const  mappedModel  =  this . reverseModelNameMap ( model ) ; 
1264+                     const  mappedField  =  this . reverseModelNameMap ( field ) ; 
1265+ 
12111266                    const  relator  =  new  Relator ( 
12121267                        async  ( data )  =>  { 
12131268                            return  ( data  as  any ) [ field ] ; 
@@ -1218,16 +1273,16 @@ class RequestHandler extends APIHandlerBase {
12181273                            linkers : { 
12191274                                related : new  Linker ( ( primary )  => 
12201275                                    this . makeLinkUrl ( 
1221-                                         `/${ lowerCaseFirst ( model ) } ${ this . getId ( model ,  primary ,  modelMeta ) } ${ field }  
1276+                                         `/${ lowerCaseFirst ( mappedModel ) } ${ this . getId ( model ,  primary ,  modelMeta ) } ${ mappedField }  
12221277                                    ) 
12231278                                ) , 
12241279                                relationship : new  Linker ( ( primary )  => 
12251280                                    this . makeLinkUrl ( 
1226-                                         `/${ lowerCaseFirst ( model ) } ${ this . getId (  
1281+                                         `/${ lowerCaseFirst ( mappedModel ) } ${ this . getId (  
12271282                                            model ,  
12281283                                            primary ,  
12291284                                            modelMeta  
1230-                                         ) }  /relationships/${ field } 
1285+                                         ) }  /relationships/${ mappedField } 
12311286                                    ) 
12321287                                ) , 
12331288                            } , 
0 commit comments