Skip to main content

Sparki Code Examples & Tutorials

Comprehensive examples and walkthroughs for common development tasks

Table of Contents

  1. Getting Started
  2. Backend Examples (Go)
  3. Frontend Examples (TypeScript/React)
  4. Infrastructure Examples (Terraform)
  5. Testing Examples
  6. Integration Examples
  7. Advanced Patterns

Getting Started

Example 1: Deploy Your First Service

Goal: Deploy a simple “Hello World” API service Time: 15 minutes
# 1. Initialize environment
sparki init docker-compose

# 2. Start services
sparki start

# 3. Create new service directory
mkdir -p services/hello-world/cmd/server

# 4. Create Go main file
cat > services/hello-world/cmd/server/main.go << 'EOF'
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Response struct {
    Message string `json:"message"`
    Status  string `json:"status"`
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        response := Response{
            Message: "Hello from Sparki!",
            Status:  "ok",
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(response)
    })

    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"status": "healthy"})
    })

    log.Println("Server starting on :3000")
    if err := http.ListenAndServe(":3000", nil); err != nil {
        log.Fatal(err)
    }
}
EOF

# 5. Create Dockerfile
cat > services/hello-world/Dockerfile << 'EOF'
FROM golang:1.21 as builder
WORKDIR /app
COPY . .
RUN go build -o server ./cmd/server

FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server /server
EXPOSE 3000
CMD ["/server"]
EOF

# 6. Build and deploy
sparki deploy hello-world v1.0.0

# 7. Test it
curl http://localhost:3000
# Response: {"message":"Hello from Sparki!","status":"ok"}

Backend Examples (Go)

Example 2: Create API Endpoint with Database Access

Goal: Build endpoint that reads/writes to PostgreSQL
package main

import (
    "database/sql"
    "encoding/json"
    "net/http"

    _ "github.com/lib/pq"
)

type User struct {
    ID    int    `json:"id"`
    Email string `json:"email"`
    Name  string `json:"name"`
}

// GetUser retrieves a user by ID
func GetUser(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        id := r.URL.Query().Get("id")

        var user User
        err := db.QueryRow(
            "SELECT id, email, name FROM users WHERE id = $1",
            id,
        ).Scan(&user.ID, &user.Email, &user.Name)

        if err != nil {
            if err == sql.ErrNoRows {
                http.Error(w, "User not found", http.StatusNotFound)
                return
            }
            http.Error(w, "Database error", http.StatusInternalServerError)
            return
        }

        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(user)
    }
}

// CreateUser creates a new user
func CreateUser(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req struct {
            Email string `json:"email"`
            Name  string `json:"name"`
        }

        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
            http.Error(w, "Invalid request", http.StatusBadRequest)
            return
        }

        var userID int
        err := db.QueryRow(
            "INSERT INTO users (email, name) VALUES ($1, $2) RETURNING id",
            req.Email,
            req.Name,
        ).Scan(&userID)

        if err != nil {
            http.Error(w, "Failed to create user", http.StatusInternalServerError)
            return
        }

        user := User{ID: userID, Email: req.Email, Name: req.Name}
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusCreated)
        json.NewEncoder(w).Encode(user)
    }
}

Example 3: Implement Authentication with JWT

package auth

import (
    "fmt"
    "net/http"
    "strings"
    "time"

    "github.com/golang-jwt/jwt/v5"
)

type Claims struct {
    UserID string `json:"user_id"`
    Email  string `json:"email"`
    jwt.RegisteredClaims
}

var jwtSecret = []byte("your-secret-key-change-in-production")

// GenerateToken creates a new JWT token
func GenerateToken(userID, email string, duration time.Duration) (string, error) {
    claims := &Claims{
        UserID: userID,
        Email:  email,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtSecret)
}

// VerifyToken validates a JWT token
func VerifyToken(tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        return jwtSecret, nil
    })

    if err != nil {
        return nil, err
    }

    claims, ok := token.Claims.(*Claims)
    if !ok || !token.Valid {
        return nil, fmt.Errorf("invalid token claims")
    }

    return claims, nil
}

