Sparki Code Examples & Tutorials
Comprehensive examples and walkthroughs for common development tasksTable of Contents
- Getting Started
- Backend Examples (Go)
- Frontend Examples (TypeScript/React)
- Infrastructure Examples (Terraform)
- Testing Examples
- Integration Examples
- Advanced Patterns
Getting Started
Example 1: Deploy Your First Service
Goal: Deploy a simple “Hello World” API service Time: 15 minutesCopy
# 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 PostgreSQLCopy
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
Copy
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
Copy
// 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
Copy
// 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
Copy
# 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
# 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