モノレポに配置しているnext.jsアプリケーションとserverless functionを、1つのVercel Projectに同居して動かしたい

github-nippou-webというツールを作っている。その名の通り「日報」を書くためのツール。GitHubアカウントでログインし、指定した期間で自分が関与したissue, PRをMarkdownのリスト形式で列挙してくれる。
このリポジトリでは/web
にnext.jsのアプリを、/api
にGoのハンドラを置き、どちらもVercelで動かしている。
GoのハンドラをVercelで動かす、というのはVercelのServerless Functionsの機能を使っている
1つのリポジトリだが、next.jsアプリとGoのハンドラをVercelの別々のプロジェクトで動かしている。

達成したいことは、別々のプロジェクトで動かしているNext.jsアプリとGoのハンドラを一つのプロジェクトに統合できないか?というもの。
- 1リポジトリ : 1プロジェクトでシンプルになる
- ドメインが同じになるので、APIを呼び出す際も相対パスで呼び出せる
- 現在はできないため、プレビュー環境でAPIを呼び出すことができていない
- ローカル開発
vercel dev
がうまく動作しておらず、APIのデバッグがしづらい

前提となるGoのハンドラは以下
61行の単純なコードとなっている。これをVercelにデプロイし、 ドメイン/api/index
にアクセスすると関数を動作させられる。
Vercelの規約としては以下を満たす必要がある
-
/api
以下に関数ファイルを置く - Goの場合、
http.HandlerFunc
を満たす実装とする
また、モノレポとしてファイルを配置しているため Root directoryを /api
にしている

サンプルリポジトリを作ってそこでデバッグしていくことにする。
-
npx create-next-app@latest
の結果: https://github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/commit/880f7e34dbe4e6f6e56e8d38185adef44517f0d0 -
/api
を新設してサンプルのハンドラを置いてみる: https://github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/commit/35ad63ad99194263d84ff563a136514a946d0f18

vercel dev
でGoのハンドラが識別されるかを見てみる
vercel dev
Vercel CLI 32.5.0
? Set up and develop “~/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs”? [Y/n] y
? Which scope should contain your project? xxx
? Link to existing project? [y/N] n
? What’s your project’s name? debug-vercle-serverless-function-golang-with-nextjs
? In which directory is your code located? ./
Local settings detected in vercel.json:
Auto-detected Project Settings (Next.js):
- Build Command: next build
- Development Command: next dev --port $PORT
- Install Command: `yarn install`, `pnpm install`, `npm install`, or `bun install`
- Output Directory: Next.js default
? Want to modify these settings? [y/N] n
🔗 Linked to mh4gf/debug-vercle-serverless-function-golang-with-nextjs (created .vercel)
> Running Dev Command “next dev --port $PORT”
▲ Next.js 14.0.3
- Local: http://localhost:3000
> Ready! Available at http://localhost:3000
✓ Ready in 2.5s
○ Compiling / ...
✓ Compiled / in 2.2s (501 modules)
✓ Compiled in 108ms (235 modules)
プロンプトに従って進めてみたが、Auto-detectedされたのはnext.jsアプリのように見える。
Goのハンドラは /api/index.go
に置いたので、http://localhost:3000/api/index にアクセスしてみる
お!?
404になるかと思っていたが504、ハンドラとして登録はされているのか?
ログは以下のように出ている
> Ready! Available at http://localhost:3000
✓ Ready in 2.5s
○ Compiling / ...
✓ Compiled / in 2.2s (501 modules)
✓ Compiled in 108ms (235 modules)
Warning: Unknown Go version in /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/api/go.mod
go: module . listed in go.work file requires go >= 1.21.3, but go.work lists go 1.18; to update it:
go work use
go: module ../../../../api listed in go.work file requires go >= 1.21.3, but go.work lists go 1.18; to update it:
go work use
Error: Command failed: go build -ldflags -s -w -o ./vercel-dev-server-go ./...

