Lecciones del curso
Aprende Remix construyendo un Blog con MongoDB y Netlify
Agregando una ruta SSR feed
Para un blog es esencial que los motores de búsqueda puedan indexar correctamente cada uno de los posts de nuestro blog, para que lo ofrescan como resultado a una búsqueda. ✅
Los blogs más exitosos son aquellos que tienen temas evergreen es decir, que las búsquedas no dependen de una moda o tendencia, sino de conceptos más permanentes, de temas fundamentales. Se podrían comparar estos dos títulos para ejemplificar:
S_erverless functions con Netlify_ 📸 // Tendencia
vs
Cómo escribir un loop en JS. // Evergreen
Si queremos que nuestro blog tenga tráfico orgánico y no tener que gastar nuestros ahorros en marketing, bueno, necesitamos que Google sepa que nosotros tenemos respuestas a las búsquedas de los usuarios y de paso que sepa que tenemos herramientas para actualizar sobre las novedades, a nuestros seguidores (RSS). Si Google sabe esto, nos favorecerá. 🫱🏼🫲🏾
Creando un RSS
Podríamos escribir mucho XML a mano, lo que te haría maldecirme y preguntarte: ¿Pa qué estoy escribiendo todo este XML, si ya nadie usa RSS? Es más, ni sé que significa tener un feed, ¡maldito blissmo!
Seguro que dirías algo así, ¿no?. 😜
Pero para evitar lo anterior, vamos a usar una pequeña herramienta que viene bien en estos casos y nos evita tener que escribir XML. 😎
npm i feed
Una vez instalada, vamos a escribir una función interesante que nos ayudará con 3 formatos diferentes (JSON, ATOM1 y RSS2).
Nunca sabes quién lo necesitará para incluir tu contenido y enlistarlo en su propio sitio o simplemente seguir tus publicaciones de cerca con un cliente RSS.
No olvides que hay bots por todas partes, nos vigilan… 🤖
👀 Sí, queremos toda la difusión posible
Veamos cómo usaremos Feed.
// app/utils/feed.server.ts import { Feed } from "feed"; import { db } from "~/utils/db"; const generateFeed = async () => { const feed = new Feed({ title: "Blissmo blog", description: "¡Mantente notificado de nuevos posts!", id: "<https://curso-blog-remix.netlify.app>", link: "<https://curso-blog-remix.netlify.app>", language: "es", // optional, used only in RSS 2.0, possible values: <http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes> image: "<https://i.imgur.com/LywcMSq.png>", favicon: "<https://i.imgur.com/nVMrb56.png>", copyright: "All rights reserved 2023, Héctorbliss", updated: new Date(2023, 6, 24), // optional, default = today feedLinks: { json: "<https://curso-blog-remix.netlify.app/json>", atom: "<https://curso-blog-remix.netlify.app/atom>", rss: "<https://curso-blog-remix.netlify.app/rss2.xml>", }, author: { name: "Héctorbliss", email: "fixtergeek@gmail.com", link: "<https://hectorbliss.com>", }, }); // Conseguimos los posts de la DB const posts = await db.post.findMany({ where: { published: true }, include: { author: true }, }); // Usamos un forEach para agregar con feed.addItem posts.forEach((post) => { feed.addItem({ title: post.title, id: `https://curso-blog-remix.netlify.app/blog/${post.slug}`, link: `https://curso-blog-remix.netlify.app/blog/${post.slug}`, description: post.body.slice(1, 30), content: post.body, author: [ { name: post.author.name ?? "", email: post.author.email, link: post.author.email, }, ], contributor: [ { name: "Brenda Ortega", email: "brenda@fixter.org", link: "<https://www.brendago.design/>", }, ], date: post.createdAt, image: post.cover ?? "", }); }); return feed; }; // No dejes de tipar todo lo que puedas y entiendas export const getFeed = async ( type: "atom1" | "rss2" | "json1" = "rss2" ): Promise<string> => { const feed = await generateFeed(); return type === "rss2" ? feed.rss2() : type === "atom1" ? feed.atom1() : feed.json1(); };
Recuerda cambiar este código con tus datos. ☑️
Creando las rutas
Toma nota de el punto en el nombre del archivo. Estos corchetes nos ayudan a incluir un [.]
en el nombre, sin que sea tomado como un divisor de segmento o el inicio de la extensión.
Llamaremos a estas rutas, feedLinks.
Comencemos con el primer archivo.
// app/routes/feed[.]xml import { type LoaderFunction } from "@remix-run/node"; import { getFeed } from "~/utils/feed.server"; export const loader: LoaderFunction = async () => { const feed = await getFeed("rss2"); return new Response(feed, { headers: { "Content-Type": "application/rss+xml; charset=utf-8", "x-content-type-options": "nosniff", }, }); };
👀
x-content-type-options
Indica que el MIME type debe ser respetado.
Agregando dos rutas más
Necesitamos otras dos rutas porque, ya que feed nos regala otros dos formatos, bueno, ¿por qué no usarlos?
// app/routes/feed[.]atom import { type LoaderFunction } from "@remix-run/node"; import { getFeed } from "~/utils/feed.server"; export const loader: LoaderFunction = async () => { const feed = await getFeed("atom1"); return new Response(feed, { headers: { "Content-Type": "application/atom+xml; charset=utf-8", "x-content-type-options": "nosniff", }, }); };
Finalmente la ruta para JSON que es el formato más moderno, utilizado en APIs de todo el mundo.
// app/routes/feed[.]json import { type LoaderFunction } from "@remix-run/node"; import { getFeed } from "~/utils/feed.server"; export const loader: LoaderFunction = async () => { const feed = await getFeed("json1"); return new Response(feed, { headers: { "Content-Type": "application/json; charset=utf-8", "x-content-type-options": "nosniff", }, }); };
Visita cada una de tus nuevas rutas y comprueba que tus feed funcionan. 🤓 🔥 🕺🏻
No más cervezas. Bueno, una, nomás pa celebrar que tenemos el SEO completamente cubierto. 🎉 🍻 🥳
Enlaces relacionados
W3C feed validator: W3C Feed Validation Service, for Atom and RSS
¿Qué es un RSS? RSS
Remix cache: Performance