URLs Auto-Reparables en Astro 5: Actualización de Content Collections
Cuando publicás un artículo y alguien lo comparte, ese link puede vivir años. Si después renombrás el post o cambiás el slug, el link se rompe. Medium lo resuelve con un ID al final de la URL — mi-articulo-abc123 — que siempre apunta al post correcto sin importar cómo cambió el título.
Implementé lo mismo en este sitio con un campo selfHealing en cada nota.
El campo en el schema
// src/content/schemas/noteSchema.ts
import { z } from 'astro:content'
export const noteSchema = z.object({
draft: z.boolean(),
title: z.string(),
description: z.string(),
publishDate: z.date(),
// Código auto-reparable: 6 caracteres, sin vocales
selfHealing: z.string().regex(/^[^aeiouAEIOU-]{6}$/).length(6),
// ... otros campos
})
La colección con glob loader (Astro 5)
// src/content/notes/config.ts
import { defineCollection } from 'astro:content'
import { glob } from 'astro/loaders'
import { noteSchema } from '../schemas/noteSchema'
export const notes = defineCollection({
loader: glob({
pattern: '**/[^_]*.md',
base: './src/content/notes/en',
}),
schema: noteSchema,
})
La ruta que hace la redirección
---
// src/pages/notebook/[selfheal].astro
import { getCollection } from 'astro:content'
export const prerender = false
const path = Astro.url.pathname
// Extraer código auto-reparable potencial (6 consonantes al final)
const codeMatch = path.match(/([^aeiouAEIOU\-\/]{6})(?:\/)?$/)
if (!codeMatch) {
return Astro.redirect('/404')
}
const code = codeMatch[1]
// Encontrar nota con código selfHealing coincidente
const notes = await getCollection('notes', (entry) => {
return entry.data.selfHealing === code
})
if (notes.length > 0) {
const note = notes[0]
// En Astro 5, usar id o slug explícito
const slug = note.data.slug || note.id.replace(/\.md$/, '')
return Astro.redirect(`/notebook/${slug}`)
}
return Astro.redirect('/404')
---
El CLI para generar códigos
Para no armar los códigos a mano, usá el script incluido:
// scripts/generateSelfHealingCode.ts
function generateCode(title: string): string {
// Remover vocales y caracteres especiales
const consonants = title
.toLowerCase()
.replace(/[aeiou\s\-\_\.\,\!\?\:\;]/g, '')
.slice(0, 6)
.padEnd(6, 'x')
return consonants
}
function validateCode(code: string): boolean {
return /^[^aeiouAEIOU-]{6}$/.test(code) && code.length === 6
}
// Uso
const title = process.argv[2]
const code = generateCode(title)
console.log(`selfHealing: "${code}"`)
bun run generate:selfheal "My Post Title" # Genera código desde el título
bun run generate:selfheal --validate "rhythm" # Valida un código existente
bun run generate:selfheal --alts "Title" # Genera alternativas
La diferencia con Astro 4
Esto es lo que cambió entre versiones — lo aprendí de las malas cuando migré:
Antes (Astro 4):
const notes = await getCollection('notes')
const redirect = `/notebook/${note.slug}`
Después (Astro 5):
const notes = await getCollection('notes')
// Usar id o slug explícito del frontmatter
const slug = note.data.slug || note.id.replace(/\.md$/, '')
const redirect = `/notebook/${slug}`
En Astro 5 el slug ya no se genera automáticamente. Tenés que definirlo en el frontmatter o derivarlo del id. Ojo con eso si venís de Astro 4.
Los links que se comparten desde este sitio incluyen el selfHealing code — así sobreviven cualquier renombrado futuro. Vale la pena tenerlo en cuenta desde el arranque.



Comments for slfhln