🪞 Package Mirror
In 10 minutes, use the Packagist API client to fetch package indices, list packages, and statistics to build a lightweight "mirror view". Pure Go, no PHP required.
Why a Mirror View?
When building internal development platforms or offline mirrors, you often need "what packages exist on Packagist, what versions each has, download counts". Hand-writing HTTP + JSON parsing is verbose. The Packagist API client wraps these into methods that directly return structs.
Data Source Methods
| Purpose | Method | Return |
|---|---|---|
| Site-wide statistics | c.GetStatistics() | *StatisticsResponse (Totals.Packages/Versions/Downloads) |
| All package names | c.ListPackages() | *PackageListResponse |
| List by vendor | c.ListPackagesByVendor(vendor) | *PackageListResponse |
| List by type | c.ListPackagesByType(type) | *PackageListResponse |
| Popular packages | c.ListPopularPackages(perPage) | *PopularPackagesResponse |
| Package details | c.GetPackage(name) | *ComposerPackageInfo |
Complete Runnable Example
package main
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
type MirrorSnapshot struct {
CapturedAt time.Time `json:"captured_at"`
TotalPackages int `json:"total_packages"`
TotalVersions int `json:"total_versions"`
TotalDownloads int64 `json:"total_downloads"`
VendorPackages map[string]int `json:"vendor_packages"`
PopularTop []string `json:"popular_top"`
}
func main() {
c := client.NewComposerClient(60 * time.Second)
snap := MirrorSnapshot{CapturedAt: time.Now(), VendorPackages: map[string]int{}}
// 1️⃣ Site-wide statistics
if stats, err := c.GetStatistics(); err == nil {
snap.TotalPackages = stats.Totals.Packages
snap.TotalVersions = stats.Totals.Versions
snap.TotalDownloads = stats.Totals.Downloads
fmt.Printf("📊 Packages: %d | Versions: %d | Downloads: %d\n",
snap.TotalPackages, snap.TotalVersions, snap.TotalDownloads)
}
// 2️⃣ Count packages by vendor
for _, vendor := range []string{"symfony", "laravel", "guzzlehttp", "psr"} {
if list, err := c.ListPackagesByVendor(vendor); err == nil {
snap.VendorPackages[vendor] = len(list.PackageNames)
fmt.Printf("🏷️ %s/*: %d packages\n", vendor, len(list.PackageNames))
}
}
// 3️⃣ Popular packages Top N
if popular, err := c.ListPopularPackages(10); err == nil {
for _, p := range popular.Packages {
snap.PopularTop = append(snap.PopularTop, p.Name)
}
fmt.Printf("🌟 Top %d popular packages: %v\n", len(snap.PopularTop), snap.PopularTop)
}
// 4️⃣ Write snapshot to disk
b, _ := json.MarshalIndent(snap, "", " ")
os.WriteFile("mirror-snapshot.json", b, 0644)
fmt.Println("\n✅ Snapshot written to mirror-snapshot.json")
}Expected Output
📊 Packages: 390000 | Versions: 3200000 | Downloads: 9876543210
🏷️ symfony/*: 156 packages
🏷️ laravel/*: 24 packages
🏷️ guzzlehttp/*: 12 packages
🏷️ psr/*: 18 packages
🌟 Top 10 popular packages: [psr/log monolog/monolog symfony/console ...]
✅ Snapshot written to mirror-snapshot.jsonAdvanced: Incremental Sync & Field Pruning
c.GetPackageChanges(ctx, since)returns packages changed since timestampsince, suitable for scheduled incremental mirror sync.c.ListPackagesWithData(fields)only fetches specified fields (e.g.,name,downloads), reducing payload size.
// Incremental sync: only process packages changed in the past hour
changes, _ := c.GetPackageChanges(context.Background(), time.Now().Add(-time.Hour).Unix())
fmt.Printf("Packages changed in last hour: %d\n", len(changes.Actions))
// Slim list: only package names and download counts
slim, _ := c.ListPackagesWithData([]string{"name", "downloads"})Mirror vs Full Clone
This tutorial is a "view-level mirror" — fetching indices and statistics for dashboards/search. If you want to build an offline mirror that can actually be used for composer install, use 🏭 Satis Private Repository Builder.
Pagination & Payload Size
ListPackages() returns the full package name list, which can be tens of MB. In production, recommend fetching by vendor/type, or using ListPackagesWithData to limit fields.
Next Steps
- Combine mirror snapshot with dependency health data: 📈 Dependency Dashboard.
- Add security scanning beyond mirroring: 🔒 Building a Security Audit Pipeline.