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)

</span>
 lang="en">

     charset="UTF-8">
    Go Articles from Dev.to
     rel="stylesheet" href="/style.css">
    
        // Hybrid pagination: Client-side navigation with server-side validation
        function updatePage(delta) {
            const params = new URLSearchParams(window.location.search)
            let page = parseInt(params.get('page') || {{.Page}}
            let tag = params.get('tag') || '{{.Tag}}'

            page = Math.max(1, page + delta)
            window.location.href = `/dev-articles?tag=${tag}&page=${page}`
        }
    



     id="content">
        📰 Latest Go Articles on Dev.to

        
         method="GET" action="/dev-articles">
             type="text" name="tag" 
                   placeholder="Tag (e.g. go)" 
                   value="{{.Tag}}" />
             type="submit">Search
        

        
        {{range .Articles}}
         class="post">
             href="{{.URL}}" target="_blank">{{.Title}}
             class="description">{{.Description}}
        
        {{end}}

        
         class="pagination">
            {{if gt .Page 1}}
             onclick="updatePage(-1)">⬅️ Prev
            {{else}}
             disabled>⬅️ Prev
            {{end}}

             class="page-number">Page {{.Page}}

             onclick="updatePage(1)">Next ➡️
        
    





    Enter fullscreen mode
    


    Exit fullscreen mode
    




Template Features:

Dynamic Content Binding  



{{.Articles}} loop renders server-fetched content


{{.Tag}} and {{.Page}} maintain state between requests



Progressive Enhancement  


Search form works without JavaScript

Pagination uses minimal client-side scripting



Security Best Practices  


Auto-escaping prevents XSS vulnerabilities


target="_blank" with rel="noopener" (implicit in Go templates)



  
  
  Styling: CSS Implementation (static/style.css)

#content {
    max-width: 800px;
    margin: 0 auto;
    padding: 2rem;
    font-family: system-ui, sans-serif;
}

article.post {
    background: #fff;
    border-radius: 8px;
    padding: 1.5rem;
    margin: 1rem 0;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.pagination {
    margin-top: 2rem;
    display: flex;
    gap: 1rem;
    justify-content: center;
    align-items: center;
}

button {
    padding: 0.5rem 1rem;
    background: #3182ce;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:disabled {
    background: #a0aec0;
    cursor: not-allowed;
}

.page-number {
    font-weight: 500;
    color: #4a5568;
}



    Enter fullscreen mode
    


    Exit fullscreen mode
    




Design Philosophy:

Modern Aesthetic  


System font stack for native feel

Subtle shadows and rounded corners



Responsive Layout  


Max-width constraint for readability

Flexible spacing using rem units



Accessibility Focus  


High contrast colors

Clear button states (hover/disabled)



  
  
  Running the Application


Install Dependencies


   go mod init your-module-name
   go get gofr.dev/pkg/gofr



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Directory Structure


├── templates/
│   └── devTo.html      # HTML templates
├── static/
│   └── style.css       # Static assets
└── main.go             # Application entrypoint



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Start the Server


   go run main.go



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Access in Browser
Visit http://localhost:8000/dev-articles?tag=go&page=1


  
  
  Why GoFr Shines in Production


Built-in Observability  


Automatic request logging

Metrics endpoint at /metrics



Configuration Management


   # configs/config.yml
   services:
     dev-articles:
       url: https://dev.to/api
       timeout: 3s



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Horizontal Scaling  


Stateless architecture

Native Kubernetes support



Error Handling  


Centralized error recovery

Structured logging



  
  
  Next Steps: From Prototype to Production


Add Caching Layer


   // Redis integration example
   cached, err := ctx.Redis.Get(ctx, "articles:"+tag+":"+page)



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Implement Rate Limiting


   app.UseMiddleware(ratelimit.New(100)) // 100 requests/minute



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Add Health Checks


   app.GET("/health", func(ctx *gofr.Context) (any, error) {
       return "OK", nil
   })



    Enter fullscreen mode
    


    Exit fullscreen mode
    






Error Boundaries


   {{if .Error}}
    class="error-alert">
       ⚠️ Failed to load articles: {{.Error}}
   
   {{end}}



    Enter fullscreen mode
    


    Exit fullscreen mode
    





  
  
  Conclusion: The Power of Simplicity
This project demonstrates how Go and GoFr enable building modern web applications with:✅ Full server-side rendering
✅ Clean architecture
✅ Production-ready features
✅ Minimal dependencies  GitHub Repository:
https://github.com/coolwednesday/gofr-template-rendering-exampleReady to Go Further?  
Explore GoFr's documentation for database integration

Implement user authentication using JWT middleware

Add server-side caching for API responses

The simplicity of Go combined with GoFr's powerful abstractions makes this stack ideal for projects ranging from small internal tools to large-scale content platforms. 
  
  
  Happy coding! 🚀