### Install compute-sdk-go Source: https://context7.com/fastly/compute-sdk-go/llms.txt Use go get to install the compute-sdk-go package. ```bash go get github.com/fastly/compute-sdk-go ``` -------------------------------- ### Go build configuration for fastly.toml Source: https://context7.com/fastly/compute-sdk-go/llms.txt Configure the build script in fastly.toml to compile Go applications for WebAssembly using GOARCH=wasm GOOS=wasip1. ```toml [scripts] build = "GOARCH=wasm GOOS=wasip1 go build -o bin/main.wasm ." ``` -------------------------------- ### Handle multiple requests with fsthttp.ServeMany Source: https://context7.com/fastly/compute-sdk-go/llms.txt Use fsthttp.ServeMany to allow a Compute sandbox to serve multiple requests per execution, reducing cold-start overhead. Configure exit conditions via ServeManyOptions. ```go package main import ( "context" "fmt" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeMany(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { fmt.Fprintf(w, "Handled by reused sandbox: %s\n", r.URL.Path) }, &fsthttp.ServeManyOptions{ MaxRequests: 100, MaxLifetime: 30 * time.Second, NextTimeout: 5 * time.Second, }) } ``` -------------------------------- ### Switching Build Commands for TinyGo and Go Source: https://github.com/fastly/compute-sdk-go/blob/main/README.md Configure the build command in `fastly.toml` to switch between TinyGo and Go toolchains. Adjust the command based on your project's specific needs. ```toml [scripts] build = "tinygo build -target=wasi -o bin/main.wasm ." ``` ```toml [scripts] build = "GOARCH=wasm GOOS=wasip1 go build -o bin/main.wasm ." ``` -------------------------------- ### TinyGo build configuration for fastly.toml Source: https://context7.com/fastly/compute-sdk-go/llms.txt Configure the build script in fastly.toml to compile Go applications for WebAssembly using TinyGo with the wasip1 target. ```toml [scripts] build = "tinygo build -target=wasip1 -o bin/main.wasm ." ``` -------------------------------- ### Identify device from User-Agent string Source: https://context7.com/fastly/compute-sdk-go/llms.txt Use the `device.Lookup` function to parse a User-Agent string and identify device properties. Handles bots, mobile, tablet, and desktop devices. ```go package main import ( "context" "fmt" "github.com/fastly/compute-sdk-go/device" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { ua := r.Header.Get("User-Agent") d, err := device.Lookup(ua) if err != nil { // Device not identified; serve generic response fmt.Fprintln(w, "Unknown device") return } if d.UserAgentIsBot() { w.WriteHeader(fsthttp.StatusForbidden) fmt.Fprintln(w, "Bots not allowed") return } switch { case d.IsMobile(): fmt.Fprintf(w, "Mobile: %s %s (%s)\n", d.Brand(), d.Model(), d.UserAgentName()) case d.IsTablet(): fmt.Fprintf(w, "Tablet: %s %s\n", d.Brand(), d.Model()) case d.IsDesktop(): fmt.Fprintf(w, "Desktop browser: %s %s.%s\n", d.UserAgentName(), d.UserAgentMajor(), d.UserAgentMinor()) default: fmt.Fprintf(w, "Device: %s (hwtype: %s)\n", d.Name(), d.HWType()) } }) } ``` -------------------------------- ### Read-only config store access with Go Source: https://context7.com/fastly/compute-sdk-go/llms.txt Opens a Fastly Config Store and retrieves values. Handles store not found and key not found errors. Checks for maintenance mode before proceeding. ```go package main import ( "context" "errors" "fmt" "github.com/fastly/compute-sdk-go/configstore" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { store, err := configstore.Open("app-config") if errors.Is(err, configstore.ErrStoreNotFound) { fsthttp.Error(w, "config store not found", fsthttp.StatusServiceUnavailable) return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } // Check existence before reading if ok, _ := store.Has("maintenance-mode"); ok { w.WriteHeader(fsthttp.StatusServiceUnavailable) fmt.Fprintln(w, "Service under maintenance") return } apiURL, err := store.Get("api-base-url") if errors.Is(err, configstore.ErrKeyNotFound) { apiURL = "https://api.default.example.com" } else if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } fmt.Fprintf(w, "Routing to: %s%s\n", apiURL, r.URL.Path) }) } ``` -------------------------------- ### Register Dynamic Backend with fsthttp.RegisterDynamicBackend Source: https://context7.com/fastly/compute-sdk-go/llms.txt Dynamically registers a new backend during request handling. Supports TLS configuration, timeouts, and connection pooling. Use when routing to origins not known at deploy time. ```go package main import ( "context" "io" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { opts := fsthttp.NewBackendOptions(). UseSSL(true). CertHostname("api.example.com"). ConnectTimeout(2 * time.Second). FirstByteTimeout(10 * time.Second) backend, err := fsthttp.RegisterDynamicBackend("dynamic-api", "api.example.com:443", opts) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway) return } req, _ := fsthttp.NewRequest("GET", "https://api.example.com/status", nil) req.CacheOptions.Pass = true resp, err := req.Send(ctx, backend.Name()) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway) return } defer resp.Body.Close() w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }) } ``` -------------------------------- ### Logging with Standard Output Source: https://github.com/fastly/compute-sdk-go/blob/main/README.md Log messages to standard output using `fmt.Printf`. This can be useful for debugging or tracking request information. ```go fmt.Printf("request received: %s\n", r.URL.String()) ``` -------------------------------- ### Register a request handler with fsthttp.ServeFunc Source: https://context7.com/fastly/compute-sdk-go/llms.txt Use fsthttp.ServeFunc to wrap a plain function as a Handler for serving a single incoming client request per execution. ```go package main import ( "context" "fmt" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(fsthttp.StatusOK) fmt.Fprintf(w, "Hello from edge! Client IP: %s\n", r.RemoteAddr) }) } ``` -------------------------------- ### kvstore Source: https://context7.com/fastly/compute-sdk-go/llms.txt Provides access to Fastly KV Store for durable, globally distributed key-value storage. Supports reading, writing, deleting, and listing keys. Large values should be streamed. ```APIDOC ## `kvstore` — Fastly KV Store: read, write, delete, and list keys Provides durable, globally distributed key-value storage. Keys and values can be up to platform limits; large values should be streamed rather than loaded via `.String()`. ```go package main import ( "context" "errors" "fmt" "io" "strings" "time" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/kvstore" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { store, err := kvstore.Open("my-kv-store") if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } switch r.Method { case "GET": entry, err := store.Lookup("config/feature-flags") if errors.Is(err, kvstore.ErrKeyNotFound) { fsthttp.Error(w, "not found", fsthttp.StatusNotFound) return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } fmt.Fprintf(w, "value: %s\n", entry.String()) case "PUT": body, _ := io.ReadAll(r.Body) err := store.InsertWithConfig("config/feature-flags", strings.NewReader(string(body)), &kvstore.InsertConfig{ Mode: kvstore.InsertModeOverwrite, TTLSec: uint32((24 * time.Hour).Seconds()), }) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } w.WriteHeader(fsthttp.StatusNoContent) case "DELETE": if err := store.Delete("config/feature-flags"); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } w.WriteHeader(fsthttp.StatusNoContent) case "LIST": iter := store.List(&kvstore.ListConfig{ Prefix: "config/", Limit: 50, Mode: kvstore.ListConsistencyStrong, }) for iter.Next() { for _, key := range iter.Page().Data { fmt.Fprintln(w, key) } } if err := iter.Err(); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) } } }) } ``` ``` -------------------------------- ### Real-time Logging with Fastly Compute SDK Source: https://context7.com/fastly/compute-sdk-go/llms.txt Opens a named real-time logging endpoint and returns an io.Writer. Each Write call creates a log event. Compatible with standard Go logging functions. Requires the log endpoint name to be configured in your Fastly service. ```go package main import ( "context" "fmt" "io" "log" "os" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/rtlog" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { // Open a named log endpoint configured in your Fastly service logEndpoint := rtlog.Open("my-datadog-endpoint") // Write to multiple destinations simultaneously sink := io.MultiWriter(os.Stdout, logEndpoint) logger := log.New(sink, fmt.Sprintf("[%s] ", os.Getenv("FASTLY_POP")), log.LstdFlags|log.LUTC) logger.Printf("Request: %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr) // ... handle request ... w.WriteHeader(fsthttp.StatusOK) fmt.Fprintln(w, "OK") logger.Printf("Response: 200 OK") }) } ``` -------------------------------- ### Runtime Introspection with Fastly Compute SDK Source: https://context7.com/fastly/compute-sdk-go/llms.txt Exposes Compute runtime metrics: GetVCPUTime returns vCPU milliseconds consumed, and GetHeapMiB returns current dynamic memory usage. Useful for performance profiling and monitoring. Includes a simulated work delay. ```go package main import ( "context" "fmt" "time" "github.com/fastly/compute-sdk-go/compute" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { startCPU, err := compute.GetVCPUTime() if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } // Simulate some work time.Sleep(1 * time.Millisecond) endCPU, _ := compute.GetVCPUTime() heapMiB, _ := compute.GetHeapMiB() w.Header().Set("Content-Type", "text/plain") fmt.Fprintf(w, "vCPU time used: %s\n", endCPU-startCPU) fmt.Fprintf(w, "Heap usage: %d MiB\n", heapMiB) }) } ``` -------------------------------- ### fsthttp.ServeFunc — Register a request handler function Source: https://context7.com/fastly/compute-sdk-go/llms.txt This function is the entry point for a Compute application. It wraps a plain `func(ctx, w, r)` as a `Handler` and serves exactly one incoming client request per execution. ```APIDOC ## fsthttp.ServeFunc — Register a request handler function ### Description Entry point for the Compute application. Wraps a plain `func(ctx, w, r)` as a `Handler` and starts serving exactly one incoming client request per execution. ### Example ```go package main import ( "context" "fmt" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(fsthttp.StatusOK) fmt.Fprintf(w, "Hello from edge! Client IP: %s\n", r.RemoteAddr) }) } ``` ``` -------------------------------- ### Create and Send Outbound HTTP Request (Pass Cache) Source: https://context7.com/fastly/compute-sdk-go/llms.txt Constructs an outgoing request and dispatches it to a named backend, bypassing the cache. Ensure the backend is preconfigured. ```go package main import ( "context" "io" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { // Forward the incoming request to a backend, bypass cache r.URL.Scheme = "https" r.URL.Host = "api.example.com" r.Header.Set("Host", "api.example.com") r.CacheOptions.Pass = true resp, err := r.Send(ctx, "my-origin-backend") if err != nil { fsthttp.Error(w, "bad gateway: "+err.Error(), fsthttp.StatusBadGateway) return } defer resp.Body.Close() w.Header().Reset(resp.Header) w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }) } ``` -------------------------------- ### Enabling Readthrough Cache with TinyGo Source: https://github.com/fastly/compute-sdk-go/blob/main/README.md Enable custom cache behavior by adding the `-tags=fsthttp_guest_cache` flag to your TinyGo build command in `fastly.toml`. This is an opt-in feature. ```toml [scripts] build = "tinygo build -target=wasip1 -tags=fsthttp_guest_cache -o bin/main.wasm ." ``` -------------------------------- ### fsthttp.ServeMany — Handle multiple requests in a single instance Source: https://context7.com/fastly/compute-sdk-go/llms.txt This function allows a Compute sandbox to serve more than one request per execution, reducing cold-start overhead. Exit conditions (max requests, max lifetime, next-request timeout) are configurable via `ServeManyOptions`. ```APIDOC ## fsthttp.ServeMany — Handle multiple requests in a single instance ### Description Allows a Compute sandbox to serve more than one request per execution, reducing cold-start overhead. Exit conditions (max requests, max lifetime, next-request timeout) are configurable via `ServeManyOptions`. ### Example ```go package main import ( "context" "fmt" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeMany(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { fmt.Fprintf(w, "Handled by reused sandbox: %s\n", r.URL.Path) }, &fsthttp.ServeManyOptions{ MaxRequests: 100, MaxLifetime: 30 * time.Second, NextTimeout: 5 * time.Second, }) } ``` ``` -------------------------------- ### Adapt net/http Handlers with fsthttp.Adapt Source: https://context7.com/fastly/compute-sdk-go/llms.txt Wraps standard Go `http.Handler` types for use as `fsthttp.Handler`. Enables reuse of existing Go HTTP middleware and handlers with Fastly Compute. ```go package main import ( "net/http" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("Hello from standard net/http handler!")) }) mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pong")) }) fsthttp.Serve(fsthttp.Adapt(mux)) } ``` -------------------------------- ### Register a Handler interface with fsthttp.Serve Source: https://context7.com/fastly/compute-sdk-go/llms.txt Implement the fsthttp.Handler interface and use fsthttp.Serve to enable composable middleware patterns for handling requests. ```go package main import ( "context" "fmt" "log" "os" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) type loggingMiddleware struct{ next fsthttp.Handler } func (mw *loggingMiddleware) ServeHTTP(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { start := time.Now() mw.next.ServeHTTP(ctx, w, r) log.Printf("%s %s %s — %s", r.Method, r.URL, r.RemoteAddr, time.Since(start)) } type appHandler struct{ pop string } func (h *appHandler) ServeHTTP(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { fmt.Fprintf(w, "Hello from %s!\n", h.pop) } func main() { var h fsthttp.Handler = &appHandler{pop: os.Getenv("FASTLY_POP")} h = &loggingMiddleware{next: h} fsthttp.Serve(h) } ``` -------------------------------- ### fsthttp.RegisterDynamicBackend Source: https://context7.com/fastly/compute-sdk-go/llms.txt Registers a new backend dynamically during request handling. This allows routing to origins not known at deploy time and supports full TLS configuration, timeouts, and connection pooling. ```APIDOC ## `fsthttp.RegisterDynamicBackend` — Create backends at runtime Registers a new backend dynamically during request handling, enabling routing to origins not known at deploy time. Supports full TLS configuration, timeouts, and connection pooling. ```go package main import ( "context" "io" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { opts := fsthttp.NewBackendOptions(). UseSSL(true). CertHostname("api.example.com"). ConnectTimeout(2 * time.Second). FirstByteTimeout(10 * time.Second) backend, err := fsthttp.RegisterDynamicBackend("dynamic-api", "api.example.com:443", opts) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway) return } req, _ := fsthttp.NewRequest("GET", "https://api.example.com/status", nil) req.CacheOptions.Pass = true resp, err := req.Send(ctx, backend.Name()) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway) return } defer resp.Body.Close() w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }) } ``` ``` -------------------------------- ### Manage Cache with Core Cache API and Transactions Source: https://context7.com/fastly/compute-sdk-go/llms.txt Utilize `cache/core` for high-performance caching with request collapsing and streaming. `NewTransaction` manages concurrent lookups, and `InsertAndStreamBack` writes to cache while streaming to the client. ```go package main import ( "context" "crypto/sha256" "errors" "fmt" "io" "time" "github.com/fastly/compute-sdk-go/cache/core" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/purge" ) func cacheKey(path string) []byte { h := sha256.Sum256([]byte(path)) return h[:] } func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { key := cacheKey(r.URL.Path) switch r.Method { case "GET": tx, err := core.NewTransaction(key, core.LookupOptions{}) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } defer tx.Close() found, err := tx.Found() if errors.Is(err, core.ErrNotFound) { fsthttp.Error(w, "not found", fsthttp.StatusNotFound) return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } defer found.Body.Close() w.Header().Set("Content-Type", "text/plain") w.Header().Set("X-Cache-Age", fmt.Sprintf("%.0f", found.Age.Seconds())) io.Copy(w, found.Body) case "POST": body, _ := io.ReadAll(r.Body) tx, err := core.NewTransaction(key, core.LookupOptions{}) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } defer tx.Close() if !tx.MustInsert() { fsthttp.Error(w, "already exists", fsthttp.StatusConflict) return } insertBody, found, err := tx.InsertAndStreamBack(core.WriteOptions{ TTL: 10 * time.Minute, Length: uint64(len(body)), SurrogateKeys: []string{"content-" + r.URL.Path[1:]}, UserMetadata: []byte(`{"source":"edge"}`), }) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } defer found.Body.Close() insertBody.Write(body) insertBody.Close() w.WriteHeader(fsthttp.StatusCreated) io.Copy(w, found.Body) case "DELETE": // Purge by surrogate key if err := purge.PurgeSurrogateKey("content-"+r.URL.Path[1:], purge.PurgeOptions{Soft: false}); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } w.WriteHeader(fsthttp.StatusAccepted) } }) } ``` -------------------------------- ### fsthttp.Adapt Source: https://context7.com/fastly/compute-sdk-go/llms.txt Wraps any standard `net/http` Handler for use as an `fsthttp.Handler`. This enables the reuse of existing Go HTTP middleware and handlers with Fastly Compute. ```APIDOC ## `fsthttp.Adapt` — Use standard `net/http` handlers `Adapt` wraps any `http.Handler` for use as an `fsthttp.Handler`, enabling reuse of existing Go HTTP middleware and handlers with Fastly Compute. ```go package main import ( "net/http" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("Hello from standard net/http handler!")) }) mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pong")) }) fsthttp.Serve(fsthttp.Adapt(mux)) } ``` ``` -------------------------------- ### fsthttp.Serve — Register a Handler interface Source: https://context7.com/fastly/compute-sdk-go/llms.txt This function serves one incoming request by calling `h.ServeHTTP`. Use this variant when implementing the `fsthttp.Handler` interface directly, which enables composable middleware patterns. ```APIDOC ## fsthttp.Serve — Register a Handler interface ### Description Serves one incoming request by calling `h.ServeHTTP`. Use this variant when implementing the `fsthttp.Handler` interface directly, which enables composable middleware patterns. ### Example ```go package main import ( "context" "fmt" "log" "os" "time" "github.com/fastly/compute-sdk-go/fsthttp" ) type loggingMiddleware struct{ next fsthttp.Handler } func (mw *loggingMiddleware) ServeHTTP(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { start := time.Now() mw.next.ServeHTTP(ctx, w, r) log.Printf("%s %s %s — %s", r.Method, r.URL, r.RemoteAddr, time.Since(start)) } type appHandler struct{ pop string } func (h *appHandler) ServeHTTP(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { fmt.Fprintf(w, "Hello from %s!\n", h.pop) } func main() { var h fsthttp.Handler = &appHandler{pop: os.Getenv("FASTLY_POP")} h = &loggingMiddleware{next: h} fsthttp.Serve(h) } ``` ``` -------------------------------- ### acl.Open and acl.Lookup Source: https://context7.com/fastly/compute-sdk-go/llms.txt Opens a named Fastly ACL and looks up an IP address to find the matching CIDR prefix and its associated action. Returns `ErrNoContent` when the IP has no matching entry. ```APIDOC ## acl.Open and acl.Lookup ### Description Opens a named Fastly ACL and looks up an IP address to find the matching CIDR prefix and its associated action (e.g., `"allow"`, `"block"`). Returns `ErrNoContent` when the IP has no matching entry. ### `acl.Open` Method #### Description Opens a named Fastly ACL. #### Parameters - **name** (string) - Required - The name of the ACL to open. #### Response - **ACLHandle** (object) - A handle to the opened ACL. - **error** - An error if the ACL cannot be opened or is not found. ### `aclHandle.Lookup` Method #### Description Looks up an IP address in the opened ACL. #### Parameters - **ip** (net.IP) - Required - The IP address to look up. #### Response - **LookupResult** (object) - The result of the lookup. - **Prefix** (string) - The matching CIDR prefix. - **Action** (string) - The action associated with the matching prefix (e.g., `"allow"`, `"block"`). - **error** - An error if the IP is not found in the ACL (`acl.ErrNoContent`) or another error occurs. ``` -------------------------------- ### Implement Edge Rate Limiting with erl Source: https://context7.com/fastly/compute-sdk-go/llms.txt Use `erl.RateLimiter` to combine a `RateCounter` and `PenaltyBox` for per-client request rate limiting. `CheckRate` atomically checks and enforces the rate policy. ```go package main import ( "context" "fmt" "time" "github.com/fastly/compute-sdk-go/erl" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { rc := erl.OpenRateCounter("api-rate-counter") pb := erl.OpenPenaltyBox("api-penalty-box") rl := erl.NewRateLimiter(rc, pb) policy := &erl.Policy{ RateWindow: erl.RateWindow10s, MaxRate: 100, // max 100 req/s over 10s window PenaltyBoxDuration: 5 * time.Minute, } fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { clientKey := r.RemoteAddr // use IP as rate limit key blocked, err := rl.CheckRate(clientKey, 1, policy) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } if blocked { w.Header().Set("Retry-After", "300") w.WriteHeader(fsthttp.StatusTooManyRequests) fmt.Fprintln(w, "Rate limit exceeded. Try again later.") return } // Check current rate for diagnostics rate, _ := rc.LookupRate(clientKey, erl.RateWindow10s) w.Header().Set("X-RateLimit-Current", fmt.Sprintf("%d", rate)) fmt.Fprintf(w, "Request served. Current rate: %d/s\n", rate) }) } ``` -------------------------------- ### rtlog.Open Source: https://context7.com/fastly/compute-sdk-go/llms.txt Opens a named real-time logging endpoint configured in your Fastly service and returns an io.Writer. Each Write call produces a single log event. ```APIDOC ## `rtlog.Open` — Real-time logging endpoints Opens a named Fastly real-time logging endpoint (configured in your Fastly service) and returns an `io.Writer`. Each `Write` call produces a single log event. Compatible with `log.New`, `fmt.Fprintln`, and `io.MultiWriter`. ### Method Signature ```go func Open(name string) io.Writer ``` ### Parameters - **name** (string): The name of the real-time logging endpoint as configured in your Fastly service. ### Returns - **io.Writer**: An `io.Writer` interface that can be used to write log events to the specified endpoint. ### Example Usage ```go import ( "io" "log" "os" "github.com/fastly/compute-sdk-go/rtlog" ) // Open a named log endpoint logEndpoint := rtlog.Open("my-datadog-endpoint") // Use it with io.MultiWriter and log.New sink := io.MultiWriter(os.Stdout, logEndpoint) logger := log.New(sink, "[my-app] ", log.LstdFlags|log.LUTC) logger.Println("This is a log message.") ``` ``` -------------------------------- ### Lookup IP address in Fastly ACL Source: https://context7.com/fastly/compute-sdk-go/llms.txt Open a named ACL using `acl.Open` and perform an IP lookup with `aclHandle.Lookup`. Handles cases where the ACL is not found or the IP has no matching entry. ```go package main import ( "context" "errors" "fmt" "net" "github.com/fastly/compute-sdk-go/acl" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { aclHandle, err := acl.Open("ip-blocklist") if errors.Is(err, acl.ErrNotFound) { fsthttp.Error(w, "ACL not configured", fsthttp.StatusServiceUnavailable) return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } ip := net.ParseIP(r.RemoteAddr) result, err := aclHandle.Lookup(ip) if errors.Is(err, acl.ErrNoContent) { // No ACL match → allow through fmt.Fprintln(w, "Access granted (no ACL match)") return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } if result.Action == "block" { w.WriteHeader(fsthttp.StatusForbidden) fmt.Fprintf(w, "Blocked: matched prefix %s\n", result.Prefix) return } fmt.Fprintf(w, "ACL match: prefix=%s action=%s\n", result.Prefix, result.Action) }) } ``` -------------------------------- ### compute.GetVCPUTime and compute.GetHeapMiB Source: https://context7.com/fastly/compute-sdk-go/llms.txt Exposes Compute runtime metrics: GetVCPUTime returns the vCPU milliseconds consumed so far, and GetHeapMiB returns the current dynamic memory usage. ```APIDOC ## `compute` — Runtime introspection (vCPU time and heap usage) Exposes Compute runtime metrics: `GetVCPUTime` returns the vCPU milliseconds consumed so far (useful for relative performance profiling), and `GetHeapMiB` returns the current dynamic memory usage rounded up to the nearest MiB. ### Method Signatures ```go func GetVCPUTime() (time.Duration, error) func GetHeapMiB() (int64, error) ``` ### Returns - **GetVCPUTime**: Returns the vCPU time consumed as a `time.Duration` and an error if retrieval fails. - **GetHeapMiB**: Returns the current heap memory usage in MiB as an `int64` and an error if retrieval fails. ### Example Usage ```go import ( "fmt" "github.com/fastly/compute-sdk-go/compute" ) startCPU, err := compute.GetVCPUTime() // ... handle error ... // Simulate some work endCPU, _ := compute.GetVCPUTime() heapMiB, _ := compute.GetHeapMiB() fmt.Printf("vCPU time used: %s\n", endCPU-startCPU) fmt.Printf("Heap usage: %d MiB\n", heapMiB) ``` ``` -------------------------------- ### Parallel Outbound Requests with Goroutines Source: https://context7.com/fastly/compute-sdk-go/llms.txt Leverages goroutines to perform multiple outbound HTTP requests concurrently to different backends. This pattern is effective for fan-out scenarios. ```go package main import ( "context" "fmt" "io" "sync" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { urls := []string{ "https://api.example.com/data/a", "https://api.example.com/data/b", "https://api.example.com/data/c", } type result struct { url string body []byte err error } results := make(chan result, len(urls)) var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func(url string) { defer wg.Done() req, _ := fsthttp.NewRequest("GET", url, nil) req.CacheOptions.Pass = true resp, err := req.Send(ctx, "api-backend") if err != nil { results <- result{url: url, err: err} return } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) results <- result{url: url, body: b, err: err} }(url) } wg.Wait() close(results) w.WriteHeader(fsthttp.StatusOK) for res := range results { if res.err != nil { fmt.Fprintf(w, "ERROR %s: %v\n", res.url, res.err) } else { fmt.Fprintf(w, "%s: %s\n", res.url, res.body) } } }) } ``` -------------------------------- ### Fastly KV Store Operations Source: https://context7.com/fastly/compute-sdk-go/llms.txt Performs read, write, delete, and list operations on a Fastly KV Store. Supports durable, globally distributed key-value storage. Large values should be streamed. ```go package main import ( "context" "errors" "fmt" "io" "strings" "time" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/kvstore" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { store, err := kvstore.Open("my-kv-store") if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } switch r.Method { case "GET": entry, err := store.Lookup("config/feature-flags") if errors.Is(err, kvstore.ErrKeyNotFound) { fsthttp.Error(w, "not found", fsthttp.StatusNotFound) return } if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } fmt.Fprintf(w, "value: %s\n", entry.String()) case "PUT": body, _ := io.ReadAll(r.Body) err := store.InsertWithConfig("config/feature-flags", strings.NewReader(string(body)), &kvstore.InsertConfig{ Mode: kvstore.InsertModeOverwrite, TTLSec: uint32((24 * time.Hour).Seconds()), }) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } w.WriteHeader(fsthttp.StatusNoContent) case "DELETE": if err := store.Delete("config/feature-flags"); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } w.WriteHeader(fsthttp.StatusNoContent) case "LIST": iter := store.List(&kvstore.ListConfig{ Prefix: "config/", Limit: 50, Mode: kvstore.ListConsistencyStrong, }) for iter.Next() { for _, key := range iter.Page().Data { fmt.Fprintln(w, key) } } if err := iter.Err(); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) } } }) } ``` -------------------------------- ### Create and Send Outbound HTTP Requests Source: https://context7.com/fastly/compute-sdk-go/llms.txt Constructs an outgoing request using `NewRequest` and dispatches it to a named preconfigured backend using `Send`. The response is read-through cached by default, but this can be bypassed by setting `CacheOptions.Pass = true`. ```APIDOC ## `fsthttp.NewRequest` / `Request.Send` — Create and send outbound HTTP requests `NewRequest` constructs an outgoing request. `Send` dispatches it to a named preconfigured backend and returns the response. By default, the response is read-through cached; set `CacheOptions.Pass = true` to bypass. ### Request Example ```go package main import ( "context" "io" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { // Forward the incoming request to a backend, bypass cache r.URL.Scheme = "https" r.URL.Host = "api.example.com" r.Header.Set("Host", "api.example.com") r.CacheOptions.Pass = true resp, err := r.Send(ctx, "my-origin-backend") if err != nil { fsthttp.Error(w, "bad gateway: "+err.Error(), fsthttp.StatusBadGateway) return } defer resp.Body.Close() w.Header().Reset(resp.Header) w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }) } ``` ``` -------------------------------- ### Purge Surrogate Key Cache with Fastly Compute SDK Source: https://context7.com/fastly/compute-sdk-go/llms.txt Purges cached objects tagged with a surrogate key. Use the 'soft' option for a soft purge, marking items as stale instead of immediately invalidating them. Requires the surrogate key as a query parameter. ```go package main import ( "context" "fmt" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/purge" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { if r.Method != "POST" { w.WriteHeader(fsthttp.StatusMethodNotAllowed) return } surrogateKey := r.URL.Query().Get("key") if surrogateKey == "" { fsthttp.Error(w, "key query param required", fsthttp.StatusBadRequest) return } soft := r.URL.Query().Get("soft") == "true" if err := purge.PurgeSurrogateKey(surrogateKey, purge.PurgeOptions{Soft: soft}); err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } purgeType := "hard" if soft { purgeType = "soft" } fmt.Fprintf(w, "Purged (%s) surrogate key: %s\n", purgeType, surrogateKey) }) } ``` -------------------------------- ### Perform bot detection on request Source: https://context7.com/fastly/compute-sdk-go/llms.txt Utilize `r.BotDetection()` to check if a request is from a known bot using Fastly's infrastructure. Returns `BotInfo` with detection status, name, and category. ```go package main import ( "context" "fmt" "log" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { bot, err := r.BotDetection() if err != nil { // Detection may be unavailable — fail open log.Println("bot detection unavailable:", err) } if bot.Analyzed && bot.Detected { w.WriteHeader(fsthttp.StatusForbidden) fmt.Fprintf(w, "Bot detected: %s (%s)\n", bot.Name, bot.Category) return } w.WriteHeader(fsthttp.StatusOK) fmt.Fprintln(w, "Welcome, human!") }) } ``` -------------------------------- ### Geolocation lookup by IP address with Go Source: https://context7.com/fastly/compute-sdk-go/llms.txt Performs a geolocation lookup for a given IP address. Returns an empty struct if no data is available for the IP. Handles invalid IP addresses and lookup errors. ```go package main import ( "context" "fmt" "net" "github.com/fastly/compute-sdk-go/fsthttp" "github.com/fastly/compute-sdk-go/geo" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { ip := net.ParseIP(r.RemoteAddr) if ip == nil { fsthttp.Error(w, "invalid IP", fsthttp.StatusBadRequest) return } g, err := geo.Lookup(ip) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } fmt.Fprintf(w, "IP: %s\n", r.RemoteAddr) fmt.Fprintf(w, "Country: %s (%s)\n", g.CountryName, g.CountryCode) fmt.Fprintf(w, "City: %s\n", g.City) fmt.Fprintf(w, "Region: %s\n", g.Region) fmt.Fprintf(w, "ASN: %d (%s)\n", g.AsNumber, g.AsName) fmt.Fprintf(w, "Coords: %.4f, %.4f\n", g.Latitude, g.Longitude) fmt.Fprintf(w, "Conn: %s / %s\n", g.ConnType, g.ConnSpeed) }) } ``` -------------------------------- ### device.Lookup Source: https://context7.com/fastly/compute-sdk-go/llms.txt Identifies device type, brand, model, and user-agent properties from a User-Agent header string. This function is useful for serving device-appropriate content or redirecting mobile users. ```APIDOC ## device.Lookup ### Description Identifies device type, brand, model, and user-agent properties from the `User-Agent` header string. Useful for serving device-appropriate content or redirecting mobile users. ### Function Signature `device.Lookup(userAgent string) (Device, error)` ### Parameters #### Path Parameters - **userAgent** (string) - Required - The User-Agent header string to parse. ### Response #### Success Response - **Device** (object) - An object containing device properties. - **Brand()** (string) - Returns the device brand. - **Model()** (string) - Returns the device model. - **UserAgentName()** (string) - Returns the name of the user agent. - **UserAgentMajor()** (string) - Returns the major version of the user agent. - **UserAgentMinor()** (string) - Returns the minor version of the user agent. - **IsMobile()** (bool) - Returns true if the device is a mobile phone. - **IsTablet()** (bool) - Returns true if the device is a tablet. - **IsDesktop()** (bool) - Returns true if the device is a desktop computer. - **Name()** (string) - Returns the general device name. - **HWType()** (string) - Returns the hardware type of the device. - **UserAgentIsBot()** (bool) - Returns true if the user agent is identified as a bot. #### Error Response - **error** - An error if the User-Agent cannot be parsed or identified. ``` -------------------------------- ### fsthttp.Request.BotDetection Source: https://context7.com/fastly/compute-sdk-go/llms.txt Inspects the request using Fastly's bot-detection infrastructure and returns a `BotInfo` value describing whether a bot was detected and its category and name. ```APIDOC ## fsthttp.Request.BotDetection ### Description Inspects the request using Fastly's bot-detection infrastructure and returns a `BotInfo` value describing whether a bot was detected and its category and name. ### Method `BotDetection() (BotInfo, error)` ### Parameters This method does not take any parameters. ### Response #### Success Response - **BotInfo** (object) - An object containing bot detection information. - **Analyzed** (bool) - Indicates if bot analysis was performed. - **Detected** (bool) - Indicates if a bot was detected. - **Name** (string) - The name of the detected bot. - **Category** (string) - The category of the detected bot. #### Error Response - **error** - An error if bot detection is unavailable. ``` -------------------------------- ### Control Outbound Request Caching with CacheOptions Source: https://context7.com/fastly/compute-sdk-go/llms.txt Configures caching behavior for outbound requests, including TTL, stale-while-revalidate, and surrogate keys. Use `fsthttp.NewRequest` to create the request before setting `CacheOptions`. ```go package main import ( "context" "io" "github.com/fastly/compute-sdk-go/fsthttp" ) func main() { fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) { req, err := fsthttp.NewRequest("GET", "https://cdn.example.com/image.png", nil) if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError) return } req.CacheOptions = fsthttp.CacheOptions{ TTL: 3600, // cache fresh for 1 hour StaleWhileRevalidate: 600, // serve stale for 10 min while refreshing SurrogateKey: "images", // enables surrogate-key purging } resp, err := req.Send(ctx, "cdn-backend") if err != nil { fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway) return } defer resp.Body.Close() w.Header().Reset(resp.Header) w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }) } ```