ちょっとしたプレーンテキストをpcとスマホ(iphone)間で共有しようとしたときに、以前まではLINEやメモ帳(icloud経由共有)やgmailの下書きに保存、notionなどの方法を使っていました。
しかしLINEはPCで立ち上げるのが面倒だし、icloudのメモ帳は立ち上がりが遅かったりプレーンテキストじゃなかったりするし、gmailの下書きも最近(?)プレーンテキストじゃなくなったりと何かと微妙な点を感じていました。notionはかなり良かったですが。
そこで、簡単なプレーンテキストの共有くらいなら無課金でできないか?と考えました。
まず、webアプリユーザーが入力した情報をサーバー側に保存する必要があるので、javascriptでは完結しません。ですので前回紹介したgithub pagesは使えません。
いろいろ探したところ、renderというサービスを見つけました。本記事ではgoの自作webアプリをrenderでデプロイして動かすところまでを説明します。
Go, fiberの書き方/使い方というよりもデプロイのところに集中して説明します。
なお、javascriptで完結する場合はgithub pagesのほうが簡単です。前回の記事をご覧ください。
どういう人/場合に役立つか
- 簡単なwebアプリをとにかく作って動かしてみたい人
- ネットワーク/web技術の理解のための第一歩を踏み出したいとき
go+renderでwebアプリ作成
まず、go+renderでwebアプリを作ります。
私はこの時初めてgoを触ったので、ざっくりですが環境構築の手順も書いておきます。
- 公式サイトからインストーラーをダウンロードし、インストール
- VSCodeの拡張機能を入れる
次に、webアプリを作ります。こちらもざっくり書いておきます。
- githubリポジトリを作る。(プライベートリポジトリ可)
- プロジェクトフォルダを作り、ターミナルでgo mod init (リポジトリurl)を実行
- プログラムを書く
- go mod tidyを実行
書いたプログラム
プロジェクト/
├views/
│└index.html
├main.go
├go.mod
go.modはgo mod initを実行すると自動でできます。
package main
import(
"database/sql"
"fmt"
"log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
_ "github.com/mattn/go-sqlite3"
)
func main() {
app := fiber.New()
initDB()
// Basic認証ミドルウェア設定
app.Use(basicauth.New(basicauth.Config{
Users: map[string]string{
"arbitraryusername": "arbitrarypassword", // ユーザー名: パスワード
},
Realm: "Protected Area", // 認証ダイアログの表示名
}))
// 認証が通った場合のルート
app.Get("/", Index)
app.Get("/docs", getdocs) // documents.html
app.Post("/post", post)
app.Listen(":3000")
}
func Index(c *fiber.Ctx) error {
return c.SendFile("./views/index.html")
}
func getdocs(c *fiber.Ctx) error {
// テーブル取得のsqlコマンド(order指定がないので作成日時順になる)
rows, err := db.Query("SELECT id, content FROM docs")
if err != nil {
log.Println("DB Query Error:", err)
return c.Status(500).SendString("Internal Server Error")
}
// forループが終わったらqueryを戻す(テーブルのロックが解除される)
defer rows.Close()
var posts []map[string]string
for rows.Next() {
var id, content string
if err := rows.Scan(&id, &content); err != nil {
log.Println("DB Scan Error:", err)
continue
}
posts = append(posts, map[string]string{
"id": id,
"content": content,
})
}
return c.JSON(posts)
}
func post(c *fiber.Ctx) error {
// 受信するデータの構造体
type RequestData struct {
Content string `json:"content"`
}
var data RequestData
err := c.BodyParser(&data)
if err != nil {
log.Println("json error")
return c.Status(400).SendString("Invalid JSON")
}
// 受け取ったデータをsqlにinsert
_, err = db.Exec("INSERT INTO docs ( content) VALUES ( ?)",
data.Content)
if err != nil {
log.Println("DB Insert Error:", err)
return c.Status(400).SendString("SQL Insert error")
}
return nil
}
var db *sql.DB
func initDB() {
var err error
db, err = sql.Open("sqlite3", "database.db")
if err != nil {
log.Fatal(err)
}
// テーブル作成
createTableSQL := `
CREATE TABLE IF NOT EXISTS docs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT
);
`
_, err = db.Exec(createTableSQL)
if err != nil {
log.Fatal(err)
}
fmt.Println("Database docs initialized")
}
<!DOCTYPE html>
<h1>contents</h1>
<label>posts:<textarea type="text" id="content">aiueo</textarea></label></br>
<label>got:<textarea type="text" id="got"></textarea></label>
<button type="button" onclick="createform()">create</button>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody id="postsTable"></tbody>
</table>
<script>
function createform() {
// 入力値(json用)を取得
let inputContent = document.getElementById("content").value;
// 創成
// 構造体に変換
let data = {
content: inputContent,
};
// リクエスト送信
fetch(`/post`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
}
async function loadPostsdefault() {
const response = await fetch("/docs");
if (!response.ok) {
console.error("Failed to fetch posts");
return;
}
const posts = await response.json();
const table = document.getElementById("postsTable");
table.innerHTML = ""; // クリア
posts.forEach((post) => {
let row = `<tr>
<td><button type="button" onclick="flushcontent(${post.id})">${post.id}</button></td>
<td>${post.content}</td>
</tr>`;
table.innerHTML += row;
});
}
async function flushcontent(id){
const response = await fetch("/docs");
if (!response.ok) {
console.error("Failed to fetch posts");
return;
}
const posts = await response.json();
const got = document.getElementById("got");
got.value = posts[id-1].content;
navigator.clipboard.writeText(posts[id - 1].content);
}
loadPostsdefault();
</script>
goの各ツール(go.exeから実行できるもの(go modなど))がどういうものなのかについてはあまり深く理解できていませんので割愛します。
ローカルでテスト
webアプリを作るのも初めてでして、色々調べながらやっていたところローカルでテストができることも初めて知りました。
go run main.goを実行すると、windows defenderの警告画面が出ます。プライベートにチェックを入れてやるとfiber実行中の旨表示されます。
ブラウザのアドレスバーにlocalhost:3000と入力するとwebアプリの動作確認ができます。
githubにアップロード
githubに作ったものをアップロードします。
githubのリポジトリを作ったときに下の方に書いてある通りのコマンドを実行します。
git init
git add main.go go.mod go.sum views/index.html
git remote add origin (リポジトリのURL)
git push -u origin main
database.dbはテストでできたデータなのでアップロードしないよう注意してください。
renderに登録
ここからはrenderを使ってデプロイしていきます。
登録~githubと連携は割愛します。
新しいプロジェクトをつくる

githubリポジトリを選んでやると大体デフォルトで入力されている項目でよさそうなのでdeployボタンを押します。プランはfreeを選択しました。build commandとstart commandは理解できていません。
数分待つとビルドが完了してメールが届きます。
urlが出ていると思うので、そこにアクセスするとwebアプリが使えます。

なおfreeプランの制限で、このインスタンスは数分程度アクセスがないと初期化されるようです。
まとめ
- renderの基本の使い方を理解した。
- ネットワークやweb技術を理解する第一歩を踏み出せた。
わかっていないこと
- goの各ツール(go.exe)
最後までお読みいただきありがとうございました。