// AuthMiddleware extracts and validates JWT from request
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Missing authorization header", http.StatusUnauthorized)
            return
        }

        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            http.Error(w, "Invalid authorization format", http.StatusUnauthorized)
            return
        }

        claims, err := VerifyToken(parts[1])
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }

        // Store claims in request context for handler access
        r.Header.Set("X-User-ID", claims.UserID)
        r.Header.Set("X-User-Email", claims.Email)

        next(w, r)
    }
}

// Usage in main
func main() {
    http.HandleFunc("/api/protected", AuthMiddleware(protectedHandler))
}

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.Header.Get("X-User-ID")
    w.Write([]byte(fmt.Sprintf("Hello, user %s!", userID)))
}

Frontend Examples (TypeScript/React)

Example 4: React Component with API Integration

// src/components/UserProfile.tsx
import React, { useEffect, useState } from "react";
import { User } from "@/types";
import { fetchUser, updateUser } from "@/api/users";

interface UserProfileProps {
    userId: string;
}

export const UserProfile: React.FC<UserProfileProps> = ({ userId }) => {
    const [user, setUser] = useState<User | null>(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState<string | null>(null);
    const [isEditing, setIsEditing] = useState(false);
    const [formData, setFormData] = useState<Partial<User>>({});

    // Fetch user on component mount
    useEffect(() => {
        const loadUser = async () => {
            try {
                setLoading(true);
                const userData = await fetchUser(userId);
                setUser(userData);
                setFormData(userData);
            } catch (err) {
                setError(err instanceof Error ? err.message : "Failed to load user");
            } finally {
                setLoading(false);
            }
        };

        loadUser();
    }, [userId]);

    const handleSave = async () => {
        try {
            const updated = await updateUser(userId, formData);
            setUser(updated);
            setIsEditing(false);
        } catch (err) {
            setError(err instanceof Error ? err.message : "Failed to update user");
        }
    };

    if (loading) return <div>Loading...</div>;
    if (error) return <div className="error">{error}</div>;
    if (!user) return <div>User not found</div>;

    return (
        <div className="user-profile">
            <h1>{user.name}</h1>

            {isEditing ? (
                <div className="edit-form">
                    <input
                        value={formData.name || ""}
                        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
                        placeholder="Name"
                    />
                    <input
                        value={formData.email || ""}
                        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
                        placeholder="Email"
                    />
                    <button onClick={handleSave}>Save</button>
                    <button onClick={() => setIsEditing(false)}>Cancel</button>
                </div>
            ) : (
                <div className="user-info">
                    <p>Email: {user.email}</p>
                    <button onClick={() => setIsEditing(true)}>Edit</button>
                </div>
            )}
        </div>
    );
};

Example 5: API Client with Authentication

// src/api/client.ts
import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios";

class SparkiAPIClient {
    private client: AxiosInstance;
    private token: string | null = null;

    constructor(baseURL: string) {
        this.client = axios.create({
            baseURL,
            headers: {
                "Content-Type": "application/json",
            },
        });

        // Add request interceptor for authentication
        this.client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
            if (this.token) {
                config.headers.Authorization = `Bearer ${this.token}`;
            }
            return config;
        });

        // Add response interceptor for error handling
        this.client.interceptors.response.use(
            (response) => response,
            (error) => {
                if (error.response?.status === 401) {
                    // Handle unauthorized - refresh token or redirect to login
                    this.logout();
                }
                return Promise.reject(error);
            }
        );
    }

    setToken(token: string) {
        this.token = token;
    }

    logout() {
        this.token = null;
        // Redirect to login page
        window.location.href = "/login";
    }

    async login(email: string, password: string) {
        const response = await this.client.post("/auth/login", { email, password });
        this.setToken(response.data.token);
        return response.data;
    }

    async getUser(id: string) {
        return this.client.get(`/users/${id}`);
    }

    async createProject(name: string, description: string) {
        return this.client.post("/projects", { name, description });
    }
}

export const apiClient = new SparkiAPIClient(process.env.REACT_APP_API_URL || "http://localhost:3000/api");

Infrastructure Examples (Terraform)

Example 6: Deploy RDS Database

# terraform/modules/database/main.tf
resource "aws_db_subnet_group" "main" {
  name       = "sparki-db-subnet"
  subnet_ids = var.private_subnet_ids

  tags = {
    Name = "sparki-db-subnet"
  }
}

