M
Melvynx
#TanStack Start#Next.js#Migration

Migration Next.js vers TanStack Start : Performance et IA

Découvrez comment la migration d'une plateforme de formation de Next.js vers TanStack Start a amélioré les temps de build, la réactivité UI et la compatibilité IA grâce à une architecture déclarative.

5 min de lectureGuide IA

Introduction

La migration d'une plateforme de formation de Next.js vers TanStack Start apporte des améliorations concrètes en termes de performance, de réactivité de l'interface utilisateur et de compatibilité avec les outils d'intelligence artificielle, grâce à une architecture plus déclarative et explicite.

Précis de configuration

Élément Version / Lien
Langage / Runtime TypeScript, JavaScript, Node.js
Librairie principale TanStack Start (remplace Next.js)
APIs requises Vercel (pour le déploiement), GitHub (pour la gestion de code)
Clés / credentials nécessaires Non spécifié (implicite pour Vercel/GitHub)

Guide étape par étape

Temps de build

Pourquoi : Les temps de build longs de Next.js (environ 3 minutes 40 secondes en moyenne) ralentissaient les déploiements et la capacité à réagir rapidement aux problèmes en production. TanStack Start réduit considérablement ce délai.

Explication : La vidéo montre des logs de déploiement Vercel. Avant la migration, les déploiements prenaient entre 3 minutes 17 secondes et 3 minutes 47 secondes. Après la migration vers TanStack Start, ces temps ont chuté à 1 minute 9 secondes à 1 minute 35 secondes. Cela représente une réduction de plus de 50%, soit un gain d'environ 2 minutes 20 secondes par déploiement.

Code : Aucun code spécifique au processus de build n'est montré, mais les logs de déploiement sont visibles.

Réactivité

Réactivité
Pourquoi : L'ancienne version basée sur Next.js présentait des loaders et des placeholders visibles lors des rafraîchissements de page et de la navigation, ce qui dégradait l'expérience utilisateur. TanStack Start offre un chargement quasi instantané des pages.

Explication : Une comparaison côte à côte des deux versions (legacy.codelyne.app pour Next.js et codelyne.app pour TanStack Start) est effectuée. La version Next.js affiche des loaders et des placeholders lors du rafraîchissement et de la navigation (par exemple, en cliquant sur "NextTailwind" ou "Settings"). En revanche, la version TanStack Start charge les pages presque instantanément, sans aucun loader visible ni élément d'interface utilisateur inutile.

Code : Aucun code spécifique n'est montré, mais le comportement de l'interface utilisateur est démontré.

IA friendly

IA friendly
Pourquoi : Next.js, bien que mature et bien documenté, a subi de nombreux changements d'architecture (pages/, app/, server components, API routes, server actions/functions, middleware/proxy, use cache). Cette complexité et ces abstractions implicites le rendent difficile à comprendre et à manipuler pour les outils d'IA. TanStack Start, avec sa nature déclarative, facilite l'interprétation et la génération de code correct par l'IA.

Explication :

  • Problèmes de Next.js : Le présentateur énumère divers concepts et évolutions de Next.js, soulignant la complexité pour l'IA. Il montre un fichier page.tsx (08:44) avec des métadonnées, l'analyse des paramètres de recherche et la logique de récupération des données. Il met en évidence la nécessité de faire défiler le code pour trouver use client ou async afin de déterminer si un composant est côté serveur ou client. Ce comportement implicite est difficile à gérer pour l'IA.

  • Avantages de TanStack Start : Le présentateur montre un fichier index.tsx (09:32) utilisant createFileRoute. Tout est explicitement déclaré dans un seul objet : l'URL, le composant en attente (loader), beforeLoad (middleware), validateSearch (validation typée des paramètres de recherche), loaderDeps (dépendances pour le re-fetching), loader (récupération des données), head (métadonnées) et component. Cette structure déclarative est beaucoup plus claire pour les humains et l'IA.

  • Fonctions Serveur : Il montre également editor.functions.ts (12:43) qui illustre l'utilisation de createServerFn pour les fonctions serveur, InputValidator et middleware. Cette déclaration explicite contraste avec la directive use server de Next.js.

Code Examples :

Next.js page.tsx (simplifié de la vidéo) :

// src/app/admin/school/[schoolId]/bundles/page.tsx
import { getAdminSchool } from "@/server/db/helper/school.admin.url";
import { PageParams } from "@/types/next";
// ... other imports

