diff --git a/packages/server/src/api/rest/index.ts b/packages/server/src/api/rest/index.ts index 36442f4f..32367df4 100644 --- a/packages/server/src/api/rest/index.ts +++ b/packages/server/src/api/rest/index.ts @@ -5,6 +5,7 @@ import { DbClientContract, FieldInfo, PrismaErrorCode, + clone, enumerate, getIdFields, isPrismaClientKnownRequestError, @@ -1120,22 +1121,8 @@ class RequestHandler extends APIHandlerBase { throw new Error(`serializer not found for model ${model}`); } - const typeInfo = this.typeMap[model]; - - let itemsWithId: any = items; - if (typeInfo.idFields.length > 1 && Array.isArray(items)) { - itemsWithId = items.map((item: any) => { - return { - ...item, - [this.makeIdKey(typeInfo.idFields)]: this.makeCompoundId(typeInfo.idFields, item), - }; - }); - } else if (typeInfo.idFields.length > 1 && typeof items === 'object') { - itemsWithId = { - ...items, - [this.makeIdKey(typeInfo.idFields)]: this.makeCompoundId(typeInfo.idFields, items), - }; - } + const itemsWithId = clone(items); + this.injectCompoundId(model, itemsWithId); // serialize to JSON:API structure const serialized = await serializer.serialize(itemsWithId, options); @@ -1154,6 +1141,31 @@ class RequestHandler extends APIHandlerBase { return result; } + private injectCompoundId(model: string, items: unknown) { + const typeInfo = this.typeMap[lowerCaseFirst(model)]; + if (!typeInfo) { + return; + } + + // recursively traverse the entity to create synthetic ID field for models with compound ID + enumerate(items).forEach((item: any) => { + if (!item) { + return; + } + + if (typeInfo.idFields.length > 1) { + item[this.makeIdKey(typeInfo.idFields)] = this.makeCompoundId(typeInfo.idFields, item); + } + + for (const [key, value] of Object.entries(item)) { + if (typeInfo.relationships[key]) { + // field is a relationship, recurse + this.injectCompoundId(typeInfo.relationships[key].type, value); + } + } + }); + } + private toPlainObject(data: any): any { if (data === undefined || data === null) { return data; diff --git a/packages/server/tests/api/rest.test.ts b/packages/server/tests/api/rest.test.ts index f0ba24df..fa7d0cfb 100644 --- a/packages/server/tests/api/rest.test.ts +++ b/packages/server/tests/api/rest.test.ts @@ -1373,6 +1373,40 @@ describe('REST server tests', () => { }, }); }); + + it('get as relationship', async () => { + const r = await handler({ + method: 'get', + path: `/post/1`, + query: { include: 'likes' }, + prisma, + }); + + expect(r.status).toBe(200); + expect(r.body).toMatchObject({ + data: { + relationships: { + likes: { + data: [{ type: 'postLike', id: `1${idDivider}user2` }], + }, + }, + }, + included: [ + expect.objectContaining({ + type: 'postLike', + id: '1_user2', + attributes: { + postId: 1, + userId: 'user2', + superLike: false, + }, + links: { + self: 'http://localhost/api/postLike/1_user2', + }, + }), + ], + }); + }); }); });