---
title: Go on the Compute platform
summary: null
url: https://www.fastly.com/documentation/guides/compute/developer-guides/go
---


import FiddleDepsProvider from '~providers/fiddle-dependencies'

The Go tooling for the Compute platform builds Go application code into [Wasm](https://webassembly.org/) using either the standard [Go](https://go.dev) compiler or [TinyGo](https://tinygo.org). Go is a reliable and efficient language for building performant applications. It is a great SDK to get started with on the Compute platform if you're familiar with traditional server-side languages.

## Quick access

<CardGallery
  data={[
    {
      title: 'SDK reference',
      link: 'https://pkg.go.dev/github.com/fastly/compute-sdk-go',
      content: (<p>Documentation for the <code>compute-sdk-go</code> Go module.</p>)
    }, {
      title: 'Code examples',
      link: '/solutions/examples/go/',
      content: (<p>Filter all our code examples for those with implementations in Go.</p>)
    }, {
      title: 'Starter kit',
      link: '/solutions/starters/compute-starter-kit-go-default/',
      content: (<p>Start a Go project from a simple but complete example application.</p>)
    }
  ]}
/>

## Project layout

If you don't yet have a working toolchain and Compute service set up, start by [getting set up](/guides/compute/getting-started-with-compute/).

At the end of the initialization process, the current working directory will contain a file tree resembling the following:

```plain nocopy
.
├── .gitignore
├── README.md
├── fastly.toml
├── go.mod
├── go.sum
└── main.go
```

The most important file to work on is `main.go`, which contains the logic you'll run on incoming requests. If you initialized your project from the default starter template, the contents of this file will match the [one in the template's repository](https://github.com/fastly/compute-starter-kit-go-default/blob/main/main.go). The other files include:

* Module metadata: `go.mod` and `go.sum` describe the dependencies of your module, managed using [`go get`](https://pkg.go.dev/cmd/go#hdr-Add_dependencies_to_current_module_and_install_them).
* Fastly metadata: The `fastly.toml` file contains metadata required by Fastly to deploy your package to a Fastly service. It is generated by the <kbd>fastly compute init</kbd> command. [Learn more about `fastly.toml`](/reference/compute/fastly-toml/).

## Choosing a compiler

The `scripts.build` property in [fastly.toml](/reference/compute/fastly-toml) tells the [Fastly CLI](/reference/tools/cli) how to build your project. Our [starter kits](/solutions/starters/go/) ship with this property set assuming you want to use the standard Go compiler, however, you can also choose to use [TinyGo](https://tinygo.org) if you prefer. TinyGo produces smaller, faster binaries, but its support for [Reflection and other Go features](https://tinygo.org/docs/reference/lang-support/) is incomplete, so it [doesn't support all standard Go libraries yet](https://tinygo.org/docs/reference/lang-support/stdlib/).

Fastly supported TinyGo before standard Go, so if your fastly.toml file does not specify a `scripts.build`, the Fastly CLI will use TinyGo by default and emit a warning.

If using the standard Go compiler, you will likely also want to set environment variables `GOARCH=wasm` and `GOOS=wasip1`, which can be done in fastly.toml using the `env_vars` property of `scripts`:

```toml
[scripts]
    env_vars = ["GOARCH=wasm", "GOOS=wasip1"]
    build = "go build -o bin/main.wasm ."
```

The following are the typical build commands for each compiler:

* Standard Go: `go build -o bin/main.wasm .`
* TinyGo: `tinygo build -target=wasip1 -o bin/main.wasm ./`

## Main interface

The most common way to start a Compute program is to define a `main()` function that calls the `fsthttp.ServeFunc` function using the following type signature:

```go linenums
package main

import (
	"context"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

// section visible
func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
      // ...
	})
}
// section visible
```

The [fastly/compute-sdk-go](https://pkg.go.dev/github.com/fastly/compute-sdk-go) module provides the core `fsthttp.Request` and `fsthttp.ResponseWriter` types referenced here. The program will be invoked for each request that Fastly receives for a domain attached to your service, and it must return a response that can be served to the client.

## Communicating with backend servers and the Fastly cache

A `fsthttp.Request` can be forwarded to any [backend](/guides/integrations/non-fastly-services/developer-guide-backends) defined on your service. If you specify a backend hostname as part of completing the <kbd>fastly compute deploy</kbd> wizard, it will be named the same as the hostname or IP address, but with `.` replaced with `_` (e.g., `151_101_129_57`). It's a good idea to define backend names as constants:

```go no-compile-test
const BackendName = "my_backend_name"
```

And then reference them when you want to forward a request to a backend:

```go linenums
package main

import (
	"context"
	"fmt"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

// section visible
const BackendName = "my_backend_name"
// section-end visible

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
    // section visible
		resp, err := r.Send(ctx, BackendName)
		if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
    // section-end visible
	})
}
```

Requests forwarded to a backend will typically transit the Fastly cache, and the response may come from cache. For more precise or explicit control over the Fastly edge cache see [Caching content with Fastly](/guides/concepts/cache).

The Go SDK supports [dynamic backends](/guides/integrations/non-fastly-services/developer-guide-backends#dynamic-backends) created at runtime using [`RegisterDynamicBackend`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/fsthttp#RegisterDynamicBackend).

### Customizing cache behavior

<div id="advanced-caching" />

<Partial name="http-cache-go-flag" />

The readthrough cache interface can be further customized in the way it interacts with the backend, supporting use cases such as:

* making modifications to a request only when it is forwarded to a backend
* reading and modifying response status code and headers, and adjusting cache controls
* transforming the body of the response that is stored into the cache

See [Customizing cache interaction with the backend](/guides/concepts/cache#customizing-cache-interaction-with-the-backend) for details.

> **HINT:** Check out the [Advanced caching starter kit for Go](/solutions/starters/compute-starter-kit-go-advanced-caching/), which includes some working examples of this feature and can be used as a starting point for your application.

## Composing requests and responses

In addition to the request passed into `fsthttp.ServeFunc` and responses returned from `fsthttp.Request.Send`, requests and responses can also be constructed. This is useful if you want to make an arbitrary API call that is not derived from the client request, or if you want to make a response to the client without making any backend fetch at all.

The [`fsthttp.NewRequest`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/fsthttp#NewRequest) constructor can be used to customize the request:

```go linenums
package main

import (
	"context"
	"fmt"
	"io"
	"log"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

// BackendName is the name of our service backend.
const BackendName = "httpbin"

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, _ *fsthttp.Request) {
    // section visible
		// A different URL to the incoming request.
		url := "https://httpbin.org/headers"

		req, err := fsthttp.NewRequest("GET", url, nil)
		if err != nil {
			log.Printf("%s: create request: %v", url, err)
			return
		}

		req.Header.Add("foo", "bar")

		resp, err := req.Send(ctx, BackendName)
    // section-end visible
		if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

The [`fsthttp.Response`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/fsthttp#Response) struct can be used to customize the response:

```go linenums
package main

import (
	"context"
	"io"
	"strings"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, _ *fsthttp.Request) {
    // section visible
		h := fsthttp.NewHeader()
		h.Add("foo", "bar")

		resp := fsthttp.Response{
			Header:     h,
			StatusCode: fsthttp.StatusOK,
			Body:       io.NopCloser(strings.NewReader("Hello, world!")),
		}
    // section-end visible

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}
```

## Parsing and transforming responses

Requests and responses in the Compute platform are streams, which allows large payloads to move through your service without buffering or running out of memory. Conversely, running methods such as [`io.ReadAll`](https://pkg.go.dev/io@go1.21#ReadAll) on a `fsthttp.Response.Body` will force the stream to be consumed entirely into memory. This can be appropriate where a response is known to be small or needs to be complete to be parsable.

This example will read a backend response into memory, replace every occurrence of "cat" with "dog" in the body, and then create a new body with the transformed bytes:

```go
package main

import (
	"bytes"
	"context"
	"fmt"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

// BackendName is the name of our service backend.
const BackendName = "httpbin"

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		// Modify the path to something that will return cat related info.
		r.URL.Path = "/response-headers"
		qs := r.URL.Query()
		qs.Add("cat1", "foo")
		qs.Add("cat2", "bar")
		qs.Add("cat3", "baz")
		r.URL.RawQuery = qs.Encode()

		fmt.Printf("%+v\n", r.URL.String())

		// This requires your service to be configured with a backenAd
		// named "httpbin" and pointing to "https://httpbin.org".
		resp, err := r.Send(ctx, BackendName)
		if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}

		// section visible
		// Consume all response body data into memory.
		bs, err := io.ReadAll(resp.Body)
		if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}

		// Replace all references to "cat" with "dog".
		bs = bytes.ReplaceAll(bs, []byte("cat"), []byte("dog"))

		// Reassign the updated body content
		resp.Body = io.NopCloser(bytes.NewReader(bs))
		// section-end visible

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
		return
	})
}
```

### Compression

Fastly can compress and decompress content automatically, and it is often easier to use these features than to try to perform compression or decompression within your Go code. Learn more about [compression with Fastly](/guides/concepts/compression).

## Using edge data

Fastly allows you to configure various forms of data stores to your services, both for dynamic configuration and for storing data at the edge. The Go SDK exposes the [`configstore`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/configstore), [`kvstore`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/kvstore), and [`secretstore`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/secretstore) packages to allow access to these APIs.

All edge data resources are account-level, [service-linked resources](/reference/api/services/resource), allowing a single store to be accessed from multiple Fastly services.

## Logging

The [`rtlog`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/rtlog) package provides a standardized interface for sending logs to [Fastly real-time logging](/guides/integrations/non-fastly-services/developer-guide-logging/), which can be attached to many third-party logging providers. Before adding logging code to your Compute program, set up your log endpoint using the CLI, API, or web interface. Log endpoints are referenced in your code by name:

```go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/fastly/compute-sdk-go/fsthttp"
	// section visible
	"github.com/fastly/compute-sdk-go/rtlog"
	// section-end visible
)

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		// section visible
		fmt.Fprintln(os.Stdout, "os.Stdout can be streamed via `fastly logs tail`")
		fmt.Fprintln(os.Stderr, "os.Stderr can be streamed via `fastly logs tail`")

		endpoint := rtlog.Open("my-logging-endpoint")
		fmt.Fprintln(endpoint, "Real-time logging is available via `package rtlog`")

		mw := io.MultiWriter(os.Stdout, endpoint, w)
		fmt.Fprintln(mw, "Mix-and-match destinations with helpers like io.MultiWriter")

		fmt.Fprintln(mw, "Several environment variables are defined by default...")
		for _, key := range []string{
			"FASTLY_CACHE_GENERATION",
			"FASTLY_CUSTOMER_ID",
			"FASTLY_HOSTNAME",
			"FASTLY_POP",
			"FASTLY_REGION",
			"FASTLY_SERVICE_ID",
			"FASTLY_SERVICE_VERSION",
			"FASTLY_TRACE_ID",
		} {
			fmt.Fprintf(mw, "%s=%s\n", key, os.Getenv(key))
		}

		prefix := fmt.Sprintf("%s | %s | ", os.Getenv("FASTLY_SERVICE_VERSION"), r.RemoteAddr)
		logger := log.New(os.Stdout, prefix, log.LstdFlags|log.LUTC)
		logger.Printf("It can be useful to create a logger with request-specific metadata built in")
		// section-end visible
	})
}
```

## Using dependencies

Any Go packages that can be compiled to WebAssembly by your chosen compiler (Standard Go or TinyGo) can be imported in a Compute Go project.

Our [Fiddle](https://fiddle.fastly.dev) tool allows the use of a subset of packages that we have tested and confirmed will work with the Compute platform. These include the packages most commonly used in Fastly projects and some that can fill the gaps where language features are missing:

<FiddleDepsProvider language='go' render={nodes => (
  <!-- ColumnsList component: 
    {nodes.map((node) => (
      <li key={node.name+node.version}><a href={`https://pkg.go.dev/${node.name}@v${node.version}`}>{node.name}</a> (v{node.version})</li>
    ))}
   -->
)} />

