Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Cómo usar la API de Hashnode con Astro y desplegarlo en Vercel

Introducción ¿Te gustaría tener tu propio sitio web con un diseño personalizado pero sin perder el excelente editor de Hashnode? En este artículo aprenderás a crear un blog con Astro que consume contenido directamente desde la API de Hash…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!




Introducción



¿Te gustaría tener tu propio sitio web con un diseño personalizado pero sin perder el excelente editor de Hashnode? En este artículo aprenderás a crear un blog con Astro que consume contenido directamente desde la API de Hashnode y cómo desplegarlo en Vercel.



¿Por qué esta combinación?





  • Hashnode: Editor potente con Markdown, sintaxis de código, imágenes y SEO integrado


  • Astro: Framework ultrarrápido con excelente rendimiento y SEO


  • Vercel: Despliegue automático y CDN global sin configuración






Requisitos previos




  • Node.js 18 o superior instalado

  • Una cuenta en Hashnode

  • Una cuenta en Vercel (gratis)

  • Conocimientos básicos de JavaScript/TypeScript






Paso 1: Configurar tu blog en Hashnode




  1. Crea una cuenta en Hashnode

  2. Configura tu blog en Hashnode (puedes usar un subdominio gratuito)

  3. Escribe algunos artículos de prueba

  4. Obtén tu Publication ID desde la configuración de tu blog



Para encontrar tu Publication ID:




  • Ve a tu dashboard de Hashnode

  • Entra en la configuración de tu publicación

  • Busca el ID en la URL o en la sección de API






Paso 2: Crear el proyecto con Astro



Abre tu terminal y ejecuta:




npm create astro@latest mi-blog-hashnode
cd mi-blog-hashnode






Selecciona las siguientes opciones:




  • Template: Empty

  • TypeScript: Yes

  • Install dependencies: Yes

  • Git repository: Yes






Paso 3: Instalar dependencias necesarias






npm install graphql-request graphql









Paso 4: Configurar variables de entorno



Crea un archivo .env en la raíz del proyecto:




HASHNODE_PUBLICATION_ID=tu-publication-id-aqui









Paso 5: Crear el cliente de la API de Hashnode



Crea el archivo src/lib/hashnode.ts:




import { GraphQLClient, gql } from 'graphql-request';

const endpoint = 'https://gql.hashnode.com';
const client = new GraphQLClient(endpoint);

export interface Post {
id: string;
title: string;
brief: string;
slug: string;
coverImage?: {
url: string;
};
content: {
markdown: string;
};
publishedAt: string;
tags?: Array<{
name: string;
slug: string;
}>;
author: {
name: string;
profilePicture?: string;
};
}

const GET_POSTS = gql`
query GetPosts($host: String!, $first: Int!) {
publication(host: $host) {
posts(first: $first) {
edges {
node {
id
title
brief
slug
coverImage {
url
}
publishedAt
tags {
name
slug
}
author {
name
profilePicture
}
}
}
}
}
}
`
;

const GET_POST = gql`
query GetPost($host: String!, $slug: String!) {
publication(host: $host) {
post(slug: $slug) {
id
title
brief
slug
coverImage {
url
}
content {
markdown
}
publishedAt
tags {
name
slug
}
author {
name
profilePicture
}
}
}
}
`
;

export async function getPosts(host: string, first = 20): Promise<Post[]> {
const data: any = await client.request(GET_POSTS, { host, first });
return data.publication.posts.edges.map((edge: any) => edge.node);
}

export async function getPost(host: string, slug: string): Promise<Post | null> {
const data: any = await client.request(GET_POST, { host, slug });
return data.publication.post;
}









Paso 6: Crear la página principal



Edita src/pages/index.astro:




---
import { getPosts } from '../lib/hashnode';

const publicationHost = 'tu-blog.hashnode.dev'; // Reemplaza con tu host de Hashnode
const posts = await getPosts(publicationHost, 10);
---

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi Blog</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
background: #f5f5f5;
}

.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}

header {
text-align: center;
margin-bottom: 3rem;
}

h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
color: #2563eb;
}

.posts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}

.post-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: transform 0.2s;
text-decoration: none;
color: inherit;
display: block;
}

.post-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}

.post-image {
width: 100%;
height: 200px;
object-fit: cover;
}

.post-content {
padding: 1.5rem;
}

.post-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
color: #1e293b;
}

.post-brief {
color: #64748b;
margin-bottom: 1rem;
}

.post-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
color: #94a3b8;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Mi Blog</h1>
<p>Artículos técnicos y tutoriales</p>
</header>

<div class="posts-grid">
{posts.map(post => (
<a href={`/post/${post.slug}`} class="post-card">
{post.coverImage && (
<img
src={post.coverImage.url}
alt={post.title}
class="post-image"
/>
)}
<div class="post-content">
<h2 class="post-title">{post.title}</h2>
<p class="post-brief">{post.brief}</p>
<div class="post-meta">
<span>{post.author.name}</span>
<span>{new Date(post.publishedAt).toLocaleDateString('es-ES')}</span>
</div>
</div>
</a>
))}
</div>
</div>
</body>
</html>









Paso 7: Crear la página de artículo individual



Crea src/pages/post/[slug].astro:




---
import { getPost, getPosts } from '../../lib/hashnode';
import { marked } from 'marked';

const { slug } = Astro.params;
const publicationHost = 'tu-blog.hashnode.dev'; // Reemplaza con tu host

if (!slug) {
return Astro.redirect('/');
}

const post = await getPost(publicationHost, slug);

if (!post) {
return Astro.redirect('/');
}

const htmlContent = marked(post.content.markdown);

