diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts
index 5c101a0793..5b40234e8d 100644
--- a/e2e/src/api/specs/album.e2e-spec.ts
+++ b/e2e/src/api/specs/album.e2e-spec.ts
@@ -142,6 +142,10 @@ describe('/albums', () => {
         ...user1Albums[0],
         assets: [expect.objectContaining({ isFavorite: false })],
         lastModifiedAssetTimestamp: expect.any(String),
+        startDate: expect.any(String),
+        endDate: expect.any(String),
+        shared: true,
+        albumUsers: expect.any(Array),
       });
     });
 
@@ -299,6 +303,10 @@ describe('/albums', () => {
         ...user1Albums[0],
         assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })],
         lastModifiedAssetTimestamp: expect.any(String),
+        startDate: expect.any(String),
+        endDate: expect.any(String),
+        albumUsers: expect.any(Array),
+        shared: true,
       });
     });
 
@@ -330,6 +338,10 @@ describe('/albums', () => {
         ...user1Albums[0],
         assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })],
         lastModifiedAssetTimestamp: expect.any(String),
+        startDate: expect.any(String),
+        endDate: expect.any(String),
+        albumUsers: expect.any(Array),
+        shared: true,
       });
     });
 
@@ -344,6 +356,10 @@ describe('/albums', () => {
         assets: [],
         assetCount: 1,
         lastModifiedAssetTimestamp: expect.any(String),
+        endDate: expect.any(String),
+        startDate: expect.any(String),
+        albumUsers: expect.any(Array),
+        shared: true,
       });
     });
   });
diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts
index 76f4fdfc98..2f99b958c4 100644
--- a/server/src/dtos/album.dto.ts
+++ b/server/src/dtos/album.dto.ts
@@ -29,7 +29,7 @@ export class AddUsersDto {
   albumUsers!: AlbumUserAddDto[];
 }
 
-class AlbumUserCreateDto {
+export class AlbumUserCreateDto {
   @ValidateUUID()
   userId!: string;
 
diff --git a/server/src/interfaces/album.interface.ts b/server/src/interfaces/album.interface.ts
index 24c64bdc9d..7af1bd97e1 100644
--- a/server/src/interfaces/album.interface.ts
+++ b/server/src/interfaces/album.interface.ts
@@ -1,3 +1,6 @@
+import { Insertable, Updateable } from 'kysely';
+import { Albums } from 'src/db';
+import { AlbumUserCreateDto } from 'src/dtos/album.dto';
 import { AlbumEntity } from 'src/entities/album.entity';
 import { IBulkAsset } from 'src/utils/asset.util';
 
@@ -15,7 +18,7 @@ export interface AlbumInfoOptions {
 }
 
 export interface IAlbumRepository extends IBulkAsset {
-  getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null>;
+  getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | undefined>;
   getByAssetId(ownerId: string, assetId: string): Promise<AlbumEntity[]>;
   removeAsset(assetId: string): Promise<void>;
   getMetadataForIds(ids: string[]): Promise<AlbumAssetCount[]>;
@@ -25,8 +28,8 @@ export interface IAlbumRepository extends IBulkAsset {
   restoreAll(userId: string): Promise<void>;
   softDeleteAll(userId: string): Promise<void>;
   deleteAll(userId: string): Promise<void>;
-  create(album: Partial<AlbumEntity>): Promise<AlbumEntity>;
-  update(album: Partial<AlbumEntity>): Promise<AlbumEntity>;
+  create(album: Insertable<Albums>, assetIds: string[], albumUsers: AlbumUserCreateDto[]): Promise<AlbumEntity>;
+  update(id: string, album: Updateable<Albums>): Promise<AlbumEntity>;
   delete(id: string): Promise<void>;
   updateThumbnails(): Promise<number | undefined>;
 }
diff --git a/server/src/queries/album.repository.sql b/server/src/queries/album.repository.sql
index 196a1d1609..89c9e3b4a9 100644
--- a/server/src/queries/album.repository.sql
+++ b/server/src/queries/album.repository.sql
@@ -1,460 +1,490 @@
 -- NOTE: This file is auto generated by ./sql-generator
 
 -- AlbumRepository.getById
-SELECT DISTINCT
-  "distinctAlias"."AlbumEntity_id" AS "ids_AlbumEntity_id"
-FROM
+select
+  "albums".*,
   (
-    SELECT
-      "AlbumEntity"."id" AS "AlbumEntity_id",
-      "AlbumEntity"."ownerId" AS "AlbumEntity_ownerId",
-      "AlbumEntity"."albumName" AS "AlbumEntity_albumName",
-      "AlbumEntity"."description" AS "AlbumEntity_description",
-      "AlbumEntity"."createdAt" AS "AlbumEntity_createdAt",
-      "AlbumEntity"."updatedAt" AS "AlbumEntity_updatedAt",
-      "AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
-      "AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
-      "AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
-      "AlbumEntity"."order" AS "AlbumEntity_order",
-      "AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
-      "AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
-      "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin",
-      "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email",
-      "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel",
-      "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId",
-      "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath",
-      "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword",
-      "AlbumEntity__AlbumEntity_owner"."createdAt" AS "AlbumEntity__AlbumEntity_owner_createdAt",
-      "AlbumEntity__AlbumEntity_owner"."deletedAt" AS "AlbumEntity__AlbumEntity_owner_deletedAt",
-      "AlbumEntity__AlbumEntity_owner"."status" AS "AlbumEntity__AlbumEntity_owner_status",
-      "AlbumEntity__AlbumEntity_owner"."updatedAt" AS "AlbumEntity__AlbumEntity_owner_updatedAt",
-      "AlbumEntity__AlbumEntity_owner"."quotaSizeInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaSizeInBytes",
-      "AlbumEntity__AlbumEntity_owner"."quotaUsageInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaUsageInBytes",
-      "AlbumEntity__AlbumEntity_owner"."profileChangedAt" AS "AlbumEntity__AlbumEntity_owner_profileChangedAt",
-      "AlbumEntity__AlbumEntity_albumUsers"."albumsId" AS "AlbumEntity__AlbumEntity_albumUsers_albumsId",
-      "AlbumEntity__AlbumEntity_albumUsers"."usersId" AS "AlbumEntity__AlbumEntity_albumUsers_usersId",
-      "AlbumEntity__AlbumEntity_albumUsers"."role" AS "AlbumEntity__AlbumEntity_albumUsers_role",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_id",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."name" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_name",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."isAdmin" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_isAdmin",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."email" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_email",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."storageLabel" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_storageLabel",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."oauthId" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_oauthId",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileImagePath" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileImagePath",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."shouldChangePassword" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_shouldChangePassword",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."createdAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_createdAt",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_deletedAt",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."status" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_status",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."updatedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_updatedAt",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaSizeInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaSizeInBytes",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaUsageInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaUsageInBytes",
-      "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileChangedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileChangedAt",
-      "AlbumEntity__AlbumEntity_sharedLinks"."id" AS "AlbumEntity__AlbumEntity_sharedLinks_id",
-      "AlbumEntity__AlbumEntity_sharedLinks"."description" AS "AlbumEntity__AlbumEntity_sharedLinks_description",
-      "AlbumEntity__AlbumEntity_sharedLinks"."password" AS "AlbumEntity__AlbumEntity_sharedLinks_password",
-      "AlbumEntity__AlbumEntity_sharedLinks"."userId" AS "AlbumEntity__AlbumEntity_sharedLinks_userId",
-      "AlbumEntity__AlbumEntity_sharedLinks"."key" AS "AlbumEntity__AlbumEntity_sharedLinks_key",
-      "AlbumEntity__AlbumEntity_sharedLinks"."type" AS "AlbumEntity__AlbumEntity_sharedLinks_type",
-      "AlbumEntity__AlbumEntity_sharedLinks"."createdAt" AS "AlbumEntity__AlbumEntity_sharedLinks_createdAt",
-      "AlbumEntity__AlbumEntity_sharedLinks"."expiresAt" AS "AlbumEntity__AlbumEntity_sharedLinks_expiresAt",
-      "AlbumEntity__AlbumEntity_sharedLinks"."allowUpload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowUpload",
-      "AlbumEntity__AlbumEntity_sharedLinks"."allowDownload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowDownload",
-      "AlbumEntity__AlbumEntity_sharedLinks"."showExif" AS "AlbumEntity__AlbumEntity_sharedLinks_showExif",
-      "AlbumEntity__AlbumEntity_sharedLinks"."albumId" AS "AlbumEntity__AlbumEntity_sharedLinks_albumId"
-    FROM
-      "albums" "AlbumEntity"
-      LEFT JOIN "users" "AlbumEntity__AlbumEntity_owner" ON "AlbumEntity__AlbumEntity_owner"."id" = "AlbumEntity"."ownerId"
-      AND (
-        "AlbumEntity__AlbumEntity_owner"."deletedAt" IS NULL
-      )
-      LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
-      LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId"
-      AND (
-        "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL
-      )
-      LEFT JOIN "shared_links" "AlbumEntity__AlbumEntity_sharedLinks" ON "AlbumEntity__AlbumEntity_sharedLinks"."albumId" = "AlbumEntity"."id"
-    WHERE
-      ((("AlbumEntity"."id" = $1)))
-      AND ("AlbumEntity"."deletedAt" IS NULL)
-  ) "distinctAlias"
-ORDER BY
-  "AlbumEntity_id" ASC
-LIMIT
-  1
+    select
+      to_json(obj)
+    from
+      (
+        select
+          "id",
+          "email",
+          "createdAt",
+          "profileImagePath",
+          "isAdmin",
+          "shouldChangePassword",
+          "deletedAt",
+          "oauthId",
+          "updatedAt",
+          "storageLabel",
+          "name",
+          "quotaSizeInBytes",
+          "quotaUsageInBytes",
+          "status",
+          "profileChangedAt"
+        from
+          "users"
+        where
+          "users"."id" = "albums"."ownerId"
+      ) as obj
+  ) as "owner",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          "album_users".*,
+          (
+            select
+              to_json(obj)
+            from
+              (
+                select
+                  "id",
+                  "email",
+                  "createdAt",
+                  "profileImagePath",
+                  "isAdmin",
+                  "shouldChangePassword",
+                  "deletedAt",
+                  "oauthId",
+                  "updatedAt",
+                  "storageLabel",
+                  "name",
+                  "quotaSizeInBytes",
+                  "quotaUsageInBytes",
+                  "status",
+                  "profileChangedAt"
+                from
+                  "users"
+                where
+                  "users"."id" = "album_users"."usersId"
+              ) as obj
+          ) as "user"
+        from
+          "albums_shared_users_users" as "album_users"
+        where
+          "album_users"."albumsId" = "albums"."id"
+      ) as agg
+  ) as "albumUsers",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          *
+        from
+          "shared_links"
+        where
+          "shared_links"."albumId" = "albums"."id"
+      ) as agg
+  ) as "sharedLinks"
+from
+  "albums"
+where
+  "albums"."id" = $1
+  and "albums"."deletedAt" is null
 
 -- AlbumRepository.getByAssetId
