---
title: Migrate from VCL
summary: null
url: https://www.fastly.com/documentation/guides/compute/developer-guides/migrate
---

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

This guide is intended as a quick-start for an initial migration to the Compute platform, not to be a comprehensive library of code examples. If the pattern you are trying to migrate 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)
	})
}
```


