[Netlify](https://www.netlify.com/) is an web host that specializes in hosting static files. That makes it ideal for hosting a developer blog, a brochure site, or even just a [one-off joke](https://bellriots.netlify.com). It even has built-in support for [Hugo](/tags/hugo/). But Netlify also has various solutions for dynamic hosting, and their ["Functions"](https://docs.netlify.com/functions/overview/) service turns out to be a very easy way to host a Go web application, often for free. In this post, I will walk through [a demo repo](https://github.com/earthboundkid/netlify-go-function-demo) I've made that shows how to do this. [Netlify's documentation](https://docs.netlify.com) is pretty good, so I definitely recommend to consult them for more in-depth information, but here I would like to walk through a basic scenario for why you might want a Go backend service and how to set it up. You can see the final version of all of the code [on Github](https://github.com/earthboundkid/netlify-go-function-demo) and [a live demo on Netlify](https://go-function-demo.netlify.com). - - - - Let's say you have a static HTML webpage, but you would like to have a dynamically populated news box on the page. For example, I have made webpages that were themselves static but [pulled in related links](https://github.com/baltimore-sun-data/voter-guide-2018/blob/master/layouts/index.html#L73) from a sister site using JavaScript. This is a good way to have a long cache time for your page without sacrificing the ability to include some other content. I have used services for this before like [Yahoo Query Language](https://developer.yahoo.com/yql/guide/users-overview.html)---which was deprecated and stopped working---and [rss2json.com](https://rss2json.com/)---which worked fine itself but got cut off by [an anti-GDPR EU block](https://web.archive.org/web/20180525235055/https://www.bloomberg.com/news/articles/2018-05-25/blocking-500-million-users-is-easier-than-complying-with-gdpr) of the feed I was requesting. Ideally, we could host this ourselves but without all the hassle entailed by deploying a scraper that periodically dumps the links into a database. For the sake of example, let's use [Hacker News's](https://news.ycombinator.com/) RSS feed, which is at `https://news.ycombinator.com/rss`. Unfortunately, this RSS feed does not have an `Access-Control-Allow-Origin: *` header, so it is not possible to fetch it via JavaScript and display it from another website due to [cross-origin resource sharing rules](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing). In addition, RSS is a form of XML, and handling XML is a bit verbose, so it would be nice to have a [JSON Feed](https://jsonfeed.org) of the content instead of RSS. Our goal then is to have a backend fetch the RSS feed we need and return a JSON Feed from the same origin as our website. Go makes it easy to write a service to turn a URL with XML in it into JSON, and as luck would have it, I have [already written a module](https://pkg.go.dev/github.com/carlmjohnson/feed2json?tab=doc#Handler) that uses the standard library http.Handler interface which will do this for us. So, let's write a quick Go application that uses the feed2json module to create an HTTP server: ```go package main import ( "flag" "fmt" "log" "net/http" "github.com/carlmjohnson/feed2json" ) func main() { port := flag.Int("port", -1, "specify a port") flag.Parse() http.Handle("/api/feed", feed2json.Handler( feed2json.StaticURLInjector("https://news.ycombinator.com/rss"), nil, nil, nil)) log.Fatal(http.ListenAndServe(":"+*port, nil)) } ``` If we run this from the command line with `go run myfile.go -port 8000`, the Go standard flag package will parse `-port 8000` for us and start a server that we call open at `http://localhost:800/api/feed`. When we look at the docs for Netlify, we find our first challenge is that they use an [AWS Lambda](https://expeditedsecurity.com/aws-in-plain-english/lambda/), rather than a normal HTTP service, so we'll either need to rewrite our service to use Lambda or find an adapter. It is my belief that it's better to avoid tightly coupling your architecture to a particular cloud provider in ways that make it difficult to ever migrate off of a platform, so we don't want to write more AWS-specific code than necessary. Fortunately, adapters between AWS Lambda and the Go standard http package already exist. They take the JSON objects that AWS Lambda functions consume and emit and adapt them into normal Go http.Handler calls. [Gateway](https://github.com/earthboundkid/gateway) (which I [forked from Apex](https://github.com/apex/gateway)) is easy to use. They also make it easy to try out our code in local development without spinning up an [AWS Lambda simulator](https://aws.amazon.com/serverless/developer-tools/) of some kind. ```go package main import ( "flag" "fmt" "log" "net/http" "github.com/apex/gateway" "github.com/carlmjohnson/feed2json" ) func main() { port := flag.Int("port", -1, "specify a port to use http rather than AWS Lambda") flag.Parse() listener := gateway.ListenAndServe portStr := "n/a" if *port != -1 { portStr = fmt.Sprintf(":%d", *port) listener = http.ListenAndServe http.Handle("/", http.FileServer(http.Dir("./public"))) } http.Handle("/api/feed", feed2json.Handler( feed2json.StaticURLInjector("https://news.ycombinator.com/rss"), nil, nil, nil)) log.Fatal(listener(portStr, nil)) } ``` With this version of the code, if we run `go run main.go -port 8000`, the server will start on `http://localhost:8000`, but if we omit a port, it will start in AWS Lambda mode. It also runs a file server in HTTP mode, so we can preview the static files that we're working on in our browser as we're developing. Next, let's add a static webpage with some basic JavaScript to display the feed: ```html