-SELECT
-  "AlbumEntity"."id" AS "AlbumEntity_id",
-  "AlbumEntity"."ownerId" AS "AlbumEntity_ownerId",
-  "AlbumEntity"."albumName" AS "AlbumEntity_albumName",
-  "AlbumEntity"."description" AS "AlbumEntity_description",
-  "AlbumEntity"."createdAt" AS "AlbumEntity_createdAt",
-  "AlbumEntity"."updatedAt" AS "AlbumEntity_updatedAt",
-  "AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
-  "AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
-  "AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
-  "AlbumEntity"."order" AS "AlbumEntity_order",
-  "AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
-  "AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
-  "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin",
-  "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email",
-  "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel",
-  "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId",
-  "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath",
-  "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword",
-  "AlbumEntity__AlbumEntity_owner"."createdAt" AS "AlbumEntity__AlbumEntity_owner_createdAt",
-  "AlbumEntity__AlbumEntity_owner"."deletedAt" AS "AlbumEntity__AlbumEntity_owner_deletedAt",
-  "AlbumEntity__AlbumEntity_owner"."status" AS "AlbumEntity__AlbumEntity_owner_status",
-  "AlbumEntity__AlbumEntity_owner"."updatedAt" AS "AlbumEntity__AlbumEntity_owner_updatedAt",
-  "AlbumEntity__AlbumEntity_owner"."quotaSizeInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaSizeInBytes",
-  "AlbumEntity__AlbumEntity_owner"."quotaUsageInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaUsageInBytes",
-  "AlbumEntity__AlbumEntity_owner"."profileChangedAt" AS "AlbumEntity__AlbumEntity_owner_profileChangedAt",
-  "AlbumEntity__AlbumEntity_albumUsers"."albumsId" AS "AlbumEntity__AlbumEntity_albumUsers_albumsId",
-  "AlbumEntity__AlbumEntity_albumUsers"."usersId" AS "AlbumEntity__AlbumEntity_albumUsers_usersId",
-  "AlbumEntity__AlbumEntity_albumUsers"."role" AS "AlbumEntity__AlbumEntity_albumUsers_role",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_id",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."name" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_name",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."isAdmin" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_isAdmin",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."email" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_email",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."storageLabel" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_storageLabel",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."oauthId" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_oauthId",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileImagePath" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileImagePath",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."shouldChangePassword" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_shouldChangePassword",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."createdAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_createdAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_deletedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."status" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_status",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."updatedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_updatedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaSizeInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaSizeInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaUsageInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaUsageInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileChangedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileChangedAt"
-FROM
-  "albums" "AlbumEntity"
-  LEFT JOIN "users" "AlbumEntity__AlbumEntity_owner" ON "AlbumEntity__AlbumEntity_owner"."id" = "AlbumEntity"."ownerId"
-  AND (
-    "AlbumEntity__AlbumEntity_owner"."deletedAt" IS NULL
-  )
-  LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId"
-  AND (
-    "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL
-  )
-  LEFT JOIN "albums_assets_assets" "AlbumEntity_AlbumEntity__AlbumEntity_assets" ON "AlbumEntity_AlbumEntity__AlbumEntity_assets"."albumsId" = "AlbumEntity"."id"
-  LEFT JOIN "assets" "AlbumEntity__AlbumEntity_assets" ON "AlbumEntity__AlbumEntity_assets"."id" = "AlbumEntity_AlbumEntity__AlbumEntity_assets"."assetsId"
-  AND (
-    "AlbumEntity__AlbumEntity_assets"."deletedAt" IS NULL
-  )
-WHERE
+select
+  "albums".*,
+  (
+    select
+      to_json(obj)
+    from
+      (
+        select
+          "id",
+          "email",
+          "createdAt",
+          "profileImagePath",
+          "isAdmin",
+          "shouldChangePassword",
+          "deletedAt",
+          "oauthId",
+          "updatedAt",
+          "storageLabel",
+          "name",
+          "quotaSizeInBytes",
+          "quotaUsageInBytes",
+          "status",
+          "profileChangedAt"
+        from
+          "users"
+        where
+          "users"."id" = "albums"."ownerId"
+      ) as obj
+  ) as "owner",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          "album_users".*,
+          (
+            select
+              to_json(obj)
+            from
+              (
+                select
+                  "id",
+                  "email",
+                  "createdAt",
+                  "profileImagePath",
+                  "isAdmin",
+                  "shouldChangePassword",
+                  "deletedAt",
+                  "oauthId",
+                  "updatedAt",
+                  "storageLabel",
+                  "name",
+                  "quotaSizeInBytes",
+                  "quotaUsageInBytes",
+                  "status",
+                  "profileChangedAt"
+                from
+                  "users"
+                where
+                  "users"."id" = "album_users"."usersId"
+              ) as obj
+          ) as "user"
+        from
+          "albums_shared_users_users" as "album_users"
+        where
+          "album_users"."albumsId" = "albums"."id"
+      ) as agg
+  ) as "albumUsers"
+from
+  "albums"
+  left join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
+  left join "albums_shared_users_users" as "album_users" on "album_users"."albumsId" = "albums"."id"
+where
   (
     (
-      (
-        (
-          ("AlbumEntity"."ownerId" = $1)
-          AND ((("AlbumEntity__AlbumEntity_assets"."id" = $2)))
-        )
-      )
-      OR (
-        (
-          (
-            (
-              (
-                "AlbumEntity__AlbumEntity_albumUsers"."usersId" = $3
-              )
-            )
-          )
-          AND ((("AlbumEntity__AlbumEntity_assets"."id" = $4)))
-        )
-      )
+      "albums"."ownerId" = $1
+      and "album_assets"."assetsId" = $2
+    )
+    or (
+      "album_users"."usersId" = $3
+      and "album_assets"."assetsId" = $4
     )
   )
-  AND ("AlbumEntity"."deletedAt" IS NULL)
-ORDER BY
-  "AlbumEntity"."createdAt" DESC
+  and "albums"."deletedAt" is null
+order by
+  "albums"."createdAt" desc,
+  "albums"."createdAt" desc
 
 -- AlbumRepository.getMetadataForIds
