Saltar al contenido principal
SEO Automático con TypeScript: Meta Tags y Schema.org

SEO Automático con TypeScript: Meta Tags y Schema.org

2 de junio de 2024 4 min de lectura
Compartir:

El SEO manual es propenso a errores y difícil de mantener. Después de optimizar decenas de sitios web, he desarrollado un sistema completamente automático que genera SEO perfecto usando TypeScript.

El Problema del SEO Manual

La mayoría de desarrolladores manejan SEO de forma manual y fragmentada:

<!--  SEO manual propenso a errores -->
<title>Mi Página - Sitio Web</title>
<meta name="description" content="Descripción...">
<meta property="og:title" content="Mi Página">
<meta property="og:description" content="Descripción diferente...">
<meta name="twitter:title" content="Otro título...">

Problemas comunes:

  • Inconsistencias entre meta tags
  • Duplicación de información
  • Errores en URLs y formatos
  • Schema.org incompleto o incorrecto
  • Performance no optimizada

La Solución: SEO Automático con TypeScript

Mi sistema genera todo el SEO automáticamente desde una sola fuente de verdad:

// Una sola configuración genera TODO el SEO
const seoData = {
  title: "Arquitectura Modular en Astro",
  description: "Guía completa para implementar features reutilizables",
  image: "/images/blog/architecture-cover.webp",
  type: "article",
  publishedDate: new Date("2024-06-02"),
  keywords: ["astro", "arquitectura", "typescript"]
};

//  Genera automáticamente:
// - Meta tags básicos
// - Open Graph completo  
// - Twitter Cards
// - Schema.org estructurado
// - URLs canónicas
// - Sitemap XML
// - RSS feed

Arquitectura del Sistema SEO

1. Meta Tags Engine: Generación Inteligente

// src/features/meta-tags/engine/generator.ts
export class MetaTagGenerator {
  generateCompleteTags(props: MetaTagProps): MetaTag[] {
    return [
      ...this.generateBasicTags(props),
      ...this.generateOpenGraphTags(props),
      ...this.generateTwitterTags(props),
      ...this.generateTechnicalTags(props)
    ];
  }
  
  private generateOpenGraphTags(props: MetaTagProps): MetaTag[] {
    const baseUrl = this.config.site.url;
    
    return [
      { property: 'og:title', content: props.title },
      { property: 'og:description', content: props.description },
      { property: 'og:url', content: `${baseUrl}${props.url}` },
      { property: 'og:type', content: props.type || 'website' },
      { property: 'og:image', content: this.resolveImageUrl(props.image) },
      { property: 'og:site_name', content: this.config.site.title }
    ];
  }
}

2. Schema.org Engine: Structured Data Automático

// src/features/schema/engine.ts
export class SchemaGenerator {
  generateBlogPostSchema(post: BlogPost): Schema {
    return {
      "@context": "https://schema.org",
      "@type": "BlogPosting",
      "headline": post.title,
      "description": post.description,
      "image": this.toAbsoluteUrl(post.image),
      "datePublished": post.date.toISOString(),
      "dateModified": post.modifiedDate?.toISOString() || post.date.toISOString(),
      "author": this.generatePersonSchema(),
      "publisher": this.generateOrganizationSchema(),
      "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": this.toAbsoluteUrl(post.url)
      }
    };
  }
  
  private generatePersonSchema(): PersonSchema {
    return {
      "@type": "Person",
      "name": this.config.author.name,
      "url": this.config.author.url,
      "sameAs": this.config.author.socialProfiles
    };
  }
}

3. Validation Engine: Calidad Garantizada

// src/features/meta-tags/engine/validator.ts
export class MetaTagValidator {
  validateCanonicalUrl(url: string, siteUrl: string): UrlValidationResult {
    let normalizedUrl = url;
    
    // Corregir URLs relativas
    if (!url.startsWith('http')) {
      normalizedUrl = `${siteUrl}${url.startsWith('/') ? '' : '/'}${url}`;
    }
    
    // Enforcer HTTPS
    if (normalizedUrl.startsWith('http:')) {
      normalizedUrl = normalizedUrl.replace('http:', 'https:');
    }
    
    // Remover trailing slashes inconsistentes
    if (normalizedUrl.endsWith('/') && normalizedUrl !== `${siteUrl}/`) {
      normalizedUrl = normalizedUrl.slice(0, -1);
    }
    
    return {
      isValid: this.isValidUrl(normalizedUrl),
      normalizedUrl,
      errors: this.validateUrlFormat(normalizedUrl)
    };
  }
}

Uso en Componentes Astro

Implementación Simple

---
// src/components/seo/SEOHead.astro
import { MetaTags } from '../../features/meta-tags/components';
import { AutoSchema } from '../../features/schema';
import { generateMetaTags } from '../../features/meta-tags';

