GoでVercel Serverless Functions

[githubリポジトリ](https://github.com/mugi111/go-vercel-functions)

ホスティングサービスをNetlifyからVercelに乗り換えたついでにServerless Functionsが気になったので試してみました。

## Vercel
Next.jsの運営元であるVercel(元Zeit)のWebアプリケーションホスティングサービスです。ちょうどNetlifyとかと同じポジションですね。

## 前準備
### アカウント作成
Vercelのトップページの右上にあるSignUpボタンからGithub, GitLab, BitBucketのアカウントを使用してアカウント作成をします。
![vercelsignup.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256522/d76931ef-f9f9-9455-f5f9-390b04ce8602.png)

### Vercel CLI
npmパッケージとして提供されているVercel CLIをインストールします。
これは主にローカルからデプロイを行う際に使用します。

```bash:npm
npm i -g vercel
```

```bash:yarn
yarn global add vercel
```

インストール後

```bash
vercel login
```
を実行し、上で登録したemailアドレスの入力と確認を済ませます。

## サーバーレス関数の作成
プロジェクトフォルダの直下に`index.go`を作成します。
この`index.go`は`http.ResponseWriter`と`*http.Request`を引数として持つ関数が存在する必要があります。
まずは`Vercel Serverless Functions Go Example`というテキストを返すだけの関数を実装します。

```go:index.js
package main

import (
  "fmt"
  "net/http"
)

func Handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Vercel Serverless Functions Go Example")
}
```

## vercel.jsonの作成
```json:vercel.json
{
  "version": 2,
  "name": "go",
  "builds": [
    { "src": "*.go", "use": "@vercel/go" }
  ]
}
```

## ルーティング
日時を返すエンドポイントを作成していきます。
今回はわかりやすく下記のようにエンドポイントごとにフォルダを分けて作成します。

```
root
├ /date
│ └ index.go
└ index.go
```

### 型の作成

```go:date/index.go
type ResDate struct {
	Date string `json:"date"`
}
```

### リクエストハンドラの実装
`w.Header().Set("Content-Type", "application/json")`で`Content-Type`の指定を行います。

```go:date/index.go
func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	d := ResDate{ time.Now().Format(time.RFC850) }
	bytes, _ := json.Marshal(d)
	fmt.Fprintf(w, string(bytes))
}
```

### date/index.go

```go:date/index.go
package date

import (
	"fmt"
	"net/http"
	"time"
	"encoding/json"
)

type ResDate struct {
	Date string `json:"date"`
}

func Handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	d := ResDate{ time.Now().Format(time.RFC850) }
	bytes, _ := json.Marshal(d)
	fmt.Fprintf(w, string(bytes))
}
```

### vercel.jsonでルーティングの設定
ルーティングの設定を行うために`vercel.json`に`routes`の要素を追加します。

```json:routes
  "routes": [
    { "src": "/date", "dest": "/date" }
  ]
```

加えて、ビルド対象フォルダに先程作成した`date`フォルダ内の`go`ファイルを追加します。

```json:builds
  "builds": [
    { "src": "*.go", "use": "@vercel/go" },
    { "src": "/date/*.go", "use": "@vercel/go" }
  ]
```

```json:vercel.json
{
  "version": 2,
  "name": "go-vercel-functions-demo",
  "builds": [
    { "src": "*.go", "use": "@vercel/go" },
    { "src": "/date/*.go", "use": "@vercel/go" }
  ],
  "routes": [
    { "src": "/date", "dest": "/date" }
  ]
}
```

## デプロイ

```bash
vercel
```

これだけです。

本番デプロイをするのであれば`--prod`オプションをつけるだけです。

```bash
vercel --prod
```

2020/10/05 タイポがあったため修正しました。prob -> prod
@kimihito_ さん ご指摘ありがとうございます。

デプロイ後の以下のURLにアクセスすると
https://go-vercel-function-example.mugi111.vercel.app
一番最初に作成した関数通り`Vercel Serverless Functions Go Example`と表示されているのが確認できました。
![vercelroot.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256522/fa061da4-0005-c5ac-76ad-3f99a4b3b58c.png)

dateエンドポイントも正常に動作していることが確認できました。
https://go-vercel-function-example.mugi111.vercel.app/date
![verceldate.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256522/b73ee635-39a9-952f-ba27-765117f336f7.png)