resource "aws_rds_cluster" "postgres" {
  cluster_identifier      = "sparki-postgres"
  engine                  = "aurora-postgresql"
  engine_version          = "15.2"
  database_name           = "sparki"
  master_username         = "admin"
  master_password         = random_password.db_password.result

  db_subnet_group_name            = aws_db_subnet_group.main.name
  db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.main.name

  storage_encrypted               = true
  kms_key_id                     = aws_kms_key.rds.arn

  backup_retention_period         = 30
  preferred_backup_window         = "03:00-04:00"
  preferred_maintenance_window    = "sun:04:00-sun:05:00"

  enabled_cloudwatch_logs_exports = ["postgresql"]

  tags = {
    Name = "sparki-postgres"
  }
}

resource "aws_rds_cluster_instance" "postgres" {
  count              = 2
  cluster_identifier = aws_rds_cluster.postgres.id
  instance_class     = "db.r6i.large"
  engine              = aws_rds_cluster.postgres.engine
  engine_version      = aws_rds_cluster.postgres.engine_version

  performance_insights_enabled    = true
  monitoring_interval             = 60
  monitoring_role_arn             = aws_iam_role.rds_monitoring.arn

  tags = {
    Name = "sparki-postgres-${count.index + 1}"
  }
}

resource "random_password" "db_password" {
  length  = 32
  special = true
}

resource "aws_secretsmanager_secret" "db_password" {
  name = "sparki/rds/password"
}

resource "aws_secretsmanager_secret_version" "db_password" {
  secret_id     = aws_secretsmanager_secret.db_password.id
  secret_string = random_password.db_password.result
}

Testing Examples

Example 7: Write Unit Tests in Go

// services/users/handler_test.go
package users

import (
    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestCreateUser(t *testing.T) {
    tests := []struct {
        name           string
        input          map[string]string
        expectedStatus int
        expectedError  string
    }{
        {
            name: "valid user creation",
            input: map[string]string{
                "email": "test@example.com",
                "name":  "Test User",
            },
            expectedStatus: http.StatusCreated,
        },
        {
            name:           "missing email",
            input:          map[string]string{"name": "Test User"},
            expectedStatus: http.StatusBadRequest,
            expectedError:  "email is required",
        },
        {
            name:           "invalid email",
            input:          map[string]string{"email": "invalid", "name": "Test"},
            expectedStatus: http.StatusBadRequest,
            expectedError:  "invalid email format",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            body, _ := json.Marshal(tt.input)
            req := httptest.NewRequest(http.MethodPost, "/users", bytes.NewReader(body))
            w := httptest.NewRecorder()

            handler := CreateUser(nil) // Would use mock DB in real test
            handler(w, req)

            assert.Equal(t, tt.expectedStatus, w.Code)

            if tt.expectedError != "" {
                var result map[string]string
                json.NewDecoder(w.Body).Decode(&result)
                assert.Contains(t, result["error"], tt.expectedError)
            }
        })
    }
}

func BenchmarkCreateUser(b *testing.B) {
    // Benchmark user creation performance
    input := map[string]string{
        "email": "test@example.com",
        "name":  "Test User",
    }
    body, _ := json.Marshal(input)

    for i := 0; i < b.N; i++ {
        req := httptest.NewRequest(http.MethodPost, "/users", bytes.NewReader(body))
        w := httptest.NewRecorder()
        handler := CreateUser(nil)
        handler(w, req)
    }
}

Example 8: Write E2E Tests with Playwright

// web/tests/e2e/user-profile.spec.ts
import { test, expect, Page } from "@playwright/test";