指示された通り go work use
をやってみる
go work use
go: no go.work file found
(run 'go work init' first or specify path using GOWORK environment variable)
エラーになったので go work init
をしてみる
go work init
go work use
エラーはなさそうだ。こんな感じのファイルが生成された: https://github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/commit/6c7278b3e44c0dd2748cf3c974e63adbd45b7593
もう一度 vercel dev
を実行したが、goのdownloadが行われるようになったがまだエラーは出る
vercel dev
Vercel CLI 32.5.0
> Running Dev Command “next dev --port $PORT”
▲ Next.js 14.0.3
- Local: http://localhost:3000
> Ready! Available at http://localhost:3000
✓ Ready in 2.3s
Warning: Unknown Go version in /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/go.mod
Downloading go: https://dl.google.com/go/go1.21.1.darwin-arm64.tar.gz
go: module . listed in go.work file requires go >= 1.21.3, but go.work lists go 1.18; to update it:
go work use
Error: Command failed: go build -ldflags -s -w -o ./vercel-dev-server-go ./...

go.workに記載されているのは1.18
とエラーメッセージがあるので、試しにgo.modとgo.workに記載されているバージョンを1.18に下げてみる: https://github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/commit/d46fd102c4317c8f56edaefeb3e7fe586a84d4f1
と動いた!
他のパスも問題なく動いている

ちょっと横道に逸れるが、vercel dev
の挙動が気になるので色々読んでみる。
vercel dev --debug
でデバッグログを有効化して http://localhost:3000/api/index にアクセスする
> Ready! Available at http://localhost:3000
✓ Ready in 2.6s
> [debug] [2023-11-26T02:42:14.604Z] GET /api/index
> [debug] [2023-11-26T02:42:14.605Z] Reading `package.json` file
> [debug] [2023-11-26T02:42:14.605Z] Reading `vercel.json` file
> [debug] [2023-11-26T02:42:14.605Z] No `vercel.json` file present
> [debug] [2023-11-26T02:42:14.605Z] Locating files /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs
> [debug] [2023-11-26T02:42:14.605Z] Ignoring /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/.git
> [debug] [2023-11-26T02:42:14.606Z] Ignoring /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/.gitignore
> [debug] [2023-11-26T02:42:14.606Z] Ignoring /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/.next
> [debug] [2023-11-26T02:42:14.606Z] Ignoring /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/.vercel
> [debug] [2023-11-26T02:42:14.606Z] Ignoring /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/node_modules
> [debug] [2023-11-26T02:42:14.606Z] Locating files /Users/mh4gf/ghq/github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs [1ms]
> [debug] [2023-11-26T02:42:14.610Z] Imported Builder "@vercel/go" from "/Users/mh4gf/.asdf/installs/nodejs/18.16.1/lib/node_modules/vercel/node_modules/@vercel/go"
> [debug] [2023-11-26T02:42:15.214Z] Proxying to "@vercel/go" dev server (port=65010, pid=86054)
> [debug] [2023-11-26T02:42:15.223Z] Killing builder dev server with PID 86054
> [debug] [2023-11-26T02:42:15.255Z] Killed builder dev server with PID 86054
- "@vercel/go" をインポートしている。バンドラとのこと
- "@vercel/go" dev serverを起動しプロキシしている?
- bundler dev serverをkillしている
おそらくリクエストのたびにGoのハンドラをバンドル(≒ビルド?)し立ち上げている?

