Integrar Dev.to API + NextJS + Typescript + TailwindCSS
Tags: nextjs, typescript, programming, tailwindcss
Published: 2022-10-29 23:05:21

**[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](http://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](https://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`


![pages structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tz3lwyotm5zt0v4m02b.png)

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í👇🏾

```tsx
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

```jsx
<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👇🏾

```tsx
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

```tsx
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`:

```tsx
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👇🏾

```tsx
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í:

```tsx
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

```tsx
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** 

[@viistorrr](https://twitter.com/viistorrr)

[viistorrr.com](https://www.viistorrr.com/)