export const metadata = {
  title: "Codelyne - Bundle",
};

const PAGE_SIZE_OPTIONS = ["20", "50", "100"] as const;

// Complex search param parsing and caching logic
const searchParamsCache = createSearchParamCache({
  page: parseInteger.withDefault(1),
  order: parseStringEnum.withDefault("desc"),
  status: parseStringLiteral("ACTIVES"),
  // ... more params
});

export default async function BundlePage({ props }: PageParams) {
  const { page, query, order, status, pageSize } = await props.searchParams.parse(searchParamsCache);

  const currentSchool = await getAdminSchool(schoolId, "SUPPORT", "EDITOR");

  if (!currentSchool) {
    return <NotAuthorized />;
  }

  // ... data fetching and rendering logic
  return (
    <Layout size="lg" spacing="lg">
      <LayoutHeader>
        <LayoutTitle>Bundles</LayoutTitle>
      </LayoutHeader>
      <LayoutContent>
        {/* ... other components */}
      </LayoutContent>
    </Layout>
  );
}

TanStack Start index.tsx (simplifié de la vidéo) :

// src/routes/admin/$schoolId/bundles/index.tsx
import { Link, createFileRoute, useRouter } from "@tanstack/react-router";
import { AdminTablePageSkeleton } from "@/routes/route-skeletons";
import { getStartMetadata } from "@/routes/metadata";
import { requireAdminSchool } from "@/server/auth/require-admin-school";
import { BundleSearch } from "./__bundlesSearch"; // Assuming this is generated/defined elsewhere
import { getAdminBundleData } from "@/admin/functions"; // Assuming this is a server function

export const Route = createFileRoute("/admin/$schoolId/bundles")({
  pendingComponent: AdminTablePageSkeleton, // Explicit loader
  beforeLoad: ({ params, location }) => {
    // Middleware-like logic
    requireAdminSchool(params.schoolId, location.href);
  },
  validateSearch: (search: Record<string, unknown>): BundleSearch => {
    // Type-safe search param validation
    return {
      query: typeof search.query === "string" ? search.query : "",
      order: typeof search.order === "string" ? search.order : "desc",
      status: isBundleStatusFilter(search.status) ? search.status : "ACTIVES",
      page: Math.max(1, Number.parseInt(search.page as string, 10) || 1),
      pageSize: parsePageSize(search.pageSize),
    };
  },
  loaderDeps: ({ search }) => ({
    query: search.query,
    order: search.order,
    status: search.status,
    page: search.page,
    pageSize: search.pageSize,
  }),
  loader: ({ deps, params }) => getAdminBundleData({
    schoolId: params.schoolId,
    query: deps.query,
    order: deps.order,
    status: deps.status,
    page: deps.page,
    pageSize: deps.pageSize,
  }),
  head: ({ params }) => getStartMetadata({
    title: "Codelyne - Bundles",
    url: `/admin/${params.schoolId}/bundles/`,
  }),
  component: AdminBundlesRoute,
});

function AdminBundlesRoute() {
  const { data } = Route.useLoaderData(); // Type-safe data
  const { params } = Route.useParams(); // Type-safe params
  const { search } = Route.useSearch(); // Type-safe search

  // ... rendering logic
  return (
    <Layout size="lg" spacing="lg">
      <LayoutHeader>
        <LayoutTitle>Bundle</LayoutTitle>
      </LayoutHeader>
      <LayoutContent>
        {/* ... other components */}
      </LayoutContent>
    </Layout>
  );
}

TanStack Start editor.functions.ts (simplifié de la vidéo) :

// src/routes/admin/editor.functions.ts
import { createServerFn } from "@tanstack/react-start/server";
import { z } from "zod";
import { ZodRouteError } from "@/lib/errors";
import { isEditorRole } from "@/server/db/prisma"; // Assuming this is defined
import { getProductSections } from "@/server/db/helpers/section-cache";
import { getAdminSchool } from "@/server/db/prisma"; // Assuming this is defined