-SELECT
-  "album"."id" AS "album_id",
-  MIN("assets"."fileCreatedAt") AS "start_date",
-  MAX("assets"."fileCreatedAt") AS "end_date",
-  COUNT("assets"."id") AS "asset_count"
-FROM
-  "albums" "album"
-  LEFT JOIN "albums_assets_assets" "album_assets" ON "album_assets"."albumsId" = "album"."id"
-  LEFT JOIN "assets" "assets" ON "assets"."id" = "album_assets"."assetsId"
-  AND "assets"."deletedAt" IS NULL
-WHERE
-  ("album"."id" IN ($1))
-  AND ("album"."deletedAt" IS NULL)
-GROUP BY
-  "album"."id"
+select
+  "albums"."id",
+  min("assets"."fileCreatedAt") as "startDate",
+  max("assets"."fileCreatedAt") as "endDate",
+  count("assets"."id") as "assetCount"
+from
+  "albums"
+  left join "albums_assets_assets" as "album_assets" on "album_assets"."albumsId" = "albums"."id"
+  left join "assets" on "assets"."id" = "album_assets"."assetsId"
+where
+  "albums"."id" in ($1)
+group by
+  "albums"."id"
 
 -- AlbumRepository.getOwned
-SELECT
-  "AlbumEntity"."id" AS "AlbumEntity_id",
-  "AlbumEntity"."ownerId" AS "AlbumEntity_ownerId",
-  "AlbumEntity"."albumName" AS "AlbumEntity_albumName",
-  "AlbumEntity"."description" AS "AlbumEntity_description",
-  "AlbumEntity"."createdAt" AS "AlbumEntity_createdAt",
-  "AlbumEntity"."updatedAt" AS "AlbumEntity_updatedAt",
-  "AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
-  "AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
-  "AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
-  "AlbumEntity"."order" AS "AlbumEntity_order",
-  "AlbumEntity__AlbumEntity_albumUsers"."albumsId" AS "AlbumEntity__AlbumEntity_albumUsers_albumsId",
-  "AlbumEntity__AlbumEntity_albumUsers"."usersId" AS "AlbumEntity__AlbumEntity_albumUsers_usersId",
-  "AlbumEntity__AlbumEntity_albumUsers"."role" AS "AlbumEntity__AlbumEntity_albumUsers_role",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_id",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."name" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_name",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."isAdmin" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_isAdmin",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."email" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_email",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."storageLabel" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_storageLabel",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."oauthId" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_oauthId",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileImagePath" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileImagePath",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."shouldChangePassword" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_shouldChangePassword",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."createdAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_createdAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_deletedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."status" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_status",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."updatedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_updatedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaSizeInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaSizeInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaUsageInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaUsageInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileChangedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileChangedAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."id" AS "AlbumEntity__AlbumEntity_sharedLinks_id",
-  "AlbumEntity__AlbumEntity_sharedLinks"."description" AS "AlbumEntity__AlbumEntity_sharedLinks_description",
-  "AlbumEntity__AlbumEntity_sharedLinks"."password" AS "AlbumEntity__AlbumEntity_sharedLinks_password",
-  "AlbumEntity__AlbumEntity_sharedLinks"."userId" AS "AlbumEntity__AlbumEntity_sharedLinks_userId",
-  "AlbumEntity__AlbumEntity_sharedLinks"."key" AS "AlbumEntity__AlbumEntity_sharedLinks_key",
-  "AlbumEntity__AlbumEntity_sharedLinks"."type" AS "AlbumEntity__AlbumEntity_sharedLinks_type",
-  "AlbumEntity__AlbumEntity_sharedLinks"."createdAt" AS "AlbumEntity__AlbumEntity_sharedLinks_createdAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."expiresAt" AS "AlbumEntity__AlbumEntity_sharedLinks_expiresAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowUpload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowUpload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowDownload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowDownload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."showExif" AS "AlbumEntity__AlbumEntity_sharedLinks_showExif",
-  "AlbumEntity__AlbumEntity_sharedLinks"."albumId" AS "AlbumEntity__AlbumEntity_sharedLinks_albumId",
-  "AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
-  "AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
-  "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin",
-  "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email",
-  "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel",
-  "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId",
-  "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath",
-  "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword",
-  "AlbumEntity__AlbumEntity_owner"."createdAt" AS "AlbumEntity__AlbumEntity_owner_createdAt",
-  "AlbumEntity__AlbumEntity_owner"."deletedAt" AS "AlbumEntity__AlbumEntity_owner_deletedAt",
-  "AlbumEntity__AlbumEntity_owner"."status" AS "AlbumEntity__AlbumEntity_owner_status",
-  "AlbumEntity__AlbumEntity_owner"."updatedAt" AS "AlbumEntity__AlbumEntity_owner_updatedAt",
-  "AlbumEntity__AlbumEntity_owner"."quotaSizeInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaSizeInBytes",
-  "AlbumEntity__AlbumEntity_owner"."quotaUsageInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaUsageInBytes",
-  "AlbumEntity__AlbumEntity_owner"."profileChangedAt" AS "AlbumEntity__AlbumEntity_owner_profileChangedAt"
-FROM
-  "albums" "AlbumEntity"
-  LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId"
-  AND (
-    "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL
-  )
-  LEFT JOIN "shared_links" "AlbumEntity__AlbumEntity_sharedLinks" ON "AlbumEntity__AlbumEntity_sharedLinks"."albumId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "AlbumEntity__AlbumEntity_owner" ON "AlbumEntity__AlbumEntity_owner"."id" = "AlbumEntity"."ownerId"
-  AND (
-    "AlbumEntity__AlbumEntity_owner"."deletedAt" IS NULL
-  )
-WHERE
-  ((("AlbumEntity"."ownerId" = $1)))
-  AND ("AlbumEntity"."deletedAt" IS NULL)
-ORDER BY
-  "AlbumEntity"."createdAt" DESC
+select
+  "albums".*,
+  (
+    select
+      to_json(obj)
+    from
+      (
+        select
+          "id",
+          "email",
+          "createdAt",
+          "profileImagePath",
+          "isAdmin",
+          "shouldChangePassword",
+          "deletedAt",
+          "oauthId",
+          "updatedAt",
+          "storageLabel",
+          "name",
+          "quotaSizeInBytes",
+          "quotaUsageInBytes",
+          "status",
+          "profileChangedAt"
+        from
+          "users"
+        where
+          "users"."id" = "albums"."ownerId"
+      ) as obj
+  ) as "owner",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          "album_users".*,
+          (
+            select
+              to_json(obj)
+            from
+              (
+                select
+                  "id",
+                  "email",
+                  "createdAt",
+                  "profileImagePath",
+                  "isAdmin",
+                  "shouldChangePassword",
+                  "deletedAt",
+                  "oauthId",
+                  "updatedAt",
+                  "storageLabel",
+                  "name",
+                  "quotaSizeInBytes",
+                  "quotaUsageInBytes",
+                  "status",
+                  "profileChangedAt"
+                from
+                  "users"
+                where
+                  "users"."id" = "album_users"."usersId"
+              ) as obj
+          ) as "user"
+        from
+          "albums_shared_users_users" as "album_users"
+        where
+          "album_users"."albumsId" = "albums"."id"
+      ) as agg
+  ) as "albumUsers",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          *
+        from
+          "shared_links"
+        where
+          "shared_links"."albumId" = "albums"."id"
+      ) as agg
+  ) as "sharedLinks"
+from
+  "albums"
+where
+  "albums"."ownerId" = $1
+  and "albums"."deletedAt" is null
+order by
+  "albums"."createdAt" desc
 
 -- AlbumRepository.getShared
