Supabase User Management para Next.js App Router
Módulo de gestión de usuarios de autoservicio: obtener/actualizar el perfil, cambiar el email, cambiar la contraseña, avatar, nombre, metadata, preferencias del usuario y eliminar la propia cuenta. Está separado del módulo de auth: es autocontenido y vive en su propia carpeta.
Dependencias
@supabase/ssr@supabase/supabase-js
Dónde vive cada cosa
| Archivo | Propósito |
|---|---|
types.ts | Tipos compartidos (UserProfile, UserManagementResult<T>, UserManagementError, etc.) |
service.ts | Lógica interna: cliente de Supabase para el servidor + todas las operaciones de gestión de usuarios. No se importa fuera de este módulo. |
actions.ts | "use server" — la API pública del lado del servidor. Importá desde acá en Server Components / formularios. |
client.ts | Cliente de Supabase para el navegador + funciones listas para usar en Client Components. |
env.example.md | Variables de entorno necesarias. |
Este módulo no incluye proxy.ts ni route.ts: la protección de rutas ya la resuelve el proxy.ts del módulo de auth, y la confirmación del cambio de email reutiliza tal cual el app/auth/callback/route.ts del módulo de auth.
Uso
Leer el perfil actual (Server Component)
import { getCurrentUserProfile } from '@/lib/user-management/supabase/actions';
export default async function AccountPage() {
const { data: profile } = await getCurrentUserProfile();
return <p>{profile?.name ?? profile?.email}</p>;
}
Actualizar nombre / metadata / preferencias (Client Component)
'use client';
import { updateNameClient, updatePreferencesClient } from '@/lib/user-management/supabase/client';
await updateNameClient('Dari');
await updatePreferencesClient({ theme: 'dark' });
Cambiar la contraseña (requiere la contraseña actual)
import { changePasswordAction } from '@/lib/user-management/supabase/actions';
const { error } = await changePasswordAction({ currentPassword, newPassword });
Cambiar el email
import { changeEmailAction } from '@/lib/user-management/supabase/actions';
await changeEmailAction({ newEmail }); // confirmation email → app/auth/callback/route.ts
Subir un avatar (Server Action desde un formulario)
import { uploadAvatarAction } from '@/lib/user-management/supabase/actions';
<form action={uploadAvatarAction}>
<input type="file" name="avatar" accept="image/*" />
<button type="submit">Upload</button>
</form>
Eliminar la propia cuenta
import { deleteOwnAccountAction } from '@/lib/user-management/supabase/actions';
await deleteOwnAccountAction(); // signs the user out afterward
Configuración
Pasos que el CLI no puede automatizar: hacelos manualmente en el dashboard de Supabase:
- Bucket de Storage: creá un bucket llamado
avatars(Storage → New bucket). Hacelo público si querés quegetPublicUrlsirva las imágenes directamente; si lo dejás privado, reemplazágetPublicUrlporcreateSignedUrlenservice.ts/client.ts. - Políticas RLS de Storage en
avatars: restringíinsert/update/deleteaauth.uid()::text = (storage.foldername(name))[1]para que cada usuario solo pueda escribir dentro de su propia carpeta (el módulo ya sube a<user_id>/<filename>). - Service role key: copiá
SUPABASE_SERVICE_ROLE_KEYdesde Project Settings → API a tu.env.local. Mantenela fuera del control de versiones y nunca la expongas al cliente. - Dependencia del módulo de auth: este módulo asume que
app/auth/callback/route.ts(del boilerplate de auth) ya existe en el proyecto: las confirmaciones de cambio de email redirigen ahí.
Alcance
Solo autoservicio. No hay listUsers, searchUsers ni eliminación de cuentas de otros usuarios: son operaciones de nivel administrador que necesitarían su propia capa de autorización, algo que queda fuera del alcance de este módulo.