interface Props {
  title: string;
  description: string;
  image?: string;
  type?: 'website' | 'article';
  publishedDate?: Date;
  keywords?: string[];
}

//  Una línea genera TODO el SEO
const seoData = generateMetaTags(Astro.props);
---

<head>
  <!-- Meta tags automáticos -->
  <MetaTags {...seoData} />
  
  <!-- Schema.org automático -->
  <AutoSchema />
  
  <!-- Canonical URL automático -->
  <link rel="canonical" href={seoData.canonicalUrl} />
</head>

Uso en Páginas

---
// src/pages/blog/[slug].astro
import SEOHead from '../../components/seo/SEOHead.astro';

const { post } = Astro.props;
---

<html>
<head>
  <SEOHead
    title={post.data.title}
    description={post.data.description}
    image={post.data.image}
    type="article"
    publishedDate={post.data.date}
    keywords={post.data.tags}
  />
</head>
<body>
  <!-- Contenido del post -->
</body>
</html>

Testing Automático de SEO

Tests de Producción

// src/__tests__/seo/production.test.ts
describe('SEO Production Tests', () => {
  test('should have proper meta tags', async () => {
    const response = await fetch('https://cappato.dev');
    const html = await response.text();
    const dom = new JSDOM(html);
    
    // Validar meta tags
    const title = dom.window.document.querySelector('title')?.textContent;
    const description = dom.window.document.querySelector('meta[name="description"]')?.getAttribute('content');
    const ogTitle = dom.window.document.querySelector('meta[property="og:title"]')?.getAttribute('content');
    
    expect(title).toBeTruthy();
    expect(description).toBeTruthy();
    expect(title).toBe(ogTitle); // Consistencia automática
  });
  
  test('should have valid Schema.org', async () => {
    const response = await fetch('https://cappato.dev');
    const html = await response.text();
    
    const schemas = extractSchemas(html);
    expect(schemas.length).toBeGreaterThan(0);
    
    schemas.forEach(schema => {
      expect(schema['@context']).toBe('https://schema.org');
      expect(schema['@type']).toBeTruthy();
    });
  });
});

Performance Testing

// src/__tests__/seo/performance.test.ts
describe('Performance SEO Tests', () => {
  test('should have fast response times', async () => {
    const start = Date.now();
    const response = await fetch('https://cappato.dev');
    const timing = Date.now() - start;
    
    expect(response.status).toBe(200);
    expect(timing).toBeLessThan(1000); // < 1 segundo
  });
  
  test('should have optimized images', async () => {
    const response = await fetch('https://cappato.dev');
    const html = await response.text();
    const dom = new JSDOM(html);
    
    const images = dom.window.document.querySelectorAll('img');
    images.forEach(img => {
      expect(img.getAttribute('alt')).toBeTruthy();
      expect(img.getAttribute('loading')).toBeTruthy();
    });
  });
});

Resultados Reales

Antes vs Después

MétricaManualAutomático
Tiempo Setup2-3 horas5 minutos
Errores15-20%0%
Consistencia60%100%
Lighthouse SEO85-90100
Schema Errors5-100

Métricas de Performance

 Bundle Size: 2.2MB total
 JavaScript: 12.3KB (4.3KB gzipped)
 SEO Score: 100/100
 Performance: 95+/100
 Schema.org: 0 errores
 Meta Tags: 100% consistencia

Automatización Completa

1. Sitemap Automático

// src/features/sitemap/engine.ts
export class SitemapGenerator {
  generateSitemap(posts: BlogPost[]): string {
    const urls = [
      this.generateStaticUrls(),
      ...posts.map(post => this.generatePostUrl(post))
    ];
    
    return this.renderXML(urls);
  }
}

2. RSS Feed Automático

// src/features/rss-feed/engine.ts
export class RSSGenerator {
  generateFeed(posts: BlogPost[]): string {
    return `<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0">
      <channel>
        <title>${this.config.site.title}</title>
        <description>${this.config.site.description}</description>
        ${posts.map(post => this.generateItem(post)).join('')}
      </channel>
    </rss>`;
  }
}

3. AI Metadata Automático

// src/features/ai-metadata/engine.ts
export class AIMetadataGenerator {
  generateAITags(content: BlogPost): AIMetadata {
    return {
      'ai:type': 'article',
      'ai:reading_time': this.calculateReadingTime(content.body),
      'ai:word_count': this.countWords(content.body),
      'ai:topics': content.tags,
      'ai:difficulty': this.assessDifficulty(content.body)
    };
  }
}

Conclusión

El SEO automático no es solo una optimización, es una transformación en cómo desarrollamos para web:

  • Zero errores humanos
  • Consistencia garantizada
  • Performance optimizada
  • Mantenimiento mínimo
  • Escalabilidad infinita

¿Estás listo para automatizar tu SEO? ¡El código está disponible y es completamente reutilizable!


Enlace copiado al portapapeles