-SELECT
-  "AlbumEntity"."id" AS "AlbumEntity_id",
-  "AlbumEntity"."ownerId" AS "AlbumEntity_ownerId",
-  "AlbumEntity"."albumName" AS "AlbumEntity_albumName",
-  "AlbumEntity"."description" AS "AlbumEntity_description",
-  "AlbumEntity"."createdAt" AS "AlbumEntity_createdAt",
-  "AlbumEntity"."updatedAt" AS "AlbumEntity_updatedAt",
-  "AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
-  "AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
-  "AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
-  "AlbumEntity"."order" AS "AlbumEntity_order",
-  "AlbumEntity__AlbumEntity_albumUsers"."albumsId" AS "AlbumEntity__AlbumEntity_albumUsers_albumsId",
-  "AlbumEntity__AlbumEntity_albumUsers"."usersId" AS "AlbumEntity__AlbumEntity_albumUsers_usersId",
-  "AlbumEntity__AlbumEntity_albumUsers"."role" AS "AlbumEntity__AlbumEntity_albumUsers_role",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_id",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."name" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_name",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."isAdmin" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_isAdmin",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."email" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_email",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."storageLabel" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_storageLabel",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."oauthId" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_oauthId",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileImagePath" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileImagePath",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."shouldChangePassword" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_shouldChangePassword",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."createdAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_createdAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_deletedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."status" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_status",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."updatedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_updatedAt",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaSizeInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaSizeInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."quotaUsageInBytes" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_quotaUsageInBytes",
-  "a641d58cf46d4a391ba060ac4dc337665c69ffea"."profileChangedAt" AS "a641d58cf46d4a391ba060ac4dc337665c69ffea_profileChangedAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."id" AS "AlbumEntity__AlbumEntity_sharedLinks_id",
-  "AlbumEntity__AlbumEntity_sharedLinks"."description" AS "AlbumEntity__AlbumEntity_sharedLinks_description",
-  "AlbumEntity__AlbumEntity_sharedLinks"."password" AS "AlbumEntity__AlbumEntity_sharedLinks_password",
-  "AlbumEntity__AlbumEntity_sharedLinks"."userId" AS "AlbumEntity__AlbumEntity_sharedLinks_userId",
-  "AlbumEntity__AlbumEntity_sharedLinks"."key" AS "AlbumEntity__AlbumEntity_sharedLinks_key",
-  "AlbumEntity__AlbumEntity_sharedLinks"."type" AS "AlbumEntity__AlbumEntity_sharedLinks_type",
-  "AlbumEntity__AlbumEntity_sharedLinks"."createdAt" AS "AlbumEntity__AlbumEntity_sharedLinks_createdAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."expiresAt" AS "AlbumEntity__AlbumEntity_sharedLinks_expiresAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowUpload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowUpload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowDownload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowDownload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."showExif" AS "AlbumEntity__AlbumEntity_sharedLinks_showExif",
-  "AlbumEntity__AlbumEntity_sharedLinks"."albumId" AS "AlbumEntity__AlbumEntity_sharedLinks_albumId",
-  "AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
-  "AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
-  "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin",
-  "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email",
-  "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel",
-  "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId",
-  "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath",
-  "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword",
-  "AlbumEntity__AlbumEntity_owner"."createdAt" AS "AlbumEntity__AlbumEntity_owner_createdAt",
-  "AlbumEntity__AlbumEntity_owner"."deletedAt" AS "AlbumEntity__AlbumEntity_owner_deletedAt",
-  "AlbumEntity__AlbumEntity_owner"."status" AS "AlbumEntity__AlbumEntity_owner_status",
-  "AlbumEntity__AlbumEntity_owner"."updatedAt" AS "AlbumEntity__AlbumEntity_owner_updatedAt",
-  "AlbumEntity__AlbumEntity_owner"."quotaSizeInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaSizeInBytes",
-  "AlbumEntity__AlbumEntity_owner"."quotaUsageInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaUsageInBytes",
-  "AlbumEntity__AlbumEntity_owner"."profileChangedAt" AS "AlbumEntity__AlbumEntity_owner_profileChangedAt"
-FROM
-  "albums" "AlbumEntity"
-  LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId"
-  AND (
-    "a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL
-  )
-  LEFT JOIN "shared_links" "AlbumEntity__AlbumEntity_sharedLinks" ON "AlbumEntity__AlbumEntity_sharedLinks"."albumId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "AlbumEntity__AlbumEntity_owner" ON "AlbumEntity__AlbumEntity_owner"."id" = "AlbumEntity"."ownerId"
-  AND (
-    "AlbumEntity__AlbumEntity_owner"."deletedAt" IS NULL
-  )
-WHERE
+select distinct
+  on ("albums"."createdAt") "albums".*,
   (
-    (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
       (
-        (
+        select
+          "album_users".*,
           (
-            (
+            select
+              to_json(obj)
+            from
               (
-                "AlbumEntity__AlbumEntity_albumUsers"."usersId" = $1
-              )
-            )
-          )
-        )
-      )
-      OR (
-        (
-          (
-            (
-              (
-                "AlbumEntity__AlbumEntity_sharedLinks"."userId" = $2
-              )
-            )
-          )
-        )
-      )
-      OR (
-        (
-          ("AlbumEntity"."ownerId" = $3)
-          AND (
-            (
-              (
-                NOT (
-                  "AlbumEntity__AlbumEntity_albumUsers"."usersId" IS NULL
-                )
-              )
-            )
-          )
-        )
-      )
+                select
+                  "id",
+                  "email",
+                  "createdAt",
+                  "profileImagePath",
+                  "isAdmin",
+                  "shouldChangePassword",
+                  "deletedAt",
+                  "oauthId",
+                  "updatedAt",
+                  "storageLabel",
+                  "name",
+                  "quotaSizeInBytes",
+                  "quotaUsageInBytes",
+                  "status",
+                  "profileChangedAt"
+                from
+                  "users"
+                where
+                  "users"."id" = "album_users"."usersId"
+              ) as obj
+          ) as "user"
+        from
+          "albums_shared_users_users" as "album_users"
+        where
+          "album_users"."albumsId" = "albums"."id"
+      ) as agg
+  ) as "albumUsers",
+  (
+    select
+      to_json(obj)
+    from
+      (
+        select
+          "id",
+          "email",
+          "createdAt",
+          "profileImagePath",
+          "isAdmin",
+          "shouldChangePassword",
+          "deletedAt",
+          "oauthId",
+          "updatedAt",
+          "storageLabel",
+          "name",
+          "quotaSizeInBytes",
+          "quotaUsageInBytes",
+          "status",
+          "profileChangedAt"
+        from
+          "users"
+        where
+          "users"."id" = "albums"."ownerId"
+      ) as obj
+  ) as "owner",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          *
+        from
+          "shared_links"
+        where
+          "shared_links"."albumId" = "albums"."id"
+      ) as agg
+  ) as "sharedLinks"
+from
+  "albums"
+  left join "albums_shared_users_users" as "shared_albums" on "shared_albums"."albumsId" = "albums"."id"
+  left join "shared_links" on "shared_links"."albumId" = "albums"."id"
+where
+  (
+    "shared_albums"."usersId" = $1
+    or "shared_links"."userId" = $2
+    or (
+      "albums"."ownerId" = $3
+      and "shared_albums"."usersId" is not null
     )
   )
-  AND ("AlbumEntity"."deletedAt" IS NULL)
-ORDER BY
-  "AlbumEntity"."createdAt" DESC
+  and "albums"."deletedAt" is null
+order by
+  "albums"."createdAt" desc
 
 -- AlbumRepository.getNotShared
