Frameworks coté Serveur

Master 2 – Développement Web (Partie 1)

Vers une application Web complète : SSR + ORM + Auth

Introduction à Next.js, Prisma et Authentification


Objectifs du cours


1. La “Big Picture” – Comment tout s’articule

Une application Web moderne repose sur trois couches principales :

Couche Rôle Outil choisi
Interface (Frontend) Ce que voit l’utilisateur : pages, formulaires, navigation React (via Next.js)
Logique Serveur (Backend) Traitement des requêtes, génération des pages, API Next.js (API Routes)
Données (Base + ORM) Stockage et accès aux informations persistantes Prisma + PostgreSQL
Sécurité (Auth) Gestion des utilisateurs, sessions, permissions NextAuth.js

💡 Le tout fonctionne dans un seul projet TypeScript grâce à Next.js.

Exemple : flux complet d’une page “Annonces”

  1. Un utilisateur visite /annonces.
  2. Next.js exécute du code serveur → va chercher les annonces via Prisma dans la base.
  3. La page est rendu côté serveur (SSR) et envoyée au navigateur.
  4. Si l’utilisateur est connecté (via NextAuth.js), il peut créer une annonce.
  5. Les actions (POST, DELETE…) passent par les API Routes internes à Next.js.
Navigateur ──▶ Next.js (SSR) ──▶ Prisma ──▶ Base de données
       ▲             │
       │             └──▶ API /auth (NextAuth.js)
       │
       └──◀─── Données + HTML

Choix d’architecture

Concept Avantage pédagogique
SSR (Server-Side Rendering) Pages dynamiques rapides, SEO friendly, code React réutilisé
ORM (Prisma) Moins d’erreurs SQL, typage fort, génération automatique de requêtes
Auth.js Authentification intégrée à Next.js, rôles et sessions faciles
TypeScript Sécurité du typage du front au back
Full-stack intégré Pas besoin de gérer deux serveurs distincts (front/back)

2. Créer un projet Next.js (base technique)

npx create-next-app@latest immobilier-app --typescript --tailwind --app
cd immobilier-app
npm run dev

http://localhost:3000

Structure du dossier :

immobilier-app/
├─ src/
│  ├─ app/
│  │  ├─ page.tsx            # Page d’accueil
│  │  ├─ about/page.tsx      # Page "about"
│  │  ├─ annonces/[id]/page.tsx  # Page dynamique
│  │  └─ api/                # Routes backend
│  └─ lib/                   # outils (prisma, auth, etc.)
└─ package.json

3. Pages et navigation

Chaque dossier sous src/app correspond à une route.

Exemple : page statique

// src/app/about/page.tsx
export default function AboutPage() {
  return <h1>À propos de notre agence immobilière</h1>
}

http://localhost:3000/about

Exemple : page dynamique avec paramètre

// src/app/annonces/[id]/page.tsx
export default function AnnoncePage({ params }: { params: { id: string } }) {
  return <h1>Annonce n°{params.id}</h1>
}

http://localhost:3000/annonces/12


4. Rendu SSR (Server-Side Rendering)

Les composants dans le dossier src/app/ peuvent être asynchrones : On peut directement y faire des requêtes côté serveur.

// src/app/page.tsx
async function getData() {
  const annonces = [
    { id: 1, titre: "Appartement Paris 11e" },
    { id: 2, titre: "Maison Bordeaux" },
  ]
  return annonces
}

export default async function HomePage() {
  const annonces = await getData()
  return (
    <ul>
      {annonces.map(a => (
        <li key={a.id}>{a.titre}</li>
      ))}
    </ul>
  )
}


5. Créer une API backend dans Next.js

Chaque fichier dans src/app/api/... devient une route API.

// src/app/api/hello/route.ts
export async function GET() {
  return Response.json({ message: "Bonjour depuis l’API Next.js !" })
}

http://localhost:3000/api/hello

Exemple API avec POST

// src/app/api/messages/route.ts
let messages: string[] = []

export async function GET() {
  return Response.json(messages)
}

export async function POST(req: Request) {
  const { content } = await req.json()
  messages.push(content)
  return Response.json({ ok: true })
}


6. Persistance des données avec Prisma ORM

Prisma est un ORM moderne pour TypeScript : il traduit les modèles d’objets en tables SQL.

Installation

npm install @prisma/client prisma
npx prisma init

Cela crée un dossier prisma/ avec un fichier schema.prisma`.

Exemple de modèle

// prisma/schema.prisma
datasource db {
  provider = "sqlite" // PostgreSQL par défaut
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
  // ⚠ pas de clé "output" pour rester compatible averc Next
}


model Annonce {
  id          Int      @id @default(autoincrement())
  titre       String
  description String
  prix        Float
  statut      Statut   @default(PUBLIE)
  createdAt   DateTime @default(now())
}

enum Statut {
  BROUILLON
  PUBLIE
}

Puis :

npx prisma migrate dev --name init

Tester la création d’une annonce

Depuis une interface graphique :

npx prisma studio

Depuis le code :

// scripts/seed.ts
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()

async function main() {
  await prisma.annonce.create({
    data: {
      titre: "Appartement Paris 11e",
      description: "Bel appartement 2 pièces",
      prix: 450000,
    },
  })
}
main()

7. Utiliser Prisma dans Next.js

On déclare un client partagé :

// src/lib/prisma.ts
import { PrismaClient } from "@prisma/client"

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma =
  globalForPrisma.prisma ||
  new PrismaClient({
    log: ["query"],
  })

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma

Exemple d’utilisation dans une page SSR :

// src/app/page.tsx
import { prisma } from "@/lib/prisma"

export default async function HomePage() {
  const annonces = await prisma.annonce.findMany()

  return (
    <div>
      <h1>Annonces immobilières</h1>
      <ul>
        {annonces.map(a => (
          <li key={a.id}>
            {a.titre}{a.prix}</li>
        ))}
      </ul>
    </div>
  )
}

✅ SSR + ORM.

8. Ajouter TailwindCSS pour la mise en forme

Avec l’option --tailwind lors de la création du projet, tout est prêt.

Exemple

export default function CardAnnonce({ titre, prix }: { titre: string; prix: number }) {
  return (
    <div className="border rounded-lg shadow p-4 hover:bg-gray-50">
      <h2 className="text-xl font-semibold">{titre}</h2>
      <p className="text-gray-600">{prix}</p>
    </div>
  )
}

9. Authentification et gestion des rôles

Objectifs


Les trois briques principales

Élément Rôle dans l’application Outil associé
Authentification Vérifie l’identité d’un utilisateur (connexion) NextAuth
Persistance des utilisateurs Stocke les comptes, mots de passe, sessions Prisma + Base de données
Autorisation / Rôles Définit ce qu’un utilisateur peut faire Application (middleware, logique métier)

Organisation globale

L’objectif est de mettre en place un parcours utilisateur complet :

  1. Inscription (sign up) :

    • Un visiteur crée un compte (nom, email, mot de passe).
    • Son mot de passe est chiffré avant stockage.
    • Un rôle par défaut lui est attribué (ex. “USER”).
  2. Connexion (login) :

    • L’utilisateur saisit ses identifiants.
    • Le système vérifie son email et son mot de passe dans la base.
    • Une session (ou un jeton JWT) est créée et stockée.
  3. Session active :

    • L’application reconnaît l’utilisateur à chaque requête.
    • Les pages peuvent afficher du contenu personnalisé.
  4. Déconnexion (logout) :

    • La session est supprimée côté serveur et client.
  5. Autorisation / rôles :

    • Certains rôles (ex. “AGENT”) ont des droits supplémentaires :

      • publier ou modifier des annonces ;
      • gérer les utilisateurs.
    • D’autres (ex. “USER”) ne peuvent que consulter et poser des questions.


Le modèle de données à prévoir

Ce modèle est géré automatiquement par Prisma et NextAuth via un adaptateur.


Étapes à réaliser

  1. Configurer le système d’authentification :

    • Installer et connecter NextAuth à la base via Prisma.
    • Définir un provider d’authentification “email + mot de passe”.
  2. Créer les pages nécessaires :

    • Formulaire d’inscription → enregistre un nouvel utilisateur.
    • Formulaire de connexion → envoie les identifiants à NextAuth.
  3. Mettre en place la logique de session :

    • Vérifier qu’un utilisateur est connecté avant d’accéder à certaines pages.
    • Afficher son nom ou son rôle dans la barre de navigation.
  4. Introduire la notion de rôles :

    • Ajouter un champ role dans le modèle User.
    • Mettre en place une logique d’accès :

      • Les utilisateurs “AGENT” peuvent créer ou modifier des annonces.
      • Les “USER” ne peuvent que consulter.
  5. Protéger les routes sensibles :

    • Via un middleware ou via une vérification côté serveur.
    • Rediriger les utilisateurs non autorisés vers une page d’erreur ou de connexion.

📚 Ressources utiles

Next.js Docs → https://nextjs.org/docs Prisma Docs → https://www.prisma.io/docs TailwindCSS → https://tailwindcss.com/docs Auth.js (NextAuth) → https://authjs.dev