test.describe("User Profile", () => {
    let page: Page;

    test.beforeEach(async ({ page: testPage }) => {
        page = testPage;
        // Navigate to application
        await page.goto("http://localhost:3000");

        // Login if required
        await page.fill('[data-testid="email-input"]', "test@example.com");
        await page.fill('[data-testid="password-input"]', "password123");
        await page.click('[data-testid="login-button"]');
        await page.waitForNavigation();
    });

    test("should display user profile", async () => {
        // Navigate to profile page
        await page.goto("http://localhost:3000/profile");

        // Check page title
        await expect(page.locator("h1")).toContainText("Test User");

        // Check profile sections
        await expect(page.locator('[data-testid="email"]')).toContainText("test@example.com");
        await expect(page.locator('[data-testid="join-date"]')).toBeVisible();
    });

    test("should update user profile", async () => {
        await page.goto("http://localhost:3000/profile");

        // Click edit button
        await page.click('[data-testid="edit-button"]');

        // Update name
        await page.fill('[data-testid="name-input"]', "Updated Name");
        await page.fill('[data-testid="bio-input"]', "New bio");

        // Save changes
        await page.click('[data-testid="save-button"]');

        // Wait for success message
        await expect(page.locator('[data-testid="success-message"]')).toBeVisible();

        // Verify changes persisted
        await page.reload();
        await expect(page.locator("h1")).toContainText("Updated Name");
    });

    test("should handle validation errors", async () => {
        await page.goto("http://localhost:3000/profile");
        await page.click('[data-testid="edit-button"]');

        // Clear email field
        await page.fill('[data-testid="email-input"]', "");

        // Try to save
        await page.click('[data-testid="save-button"]');

        // Check error message
        await expect(page.locator('[data-testid="error-message"]')).toContainText("Email is required");
    });
});

Integration Examples

Example 9: Webhook Handler

// pkg/webhooks/handler.go
package webhooks

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "net/http"
    "time"
)

type WebhookPayload struct {
    ID        string    `json:"id"`
    Event     string    `json:"event"`
    Timestamp time.Time `json:"timestamp"`
    Data      json.RawMessage `json:"data"`
}

// VerifySignature validates webhook request came from trusted source
func VerifySignature(payload []byte, signature string, secret string) bool {
    h := hmac.New(sha256.New, []byte(secret))
    h.Write(payload)
    expectedSignature := "sha256=" + hex.EncodeToString(h.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

// HandleWebhook processes incoming webhook
func HandleWebhook(w http.ResponseWriter, r *http.Request, secret string) {
    // Read body
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Failed to read body", http.StatusBadRequest)
        return
    }

    // Verify signature
    signature := r.Header.Get("X-Signature")
    if !VerifySignature(body, signature, secret) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    // Parse payload
    var payload WebhookPayload
    if err := json.Unmarshal(body, &payload); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    // Handle based on event type
    switch payload.Event {
    case "deployment.completed":
        handleDeploymentCompleted(&payload)
    case "alert.triggered":
        handleAlertTriggered(&payload)
    default:
        // Log unknown event but return success
        println("Unknown webhook event:", payload.Event)
    }

    // Return success
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"status": "received"})
}

Advanced Patterns

Example 10: Service with Health Checks

// cmd/api/main.go
package main

import (
    "context"
    "database/sql"
    "net/http"
    "time"
)

type HealthChecker interface {
    Check(ctx context.Context) error
}

type Service struct {
    db    *sql.DB
    cache HealthChecker // Redis or similar
}

// HealthHandler returns comprehensive health status
func (s *Service) HealthHandler() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
        defer cancel()

        health := map[string]interface{}{
            "status":    "healthy",
            "timestamp": time.Now(),
            "checks": map[string]bool{
                "database": true,
                "cache":    true,
            },
        }

        // Check database
        if err := s.db.PingContext(ctx); err != nil {
            health["status"] = "degraded"
            health["checks"].(map[string]bool)["database"] = false
        }

        // Check cache
        if err := s.cache.Check(ctx); err != nil {
            health["status"] = "degraded"
            health["checks"].(map[string]bool)["cache"] = false
        }

        w.Header().Set("Content-Type", "application/json")
        if health["status"] != "healthy" {
            w.WriteHeader(http.StatusServiceUnavailable)
        }
        json.NewEncoder(w).Encode(health)
    }
}

Running Examples

# All examples assume you've started the dev environment
sparki start

# Navigate to example service
cd services/hello-world

# Run service
go run cmd/server/main.go

# In another terminal, test it
curl http://localhost:3000
curl http://localhost:3000/health

# Run tests
sparki test unit

# Run frontend example
cd web
npm install
npm start   # Runs on http://localhost:3001

Next Steps