-SELECT
-  "AlbumEntity"."id" AS "AlbumEntity_id",
-  "AlbumEntity"."ownerId" AS "AlbumEntity_ownerId",
-  "AlbumEntity"."albumName" AS "AlbumEntity_albumName",
-  "AlbumEntity"."description" AS "AlbumEntity_description",
-  "AlbumEntity"."createdAt" AS "AlbumEntity_createdAt",
-  "AlbumEntity"."updatedAt" AS "AlbumEntity_updatedAt",
-  "AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
-  "AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
-  "AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
-  "AlbumEntity"."order" AS "AlbumEntity_order",
-  "AlbumEntity__AlbumEntity_albumUsers"."albumsId" AS "AlbumEntity__AlbumEntity_albumUsers_albumsId",
-  "AlbumEntity__AlbumEntity_albumUsers"."usersId" AS "AlbumEntity__AlbumEntity_albumUsers_usersId",
-  "AlbumEntity__AlbumEntity_albumUsers"."role" AS "AlbumEntity__AlbumEntity_albumUsers_role",
-  "AlbumEntity__AlbumEntity_sharedLinks"."id" AS "AlbumEntity__AlbumEntity_sharedLinks_id",
-  "AlbumEntity__AlbumEntity_sharedLinks"."description" AS "AlbumEntity__AlbumEntity_sharedLinks_description",
-  "AlbumEntity__AlbumEntity_sharedLinks"."password" AS "AlbumEntity__AlbumEntity_sharedLinks_password",
-  "AlbumEntity__AlbumEntity_sharedLinks"."userId" AS "AlbumEntity__AlbumEntity_sharedLinks_userId",
-  "AlbumEntity__AlbumEntity_sharedLinks"."key" AS "AlbumEntity__AlbumEntity_sharedLinks_key",
-  "AlbumEntity__AlbumEntity_sharedLinks"."type" AS "AlbumEntity__AlbumEntity_sharedLinks_type",
-  "AlbumEntity__AlbumEntity_sharedLinks"."createdAt" AS "AlbumEntity__AlbumEntity_sharedLinks_createdAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."expiresAt" AS "AlbumEntity__AlbumEntity_sharedLinks_expiresAt",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowUpload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowUpload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."allowDownload" AS "AlbumEntity__AlbumEntity_sharedLinks_allowDownload",
-  "AlbumEntity__AlbumEntity_sharedLinks"."showExif" AS "AlbumEntity__AlbumEntity_sharedLinks_showExif",
-  "AlbumEntity__AlbumEntity_sharedLinks"."albumId" AS "AlbumEntity__AlbumEntity_sharedLinks_albumId",
-  "AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
-  "AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
-  "AlbumEntity__AlbumEntity_owner"."isAdmin" AS "AlbumEntity__AlbumEntity_owner_isAdmin",
-  "AlbumEntity__AlbumEntity_owner"."email" AS "AlbumEntity__AlbumEntity_owner_email",
-  "AlbumEntity__AlbumEntity_owner"."storageLabel" AS "AlbumEntity__AlbumEntity_owner_storageLabel",
-  "AlbumEntity__AlbumEntity_owner"."oauthId" AS "AlbumEntity__AlbumEntity_owner_oauthId",
-  "AlbumEntity__AlbumEntity_owner"."profileImagePath" AS "AlbumEntity__AlbumEntity_owner_profileImagePath",
-  "AlbumEntity__AlbumEntity_owner"."shouldChangePassword" AS "AlbumEntity__AlbumEntity_owner_shouldChangePassword",
-  "AlbumEntity__AlbumEntity_owner"."createdAt" AS "AlbumEntity__AlbumEntity_owner_createdAt",
-  "AlbumEntity__AlbumEntity_owner"."deletedAt" AS "AlbumEntity__AlbumEntity_owner_deletedAt",
-  "AlbumEntity__AlbumEntity_owner"."status" AS "AlbumEntity__AlbumEntity_owner_status",
-  "AlbumEntity__AlbumEntity_owner"."updatedAt" AS "AlbumEntity__AlbumEntity_owner_updatedAt",
-  "AlbumEntity__AlbumEntity_owner"."quotaSizeInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaSizeInBytes",
-  "AlbumEntity__AlbumEntity_owner"."quotaUsageInBytes" AS "AlbumEntity__AlbumEntity_owner_quotaUsageInBytes",
-  "AlbumEntity__AlbumEntity_owner"."profileChangedAt" AS "AlbumEntity__AlbumEntity_owner_profileChangedAt"
-FROM
-  "albums" "AlbumEntity"
-  LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
-  LEFT JOIN "shared_links" "AlbumEntity__AlbumEntity_sharedLinks" ON "AlbumEntity__AlbumEntity_sharedLinks"."albumId" = "AlbumEntity"."id"
-  LEFT JOIN "users" "AlbumEntity__AlbumEntity_owner" ON "AlbumEntity__AlbumEntity_owner"."id" = "AlbumEntity"."ownerId"
-  AND (
-    "AlbumEntity__AlbumEntity_owner"."deletedAt" IS NULL
-  )
-WHERE
+select distinct
+  on ("albums"."createdAt") "albums".*,
   (
-    (
-      ("AlbumEntity"."ownerId" = $1)
-      AND (
-        (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          "album_users".*,
           (
-            "AlbumEntity__AlbumEntity_albumUsers"."usersId" IS NULL
-          )
-        )
-      )
-      AND (
-        (
-          (
-            "AlbumEntity__AlbumEntity_sharedLinks"."id" IS NULL
-          )
-        )
-      )
-    )
-  )
-  AND ("AlbumEntity"."deletedAt" IS NULL)
-ORDER BY
-  "AlbumEntity"."createdAt" DESC
+            select
+              to_json(obj)
+            from
+              (
+                select
+                  "id",
+                  "email",
+                  "createdAt",
+                  "profileImagePath",
+                  "isAdmin",
+                  "shouldChangePassword",
+                  "deletedAt",
+                  "oauthId",
+                  "updatedAt",
+                  "storageLabel",
+                  "name",
+                  "quotaSizeInBytes",
+                  "quotaUsageInBytes",
+                  "status",
+                  "profileChangedAt"
+                from
+                  "users"
+                where
+                  "users"."id" = "album_users"."usersId"
+              ) as obj
+          ) as "user"
+        from
+          "albums_shared_users_users" as "album_users"
+        where
+          "album_users"."albumsId" = "albums"."id"
+      ) as agg
+  ) as "albumUsers",
+  (
+    select
+      to_json(obj)
+    from
+      (
+        select
+          "id",
+          "email",
+          "createdAt",
+          "profileImagePath",
+          "isAdmin",
+          "shouldChangePassword",
+          "deletedAt",
+          "oauthId",
+          "updatedAt",
+          "storageLabel",
+          "name",
+          "quotaSizeInBytes",
+          "quotaUsageInBytes",
+          "status",
+          "profileChangedAt"
+        from
+          "users"
+        where
+          "users"."id" = "albums"."ownerId"
+      ) as obj
+  ) as "owner",
+  (
+    select
+      coalesce(json_agg(agg), '[]')
+    from
+      (
+        select
+          *
+        from
+          "shared_links"
+        where
+          "shared_links"."albumId" = "albums"."id"
+      ) as agg
+  ) as "sharedLinks"
+from
+  "albums"
+  left join "albums_shared_users_users" as "shared_albums" on "shared_albums"."albumsId" = "albums"."id"
+  left join "shared_links" on "shared_links"."albumId" = "albums"."id"
+where
+  "albums"."ownerId" = $1
+  and "shared_albums"."usersId" is null
+  and "shared_links"."userId" is null
+  and "albums"."deletedAt" is null
+order by
+  "albums"."createdAt" desc
 
 -- AlbumRepository.getAssetIds
-SELECT
-  "albums_assets"."assetsId" AS "assetId"
-FROM
-  "albums_assets_assets" "albums_assets"
-WHERE
-  "albums_assets"."albumsId" = $1
-  AND "albums_assets"."assetsId" IN ($2)
+select
+  *
+from
+  "albums_assets_assets"
+where
+  "albums_assets_assets"."albumsId" = $1
+  and "albums_assets_assets"."assetsId" in ($2)
diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts
index 8ac352e945..bae91349f5 100644
--- a/server/src/repositories/album.repository.ts
+++ b/server/src/repositories/album.repository.ts
@@ -1,72 +1,116 @@
 import { Injectable } from '@nestjs/common';
-import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
+import { InjectRepository } from '@nestjs/typeorm';
+import { ExpressionBuilder, Insertable, Kysely, sql, Updateable } from 'kysely';
+import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
+import { InjectKysely } from 'nestjs-kysely';
+import { Albums, DB } from 'src/db';
 import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
+import { AlbumUserCreateDto } from 'src/dtos/album.dto';
 import { AlbumEntity } from 'src/entities/album.entity';
-import { AssetEntity } from 'src/entities/asset.entity';
 import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
-import {
-  DataSource,
-  EntityManager,
-  FindOptionsOrder,
-  FindOptionsRelations,
-  In,
-  IsNull,
-  Not,
-  Repository,
-} from 'typeorm';
+import { Repository } from 'typeorm';
 
