From f0e956783ad57d4dec02e611ed497bcb40ba8ac3 Mon Sep 17 00:00:00 2001 From: Nico <47644445+nicotsx@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:10:00 +0100 Subject: [PATCH] refactor: improve perf by calling api in parallel in clientLoaders (#312) * refactor: improve perf by calling api in parallel in clientLoaders * fix: dependent api calls --- .../components/schedule-mirrors-config.tsx | 5 ++- .../schedule-notifications-config.tsx | 5 ++- .../modules/backups/routes/backup-details.tsx | 33 +++++++++++++++---- .../modules/backups/routes/create-backup.tsx | 3 +- .../backups/routes/restore-snapshot.tsx | 17 ++++++---- .../repositories/routes/restore-snapshot.tsx | 10 +++--- .../repositories/routes/snapshot-details.tsx | 11 ++++--- 7 files changed, 59 insertions(+), 25 deletions(-) diff --git a/app/client/modules/backups/components/schedule-mirrors-config.tsx b/app/client/modules/backups/components/schedule-mirrors-config.tsx index 725d3969..52879c77 100644 --- a/app/client/modules/backups/components/schedule-mirrors-config.tsx +++ b/app/client/modules/backups/components/schedule-mirrors-config.tsx @@ -21,11 +21,13 @@ import { StatusDot } from "~/client/components/status-dot"; import { formatDistanceToNow } from "date-fns"; import { Link } from "react-router"; import { cn } from "~/client/lib/utils"; +import type { GetScheduleMirrorsResponse } from "~/client/api-client"; type Props = { scheduleId: number; primaryRepositoryId: string; repositories: Repository[]; + initialData: GetScheduleMirrorsResponse; }; type MirrorAssignment = { @@ -36,13 +38,14 @@ type MirrorAssignment = { lastCopyError: string | null; }; -export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, repositories }: Props) => { +export const ScheduleMirrorsConfig = ({ scheduleId, primaryRepositoryId, repositories, initialData }: Props) => { const [assignments, setAssignments] = useState>(new Map()); const [hasChanges, setHasChanges] = useState(false); const [isAddingNew, setIsAddingNew] = useState(false); const { data: currentMirrors } = useQuery({ ...getScheduleMirrorsOptions({ path: { scheduleId: scheduleId.toString() } }), + initialData, }); const { data: compatibility } = useQuery({ diff --git a/app/client/modules/backups/components/schedule-notifications-config.tsx b/app/client/modules/backups/components/schedule-notifications-config.tsx index 0a1d4f37..11a843f1 100644 --- a/app/client/modules/backups/components/schedule-notifications-config.tsx +++ b/app/client/modules/backups/components/schedule-notifications-config.tsx @@ -14,10 +14,12 @@ import { } from "~/client/api-client/@tanstack/react-query.gen"; import { parseError } from "~/client/lib/errors"; import type { NotificationDestination } from "~/client/lib/types"; +import type { GetScheduleNotificationsResponse } from "~/client/api-client"; type Props = { scheduleId: number; destinations: NotificationDestination[]; + initialData: GetScheduleNotificationsResponse; }; type NotificationAssignment = { @@ -28,13 +30,14 @@ type NotificationAssignment = { notifyOnFailure: boolean; }; -export const ScheduleNotificationsConfig = ({ scheduleId, destinations }: Props) => { +export const ScheduleNotificationsConfig = ({ scheduleId, destinations, initialData }: Props) => { const [assignments, setAssignments] = useState>(new Map()); const [hasChanges, setHasChanges] = useState(false); const [isAddingNew, setIsAddingNew] = useState(false); const { data: currentAssignments } = useQuery({ ...getScheduleNotificationsOptions({ path: { scheduleId: scheduleId.toString() } }), + initialData, }); const updateNotifications = useMutation({ diff --git a/app/client/modules/backups/routes/backup-details.tsx b/app/client/modules/backups/routes/backup-details.tsx index 1e218979..fa928ee0 100644 --- a/app/client/modules/backups/routes/backup-details.tsx +++ b/app/client/modules/backups/routes/backup-details.tsx @@ -30,7 +30,13 @@ import { ScheduleSummary } from "../components/schedule-summary"; import type { Route } from "./+types/backup-details"; import { SnapshotFileBrowser } from "../components/snapshot-file-browser"; import { SnapshotTimeline } from "../components/snapshot-timeline"; -import { getBackupSchedule, listNotificationDestinations, listRepositories } from "~/client/api-client"; +import { + getBackupSchedule, + getScheduleMirrors, + getScheduleNotifications, + listNotificationDestinations, + listRepositories, +} from "~/client/api-client"; import { ScheduleNotificationsConfig } from "../components/schedule-notifications-config"; import { ScheduleMirrorsConfig } from "../components/schedule-mirrors-config"; import { cn } from "~/client/lib/utils"; @@ -53,13 +59,23 @@ export function meta(_: Route.MetaArgs) { } export const clientLoader = async ({ params }: Route.LoaderArgs) => { - const schedule = await getBackupSchedule({ path: { scheduleId: params.id } }); - const notifs = await listNotificationDestinations(); - const repos = await listRepositories(); + const [schedule, notifs, repos, scheduleNotifs, mirrors] = await Promise.all([ + getBackupSchedule({ path: { scheduleId: params.id } }), + listNotificationDestinations(), + listRepositories(), + getScheduleNotifications({ path: { scheduleId: params.id } }), + getScheduleMirrors({ path: { scheduleId: params.id } }), + ]); if (!schedule.data) return redirect("/backups"); - return { schedule: schedule.data, notifs: notifs.data, repos: repos.data }; + return { + schedule: schedule.data, + notifs: notifs.data, + repos: repos.data, + scheduleNotifs: scheduleNotifs.data, + scheduleMirrors: mirrors.data, + }; }; export default function ScheduleDetailsPage({ params, loaderData }: Route.ComponentProps) { @@ -240,13 +256,18 @@ export default function ScheduleDetailsPage({ params, loaderData }: Route.Compon schedule={schedule} />
- +
{ - const volumes = await listVolumes(); - const repositories = await listRepositories(); + const [volumes, repositories] = await Promise.all([listVolumes(), listRepositories()]); if (volumes.data && repositories.data) return { volumes: volumes.data, repositories: repositories.data }; return { volumes: [], repositories: [] }; diff --git a/app/client/modules/backups/routes/restore-snapshot.tsx b/app/client/modules/backups/routes/restore-snapshot.tsx index 5b0c2c96..8304c0c0 100644 --- a/app/client/modules/backups/routes/restore-snapshot.tsx +++ b/app/client/modules/backups/routes/restore-snapshot.tsx @@ -24,15 +24,20 @@ export function meta({ params }: Route.MetaArgs) { export const clientLoader = async ({ params }: Route.ClientLoaderArgs) => { const schedule = await getBackupSchedule({ path: { scheduleId: params.id } }); + if (!schedule.data) return redirect("/backups"); - const repositoryId = schedule.data.repository.id; - const snapshot = await getSnapshotDetails({ - path: { id: repositoryId, snapshotId: params.snapshotId }, - }); - if (!snapshot.data) return redirect(`/backups/${params.id}`); + const [snapshot, repository] = await Promise.all([ + getSnapshotDetails({ + path: { + id: schedule.data.repositoryId, + snapshotId: params.snapshotId, + }, + }), + getRepository({ path: { id: schedule.data.repositoryId } }), + ]); - const repository = await getRepository({ path: { id: repositoryId } }); + if (!snapshot.data) return redirect(`/backups/${params.id}`); if (!repository.data) return redirect(`/backups/${params.id}`); return { diff --git a/app/client/modules/repositories/routes/restore-snapshot.tsx b/app/client/modules/repositories/routes/restore-snapshot.tsx index 171deec8..2a8410c6 100644 --- a/app/client/modules/repositories/routes/restore-snapshot.tsx +++ b/app/client/modules/repositories/routes/restore-snapshot.tsx @@ -23,12 +23,12 @@ export function meta({ params }: Route.MetaArgs) { } export const clientLoader = async ({ params }: Route.ClientLoaderArgs) => { - const snapshot = await getSnapshotDetails({ - path: { id: params.id, snapshotId: params.snapshotId }, - }); - if (!snapshot.data) return redirect("/repositories"); + const [snapshot, repository] = await Promise.all([ + getSnapshotDetails({ path: { id: params.id, snapshotId: params.snapshotId } }), + getRepository({ path: { id: params.id } }), + ]); - const repository = await getRepository({ path: { id: params.id } }); + if (!snapshot.data) return redirect("/repositories"); if (!repository.data) return redirect(`/repositories`); return { snapshot: snapshot.data, id: params.id, repository: repository.data, snapshotId: params.snapshotId }; diff --git a/app/client/modules/repositories/routes/snapshot-details.tsx b/app/client/modules/repositories/routes/snapshot-details.tsx index fcf14578..ea10fb6a 100644 --- a/app/client/modules/repositories/routes/snapshot-details.tsx +++ b/app/client/modules/repositories/routes/snapshot-details.tsx @@ -26,11 +26,14 @@ export function meta({ params }: Route.MetaArgs) { } export const clientLoader = async ({ params }: Route.ClientLoaderArgs) => { - const snapshot = getSnapshotDetails({ - path: { id: params.id, snapshotId: params.snapshotId }, - }); + const [snapshot, repository] = await Promise.all([ + getSnapshotDetails({ + path: { id: params.id, snapshotId: params.snapshotId }, + }), + getRepository({ path: { id: params.id } }), + ]); - const repository = await getRepository({ path: { id: params.id } }); + if (!snapshot.data) return redirect(`/repositories/${params.id}`); if (!repository.data) return redirect("/repositories"); return { snapshot: snapshot, repository: repository.data };