Building a Server-Rendered Dev.to scraper : HTML Templating, API Integration, and Pagination

Click to watch the companion tutorial video Why Server-Side Rendering (SSR) Matters In today's web development landscape dominated by client-side frameworks, server-side rendering offers compelling advantages: Instant Page Loads: HTML arrives ready-to-display SEO Superiority: Search engines easily crawl content Simplified Architecture: Single codebase manages both API and UI Resource Efficiency: Reduced client-side JavaScript overhead GoFr, an opinionated Go framework, makes SSR implementation remarkably straightforward. Let's build a Dev.to article reader that demonstrates these principles in action. Project Setup: Laying the Foundation 1. Initializing the GoFr Application (main.go) package main import ( "encoding/json" "gofr.dev/pkg/gofr" "strconv" ) type Article struct { Title string `json:"title"` URL string `json:"url"` Description string `json:"description"` } type PageData struct { Articles []Article Tag string Page int } func main() { // Initialize GoFr application app := gofr.New() // Configure Dev.to API service app.AddHTTPService("dev-articles", "https://dev.to/api") // Register route handler app.GET("/dev-articles", FetchArticles) // Serve static files (CSS, templates) app.AddStaticFiles("/", "./static") // Start the server app.Run() } Key Components Explained: Service Configuration:AddHTTPService creates a pre-configured HTTP client for the Dev.to API, handling connection pooling and timeouts automatically. Route Handling:The GET method associates the /dev-articles endpoint with our FetchArticles handler. Static Assets:AddStaticFiles serves CSS and templates from the static directory, essential for our SSR approach. Core Business Logic: The Article Handler 2. Implementing the FetchArticles Handler func FetchArticles(ctx *gofr.Context) (any, error) { // Get search parameters with defaults tag := ctx.Param("tag") if tag == "" { tag = "go" // Default to Go articles } page, _ := strconv.Atoi(ctx.Param("page")) if page

Apr 30, 2025 - 17:02
 0
Building a Server-Rendered Dev.to scraper : HTML Templating, API Integration, and Pagination

Watch Tutorial Video

Click to watch the companion tutorial video

Why Server-Side Rendering (SSR) Matters

In today's web development landscape dominated by client-side frameworks, server-side rendering offers compelling advantages:

  1. Instant Page Loads: HTML arrives ready-to-display
  2. SEO Superiority: Search engines easily crawl content
  3. Simplified Architecture: Single codebase manages both API and UI
  4. Resource Efficiency: Reduced client-side JavaScript overhead

GoFr, an opinionated Go framework, makes SSR implementation remarkably straightforward. Let's build a Dev.to article reader that demonstrates these principles in action.

Project Setup: Laying the Foundation

1. Initializing the GoFr Application (main.go)

package main

import (
    "encoding/json"
    "gofr.dev/pkg/gofr"
    "strconv"
)

type Article struct {
    Title       string `json:"title"`
    URL         string `json:"url"`
    Description string `json:"description"`
}

type PageData struct {
    Articles []Article
    Tag      string
    Page     int
}

func main() {
    // Initialize GoFr application
    app := gofr.New()

    // Configure Dev.to API service
    app.AddHTTPService("dev-articles", "https://dev.to/api")

    // Register route handler
    app.GET("/dev-articles", FetchArticles)

    // Serve static files (CSS, templates)
    app.AddStaticFiles("/", "./static")

    // Start the server
    app.Run()
}

Key Components Explained:

  • Service Configuration:AddHTTPService creates a pre-configured HTTP client for the Dev.to API, handling connection pooling and timeouts automatically.

  • Route Handling:The GET method associates the /dev-articles endpoint with our FetchArticles handler.

  • Static Assets:AddStaticFiles serves CSS and templates from the static directory, essential for our SSR approach.

Core Business Logic: The Article Handler

2. Implementing the FetchArticles Handler

func FetchArticles(ctx *gofr.Context) (any, error) {
    // Get search parameters with defaults
    tag := ctx.Param("tag")
    if tag == "" {
        tag = "go" // Default to Go articles
    }

    page, _ := strconv.Atoi(ctx.Param("page"))
    if page < 1 {
        page = 1 // Ensure minimum page number
    }

    // Fetch articles from Dev.to API
    service := ctx.GetHTTPService("dev-articles")
    resp, err := service.Get(ctx, "/articles", map[string]interface{}{
        "tag":      tag,
        "page":     page,
        "per_page": 4, // Optimal for initial load
    })

    if err != nil {
        return nil, err // Handle API errors
    }
    defer resp.Body.Close()

    // Parse API response
    var articles []Article
    if err := json.NewDecoder(resp.Body).Decode(&articles); err != nil {
        return nil, err // Handle parsing errors
    }

    // Render template with data
    return gofr.Template{
        Data: PageData{articles, tag, page},
        Name: "devTo.html",
    }, nil
}

Architectural Decisions:

  1. Parameter Handling

    • Default values ensure consistent behavior
    • Type conversion guards against invalid inputs
    • per_page=4 balances content density and performance
  2. Error Handling

    • Automatic error propagation through GoFr's middleware
    • Clean separation of concerns between API and rendering
  3. Service Abstraction

    • HTTP service configuration centralized in main.go
    • Easy to swap API endpoints or add caching later

Presentation Layer: HTML Templating

3. Template Implementation (static/devTo.html)


 lang="en">

     charset="UTF-8">
    </span>Go Articles from Dev.to<span class="nt">
     rel="stylesheet" href="/style.css">
    



     id="content">