-const withoutDeletedUsers = <T extends AlbumEntity | null>(album: T) => {
-  if (album) {
-    album.albumUsers = album.albumUsers.filter((albumUser) => albumUser.user && !albumUser.user.deletedAt);
-  }
-  return album;
+const userColumns = [
+  'id',
+  'email',
+  'createdAt',
+  'profileImagePath',
+  'isAdmin',
+  'shouldChangePassword',
+  'deletedAt',
+  'oauthId',
+  'updatedAt',
+  'storageLabel',
+  'name',
+  'quotaSizeInBytes',
+  'quotaUsageInBytes',
+  'status',
+  'profileChangedAt',
+] as const;
+
+const withOwner = (eb: ExpressionBuilder<DB, 'albums'>) => {
+  return jsonObjectFrom(eb.selectFrom('users').select(userColumns).whereRef('users.id', '=', 'albums.ownerId')).as(
+    'owner',
+  );
+};
+
+const withAlbumUsers = (eb: ExpressionBuilder<DB, 'albums'>) => {
+  return jsonArrayFrom(
+    eb
+      .selectFrom('albums_shared_users_users as album_users')
+      .selectAll('album_users')
+      .select((eb) =>
+        jsonObjectFrom(eb.selectFrom('users').select(userColumns).whereRef('users.id', '=', 'album_users.usersId')).as(
+          'user',
+        ),
+      )
+      .whereRef('album_users.albumsId', '=', 'albums.id'),
+  ).as('albumUsers');
+};
+
+const withSharedLink = (eb: ExpressionBuilder<DB, 'albums'>) => {
+  return jsonArrayFrom(eb.selectFrom('shared_links').selectAll().whereRef('shared_links.albumId', '=', 'albums.id')).as(
+    'sharedLinks',
+  );
+};
+
+const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
+  return eb
+    .selectFrom((eb) =>
+      eb
+        .selectFrom('assets')
+        .selectAll('assets')
+        .innerJoin('exif', 'assets.id', 'exif.assetId')
+        .select((eb) => eb.fn.toJson('exif').as('exifInfo'))
+        .innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id')
+        .whereRef('albums_assets_assets.albumsId', '=', 'albums.id')
+        .orderBy('assets.fileCreatedAt', 'desc')
+        .as('asset'),
+    )
+    .select((eb) => eb.fn.jsonAgg('asset').as('assets'))
+    .as('assets');
 };
 
 @Injectable()
 export class AlbumRepository implements IAlbumRepository {
   constructor(
-    @InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
     @InjectRepository(AlbumEntity) private repository: Repository<AlbumEntity>,
-    @InjectDataSource() private dataSource: DataSource,
+    @InjectKysely() private db: Kysely<DB>,
   ) {}
 
   @GenerateSql({ params: [DummyValue.UUID, {}] })
-  async getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | null> {
-    const relations: FindOptionsRelations<AlbumEntity> = {
-      owner: true,
-      albumUsers: { user: true },
-      assets: false,
-      sharedLinks: true,
-    };
-
-    const order: FindOptionsOrder<AlbumEntity> = {};
-
-    if (options.withAssets) {
-      relations.assets = {
-        exifInfo: true,
-      };
-
-      order.assets = {
-        fileCreatedAt: 'DESC',
-      };
-    }
-
-    const album = await this.repository.findOne({ where: { id }, relations, order });
-    return withoutDeletedUsers(album);
+  async getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | undefined> {
+    return this.db
+      .selectFrom('albums')
+      .selectAll('albums')
+      .where('albums.id', '=', id)
+      .where('albums.deletedAt', 'is', null)
+      .select(withOwner)
+      .select(withAlbumUsers)
+      .select(withSharedLink)
+      .$if(options.withAssets, (eb) => eb.select(withAssets))
+      .executeTakeFirst() as Promise<AlbumEntity | undefined>;
   }
 
   @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
   async getByAssetId(ownerId: string, assetId: string): Promise<AlbumEntity[]> {
-    const albums = await this.repository.find({
-      where: [
-        { ownerId, assets: { id: assetId } },
-        { albumUsers: { userId: ownerId }, assets: { id: assetId } },
-      ],
-      relations: { owner: true, albumUsers: { user: true } },
-      order: { createdAt: 'DESC' },
-    });
-
-    return albums.map((album) => withoutDeletedUsers(album));
+    return this.db
+      .selectFrom('albums')
+      .selectAll('albums')
+      .leftJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
+      .leftJoin('albums_shared_users_users as album_users', 'album_users.albumsId', 'albums.id')
+      .where((eb) =>
+        eb.or([
+          eb.and([eb('albums.ownerId', '=', ownerId), eb('album_assets.assetsId', '=', assetId)]),
+          eb.and([eb('album_users.usersId', '=', ownerId), eb('album_assets.assetsId', '=', assetId)]),
+        ]),
+      )
+      .where('albums.deletedAt', 'is', null)
+      .orderBy('albums.createdAt', 'desc')
+      .select(withOwner)
+      .select(withAlbumUsers)
+      .orderBy('albums.createdAt', 'desc')
+      .execute() as unknown as Promise<AlbumEntity[]>;
   }
 
   @GenerateSql({ params: [[DummyValue.UUID]] })
@@ -77,36 +121,38 @@ export class AlbumRepository implements IAlbumRepository {
       return [];
     }
 
-    // Only possible with query builder because of GROUP BY.
-    const albumMetadatas = await this.repository
-      .createQueryBuilder('album')
-      .select('album.id')
-      .addSelect('MIN(assets.fileCreatedAt)', 'start_date')
-      .addSelect('MAX(assets.fileCreatedAt)', 'end_date')
-      .addSelect('COUNT(assets.id)', 'asset_count')
-      .leftJoin('albums_assets_assets', 'album_assets', 'album_assets.albumsId = album.id')
-      .leftJoin('assets', 'assets', 'assets.id = album_assets.assetsId')
-      .where('album.id IN (:...ids)', { ids })
-      .groupBy('album.id')
-      .getRawMany();
+    const metadatas = await this.db
+      .selectFrom('albums')
+      .leftJoin('albums_assets_assets as album_assets', 'album_assets.albumsId', 'albums.id')
+      .leftJoin('assets', 'assets.id', 'album_assets.assetsId')
+      .select('albums.id')
+      .select((eb) => eb.fn.min('assets.fileCreatedAt').as('startDate'))
+      .select((eb) => eb.fn.max('assets.fileCreatedAt').as('endDate'))
+      .select((eb) => eb.fn.count('assets.id').as('assetCount'))
+      .where('albums.id', 'in', ids)
+      .groupBy('albums.id')
+      .execute();
 
-    return albumMetadatas.map<AlbumAssetCount>((metadatas) => ({
-      albumId: metadatas['album_id'],
-      assetCount: Number(metadatas['asset_count']),
-      startDate: metadatas['end_date'] ? new Date(metadatas['start_date']) : undefined,
-      endDate: metadatas['end_date'] ? new Date(metadatas['end_date']) : undefined,
+    return metadatas.map((metadatas) => ({
+      albumId: metadatas.id,
+      assetCount: Number(metadatas.assetCount),
+      startDate: metadatas.startDate ? new Date(metadatas.startDate) : undefined,
+      endDate: metadatas.endDate ? new Date(metadatas.endDate) : undefined,
     }));
   }
 
   @GenerateSql({ params: [DummyValue.UUID] })
   async getOwned(ownerId: string): Promise<AlbumEntity[]> {
-    const albums = await this.repository.find({
-      relations: { albumUsers: { user: true }, sharedLinks: true, owner: true },
-      where: { ownerId },
-      order: { createdAt: 'DESC' },
-    });
-
-    return albums.map((album) => withoutDeletedUsers(album));
+    return this.db
+      .selectFrom('albums')
+      .selectAll('albums')
+      .select(withOwner)
+      .select(withAlbumUsers)
+      .select(withSharedLink)
+      .where('albums.ownerId', '=', ownerId)
+      .where('albums.deletedAt', 'is', null)
+      .orderBy('albums.createdAt', 'desc')
+      .execute() as unknown as Promise<AlbumEntity[]>;
   }
 
   /**
@@ -114,17 +160,25 @@ export class AlbumRepository implements IAlbumRepository {
    */
   @GenerateSql({ params: [DummyValue.UUID] })
   async getShared(ownerId: string): Promise<AlbumEntity[]> {
-    const albums = await this.repository.find({
-      relations: { albumUsers: { user: true }, sharedLinks: true, owner: true },
-      where: [
-        { albumUsers: { userId: ownerId } },
-        { sharedLinks: { userId: ownerId } },
-        { ownerId, albumUsers: { user: Not(IsNull()) } },
-      ],
-      order: { createdAt: 'DESC' },
-    });
-
-    return albums.map((album) => withoutDeletedUsers(album));
+    return this.db
+      .selectFrom('albums')
+      .selectAll('albums')
+      .distinctOn('albums.createdAt')
+      .leftJoin('albums_shared_users_users as shared_albums', 'shared_albums.albumsId', 'albums.id')
+      .leftJoin('shared_links', 'shared_links.albumId', 'albums.id')
+      .where((eb) =>
+        eb.or([
+          eb('shared_albums.usersId', '=', ownerId),
+          eb('shared_links.userId', '=', ownerId),
+          eb.and([eb('albums.ownerId', '=', ownerId), eb('shared_albums.usersId', 'is not', null)]),
+        ]),
+      )
+      .where('albums.deletedAt', 'is', null)
+      .select(withAlbumUsers)
+      .select(withOwner)
+      .select(withSharedLink)
+      .orderBy('albums.createdAt', 'desc')
+      .execute() as unknown as Promise<AlbumEntity[]>;
   }
 
   /**
@@ -132,35 +186,37 @@ export class AlbumRepository implements IAlbumRepository {
    */
   @GenerateSql({ params: [DummyValue.UUID] })
   async getNotShared(ownerId: string): Promise<AlbumEntity[]> {
-    const albums = await this.repository.find({
-      relations: { albumUsers: true, sharedLinks: true, owner: true },
-      where: { ownerId, albumUsers: { user: IsNull() }, sharedLinks: { id: IsNull() } },
-      order: { createdAt: 'DESC' },
-    });
-
-    return albums.map((album) => withoutDeletedUsers(album));
+    return this.db
+      .selectFrom('albums')
+      .selectAll('albums')
+      .distinctOn('albums.createdAt')
+      .leftJoin('albums_shared_users_users as shared_albums', 'shared_albums.albumsId', 'albums.id')
+      .leftJoin('shared_links', 'shared_links.albumId', 'albums.id')
+      .where('albums.ownerId', '=', ownerId)
+      .where('shared_albums.usersId', 'is', null)
+      .where('shared_links.userId', 'is', null)
+      .where('albums.deletedAt', 'is', null)
+      .select(withAlbumUsers)
+      .select(withOwner)
+      .select(withSharedLink)
+      .orderBy('albums.createdAt', 'desc')
+      .execute() as unknown as Promise<AlbumEntity[]>;
   }
 
   async restoreAll(userId: string): Promise<void> {
-    await this.repository.restore({ ownerId: userId });
+    await this.db.updateTable('albums').set({ deletedAt: null }).where('ownerId', '=', userId).execute();
   }
 
   async softDeleteAll(userId: string): Promise<void> {
-    await this.repository.softDelete({ ownerId: userId });
+    await this.db.updateTable('albums').set({ deletedAt: new Date() }).where('ownerId', '=', userId).execute();
   }
 
   async deleteAll(userId: string): Promise<void> {
-    await this.repository.delete({ ownerId: userId });
+    await this.db.deleteFrom('albums').where('ownerId', '=', userId).execute();
   }
 
   async removeAsset(assetId: string): Promise<void> {
-    // Using dataSource, because there is no direct access to albums_assets_assets.
-    await this.dataSource
-      .createQueryBuilder()
-      .delete()
-      .from('albums_assets_assets')
-      .where('"albums_assets_assets"."assetsId" = :assetId', { assetId })
-      .execute();
+    await this.db.deleteFrom('albums_assets_assets').where('albums_assets_assets.assetsId', '=', assetId).execute();
   }
 
   @Chunked({ paramIndex: 1 })
@@ -169,14 +225,10 @@ export class AlbumRepository implements IAlbumRepository {
       return;
     }
 
-    await this.dataSource
-      .createQueryBuilder()
-      .delete()
-      .from('albums_assets_assets')
-      .where({
-        albumsId: albumId,
-        assetsId: In(assetIds),
-      })
+    await this.db
+      .deleteFrom('albums_assets_assets')
+      .where('albums_assets_assets.albumsId', '=', albumId)
+      .where('albums_assets_assets.assetsId', 'in', assetIds)
       .execute();
   }
 
@@ -194,73 +246,80 @@ export class AlbumRepository implements IAlbumRepository {
       return new Set();
     }
 
-    const results = await this.dataSource
-      .createQueryBuilder()
-      .select('albums_assets.assetsId', 'assetId')
-      .from('albums_assets_assets', 'albums_assets')
-      .where('"albums_assets"."albumsId" = :albumId', { albumId })
-      .andWhere('"albums_assets"."assetsId" IN (:...assetIds)', { assetIds })
-      .getRawMany<{ assetId: string }>();
-
-    return new Set(results.map(({ assetId }) => assetId));
+    return this.db
+      .selectFrom('albums_assets_assets')
+      .selectAll()
+      .where('albums_assets_assets.albumsId', '=', albumId)
+      .where('albums_assets_assets.assetsId', 'in', assetIds)
+      .execute()
+      .then((results) => new Set(results.map(({ assetsId }) => assetsId)));
   }
 
   async addAssetIds(albumId: string, assetIds: string[]): Promise<void> {
-    await this.addAssets(this.dataSource.manager, albumId, assetIds);
+    await this.addAssets(this.db, albumId, assetIds);
   }
 
-  create(album: Partial<AlbumEntity>): Promise<AlbumEntity> {
-    return this.dataSource.transaction<AlbumEntity>(async (manager) => {
-      const { id } = await manager.save(AlbumEntity, { ...album, assets: [] });
-      const assetIds = (album.assets || []).map((asset) => asset.id);
-      await this.addAssets(manager, id, assetIds);
-      return manager.findOneOrFail(AlbumEntity, {
-        where: { id },
-        relations: {
-          owner: true,
-          albumUsers: { user: true },
-          sharedLinks: true,
-          assets: true,
-        },
-      });
+  create(album: Insertable<Albums>, assetIds: string[], albumUsers: AlbumUserCreateDto[]): Promise<AlbumEntity> {
+    return this.db.transaction().execute(async (tx) => {
+      const newAlbum = await tx.insertInto('albums').values(album).returning('albums.id').executeTakeFirst();
+
+      if (!newAlbum) {
+        throw new Error('Failed to create album');
+      }
+
+      if (assetIds.length > 0) {
+        await this.addAssets(tx, newAlbum.id, assetIds);
+      }
+
+      if (albumUsers.length > 0) {
+        await tx
+          .insertInto('albums_shared_users_users')
+          .values(
+            albumUsers.map((albumUser) => ({ albumsId: newAlbum.id, usersId: albumUser.userId, role: albumUser.role })),
+          )
+          .execute();
+      }
+
+      return tx
+        .selectFrom('albums')
+        .selectAll()
+        .where('id', '=', newAlbum.id)
+        .select(withOwner)
+        .select(withSharedLink)
+        .select(withAssets)
+        .select(withAlbumUsers)
+        .executeTakeFirst() as unknown as Promise<AlbumEntity>;
     });
   }
 
-  update(album: Partial<AlbumEntity>): Promise<AlbumEntity> {
-    return this.save(album);
+  update(id: string, album: Updateable<Albums>): Promise<AlbumEntity> {
+    return this.db
+      .updateTable('albums')
+      .set({ ...album, updatedAt: new Date() })
+      .where('id', '=', id)
+      .returningAll('albums')
+      .returning(withOwner)
+      .returning(withSharedLink)
+      .returning(withAlbumUsers)
+      .executeTakeFirst() as unknown as Promise<AlbumEntity>;
   }
 
   async delete(id: string): Promise<void> {
-    await this.repository.delete({ id });
+    await this.db.deleteFrom('albums').where('id', '=', id).execute();
   }
 
   @Chunked({ paramIndex: 2, chunkSize: 30_000 })
-  private async addAssets(manager: EntityManager, albumId: string, assetIds: string[]): Promise<void> {
+  private async addAssets(db: Kysely<DB>, albumId: string, assetIds: string[]): Promise<void> {
     if (assetIds.length === 0) {
       return;
     }
 
-    await manager
-      .createQueryBuilder()
-      .insert()
-      .into('albums_assets_assets', ['albumsId', 'assetsId'])
+    await db
+      .insertInto('albums_assets_assets')
       .values(assetIds.map((assetId) => ({ albumsId: albumId, assetsId: assetId })))
       .execute();
   }
 
-  private async save(album: Partial<AlbumEntity>) {
-    const { id } = await this.repository.save(album);
-    return this.repository.findOneOrFail({
-      where: { id },
-      relations: {
-        owner: true,
-        albumUsers: { user: true },
-        sharedLinks: true,
-        assets: true,
-      },
-    });
-  }
-
   /**
    * Makes sure all thumbnails for albums are updated by:
    * - Removing thumbnails from albums without assets
@@ -272,28 +331,44 @@ export class AlbumRepository implements IAlbumRepository {
   async updateThumbnails(): Promise<number | undefined> {
     // Subquery for getting a new thumbnail.
 
-    const builder = this.dataSource
-      .createQueryBuilder('albums_assets_assets', 'album_assets')
-      .innerJoin('assets', 'assets', '"album_assets"."assetsId" = "assets"."id"')
-      .where('"album_assets"."albumsId" = "albums"."id"');
+    const result = await this.db
+      .updateTable('albums')
+      .set((eb) => ({
+        albumThumbnailAssetId: this.updateThumbnailBuilder(eb)
+          .select('album_assets.assetsId')
+          .orderBy('assets.fileCreatedAt', 'desc')
+          .limit(1),
+        updatedAt: new Date(),
+      }))
+      .where((eb) =>
+        eb.or([
+          eb.and([
+            eb('albumThumbnailAssetId', 'is', null),
+            eb.exists(this.updateThumbnailBuilder(eb).select(sql`1`.as('1'))), // Has assets
+          ]),
+          eb.and([
+            eb('albumThumbnailAssetId', 'is not', null),
+            eb.not(
+              eb.exists(
+                this.updateThumbnailBuilder(eb)
+                  .select(sql`1`.as('1'))
+                  .whereRef('albums.albumThumbnailAssetId', '=', 'album_assets.assetsId'), // Has invalid assets
+              ),
+            ),
+          ]),
+        ]),
+      )
+      .execute();
 
-    const newThumbnail = builder
-      .clone()
-      .select('"album_assets"."assetsId"')
-      .orderBy('"assets"."fileCreatedAt"', 'DESC')
-      .limit(1);
-    const hasAssets = builder.clone().select('1');
-    const hasInvalidAsset = hasAssets.clone().andWhere('"albums"."albumThumbnailAssetId" = "album_assets"."assetsId"');
+    return Number(result[0].numUpdatedRows);
+  }
 
-    const updateAlbums = this.repository
-      .createQueryBuilder('albums')
-      .update(AlbumEntity)
-      .set({ albumThumbnailAssetId: () => `(${newThumbnail.getQuery()})` })
-      .where(`"albums"."albumThumbnailAssetId" IS NULL AND EXISTS (${hasAssets.getQuery()})`)
-      .orWhere(`"albums"."albumThumbnailAssetId" IS NOT NULL AND NOT EXISTS (${hasInvalidAsset.getQuery()})`);
-
-    const result = await updateAlbums.execute();
-
-    return result.affected;
+  private updateThumbnailBuilder(eb: ExpressionBuilder<DB, 'albums'>) {
+    return eb
+      .selectFrom('albums_assets_assets as album_assets')
+      .innerJoin('assets', (join) =>
+        join.onRef('album_assets.assetsId', '=', 'assets.id').on('assets.deletedAt', 'is', null),
+      )
+      .whereRef('album_assets.albumsId', '=', 'albums.id');
   }
 }
diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts
index ca6b56e085..99c794adc9 100644
--- a/server/src/services/album.service.spec.ts
+++ b/server/src/services/album.service.spec.ts
@@ -135,14 +135,17 @@ describe(AlbumService.name, () => {
         assetIds: ['123'],
       });
 
-      expect(albumMock.create).toHaveBeenCalledWith({
-        ownerId: authStub.admin.user.id,
-        albumName: albumStub.empty.albumName,
-        description: albumStub.empty.description,
-        albumUsers: [{ userId: 'user-id', role: AlbumUserRole.EDITOR }],
-        assets: [{ id: '123' }],
-        albumThumbnailAssetId: '123',
-      });
+      expect(albumMock.create).toHaveBeenCalledWith(
+        {
+          ownerId: authStub.admin.user.id,
+          albumName: albumStub.empty.albumName,
+          description: albumStub.empty.description,
+
+          albumThumbnailAssetId: '123',
+        },
+        ['123'],
+        [{ userId: 'user-id', role: AlbumUserRole.EDITOR }],
+      );
 
       expect(userMock.get).toHaveBeenCalledWith('user-id', {});
       expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123']));
@@ -175,14 +178,17 @@ describe(AlbumService.name, () => {
         assetIds: ['asset-1', 'asset-2'],
       });
 
-      expect(albumMock.create).toHaveBeenCalledWith({
-        ownerId: authStub.admin.user.id,
-        albumName: 'Test album',
-        description: '',
-        albumUsers: [],
-        assets: [{ id: 'asset-1' }],
-        albumThumbnailAssetId: 'asset-1',
-      });
+      expect(albumMock.create).toHaveBeenCalledWith(
+        {
+          ownerId: authStub.admin.user.id,
+          albumName: 'Test album',
+          description: '',
+
+          albumThumbnailAssetId: 'asset-1',
+        },
+        ['asset-1'],
+        [],
+      );
       expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(
         authStub.admin.user.id,
         new Set(['asset-1', 'asset-2']),
@@ -192,7 +198,7 @@ describe(AlbumService.name, () => {
 
   describe('update', () => {
     it('should prevent updating an album that does not exist', async () => {
-      albumMock.getById.mockResolvedValue(null);
+      albumMock.getById.mockResolvedValue(void 0);
 
       await expect(
         sut.update(authStub.user1, 'invalid-id', {
@@ -238,7 +244,7 @@ describe(AlbumService.name, () => {
       });
 
       expect(albumMock.update).toHaveBeenCalledTimes(1);
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-4', {
         id: 'album-4',
         albumName: 'new album name',
       });
@@ -344,7 +350,7 @@ describe(AlbumService.name, () => {
   describe('removeUser', () => {
     it('should require a valid album id', async () => {
       accessMock.album.checkOwnerAccess.mockResolvedValue(new Set(['album-1']));
-      albumMock.getById.mockResolvedValue(null);
+      albumMock.getById.mockResolvedValue(void 0);
       await expect(sut.removeUser(authStub.admin, 'album-1', 'user-1')).rejects.toBeInstanceOf(BadRequestException);
       expect(albumMock.update).not.toHaveBeenCalled();
     });
@@ -529,7 +535,7 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-3' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-123', {
         id: 'album-123',
         updatedAt: expect.any(Date),
         albumThumbnailAssetId: 'asset-1',
@@ -547,7 +553,7 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-1' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-123', {
         id: 'album-123',
         updatedAt: expect.any(Date),
         albumThumbnailAssetId: 'asset-id',
@@ -569,7 +575,7 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-3' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-123', {
         id: 'album-123',
         updatedAt: expect.any(Date),
         albumThumbnailAssetId: 'asset-1',
@@ -606,7 +612,7 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-3' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-123', {
         id: 'album-123',
         updatedAt: expect.any(Date),
         albumThumbnailAssetId: 'asset-1',
@@ -629,7 +635,7 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-1' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
+      expect(albumMock.update).toHaveBeenCalledWith('album-123', {
         id: 'album-123',
         updatedAt: expect.any(Date),
         albumThumbnailAssetId: 'asset-1',
@@ -696,7 +702,6 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-id' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({ id: 'album-123', updatedAt: expect.any(Date) });
       expect(albumMock.removeAssetIds).toHaveBeenCalledWith('album-123', ['asset-id']);
     });
 
@@ -720,8 +725,6 @@ describe(AlbumService.name, () => {
       await expect(sut.removeAssets(authStub.admin, 'album-123', { ids: ['asset-id'] })).resolves.toEqual([
         { success: true, id: 'asset-id' },
       ]);
-
-      expect(albumMock.update).toHaveBeenCalledWith({ id: 'album-123', updatedAt: expect.any(Date) });
     });
 
     it('should reset the thumbnail if it is removed', async () => {
@@ -734,10 +737,6 @@ describe(AlbumService.name, () => {
         { success: true, id: 'asset-id' },
       ]);
 
-      expect(albumMock.update).toHaveBeenCalledWith({
-        id: 'album-123',
-        updatedAt: expect.any(Date),
-      });
       expect(albumMock.updateThumbnails).toHaveBeenCalled();
     });
   });
diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts
index f5685f84eb..efc71c4c8d 100644
--- a/server/src/services/album.service.ts
+++ b/server/src/services/album.service.ts
@@ -15,7 +15,6 @@ import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
 import { AuthDto } from 'src/dtos/auth.dto';
 import { AlbumUserEntity } from 'src/entities/album-user.entity';
 import { AlbumEntity } from 'src/entities/album.entity';
-import { AssetEntity } from 'src/entities/asset.entity';
 import { Permission } from 'src/enum';
 import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
 import { BaseService } from 'src/services/base.service';
@@ -112,16 +111,18 @@ export class AlbumService extends BaseService {
       permission: Permission.ASSET_SHARE,
       ids: dto.assetIds || [],
     });
-    const assets = [...allowedAssetIdsSet].map((id) => ({ id }) as AssetEntity);
+    const assetIds = [...allowedAssetIdsSet].map((id) => id);
 
-    const album = await this.albumRepository.create({
-      ownerId: auth.user.id,
-      albumName: dto.albumName,
-      description: dto.description,
-      albumUsers: albumUsers.map((albumUser) => albumUser as AlbumUserEntity) ?? [],
-      assets,
-      albumThumbnailAssetId: assets[0]?.id || null,
-    });
+    const album = await this.albumRepository.create(
+      {
+        ownerId: auth.user.id,
+        albumName: dto.albumName,
+        description: dto.description,
+        albumThumbnailAssetId: assetIds[0] || null,
+      },
+      assetIds,
+      albumUsers,
+    );
 
     for (const { userId } of albumUsers) {
       await this.eventRepository.emit('album.invite', { id: album.id, userId });
@@ -141,7 +142,7 @@ export class AlbumService extends BaseService {
         throw new BadRequestException('Invalid album thumbnail');
       }
     }
-    const updatedAlbum = await this.albumRepository.update({
+    const updatedAlbum = await this.albumRepository.update(album.id, {
       id: album.id,
       albumName: dto.albumName,
       description: dto.description,
@@ -170,7 +171,7 @@ export class AlbumService extends BaseService {
 
     const { id: firstNewAssetId } = results.find(({ success }) => success) || {};
     if (firstNewAssetId) {
-      await this.albumRepository.update({
+      await this.albumRepository.update(id, {
         id,
         updatedAt: new Date(),
         albumThumbnailAssetId: album.albumThumbnailAssetId ?? firstNewAssetId,
@@ -199,11 +200,8 @@ export class AlbumService extends BaseService {
     );
 
     const removedIds = results.filter(({ success }) => success).map(({ id }) => id);
-    if (removedIds.length > 0) {
-      await this.albumRepository.update({ id, updatedAt: new Date() });
-      if (album.albumThumbnailAssetId && removedIds.includes(album.albumThumbnailAssetId)) {
-        await this.albumRepository.updateThumbnails();
-      }
+    if (removedIds.length > 0 && album.albumThumbnailAssetId && removedIds.includes(album.albumThumbnailAssetId)) {
+      await this.albumRepository.updateThumbnails();
     }
 
     return results;