---
title: Comparing Compute and VCL
summary: null
url: https://www.fastly.com/documentation/guides/compute/developer-guides/comparing
---

If you already have a [Fastly CDN service](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl) with custom VCL but want to switch to a Compute service, all the logic you wrote in [VCL](https://www.fastly.com/documentation/reference/vcl) can be accomplished in Compute in any supported language. This page provides the equivalent Compute service code for the most common patterns we see in VCL.

> **HINT:** Switching from a VCL to Compute service is not necessarily the right thing for you. VCL is fully supported and remains the native, active standard for CDN Services.

This guide is intended as a quick-start on the Compute platform, not a comprehensive library of code examples. If the pattern you are trying to use is not here or you are looking to do something more complex, you might find a code example in our [examples library](https://www.fastly.com/documentation/solutions/examples).

For more information about each of the languages with official SDK support, see [choosing a language](https://www.fastly.com/documentation/guides/compute/getting-started-with-compute/#choose-a-language-to-use).

## Boilerplate

The examples below assume that you start with the default [starter kit](https://www.fastly.com/documentation/solutions/starters) for your chosen language when building your application. This is the default when you run <kbd>fastly compute init</kbd>.

## Naming conventions

These examples use a common set of naming conventions to draw parallels with VCL:

- `req`: For the incoming client request
- `beReq`: For a custom request built from scratch or by copying `req`
- `beResp`: For the return value of an origin fetch
- `resp`: For a custom response built from scratch or by copying a `beResp`

## Configuration

### Load configuration data from a separate file

### Vcl

```vcl
table settings {
  "section.key": "some-value"
}

declare local var.some_value STRING;
set var.some_value = table.lookup(
  settings,
  "section.key"
);
```

### Rust

```rust
// Using the config crate: https://docs.rs/config
use config::{Config, FileFormat};
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(_req: Request) -> Result {
    let config_builder = Config::builder().add_source(config::File::from_str(
        include_str!("config.toml"), // assumes the existence of src/config.toml
        FileFormat::Toml,
    ));
    let settings = config_builder.build()?;
    let some_value = settings.get_string("section.key")?;
    Ok(Response::from_body(some_value.as_str()))
}
```

### Javascript

```js title="index.js"
/// <reference types="@fastly/js-compute" />
// section visible

// section-end visible

function handler(event) {
  // section visible
  // Create a response with the body set to a value loaded from the config file.
  return new Response(responseText, {
    headers: { "Content-Type": "text/plain" }
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

```js title="config.js"
export const responseText = "hello world";
```

### Go

```go
package main

import (
  _ "embed"
	"bytes"
	"context"
	"io"

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

// section visible
//go:embed config.toml
var config []byte
// section-end visible

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, _ *fsthttp.Request) {
    // section visible
		resp := fsthttp.Response{
			StatusCode: fsthttp.StatusOK,
			// Create a response with the body set to a value loaded from the config file.
			Body:       io.NopCloser(bytes.NewReader(config)),
		}
    // section-end visible

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

### Load configuration data from a dictionary

### Vcl

```vcl
declare local var.some_value STRING;
set var.some_value = table.lookup(
  example_dictionary,
  "key_name"
);
```

### Rust

```rust
use fastly::{Error, Request, Response, ConfigStore};

#[fastly::main]
fn main(_req: Request) -> Result {
    let settings = ConfigStore::open("example_config_store");
    let some_value = match settings.get("key_name") {
      Some(value) => value,
      _ => panic!("Value not set")
    };
    Ok(Response::from_body(some_value))
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  let exampleStore = new ConfigStore("example_config_store");
  let someValue = exampleStore.get("key_name");
  // section-end visible

  return new Response(someValue);
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"fmt"

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

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, _ *fsthttp.Request) {
    // section visible
		d, err := configstore.Open("example_config_store")
    // section-end visible
		if err != nil {
			w.WriteHeader(fsthttp.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}

    // section visible
		v, err := d.Get("key_name")
    // section-end visible
		if err != nil {
			w.WriteHeader(fsthttp.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}

		w.WriteHeader(fsthttp.StatusOK)
		fmt.Fprintln(w, v)
	})
}
```

## Requests

### Add a header to a client request

### Vcl

```vcl
# constant value
set req.http.Accept-Encoding = "br";
# dynamic value
set req.http.Accept-Encoding = var.accept_encoding;
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let accept_encoding = "br";
    // constant value
    req.set_header("Accept-Encoding", "br");
    // dynamic value
    req.set_header("Accept-Encoding", accept_encoding);
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // Get the request from the client.
  let req = event.request;

  // section visible
  // Constant value.
  req.headers.set("Accept-Encoding", "example.com");

  // Dynamic value.
  let accept_encoding = "br";
  req.headers.set("Accept-Encoding", accept_encoding);
  // section-end visible

  return fetch(req, { backend: "example_backend" });
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
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) {
        // 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
        }

        acceptEncoding := "br"
        // section visible
        // constant value
        req.Header.Add("Accept-Encoding", "br")
        // dynamic value
        req.Header.Add("Accept-Encoding", acceptEncoding)
        // section-end visible

        resp, err := req.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)
    })
}
```

### Sort and sanitize a query string

### Vcl

```vcl
set req.url = querystring.filter_except(req.url,
  "a" + querystring.filtersep() +
  "b" + querystring.filtersep() +
  "c"
);
set req.url = querystring.sort(req.url);
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let mut qs: Vec<(String, String)> = req.get_query()?;
    qs.retain(|param| ["a", "b", "c"].contains(&param.0.as_str()));
    qs.sort_by(|(a, _), (b, _)| a.cmp(b));
    req.set_query(&qs)?;
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const req = event.request;
  const url = new URL(req.url);

  const ALLOWED = [ 'a', 'b', 'c' ];
  const searchEntries = url.searchParams.entries();
  const filteredEntries = searchEntries.filter(([k,v]) => ALLOWED.includes(k));

  const filteredParams = new URLSearchParams(filteredEntries);
  filteredParams.sort();
  url.search = filteredParams;

  // Create a new Request object with a sorted URL.
  const newReq = new Request(url, req);

  return fetch(newReq, { backend: "origin_0" });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// EXAMPLE: /anything/here?a=1&b=2&stripped=yes&c=3

		// Filter and sort query parameters.
		// section visible
		allowed := map[string]bool{
			"a": true,
			"b": true,
			"c": true,
		}
		// section-end visible

		fmt.Println("before", r.URL.RawQuery) // c=3&a=1&stripped=yes&b=2

		// section visible
		qs := r.URL.Query()
		for k := range qs {
			if _, ok := allowed[k]; !ok {
				qs.Del(k)
			}
		}
		r.URL.RawQuery = qs.Encode()
		// section-end visible

		fmt.Println("after", r.URL.RawQuery) // a=1&b=2&c=3

		// This requires your service to be configured with a backend
		// 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
		}

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

### Extract a query string parameter from the request

### Vcl

```vcl
declare local var.field STRING;
set var.field = subfield(req.url.qs, "paramName", "&");
```

### Rust

```rust
use fastly::{Error, Request, Response};
use std::collections::HashMap;

#[fastly::main]
fn main(req: Request) -> Result {
    // assuming a request http://example.com?paramName=someValue
    let params: HashMap = req.get_query()?;
    assert_eq!(params["paramName"], "someValue");
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const req = event.request;
  const url = new URL(req.url);

  const val = url.searchParams.get("myParam") ?? "myParam is not set";
  // section-end visible

  return new Response(val, {
    headers: { "Content-Type": "text/plain" }
  })
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"io"
	"strings"

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

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		// section visible
		param := "paramName"
		v := param + " is not set"
		if p := r.URL.Query().Get(param); p != "" {
			v = p
		}

		resp := fsthttp.Response{
			StatusCode: fsthttp.StatusOK,
			Body:       io.NopCloser(strings.NewReader(v)),
		}
		// section-end visible

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

### Remove a header from a client request

### Vcl

```vcl
unset req.http.Some-Header;
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    req.remove_header("some-header");
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  let req = event.request;

  // section visible
  req.headers.delete("some-header");
  // section-end visible

  return fetch(req, {
    backend: "example_backend",
  });
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		r.Header.Del("Some-Header")
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Modify a request URL path

### Vcl

```vcl
set req.url = "/new/path" + if(req.url.qs == "", "", "?") + req.url.qs;
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    req.set_path("/new/path");
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const req = event.request;
  const url = new URL(req.url);

  url.pathname = "/new/path";

  // Create a new Request object with an updated URL.
  const newReq = new Request(url, req);

  return fetch(newReq, {
    backend: "example_backend",
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		r.URL.Path = "/new/path"
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Check for header presence on a client request

### Vcl

```vcl
if (req.http.Some-Header) {
  # ... do something ...
}
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
    if req.contains_header("some-header") {
        // ... do something ...
    }
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;

  // section visible
  if (req.headers.has("some-header")) {
    // ... do something ...
  }
  // section-end visible

  return fetch(req, {
    backend: "example_backend",
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		if h := r.Header.Get("Some-Header"); h != "" {
			// ... do something ...
		}
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Check whether a request header value contains a substring

### Vcl

```vcl
if (std.strstr(req.http.foo, "someValue")) {
  # ... do something ...
}
```

### Rust

```rust
use fastly::http::HeaderValue;
use fastly::{Error, Request, Response};

fn header_val(header: Option<&HeaderValue>) -> &str {
    match header {
        Some(h) => h.to_str().unwrap_or(""),
        None => "",
    }
}

#[fastly::main]
fn main(req: Request) -> Result {
    if header_val(req.get_header("some-header")).contains("someValue") {
        // ... do something ...
    }
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;

  // section visible
  if (req.headers.get("some-header")?.includes("someValue")) {
    // ... do something ...
  }
  // section-end visible

  return fetch(req, {
    backend: "example_backend",
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"fmt"
	"io"
	"strings"

	"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) {
		// section visible
		if h := r.Header.Get("Some-Header"); strings.Contains(h, "someValue") {
			// ... do something ...
		}
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Extract constituent parts of a request

### Vcl

```vcl
declare local var.req_method STRING;
declare local var.req_url STRING;
declare local var.req_header STRING;
declare local var.req_protocol STRING;
set var.req_method = req.method;
set var.req_url = req.url;
set var.req_header = req.http.My-Header;
set var.req_body = req.body;
set var.req_protocol = if (fastly_info.is_h2, "2", "1.1");
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
    let method = req.get_method();
    let url = req.get_url();
    let my_header = req.get_header("my-header");
    let version = req.get_version();
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

async function handler(event) {
  // section visible
  // Get the request from the client.
  const req = event.request;

  const method = req.method;
  const url = new URL(req.url);
  const headers = req.headers;
  const body = await req.text();
  // section-end visible

  return fetch(req, {
    backend: "example_backend",
  });
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		fmt.Printf(`
                  method: %s
                  url: %s
                  headers: %v
                  protocol: %s
                `, r.Method, r.URL.String(), r.Header.Keys(), r.Proto)
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Identify a client's geolocation information

### Vcl

```vcl
declare local var.country_code STRING;
declare local var.country_name STRING;
declare local var.city STRING;
set var.country_code = client.geo.country_code;
set var.country_name = client.geo.country_name;
set var.city = client.geo.city;
```

### Rust

```rust
use fastly::geo::geo_lookup;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
    let client_ip = req.get_client_ip_addr().unwrap();
    let geo = geo_lookup(client_ip).unwrap();
    let country_code = geo.country_code();
    let country_name = geo.country_name();
    let city_name = geo.city();
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const clientGeo = event.client.geo;

  const countryCode = clientGeo.country_code;
  const countryName = clientGeo.country_name;
  const city = clientGeo.city;
  // section-end visible

  return new Response(city);
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```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) {
		// section visible
		ip := net.ParseIP(r.RemoteAddr)
		g, err := geo.Lookup(ip)
		if err != nil {
			w.WriteHeader(fsthttp.StatusInternalServerError)
			fmt.Fprintln(w, err.Error())
			return
		}

		fmt.Fprintf(w, "CountryCode:      %q\n", g.CountryCode)
		fmt.Fprintf(w, "CountryCode3:     %q\n", g.CountryCode3)
		fmt.Fprintf(w, "CountryName:      %q\n", g.CountryName)
		// section-end visible
	})
}
```

### Send data to a log endpoint

### Vcl

```vcl
log "syslog " + req.service_id + " request_logger :: " + now.sec + " " + client.ip + " " + req.url + " " + req.http.user-agent;
```

### Rust

```rust
use fastly::http::StatusCode;
use fastly::{compute_runtime, Error, Request, Response};
use std::time::{SystemTime, UNIX_EPOCH};

#[fastly::main]
fn main(req: Request) -> Result {
    log_fastly::init_simple("request_logger", log::LevelFilter::Info);

    let service_id = compute_runtime::service_id();

    let since_epoch = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();

    let client_ip = req
        .get_client_ip_addr()
        .map(|ip| ip.to_string())
        .unwrap_or_else(String::new);

    let req_url = req.get_url_str();

    let user_agent = req
        .get_header("USER_AGENT")
        .map(|header| header.to_str())
        .transpose()?
        .unwrap_or("");

    log::info!(
        "fastly_service_id: {service_id}, since_epoch: {since_epoch}, client_ip: {client_ip}, request_url: {req_url}, user_agent: {user_agent}"
    );

    Ok(Response::from_status(StatusCode::OK).with_body("Welcome to Fastly Compute"))
}
```

```toml title="Cargo.toml"
[dependencies]
  fastly = "^0.11.0"
  log-fastly = "^0.11.0"
  log = "^0.4.17"
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));

async function handleRequest(event) {
  // section visible
  const logger = new Logger("log_endpoint_name");

  let service_id = event.request.headers.get("FASTLY_SERVICE_ID");
  let since_epoch = Math.floor( new Date() / 1000 );
  let client_ip = event.client.address;
  let req_url = event.request.url;
  let user_agent = event.request.headers.get("USER_AGENT");

  logger.log("fastly_service_id: " + service_id + ", since_epoch: " + since_epoch + ", client_ip: " + client_ip + ", request_url: " + req_url + ", user_agent: " + user_agent);
  // section-end visible

  return new Response("Welcome to Fastly Compute", {
    status: 200,
  });
}
```

### Go

```go
package main

 import (
 	"context"
 	"fmt"
 	"os"
  "time"

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

 func main() {
 	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
 		// section visible
 		endpoint := rtlog.Open("request_logger")
 		msg := "fastly_service_id: %s, since_epoch: %d, client_ip: %s, request_url: %s, user_agent: %s\n"
 		fmt.Fprintf(endpoint, msg, os.Getenv("FASTLY_SERVICE_ID"), time.Now().UnixMilli(), r.RemoteAddr, r.URL, r.Header.Get("User-Agent"))
 		// section-end visible
 	})
 }
```

## Backends

### Route requests to backends based on URL path match

### Vcl

```vcl
if (req.url.path == "/") {
  set req.backend = F_origin_0;
} else if (req.url.path ~ "^/other/") {
  set req.backend = F_origin_1;
} else {
  set req.backend = F_origin_2;
}
```

### Rust

```rust
use fastly::http::Method;
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
    match (req.get_method(), req.get_path()) {
        (&Method::GET, "/") => Ok(req.send("backend_one")?),
        (&Method::GET, path) if path.starts_with("/other/") => Ok(req.send("backend_two")?),
        _ => Ok(req.send("default_backend")?),
    }
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const req = event.request;
  const url = new URL(req.url);

  let backendName = "default_backend";

  if (url.pathname === "/") {
    backendName = "backend_one";
  } else if (url.pathname.startsWith("/other/")) {
    backendName = "backend_two";
  }

  return fetch(clientRequest, {
    backend: backendName,
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"fmt"
	"io"
	"strings"

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

const (
  // section visible
	// Backend1 is a service backend pointing at httpbin.org.
	Backend1 = "httpbin"
	// Backend2 is a service backend pointing at example.org.
	Backend2 = "example"
  // section-end visible
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		// section visible
		backend := Backend1

		if r.URL.Path == "/about/" || strings.HasPrefix(r.URL.Path, "/other/") {
			backend = Backend2
		}

		resp, err := r.Send(ctx, backend)
		// 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)
	})
}
```

### Retry a request on error, using a different backend

### Vcl

```vcl
# not achievable for POST requests
# because after a restart the body of a POST request will not be preserved
sub vcl_recv {
  set req.backend = F_Host_1;
  if (req.restarts == 1) {
    set req.backend = F_Host_2;
  }
}

sub vcl_fetch {
   if (req.restarts == 0 && beresp.status >= 500 && beresp.status < 600 && (req.method == "GET" or req.method == "HEAD")) {
     restart;
   }
}
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    let body_bytes = req.take_body_bytes();

    req.set_body(body_bytes.as_slice());
    let mut beresp = req.send("backend_one")?;
    if beresp.get_status().is_server_error() {
        let mut retry_req = beresp.take_backend_request().unwrap();
        retry_req.set_body(body_bytes);
        let beresp_retry = retry_req.send("backend_two")?;
        if !beresp_retry.get_status().is_server_error() {
            return Ok(beresp_retry);
        }
    }
    Ok(beresp)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

async function handler(event) {
  // section visible
  const req = event.request;

  const url = req.url;
  const method = req.method;
  const headers = req.headers;
  const body = ['GET', 'HEAD'].includes(req.method) ? await req.arrayBuffer() : undefined;

  const beReq = new Request(url, { method, headers, body });
  let beResp = await fetch(beReq, {
    backend: "backend_one"
  });

  if (beResp.status >= 500 && beResp.status < 600) {
    const beReq2 = new Request(url, { method, headers, body });
    beResp = await fetch(beReq2, {
      backend: "backend_two"
    });
  }

  return beResp;
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"fmt"
	"io"

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

const (
  // section visible
	// Backend1 is a service backend pointing at httpbin.org.
	Backend1 = "httpbin"
	// Backend2 is a service backend pointing at example.org.
	Backend2 = "example"
  // section-end visible
)

// Example Request:
// http://127.0.0.1:7676/status/500 will return a 500 status code from httpbin.org.
// This will cause a failover request for the same path to example.org which returns a 404.
func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		// section visible
		backend := Backend1

		resp, err := r.Send(ctx, backend)
		if err != nil || is5xx(resp.StatusCode) {
			backend = Backend2
			r = r.Clone()

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

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

func is5xx(statusCode int) bool {
	return statusCode >= 500 && statusCode < 600
}
```

## Responses

### Build a response from scratch

### Vcl

```vcl
set obj.status = 200;
synthetic "Hello world";
return(deliver);
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(_req: Request) -> Result {
    let res = Response::from_body("Hello world");
    Ok(res)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;

  // section visible
  return new Response("Hello world", {
    status: 200,
    headers: { "Content-Type": "text/plain" }
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
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
		resp := fsthttp.Response{
			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)
	})
}
```

### Build an image response

### Vcl

```vcl
synthetic.base64 "R0lGODlh...=";
```

### Rust

```rust
use fastly::{mime, Error, Request, Response};

#[fastly::main]
fn main(_req: Request) -> Result {
    let res = Response::from_body(include_bytes!("fastly.jpg").as_ref())
        .with_content_type(mime::IMAGE_JPEG)
        .with_header("cache-control", "private, no-store");
    Ok(res)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

const IMAGE = includeBytes('src/fastly.jpg');

async function handleRequest(_event) {
  return new Response(IMAGE, {
    status: 200,
    headers: { 'content-type': 'image/jpg' }
  });
}

addEventListener('fetch', event => event.respondWith(handleRequest(event)));
```

### Go

```go title="main.go"
package main

import (
	"bytes"
	"context"
	_ "embed"
	"io"

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

// section visible
//go:embed fastly.jpg
var image []byte
// section-end visible

func main() {
	fsthttp.ServeFunc(func(_ context.Context, w fsthttp.ResponseWriter, _ *fsthttp.Request) {
    // section visible
		h := make(fsthttp.Header)
		h.Set("Content-Type", "image/jpeg")

		resp := fsthttp.Response{
			StatusCode: fsthttp.StatusOK,
			Header:     h,
			Body:       io.NopCloser(bytes.NewReader(image)),
		}
    // section-end visible

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

### Add a header to a response

### Vcl

```vcl
# constant value
set resp.http.Some-Header = "someValue";
# dynamic value
set resp.http.Set-Cookie = "origin-session=" + var.session + "; HttpOnly";
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result {
    // constant value
    let mut res = req.send("example_backend")?;
    res.set_header("some-header", "bar");
    // dynamic value
    let session = String::from("some-session-id");
    res.set_header("set-cookie", format!("origin-session={}; HttpOnly", session));
    Ok(res)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

async function handler(event) {
  const req = event.request;

  const session = "a_session_id_would_be_generated_here";

  // Send the downstream client request to an origin
  let beResp = await fetch(req, {
    backend: "example_backend",
  });

  // section visible
  beResp.headers.set('Some-Header', 'someValue');
  beResp.headers.set('Set-Cookie', "origin-session=" + session + "; HttpOnly");
  // section-end visible

  return beResp;
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"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) {
		// This requires your service to be configured with a backend
		// 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
		}

		session, err := genRandString(32)
		if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}

		// section visible
		resp.Header.Add("Some-Header", "example")
		resp.Header.Add("Set-Cookie", fmt.Sprintf("origin-session=%s; HttpOnly", session))
		// section-end visible

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

// genRandString returns a URL-safe, base64 encoded securely generated random string.
func genRandString(n int) (string, error) {
	b, err := genRandBytes(n)
	return base64.URLEncoding.EncodeToString(b), err
}

// genRandBytes returns securely generated random bytes.
func genRandBytes(n int) ([]byte, error) {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		return nil, err
	}
	return b, nil
}
```

## Controlling the cache

### Explicitly set a TTL

### Vcl

```vcl
set beresp.ttl = 60s;
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    // You can use individual helper methods, like `set_ttl`,
    // to modify individual cache override settings.
    req.set_ttl(60);

    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;
  // section visible
  // Create a cache override.
  const cacheOverride = new CacheOverride({ ttl: 60 });

  return fetch(req, {
    backend: "example_backend",
    cacheOverride
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		r.CacheOptions.TTL = 60
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Force a `pass`

### Vcl

```vcl
return(pass);
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    // drop all overrides and force pass
    req.set_pass(true);
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;
  // section visible
  let cacheOverride = new CacheOverride("pass");

  return fetch(req, {
    backend: "example_backend",
    cacheOverride
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		// Determine the framing headers (Content-Length/Transfer-Encoding)
		// based on the message body (default)
		r.ManualFramingMode = false

		// Make sure the response isn't cached.
		r.CacheOptions.Pass = true
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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

### Explicitly set stale-while-revalidate

### Vcl

```vcl
set beresp.stale_while_revalidate = 60s;
```

### Rust

```rust
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(mut req: Request) -> Result {
    req.set_stale_while_revalidate(60);
    Ok(req.send("example_backend")?)
}
```

### Javascript

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  const req = event.request;
  // section visible
  let cacheOverride = new CacheOverride({ swr: 60 });

  return fetch(req, {
    backend: "example_backend",
    cacheOverride
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Go

```go
package main

import (
	"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) {
		// section visible
		r.CacheOptions.StaleWhileRevalidate = 60
		// section-end visible

		// This requires your service to be configured with a backend
		// 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
		}

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