/vercel/cache
というディレクトリに色々ファイルが生成されているので見てみる
( u6d1o35shmg
のディレクトリは、似たようなディレクトリがたくさん作られている)
tree .vercel/cache
.vercel/cache
├── go
│ └── u6d1o35shmg
│ ├── api
│ │ ├── entrypoint.go
│ │ └── vercel-dev-server-main.go
│ ├── go.mod
│ └── go.work
└── golang -> /Users/mh4gf/Library/Caches/com.vercel.cli/golang/1.18.10_darwin_arm64
それぞれ生成されたファイルを見てみる
package main
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello from Go!</h1>")
}
package main
import (
"io/ioutil"
"net"
"net/http"
"os"
"strconv"
)
func main() {
// create a new handler
handler := http.HandlerFunc(Handler)
// https://stackoverflow.com/a/43425461/376773
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
port := listener.Addr().(*net.TCPAddr).Port
portBytes := []byte(strconv.Itoa(port))
file := os.NewFile(3, "pipe")
_, err2 := file.Write(portBytes)
if err2 != nil {
portFile := os.Getenv("VERCEL_DEV_PORT_FILE")
os.Unsetenv("VERCEL_DEV_PORT_FILE")
err3 := ioutil.WriteFile(portFile, portBytes, 0644)
if err3 != nil {
panic(err3)
}
}
panic(http.Serve(listener, handler))
}
module handler
go 1.21.3
require handler v0.0.0-unpublished
replace handler => ../../../../api/
use (
.
)
- entrypoint.goは自分が書いたGoのハンドラがコピーされているっぽい。moduleはmainになっている
- vercel-dev-server-main.goはhttpのserveをしている
- go.modでは、自分が書いたhandlerモジュールをreplaceしている
- goのバージョンを下げたのに1.21.3なのはちょっと気になる? - go.workは
use (.)
している

とりあえずローカルで動くところまでできたので、vercelにデプロイしてみる。が、エラーになる
vercel
Vercel CLI 32.5.0
🔍 Inspect: https://vercel.com/mh4gf/debug-vercle-serverless-function-golang-with-nextjs/FBkKFzSeZijN6CCbGC23uvgNwSxk [1s]
✅ Preview: https://debug-vercle-serverless-function-golang-with-nextjs-jqtx7hmar.vercel.app [1s]
Error: Command failed: go build -ldflags -s -w -o /tmp/6b30518c/handler /vercel/path0/api/main__vc__go__.go
vercelのビルドログを見てみる
Created all serverless functions in: 1.367s
Collected static files (public/, static/, .next/static): 3.665ms
Downloading go: https://dl.google.com/go/go1.18.10.linux-amd64.tar.gz
go: finding module for package github.com/vercel/go-bridge/go/bridge
go: downloading github.com/vercel/go-bridge v0.0.0-20221108222652-296f4c6bdb6d
go: found github.com/vercel/go-bridge/go/bridge in github.com/vercel/go-bridge v0.0.0-20221108222652-296f4c6bdb6d
go: finding module for package github.com/aws/aws-lambda-go/lambda
go: finding module for package github.com/aws/aws-lambda-go/events
go: downloading github.com/aws/aws-lambda-go v1.41.0
go: found github.com/aws/aws-lambda-go/events in github.com/aws/aws-lambda-go v1.41.0
go: found github.com/aws/aws-lambda-go/lambda in github.com/aws/aws-lambda-go v1.41.0
go: downloading github.com/stretchr/testify v1.7.2
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v3 v3.0.1
main__vc__go__.go:9:2: no required module provides package github.com/vercel/go-bridge/go/bridge: go.mod file not found in current directory or any parent directory; see 'go help modules'
main__vc__go__.go:8:2: package handler/handler is not in GOROOT (/vercel/path0/.vercel/cache/golang/src/handler/handler)
failed to `go build`
Error: Command failed: go build -ldflags -s -w -o /tmp/6b30518c/handler /vercel/path0/api/main__vc__go__.go
go.mod file not found in current directory or any parent directory
なのでgo.modが見つけられていないということかな。
apiディレクトリ内に押しこめたのが原因か?ということでルートに持って行ってみる: https://github.com/MH4GF/debug-vercle-serverless-function-golang-with-nextjs/commit/2783b2a3dc3a36c062511ab1c91ac3f7fb187d33
それでも動かんな...

困ったのでdiscussionに投稿してみる。返信もらえるといいが...