export async function getStaticPaths() {
const publicationHost = 'tu-blog.hashnode.dev';
const posts = await getPosts(publicationHost, 50);

return posts.map(post => ({
params: { slug: post.slug }
}));
}
---

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{post.title}</title>
<meta name="description" content={post.brief}>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
background: #f5f5f5;
}

.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}

.back-link {
display: inline-block;
margin-bottom: 2rem;
color: #2563eb;
text-decoration: none;
}

.back-link:hover {
text-decoration: underline;
}

article {
background: white;
border-radius: 8px;
padding: 3rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.cover-image {
width: 100%;
height: 400px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 2rem;
}

h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: #1e293b;
}

.meta {
color: #64748b;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e2e8f0;
}

.content {
font-size: 1.125rem;
line-height: 1.8;
}

.content h2 {
margin-top: 2rem;
margin-bottom: 1rem;
color: #1e293b;
}

.content h3 {
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: #334155;
}

.content p {
margin-bottom: 1rem;
}

.content pre {
background: #1e293b;
color: #e2e8f0;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
margin-bottom: 1rem;
}

.content code {
background: #f1f5f9;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}

.content pre code {
background: none;
padding: 0;
}

.content a {
color: #2563eb;
text-decoration: none;
}

.content a:hover {
text-decoration: underline;
}

.content ul, .content ol {
margin-bottom: 1rem;
padding-left: 2rem;
}

.content li {
margin-bottom: 0.5rem;
}
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">← Volver al inicio</a>

<article>
{post.coverImage && (
<img
src={post.coverImage.url}
alt={post.title}
class="cover-image"
/>
)}

<h1>{post.title}</h1>

<div class="meta">
<p>Por {post.author.name} • {new Date(post.publishedAt).toLocaleDateString('es-ES', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}</p>
</div>

<div class="content" set:html={htmlContent} />
</article>
</div>
</body>
</html>






Instala marked para procesar Markdown:




npm install marked









Paso 8: Configurar Astro para producción



Edita astro.config.mjs:




import { defineConfig } from 'astro/config';

export default defineConfig({
output: 'static',
build: {
inlineStylesheets: 'auto'
}
});









Paso 9: Desplegar en Vercel






Opción 1: Desde la interfaz de Vercel




  1. Sube tu código a GitHub

  2. Ve a Vercel

  3. Haz clic en "Add New Project"

  4. Importa tu repositorio de GitHub

  5. Vercel detectará automáticamente que es un proyecto Astro

  6. Añade las variables de entorno:



    • HASHNODE_PUBLICATION_ID: Tu ID de publicación



  7. Haz clic en "Deploy"






Opción 2: Desde la CLI de Vercel






npm install -g vercel
vercel login
vercel






Sigue las instrucciones en pantalla. En la configuración, añade tus variables de entorno.






Paso 10: Configurar redepliegue automático



Para que tu sitio se actualice automáticamente cuando publiques en Hashnode:




  1. En Vercel, ve a tu proyecto → Settings → Git

  2. Copia el "Deploy Hook URL"

  3. En Hashnode, ve a tu blog → Settings → Webhooks

  4. Añade el Deploy Hook URL de Vercel

  5. Selecciona el evento "post.published"



Ahora, cada vez que publiques un artículo en Hashnode, Vercel reconstruirá tu sitio automáticamente.






Optimizaciones adicionales






Añadir regeneración incremental



Edita astro.config.mjs:




export default defineConfig({
output: 'hybrid',
adapter: vercel({
edgeMiddleware: true
})
});






Instala el adaptador:




npm install @astrojs/vercel









Caché de datos



Crea src/lib/cache.ts:




const cache = new Map();
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutos

export function getCached<T>(key: string): T | null {
const cached = cache.get(key);
if (!cached) return null;

if (Date.now() - cached.timestamp > CACHE_DURATION) {
cache.delete(key);
return null;
}

return cached.data;
}

export function setCache<T>(key: string, data: T): void {
cache.set(key, {
data,
timestamp: Date.now()
});
}









Añadir sitemap






npm install @astrojs/sitemap






Actualiza astro.config.mjs:




import sitemap from '@astrojs/sitemap';

export default defineConfig({
site: 'https://tu-sitio.vercel.app',
integrations: [sitemap()]
});









Conclusión



Ahora tienes un blog ultrarrápido que combina lo mejor de tres mundos:





  • Escribes en el excelente editor de Hashnode


  • Diseñas tu sitio con total libertad en Astro


  • Despliegas automáticamente en Vercel con CDN global



Tu flujo de trabajo es simple: escribe en Hashnode, publica, y tu sitio se actualiza automáticamente. ¡Sin preocuparte por la infraestructura!






Recursos adicionales





¿Tienes preguntas? Déjalas en los comentarios o contáctame en mis redes sociales.

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
Syntax validiert (0 Fehler)
title: Detect Exploitation - Cómo usar la API de Hashnode con Astro y desplegarlo en Vercel
id: ba5e4436-e389-41fd-8027-85ba18598e8d
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-26
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-26"
        description = "YARA Signature for "
    strings:
        $str = "Cómo usar la API de Hashnode c" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("Cmo usar la API de Hashnode con Astro y ")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*Cmo usar la API de Hashnode con Astro y *"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "Cmo usar la API de Hashnode con Astro y "
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Cómo usar la API de Hashnode con Astro y.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Cómo usar la API de Hashnode con Astro y desplegarlo en Vercel

Thematisch verwandte Begriffe: Cómo, usar, Hashnode, Astro · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100620 | Capgo CLI (npm package @capgo/cli) through 7.98.2 is affected by an ove…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag