Aller au contenu principal

Next.js et Supabase

Créer une application web avec Next.js et Supabase, en respectant les bonnes pratiques de sécurité, propreté du code, et en utilisant TypeScript ainsi que shadcn-ui.

🚀 Objectif

Nous allons créer une application Next.js avec :

  • Next.js (framework React pour le rendu côté serveur et statique)
  • Supabase (alternative open-source à Firebase pour l'authentification et la base de données)
  • TypeScript (pour la sécurité et la maintenabilité du code)
  • shadcn-ui (bibliothèque de composants UI basée sur Radix UI)
  • les bonnes pratiques de sécurité (pratiques de sécurité pour protéger les données et éviter les vulnérabilités)

🛠️ 1 : Installer Next.js

Tout d'abord, installons Next.js avec TypeScript :

npx create-next-app@latest my-app --typescript
Need to install the following packages:
create-next-app@15.1.7
Ok to proceed? (y)

✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
cd my-app

Ensuite, installons les dépendances essentielles :

npm install @supabase/supabase-js @radix-ui/react-icons class-variance-authority clsx tailwind-merge zod react-hook-form

Ces dépendances servent à :

  • @supabase/supabase-js : pour interagir avec Supabase
  • @radix-ui/react-icons : pour les icônes pour l’UI
  • class-variance-authority, clsx, tailwind-merge : pour gérer les classes CSS proprement
  • zod : pour la validation de données
  • react-hook-form : pour gérer les formulaires de manière efficace

🎨 2 : Ajouter et configurer Tailwind CSS

Supprimez le fichier tailwind.config.js (inutile, car nous utilisons Typescript) :

rm tailwind.config.js

Pour que Tailwind utilise le fichier de configuration TypeScript (.ts et non pas .js), ajoutez la ligne :

"tailwind": "tailwindcss -c tailwind.config.ts"

dans package.json :

  "scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"tailwind": "tailwindcss -c tailwind.config.ts"
},
}

Installez Tailwind CSS :

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Vérifiez le fichier tailwind.config.ts :

import { type Config } from 'tailwindcss'

const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
"./app/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

export default config

Vérifiez que le fichier app/globals.css contienne bien ces 3 lignes :

@tailwind base;
@tailwind components;
@tailwind utilities;

🏗️ 3 : Installer et configurer shadcn-ui

Installez shadcn-ui :

npx shadcn@latest init

Ajoutez un composant UI, par exemple un bouton :

npx shadcn@latest add button

🗄️ 4 : Configurer Supabase

Créez un compte sur Supabase et un nouveau projet.

Dans .env.local, ajoutez vos clés Supabase :

NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

Créez un fichier lib/supabase.ts pour initialiser Supabase :

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

🔐 5 : Sécuriser l'authentification

Ajoutons un système d'authentification sécurisé avec Supabase.

Créez un fichier auth.ts dans /lib/ :

import { supabase } from "./supabase"

export const signInWithEmail = async (email: string, password: string) => {
const { data, error } = await supabase.auth.signInWithPassword({ email, password })
if (error) throw new Error(error.message)
return data.user
}

export const signUpWithEmail = async (email: string, password: string) => {
const { data, error } = await supabase.auth.signUp({ email, password })
if (error) throw new Error(error.message)
return data.user
}

export const signOut = async () => {
await supabase.auth.signOut()
}

Ensuite, créons une page de connexin dans /app/login/page.tsx :

"use client"

import { useState } from "react"
import { signInWithEmail } from "@/lib/auth"
import { Button } from "@/components/ui/button"

export default function Login() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState<string | null>(null)

const handleLogin = async () => {
try {
await signInWithEmail(email, password)
alert("Connexion réussie !")
} catch (err) {
setError((err as Error).message)
}
}

return (
<div className="flex flex-col items-center justify-center min-h-screen">
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="border p-2 rounded"
/>
<input
type="password"
placeholder="Mot de passe"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="border p-2 rounded mt-2"
/>
<Button onClick={handleLogin} className="mt-4">Se connecter</Button>
{error && <p className="text-red-500 mt-2">{error}</p>}
</div>
)
}

🔒 6 : Sécuriser les données

Ajoutez le code suivant dans un nouveau fichier /hooks/useAuth.ts pour récupérer l’utilisateur connecté :

"use client"

import { useEffect, useState } from "react"
import { supabase } from "@/lib/supabase"
import { User } from "@supabase/supabase-js"

export function useAuth() {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)

useEffect(() => {
const getUser = async () => {
const { data: { user } } = await supabase.auth.getUser()
setUser(user)
setLoading(false)
}
getUser()
}, [])

return { user, loading }
}

Pour protéger les pages, remplacez le code du fichier layout.tsx :

import { AuthProvider } from "@/components/AuthProvider"
import './globals.css' // Tailwind CSS

export const metadata = {
title: 'Mon application',
description: 'Application au top !',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="fr">
<head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mon application</title>
<meta name="description" content="Application au top !" />
<meta name="author" content="Mon nom" />
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<AuthProvider>
{children}
</AuthProvider>
</body>
</html>
)
}

✅ 7 : Démarrage en mode développement

npm run dev