Integrar Dev.to API + NextJS + Typescript + TailwindCSS
viistorrr
Posted on October 29, 2022
[Dev.to + NextJS + Typescript + TailwindCSS]
En la actualización de mi website, estaba buscando cómo y dónde publicar mi blog, soy Frontend y no quería usar solamente wordpress porque - entre otras cosas no me gusta😆 - y tratando de solucionar qué hacer, encontré Dev.to que además me ofrece una API bastante sencilla de utilizar, para hacer ésta integración encontré un post e intenté seguirlo pero me pareció algo confuso, por eso acá les voy a mostrar lo más sencillo que pueda la forma como hice la implementación para que tu también logres hacerlo si quieres publicar tus post de Dev.to en tu sitio web personal o para que se lo ofrezcas a algún cliente, simplemente en el momento que consideres te pueda servir.
Debo aclarar que estoy asumiendo que ya tienes conocimientos previos de NextJS y Typescript, por eso me voy a saltar la parte de la creación del proyecto, seteado y todas esas cosas, vamos a ir directamente a la parte de la integración del blog
Antes debo aclarar que vamos a usar NextJS con Typescript, por eso la extensión de los archivos que verás son .tsx
ahora si, vamos a lo que nos gustar, al código! 👨🏾💻
Crea una carpeta blog
dentro de pages
y agregas el archivo index.tsx
En éste index.tsx
vamos a tener la lista de todos tus posts en Dev.to, por eso, al final es a gusto personal la manera como quieras tener la UX, te mostraré lo sencilla de la mía porque no quería tener mucho diseño en ésta parte, pues lo que nos importa es cómo queda el código
Ahora, en pages/blog/index.tsx
lo primero es conectarnos al api de Dev.to, ésto lo vamos a hacer con SSR de NextJS usando getServerSideProps
es muy sencillo, sólo debes llamar a la URL
https://dev.to/api/articlesusername=${process.env.DEV_USERNAME}
quiere decir que debes tener claro tu nombre de usuario configurado en tu archivo .env
DEV_USERNAME=tunombredeusuario
Luego de ésto, el llamado a Dev.to API debe quedar así👇🏾
export const getServerSideProps = async () => {
const devDotToPosts = await fetch(
`https://dev.to/api/articles?username=${process.env.DEV_USERNAME}`
);
const res = await devDotToPosts.json();
return {
props: {
devDotToPosts: res,
},
};
};
NOTA: Vas a ver un componente ******<Layout> </Layout>
pero no es más que un wrapper
donde tengo un Header
y Footer
, no es más que ésto, eso lo podés modificar y hacerlo completamente a tu gusto
<div className="relative pt-6 pb-16 sm:pb-24 font-monserrat">
<Header />
<div className="flex overflow-hidden bg-white py-8 px-4 sm:px-2 lg:px-4 lg:py-6 justify-center">
{children}
</div>
<Footer />
</div>
Dicho eso, vamos con la portada del blog pages/blog/index.tsx
debe verse algo como ésto👇🏾
import Link from "next/link";
import Layout from "@components/Layout";
import { VImage } from "@components/image/VImage";
const Tags = (tags) => {
return tags.tags.map((tag) => (
<strong key={tag} className="text-xs text-primary">
#{tag}{" "}
</strong>
));
};
const Blog = ({ devDotToPosts }) => {
return (
<Layout>
<div className="container mx-auto px-4">
<div className="flex flex-wrap -mx-4">
{devDotToPosts &&
devDotToPosts.map((post) => (
<div className="w-full md:w-1/2 lg:w-1/3 px-4 mb-8" key={post.id}>
<Link href={`/blog/posts/${encodeURIComponent(post.slug)}`}>
<a>
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<VImage
src={post.social_image}
alt={post.title}
width={500}
height={200}
/>
<div className="p-4">
<Tags tags={post.tag_list} />
<p className="text-secondary text-sm mt-1">
{post.description}
</p>
</div>
</div>
</a>
</Link>
</div>
))}
</div>
</div>
</Layout>
);
};
export const getServerSideProps = async () => {
const devDotToPosts = await fetch(
`https://dev.to/api/articles?username=${process.env.DEV_USERNAME}`
);
const res = await devDotToPosts.json();
return {
props: {
devDotToPosts: res,
},
};
};
export default Blog;
Como ven, tengo un custom componente VImage
para cargar las imágenes, acá se los dejo
import Image from "next/image";
import { VImageProps } from "./types";
const vCustomLoader = ({ src, width, quality }) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
export const VImage = ({ src, alt, width, height, className }: VImageProps) => {
return (
<Image
loader={vCustomLoader}
src={src}
alt={alt}
width={width}
height={height}
className={className}
priority
/>
);
};
y acá las respectivas PropsTypes
:
export type VImageProps = {
src: string;
alt: string;
width: number | string;
height: number | string;
className?: string;
};
Listo!!! Con ésto tenés la portada de tu Blog en tu Website personal, ahora vamos a ver una forma sencilla de pintar cada entrada.
Para ésto debes crear el archivo [slug].tsx
dentro de la carpeta blog, así debe quedar pages/blog/posts/[slug].tsx
Una vez aquí, lo primero que se debe hacer es obtener la url del post que nos entrega Dev.to, ésto lo hacemos con getStaticPaths
pues así le estamos diciendo a NextJS dónde va a ir a consultar la información del post que quieres mostrar, ésta función debe lucir algo como ésto👇🏾
export async function getStaticPaths() {
const devDotToPosts = await fetch(
`https://dev.to/api/articles?username=${process.env.DEV_USERNAME}`
);
const posts = await devDotToPosts.json();
return {
paths: posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
}),
fallback: false,
};
}
Ahora debemos traer las Props que se van a utilizar en la vista, ésto se hace con getStaticProps
así:
export const getStaticProps = async ({ params }) => {
const devDotToPost = await fetch(
`https://dev.to/api/articles/${process.env.DEV_USERNAME}/${params.slug}`
);
const res = await devDotToPost.json();
return {
props: {
devDotToPost: res,
},
};
};
Al final pages/blog/posts/[slug].tsx
debe quedar algo como ésto
import Head from "next/head";
import Link from "next/link";
import { VImage } from "@components/image/VImage";
import Layout from "@components/Layout";
export default function Post({ devDotToPost }) {
const {
title,
published_at,
social_image,
body_html,
user,
type_of,
description,
canonical_url,
} = devDotToPost;
return (
<Layout>
<Head>
<meta property="og:type" content={type_of} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={social_image} />
<meta property="og:url" content={canonical_url} />
</Head>
<div className="flex justify-center">
<article className="text-lg w-full md:w-3/4 text-justify">
<div className="my-12 border-2 text-secondary bg-white md:rounded-lg overflow-hidden">
<div className="p-4 md:p-32">
<h1 className="text-3xl font-bold text-primary">{title}</h1>
<div className="flex items-center text-secondary rounded-lg py-6">
<VImage
className="rounded-full"
src={user.profile_image_90}
alt={user.name}
width={50}
height={50}
/>
<span className="mx-4 font-bold">{user.name}</span>
</div>
<div
className="markdown"
dangerouslySetInnerHTML={{ __html: body_html }}
/>
</div>
</div>
<div className="flex justify-center">
<Link href="/blog">
<a className="text-primary inline-flex items-center md:mb-2 lg:mb-0 cursor-pointer text-base pb-8">
<svg
className="w-6 h-6 mr-2"
stroke="currentColor"
strokeWidth="2"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
viewBox="0 0 24 24"
>
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
Volver
</a>
</Link>
</div>
</article>
</div>
</Layout>
);
}
export async function getStaticPaths() {
const devDotToPosts = await fetch(
`https://dev.to/api/articles?username=${process.env.DEV_USERNAME}`
);
const posts = await devDotToPosts.json();
return {
paths: posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
}),
fallback: false,
};
}
export const getStaticProps = async ({ params }) => {
const devDotToPost = await fetch(
`https://dev.to/api/articles/${process.env.DEV_USERNAME}/${params.slug}`
);
const res = await devDotToPost.json();
return {
props: {
devDotToPost: res,
},
};
};
Y ya!!! Con así ya tenés toda la información disponible de tu post al momento de renderizar, el resto ya es a tu imaginación, al forma como quieras que luzca la página de blog y qué tanta info, en mi caso quise hacerlo lo más sencillo posible, pero seguro vos podrás ponerlo mucho más bonito, lo importante acá era mostrarte el proceso de cómo se hace la integración de NextJS con el API de Dev.to
Eso es todo! Espero te pueda servir, dale like y me podés seguir en twitter como @viistorrr
Posted on October 29, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.