Quand un mauvais lien arrive en production
Les bugs de deep link dans les apps vibe codées commencent souvent par des problèmes d’entrée banals : un identifiant utilisateur mal formé, une URL de campagne obsolète, une valeur d’enum manquante dans un payload de push. Puis l’app traite cette entrée comme fiable et un écran plante au fond de la logique happy path.
Chaque paramètre d’URL est une entrée utilisateur. Deep links, payloads de push notification, URL partagées, QR codes : rien de tout cela n’est fiable. Quand vous vibe codez avec Cursor, Claude Code génère magnifiquement le happy path. Il ne génère le chemin paranoïaque que si vous l’exigez. Il suppose. La production punit les suppositions.
Validation Zod à la frontière de la route
Validez les paramètres exactement au point d’entrée dans l’app. Après validation, le reste de votre code travaille avec des valeurs typées et garanties sûres :
// app/(app)/profile.tsx
// Validate at the gate. Everything past this point is typed.
import { useLocalSearchParams } from 'expo-router'
import { z } from 'zod'
const profileParamsSchema = z.object({
section: z
.enum(['overview', 'security', 'preferences'])
.default('overview'),
userId: z.string().uuid().optional(),
})
type ProfileParams = z.infer<typeof profileParamsSchema>
export default function ProfileScreen() {
const raw = useLocalSearchParams()
const result = profileParamsSchema.safeParse(raw)
if (!result.success) {
return <ErrorScreen message='Invalid profile parameters' />
}
const { section, userId } = result.data
return <ProfileContent section={section} userId={userId} />
}
ProfileContent reçoit section et userId comme des valeurs correctement typées. Il ne connaît pas useLocalSearchParams. Il ne valide rien. Il reçoit seulement de bonnes données. Voilà comment on construit une app vibe codée où les features générées par IA ne cassent pas dès que le monde réel envoie des données pourries.
+native-intent : rejeter les URL malveillantes avant qu’elles n’entrent
Expo Router vous donne +native-intent.tsx pour traiter les chemins natifs entrants qui peuvent être mal formés, obsolètes ou malveillants. Ce fichier s’exécute avant même que vos providers ne montent. Il doit seulement réécrire les chemins et rejeter les mauvaises entrées :
// app/+native-intent.tsx
// This is your firewall, not your business logic.
export function redirectSystemPath({
path,
}: {
path: string
}) {
// Reject paths with suspicious patterns
if (path.includes('..') || path.includes('//')) {
return '/'
}
// Migrate old URL format to new format
if (path.startsWith('/user/')) {
const userId = path.replace('/user/', '')
if (z.string().uuid().safeParse(userId).success) {
return `/profile?userId=${userId}`
}
return '/'
}
return path
}
L’idée clé : +native-intent ne peut pas vérifier si l’utilisateur est connecté. Il doit seulement assainir les chemins. L’autorisation métier appartient aux route guards, pas à ce fichier.
Typed Routes : attraper les erreurs à la compilation
Activez typedRoutes dans votre config pour attraper les noms de routes mal orthographiés avant qu’ils n’atteignent vos utilisateurs :
// app.config.ts
{
experiments: {
typedRoutes: true,
}
}
// Now the router validates at compile time:
router.push('/profile') // OK
router.push('/profle') // TypeScript error
router.push({
pathname: '/profile',
params: { section: 'overview' },
}) // OK
Les typed routes attrapent les noms mal écrits. La validation Zod attrape les mauvais paramètres. Entre les deux, vous obtenez un filet de sécurité que la plupart des routeurs web n’offrent même pas.
L’Autotomy Expo Starter Pack inclut déjà des patterns de validation Zod, la configuration typed routes et des handlers +native-intent. Vos deep links fonctionnent. Vos utilisateurs ne plantent pas.