@@ -50,6 +50,8 @@ export type Options = {
5050 * it should be included in the charset.
5151 */
5252 urlSegmentCharset ?: string ;
53+
54+ modelNameMapping ?: Record < string , string > ;
5355} ;
5456
5557type RelationshipInfo = {
@@ -65,6 +67,19 @@ type ModelInfo = {
6567 relationships : Record < string , RelationshipInfo > ;
6668} ;
6769
70+ type Match = {
71+ type : string ;
72+ id : string ;
73+ relationship : string ;
74+ } ;
75+
76+ enum UrlPatterns {
77+ SINGLE = 'single' ,
78+ FETCH_RELATIONSHIP = 'fetchRelationship' ,
79+ RELATIONSHIP = 'relationship' ,
80+ COLLECTION = 'collection' ,
81+ }
82+
6883class InvalidValueError extends Error {
6984 constructor ( public readonly message : string ) {
7085 super ( message ) ;
@@ -220,29 +235,57 @@ class RequestHandler extends APIHandlerBase {
220235 // divider used to separate compound ID fields
221236 private idDivider ;
222237
223- private urlPatterns ;
238+ private urlPatternMap : Record < UrlPatterns , UrlPattern > ;
239+ private modelNameMapping : Record < string , string > ;
240+ private reverseModelNameMapping : Record < string , string > ;
224241
225242 constructor ( private readonly options : Options ) {
226243 super ( ) ;
227244 this . idDivider = options . idDivider ?? prismaIdDivider ;
228245 const segmentCharset = options . urlSegmentCharset ?? 'a-zA-Z0-9-_~ %' ;
229- this . urlPatterns = this . buildUrlPatterns ( this . idDivider , segmentCharset ) ;
246+
247+ this . modelNameMapping = options . modelNameMapping ?? { } ;
248+ this . reverseModelNameMapping = Object . fromEntries (
249+ Object . entries ( this . modelNameMapping ) . map ( ( [ k , v ] ) => [ v , k ] )
250+ ) ;
251+ this . urlPatternMap = this . buildUrlPatternMap ( segmentCharset ) ;
230252 }
231253
232- buildUrlPatterns ( idDivider : string , urlSegmentNameCharset : string ) {
254+ private buildUrlPatternMap ( urlSegmentNameCharset : string ) : Record < UrlPatterns , UrlPattern > {
233255 const options = { segmentValueCharset : urlSegmentNameCharset } ;
256+
257+ const buildPath = ( segments : string [ ] ) => {
258+ return '/' + segments . join ( '/' ) ;
259+ } ;
260+
234261 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 ) ,
262+ [ UrlPatterns . SINGLE ] : new UrlPattern ( buildPath ( [ ':type' , ':id' ] ) , options ) ,
263+ [ UrlPatterns . FETCH_RELATIONSHIP ] : new UrlPattern ( buildPath ( [ ':type' , ':id' , ':relationship' ] ) , options ) ,
264+ [ UrlPatterns . RELATIONSHIP ] : new UrlPattern (
265+ buildPath ( [ ':type' , ':id' , 'relationships' , ':relationship' ] ) ,
266+ options
267+ ) ,
268+ [ UrlPatterns . COLLECTION ] : new UrlPattern ( buildPath ( [ ':type' ] ) , options ) ,
243269 } ;
244270 }
245271
272+ private reverseModelNameMap ( type : string ) : string {
273+ return this . reverseModelNameMapping [ type ] ?? type ;
274+ }
275+
276+ private matchUrlPattern ( path : string , routeType : UrlPatterns ) : Match {
277+ const pattern = this . urlPatternMap [ routeType ] ;
278+ if ( ! pattern ) {
279+ throw new InvalidValueError ( `Unknown route type: ${ routeType } ` ) ;
280+ }
281+
282+ const match = pattern . match ( path ) ;
283+ if ( match ) {
284+ match . type = this . modelNameMapping [ match . type ] ?? match . type ;
285+ }
286+ return match ;
287+ }
288+
246289 async handleRequest ( {
247290 prisma,
248291 method,
@@ -274,19 +317,18 @@ class RequestHandler extends APIHandlerBase {
274317 try {
275318 switch ( method ) {
276319 case 'GET' : {
277- let match = this . urlPatterns . single . match ( path ) ;
320+ let match = this . matchUrlPattern ( path , UrlPatterns . SINGLE ) ;
278321 if ( match ) {
279322 // single resource read
280323 return await this . processSingleRead ( prisma , match . type , match . id , query ) ;
281324 }
282-
283- match = this . urlPatterns . fetchRelationship . match ( path ) ;
325+ match = this . matchUrlPattern ( path , UrlPatterns . FETCH_RELATIONSHIP ) ;
284326 if ( match ) {
285327 // fetch related resource(s)
286328 return await this . processFetchRelated ( prisma , match . type , match . id , match . relationship , query ) ;
287329 }
288330
289- match = this . urlPatterns . relationship . match ( path ) ;
331+ match = this . matchUrlPattern ( path , UrlPatterns . RELATIONSHIP ) ;
290332 if ( match ) {
291333 // read relationship
292334 return await this . processReadRelationship (
@@ -298,7 +340,7 @@ class RequestHandler extends APIHandlerBase {
298340 ) ;
299341 }
300342
301- match = this . urlPatterns . collection . match ( path ) ;
343+ match = this . matchUrlPattern ( path , UrlPatterns . COLLECTION ) ;
302344 if ( match ) {
303345 // collection read
304346 return await this . processCollectionRead ( prisma , match . type , query ) ;
@@ -311,8 +353,7 @@ class RequestHandler extends APIHandlerBase {
311353 if ( ! requestBody ) {
312354 return this . makeError ( 'invalidPayload' ) ;
313355 }
314-
315- let match = this . urlPatterns . collection . match ( path ) ;
356+ let match = this . matchUrlPattern ( path , UrlPatterns . COLLECTION ) ;
316357 if ( match ) {
317358 const body = requestBody as any ;
318359 const upsertMeta = this . upsertMetaSchema . safeParse ( body ) ;
@@ -338,8 +379,7 @@ class RequestHandler extends APIHandlerBase {
338379 ) ;
339380 }
340381 }
341-
342- match = this . urlPatterns . relationship . match ( path ) ;
382+ match = this . matchUrlPattern ( path , UrlPatterns . RELATIONSHIP ) ;
343383 if ( match ) {
344384 // relationship creation (collection relationship only)
345385 return await this . processRelationshipCRUD (
@@ -362,8 +402,7 @@ class RequestHandler extends APIHandlerBase {
362402 if ( ! requestBody ) {
363403 return this . makeError ( 'invalidPayload' ) ;
364404 }
365-
366- let match = this . urlPatterns . single . match ( path ) ;
405+ let match = this . matchUrlPattern ( path , UrlPatterns . SINGLE ) ;
367406 if ( match ) {
368407 // resource update
369408 return await this . processUpdate (
@@ -376,8 +415,7 @@ class RequestHandler extends APIHandlerBase {
376415 zodSchemas
377416 ) ;
378417 }
379-
380- match = this . urlPatterns . relationship . match ( path ) ;
418+ match = this . matchUrlPattern ( path , UrlPatterns . RELATIONSHIP ) ;
381419 if ( match ) {
382420 // relationship update
383421 return await this . processRelationshipCRUD (
@@ -395,13 +433,13 @@ class RequestHandler extends APIHandlerBase {
395433 }
396434
397435 case 'DELETE' : {
398- let match = this . urlPatterns . single . match ( path ) ;
436+ let match = this . matchUrlPattern ( path , UrlPatterns . SINGLE ) ;
399437 if ( match ) {
400438 // resource deletion
401439 return await this . processDelete ( prisma , match . type , match . id ) ;
402440 }
403441
404- match = this . urlPatterns . relationship . match ( path ) ;
442+ match = this . matchUrlPattern ( path , UrlPatterns . RELATIONSHIP ) ;
405443 if ( match ) {
406444 // relationship deletion (collection relationship only)
407445 return await this . processRelationshipCRUD (
@@ -531,11 +569,12 @@ class RequestHandler extends APIHandlerBase {
531569 }
532570
533571 if ( entity ?. [ relationship ] ) {
572+ const mappedType = this . reverseModelNameMap ( type ) ;
534573 return {
535574 status : 200 ,
536575 body : await this . serializeItems ( relationInfo . type , entity [ relationship ] , {
537576 linkers : {
538- document : new Linker ( ( ) => this . makeLinkUrl ( `/${ type } /${ resourceId } /${ relationship } ` ) ) ,
577+ document : new Linker ( ( ) => this . makeLinkUrl ( `/${ mappedType } /${ resourceId } /${ relationship } ` ) ) ,
539578 paginator,
540579 } ,
541580 include,
@@ -582,11 +621,12 @@ class RequestHandler extends APIHandlerBase {
582621 }
583622
584623 const entity : any = await prisma [ type ] . findUnique ( args ) ;
624+ const mappedType = this . reverseModelNameMap ( type ) ;
585625
586626 if ( entity ?. _count ?. [ relationship ] !== undefined ) {
587627 // build up paginator
588628 const total = entity ?. _count ?. [ relationship ] as number ;
589- const url = this . makeNormalizedUrl ( `/${ type } /${ resourceId } /relationships/${ relationship } ` , query ) ;
629+ const url = this . makeNormalizedUrl ( `/${ mappedType } /${ resourceId } /relationships/${ relationship } ` , query ) ;
590630 const { offset, limit } = this . getPagination ( query ) ;
591631 paginator = this . makePaginator ( url , offset , limit , total ) ;
592632 }
@@ -595,7 +635,7 @@ class RequestHandler extends APIHandlerBase {
595635 const serialized : any = await this . serializeItems ( relationInfo . type , entity [ relationship ] , {
596636 linkers : {
597637 document : new Linker ( ( ) =>
598- this . makeLinkUrl ( `/${ type } /${ resourceId } /relationships/${ relationship } ` )
638+ this . makeLinkUrl ( `/${ mappedType } /${ resourceId } /relationships/${ relationship } ` )
599639 ) ,
600640 paginator,
601641 } ,
@@ -680,7 +720,8 @@ class RequestHandler extends APIHandlerBase {
680720 ] ) ;
681721 const total = count as number ;
682722
683- const url = this . makeNormalizedUrl ( `/${ type } ` , query ) ;
723+ const mappedType = this . reverseModelNameMap ( type ) ;
724+ const url = this . makeNormalizedUrl ( `/${ mappedType } ` , query ) ;
684725 const options : Partial < SerializerOptions > = {
685726 include,
686727 linkers : {
@@ -1009,9 +1050,13 @@ class RequestHandler extends APIHandlerBase {
10091050
10101051 const entity : any = await prisma [ type ] . update ( updateArgs ) ;
10111052
1053+ const mappedType = this . reverseModelNameMap ( type ) ;
1054+
10121055 const serialized : any = await this . serializeItems ( relationInfo . type , entity [ relationship ] , {
10131056 linkers : {
1014- document : new Linker ( ( ) => this . makeLinkUrl ( `/${ type } /${ resourceId } /relationships/${ relationship } ` ) ) ,
1057+ document : new Linker ( ( ) =>
1058+ this . makeLinkUrl ( `/${ mappedType } /${ resourceId } /relationships/${ relationship } ` )
1059+ ) ,
10151060 } ,
10161061 onlyIdentifier : true ,
10171062 } ) ;
@@ -1156,15 +1201,16 @@ class RequestHandler extends APIHandlerBase {
11561201
11571202 for ( const model of Object . keys ( modelMeta . models ) ) {
11581203 const ids = getIdFields ( modelMeta , model ) ;
1204+ const mappedModel = this . reverseModelNameMap ( model ) ;
11591205
11601206 if ( ids . length < 1 ) {
11611207 continue ;
11621208 }
11631209
11641210 const linker = new Linker ( ( items ) =>
11651211 Array . isArray ( items )
1166- ? this . makeLinkUrl ( `/${ model } ` )
1167- : this . makeLinkUrl ( `/${ model } /${ this . getId ( model , items , modelMeta ) } ` )
1212+ ? this . makeLinkUrl ( `/${ mappedModel } ` )
1213+ : this . makeLinkUrl ( `/${ mappedModel } /${ this . getId ( model , items , modelMeta ) } ` )
11681214 ) ;
11691215 linkers [ model ] = linker ;
11701216
@@ -1208,6 +1254,9 @@ class RequestHandler extends APIHandlerBase {
12081254 }
12091255 const fieldIds = getIdFields ( modelMeta , fieldMeta . type ) ;
12101256 if ( fieldIds . length > 0 ) {
1257+ const mappedModel = this . reverseModelNameMap ( model ) ;
1258+ const mappedField = this . reverseModelNameMap ( field ) ;
1259+
12111260 const relator = new Relator (
12121261 async ( data ) => {
12131262 return ( data as any ) [ field ] ;
@@ -1218,16 +1267,20 @@ class RequestHandler extends APIHandlerBase {
12181267 linkers : {
12191268 related : new Linker ( ( primary ) =>
12201269 this . makeLinkUrl (
1221- `/${ lowerCaseFirst ( model ) } /${ this . getId ( model , primary , modelMeta ) } /${ field } `
1270+ `/${ lowerCaseFirst ( mappedModel ) } /${ this . getId (
1271+ model ,
1272+ primary ,
1273+ modelMeta
1274+ ) } /${ mappedField } `
12221275 )
12231276 ) ,
12241277 relationship : new Linker ( ( primary ) =>
12251278 this . makeLinkUrl (
1226- `/${ lowerCaseFirst ( model ) } /${ this . getId (
1279+ `/${ lowerCaseFirst ( mappedModel ) } /${ this . getId (
12271280 model ,
12281281 primary ,
12291282 modelMeta
1230- ) } /relationships/${ field } `
1283+ ) } /relationships/${ mappedField } `
12311284 )
12321285 ) ,
12331286 } ,
0 commit comments