Criando uma página pessoal com a API do dev.to e VueJS
- #Vue.js
- #TypeScript
- #API Rest
Estava há muito tempo querendo fazer uma página pessoal pra usar de portifólio e também como blog de tecnologia, e depois de estudar várias soluções, descobri que o dev.to tem uma API que possibilita a consulta dos dados do usuário assim como seus artigos. Com isso na mão, me animei a fazer uma aplicação em Vue que lesse esses dados
A API
Inicialmente cheguei neste artigo que demonstrava como consumir a API da plataforma, com exemplo em PHP.
How to use the dev.to API!
No artigo, tem um link para um outro que mostra vários endpoints que você pode consultar pra obter os dados que deseja. Pesquisando um pouco mais, cheguei à documentação oficial.
DEV API (beta) | Forem Docs
E com isso, cheguei nos 2 endpoints que precisava:
Vue
Tendo a API e os endpoints, o próximo passo foi buscar esses dados dentro do site, pra depois exibir do jeito que eu queria.
Consumindo a API
O primeiro passo foi criar um método pra centralizar as requisições à api, que fiz com axios
import axios, {AxiosInstance} from "axios";
const http: AxiosInstance = axios.create({
baseURL: "https://dev.to/api/",
headers: {
'Accept': "application/json",
'Content-Type': 'application/json'
}
})
export default http;
Depois disso, criei serviços distindos para fazer a consulta aos endpoints.
O service pra receber artigos:
import http from "@/http";
export const getArticles = () => http.get('articles', {
params: {
username: 'stsmuniz'
}
})
E o service para receber o perfil
import http from "@/http";
export const getProfile = () => http.get('users/by_username', {
params: {
url: 'stsmuniz'
}
})
Com os services criados, agora podemos usar no site
Exibindo o perfil
Antes de mais nada, é necessário fazer a consulta efetivamente. Como usei composition api, usei o método onBeforeMount
pra buscar os dados antes de renderizar a página.
setup() {
const profile = ref()
onBeforeMount(() => {
getProfile()
.then(res => profile.value = res.data)
})
....
return {
profile
}
})
E na página vai o componente que "monta" o perfil
<ProfileComponent profile="profile" v-if="profile"/>
<i class="fa-solid fa-spinner fa-spin-pulse" v-else></i>
O ProfileComponent
é o componente que recebe as informações do perfil e "monta" o perfil na tela.
<template>
<div class="profile">
<img class="profile-picture" :alt="profile.username" :src="profile.profile_image">
<h1>{{profile.name}}</h1>
<p class="description" v-html="profile.summary.replace('\n', '<br />')"></p>
<p><i class="fa-solid fa-location-dot"></i> {{profile.location}}</p>
</div>
</template>
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
name: "ProfileComponent",
props: {
profile: {
required: true
}
},
})
</script>
E com isso conseguimos fazer o perfil ser renderizado na tela. O próximo passo é fazer o mesmo processo para os artigos
Exibindo os artigos
A primeira coisa aqui é criar a view para exibir os artigos, que chamei aqui de Blog. No setup há a chamada para a API pelo service ArticleService, com o
<template>
<div class="home" v-if="articles">
<h1>Blog</h1>
<ArticleList :articles="articles"/>
</div>
<i class="fa-solid fa-spinner fa-spin-pulse" v-else></i>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref} from 'vue';
import {getArticles} from "@/service/ArticleService";
import ArticleList from '@/components/ArticleList.vue';
export default defineComponent({
name: 'BlogView',
components: {
ArticleList,
},
setup() {
const articles = ref()
onMounted(() => {
document.title = 'Blog'
})
getArticles()
.then(res => articles.value = res.data)
return {
articles
}
}
});
</script>
O ArticleList
é um componente container que, basicamente, organiza o container pra lista de artigos
<template>
<div class="container">
<ArticleItem v-for="article in articles" :article="article" :key="article.id" />
</div>
</template>
<script>
import {defineComponent} from 'vue';
import ArticleItem from "@/components/ArticleItem";
export default defineComponent({
name: 'ArticleList',
components: {ArticleItem},
props: {
articles: {
required: true
}
}
});
</script>
E por fim, o componente ArticleItem
organiza as informações no template pra fazer a exibição
<template>
<article>
<a :href="article.url"
:title="article.title"
target="_blank">
<img class="responsive"
:src="article.cover_image"
:alt="article.title" />
</a>
<div class="article-data">
<h1>
<a :href="article.url"
:title="article.title"
target="_blank">
{{ article.title }}
</a>
</h1>
<p class="article-description">{{article.description}}</p>
<p class="publish-date">
<i class="fa-solid fa-calendar"></i> <DateFormatter :date="article.published_at" />
</p>
</div>
</article>
</template>
<script>
import DateFormatter from "./DateFormatter";
import {defineComponent} from "vue";
export default defineComponent({
name: "ArticleItem",
components: {DateFormatter},
props: {
article: {
required: true
}
}
})
</script>
Como eu queria exibir a data no formato dd/mm/yyyy
, criei o componente DateFormatter
pra tratar a string que vem no formato ISO.
Deploy
Dei uma fuçada e acabei encontrando um artigo interessante que explica bem como fazer o deploy:
Automatically build and deploy a Vue.js app with GitHub Pages - LogRocket Blog
Se você for usar esse método, tome cuidado com o nome da branch principal no arquivo gh-pages-deploy.js
. No artigo ela está sendo chamada como master
.
Fechando
Com isso, mais a estrutura básica do vue e alguma estilização, pode se ter um site simples com informações básicas vindas do seu perfil do dev.to e uma página com link para seus artigos mais recentes.
Pretendo criar um repositório aberto pra compartilhar o que fiz aqui e deixar fácil pra qualquer pessoa com perfil aqui também ter sua página pessoal, assim como a minha
Obrigado por acompanhar até aqui e nos vemos na próxima 👋