This is a tiny fraction of the modules which will work on the Compute platform, but these are the most commonly useful modules when building applications.

## Testing and debugging

Logging is the main mechanism to debug Compute programs. Log output from live services can be monitored via live log tailing. The local test server and Fastly Fiddle display all log output automatically. See [Testing & debugging](/guides/compute/developer-guides/testing) for more information about choosing an environment in which to test your program.

Most common logging requirements involve HTTP requests and responses. It's important to do this in a way that doesn't affect the main program logic, since consuming a response body can only be done once. The section [Parsing and transforming responses](#parsing-and-transforming-responses) demonstrates how you can read the response body, transform the content, then reassign the updated content so it may be read again using [`io.NopCloser`](https://pkg.go.dev/io#NopCloser) before being written to the client.

### Unit testing

You may choose to write unit tests for small, independent pieces of your Go code intended for the Compute platform. However, Compute programs heavily depend on and interact with Fastly features and your own systems.

You can use `go test -tags nofastlyhostcalls` or `tinygo test -tags nofastlyhostcalls` to run unit tests for code that imports the `fastly/compute-sdk-go` package, as long as they don't do any I/O using those modules. Integration tests that do I/O can be run inside a local test server using the Fastly CLI running `fastly compute serve`.

Functions should ideally have a signature that makes it easy to test. Avoid accepting concrete Fastly types in favour of more primitive types or use an interface. This allows the test environment to easily define a mock implementation.

## Optimizations

If using TinyGo, the compiler provides various build options that can help optimize your compiled Wasm binary. These options are set on the `tinygo build` command using flags, for example:

| Option                                                                      | Result                       | Trade-off                                                    |
|-----------------------------------------------------------------------------|------------------------------|--------------------------------------------------------------|
| [`-no-debug`](https://tinygo.org/docs/reference/usage/misc-options/)        | Reduced size of Wasm binary  | No line numbers will be present in panic tracebacks          |
| [`-opt=2`](https://tinygo.org/docs/reference/usage/important-options/)      | Improved runtime performance | Slightly larger Wasm binary, and less informative tracebacks |
| [`-gc=leaking`](https://tinygo.org/docs/reference/usage/important-options/) | Improved runtime performance | No automated garbage collection                              |

The standard Go compiler has no optimization flags that are relevant to most Fastly projects.