export const getAdminLessonEditorData = createServerFn({
  method: "GET",
  inputValidator: z.object({
    schoolId: z.string().min(1),
    lessonId: z.string().min(1).optional(),
  }),
  middleware: async ({ input, context }) => {
    const currentSchool = await getAdminSchool(input.schoolId);
    if (!currentSchool || !isEditorRole(context.user, currentSchool.teamMembers)) {
      throw new ZodRouteError("Unauthorized", 403);
    }
    return { currentSchool };
  },
  handler: async ({ data, context }) => {
    const lesson = await prisma.lesson.findFirst({
      where: {
        id: data.lessonId,
        section: {
          schoolId: data.schoolId,
        },
      },
      select: {
        id: true,
        title: true,
        description: true,
        contentDraft: true,
        commentEnabled: true,
        schoolId: true,
        sectionId: true,
        updatedAt: true,
        createdAt: true,
        state: true,
        productReviewRequired: true,
        product: {
          select: {
            id: true,
            title: true,
            schoolId: true,
          },
        },
      },
    });
    // ... more logic
    return { lesson, sections: [], school: { id: data.schoolId, name: data.currentSchool.name } };
  },
});

Tableaux comparatifs

Caractéristique Next.js (Legacy) TanStack Start
Temps de Build (Moyenne) ~3m40s ~1m20s
Réactivité UI Loaders et placeholders visibles, lenteur perçue Chargement instantané des pages, pas de loaders inutiles
Clarté pour l'IA Problème de changements fréquents (pages/ vs app/, server components vs client components, API routes, server actions/functions, middleware/proxy, use cache), abstraction rendant le code difficile à interpréter. Déclaratif, explicite (routes, loaders, middlewares, metadata définis dans un seul bloc), respecte les standards du web, plus flexible.
Structure du code Fichiers séparés pour loader, head, page, etc. Tout est déclaré dans un objet createFileRoute pour une route donnée.
Typage Moins de typage explicite pour les paramètres de recherche et les données chargées. Typage fort pour les paramètres de recherche (validateSearch) et les données chargées (useLoaderData).

⚠️ Erreurs fréquentes et pièges

  1. Lenteur de déploiement : Les builds Next.js peuvent être longs, retardant les mises en production et les correctifs urgents.
    • Solution : Migrer vers des frameworks comme TanStack Start qui optimisent les temps de build.
  2. Expérience utilisateur dégradée : L'affichage de loaders et de placeholders multiples lors de la navigation ou du rafraîchissement des pages peut frustrer les utilisateurs.
    • Solution : Utiliser des frameworks offrant une réactivité native et un chargement instantané des pages.
  3. Complexité pour l'IA et la maintenance : Les changements fréquents d'architecture et les abstractions implicites de Next.js (pages/ vs app/, server components, etc.) rendent le code difficile à comprendre et à maintenir, surtout pour les outils d'IA.
    • Solution : Adopter des frameworks déclaratifs et explicites comme TanStack Start, où chaque aspect de la route (chargement, middleware, métadonnées, composant) est défini clairement dans un seul bloc.
  4. Problèmes de migration : La migration d'une application Next.js vers une autre stack peut être coûteuse en temps et en ressources, nécessitant une analyse approfondie et des ajustements manuels.
    • Solution : Utiliser des outils d'IA spécialisés (comme l'agent Apex-aev mentionné) pour automatiser et valider les étapes de migration, bien que cela puisse encore nécessiter une supervision humaine.

Glossaire

TanStack Start : Un framework de développement web qui met l'accent sur la performance, la réactivité et une structure de code déclarative, souvent utilisé avec React.
Déclaratif : Un style de programmation où l'on décrit le quoi faire plutôt que le comment le faire, permettant au framework de gérer les détails d'implémentation.
Middleware : Une fonction qui s'exécute avant ou après le traitement d'une requête, permettant d'intercepter et de modifier le flux de l'application (ex: authentification, validation).

Points clés à retenir

  • La migration vers TanStack Start a réduit les temps de build de plus de 50% par rapport à Next.js.
  • TanStack Start offre une réactivité UI supérieure, éliminant les loaders et les placeholders pour une expérience utilisateur plus fluide.
  • La nature déclarative de TanStack Start rend le code plus compréhensible et maintenable pour les développeurs et les outils d'IA.
  • Contrairement à Next.js qui a connu de nombreux changements d'architecture, TanStack Start propose une approche plus stable et logique, respectant les standards du web.
  • L'utilisation d'outils d'IA pour la migration est possible mais peut être complexe et coûteuse, nécessitant une approche étape par étape et une validation rigoureuse.
  • TanStack Start intègre des fonctionnalités comme les "pending components" (loaders), "beforeLoad" (middlewares), "validateSearch" (validation de paramètres de recherche typée), et "loaderDeps" (dépendances de rechargement) directement dans la définition de la route.

Ressources