Go × Echoで始めるWebサーバ構築入門:シンプル・高速なAPI開発を最短で学ぶ

  • golang
    golang
  • echo
    echo
2023/12/03に公開

はじめに

Go言語でWebアプリケーションを構築しようとしたとき、最初に悩むのが「どのフレームワークを選ぶか」という点です。
Goには標準ライブラリでもHTTPサーバは書けますが、ルーティングやミドルウェアの実装など、便利な機能を求めるならフレームワークの導入が効果的です。

本記事では、Go製フレームワークの中でも特に軽量で高速、そして学習コストも低いとされる「Echo」を使って、最小構成のWebサーバを立ち上げる方法を解説します。

以下のような流れで進めていきます。

  • Echoを選ぶ理由
  • Echoのインストールと初期設定
  • 基本構造とルーティングの理解
  • リクエスト処理とレスポンスの書き方
  • ミドルウェアの使い方

GoとEchoを使って、「まずは動くものを作る」を目指して一緒に進めていきましょう。
次のセクションでは、Echoを選ぶ理由について簡単に整理します。

https://echo.labstack.com/

今回作成したコードは下記のリポジトリに載せています。

https://github.com/shinagawa-web/go-echo-example

Go + Echoを選ぶ理由

Goには複数のWebフレームワークが存在します。たとえば、Gin、Fiber、Echo、Chiなどが代表的です。その中でEchoを選ぶ理由は、以下のような特徴にあります。

1. 圧倒的な高速性と軽量さ

Echoは非常に高速に動作することで知られています。独自のルーティングエンジンを持ち、必要最低限のオーバーヘッドで高パフォーマンスを実現します。

2. シンプルかつ直感的なAPI

エラーハンドリングやルーティング、ミドルウェアの導入が簡潔に記述できる設計になっており、初学者でも扱いやすいです。
また、公式ドキュメントが充実しており、導入から応用まで一貫した理解が進めやすいのも魅力です。

3. ミドルウェアや機能が標準で豊富

  • ログ出力
  • リカバリ(パニックハンドリング)
  • CORS、Gzip、JWTなどのミドルウェア

これらがEcho標準で提供されているため、別途ライブラリを探して導入する手間がありません。

4. エコシステムとの親和性

Echoは net/http をベースに作られているため、他のGoライブラリやミドルウェアと併用しやすいです。
また、Goの標準的なコンテキストやエラーハンドリングと自然に統合される設計がされており、拡張もしやすくなっています。

Ginとの比較(学習コスト・機能・記法)

GoのWebフレームワークで最もよく比較されるのが Gin と Echo です。どちらも高速かつ人気のあるフレームワークですが、用途や好みによって向き・不向きがあります。ここでは主に「学習コスト」「機能」「記法」の3つの観点で違いを整理します。

学習コスト:Echoの方がやや明示的

Gin は抽象度が高く、構文がシンプルでコンパクトに書けるため、最初はとっつきやすいです。ただし、内部的な挙動が隠蔽されている部分も多く、拡張やデバッグでつまずくこともあります。

Echo は構成が明示的で、各コンポーネントが分かれているため、「フレームワークの流れを追いやすい」という利点があります。Goの標準ライブラリに近い記述スタイルでもあり、Goらしさを学びたい人には親和性が高いです。

機能比較:Echoの方が標準機能が豊富

項目 Gin Echo
ルーティング 高速で柔軟 高速でミドルウェア連携が強力
ミドルウェア 最低限(カスタム中心) 標準で多数同梱(CORS、Gzipなど)
バリデーション バリデーションライブラリと統合可 独自のバリデーション組込み
レスポンスAPI JSON, HTML などを簡単に返せる より多様な形式に対応
サーバ設定 Gin独自の設定が多い net/http に忠実で柔軟性あり

どちらも優れた選択肢ですが、Echoはより「Goの書き方」に寄り添った設計がなされており、慣れてくると心地よく感じる人も多いです。次章では、実際にEchoをインストールし、プロジェクトを立ち上げていきます。

Echoのインストールと初期設定

Echoを使った開発を始めるには、Goの標準的なプロジェクト構成とEchoのインストールを行います。ここではシンプルなサーバーを立ち上げるところまでを確認します。

1. プロジェクトディレクトリの作成

mkdir go-echo-example
cd go-echo-example
go mod init go-echo-example

2. Echoのインストール

$ go get github.com/labstack/echo/v4

3. 最小構成の main.go

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, Echo!")
	})

	e.Logger.Fatal(e.Start(":8080"))
}

4. サーバーの起動

go run main.go

ブラウザで http://localhost:8080 にアクセスすると、Hello, Echo! と表示されるはずです。

5. フォルダ構成(将来の拡張を考慮)

本シリーズでは、以下のような構成を基本とします。

go-echo-example/
├── go.mod
├── main.go
├── handler/       // HTTPハンドラー
├── service/       // ビジネスロジック層
├── repository/    // データアクセス層
└── model/         // ドメインモデル

このように初期段階からディレクトリを分けておくことで、アプリケーションが大きくなっても無理なくスケールさせることができます。

補足: Airの導入でホットリロードを実現する

開発中にコードを変更するたびに go run を手動で実行するのは面倒です。そこで、air を導入してホットリロードを可能にします。

以下のコマンドで air をインストールします。

go install github.com/air-verse/air@latest

air.air.toml という設定ファイルを利用できます。プロジェクトルートに以下の内容で作成します。

# .air.toml
root = "."
tmp_dir = "tmp"

[build]
  cmd = "go build -o ./tmp/main ."
  bin = "tmp/main"
  include_ext = ["go"]
  exclude_dir = ["tmp", "vendor"]

[log]
  time = true
項目 意味
root 監視するディレクトリのルート
tmp_dir ビルド成果物の置き場
[build] ビルドと実行の設定
cmd ビルドコマンド(ここではGoのバイナリを tmp/main にビルド)
bin 実行するバイナリファイルのパス
include_ext 監視するファイルの拡張子
exclude_dir 監視対象から除外するディレクトリ

以下のコマンドを実行すると、air がソースコードの変更を監視し、即座に再起動してくれます。

air

Image from Gyazo

Echoの基本構造を理解する

Echoは、最小限のコードでWebアプリケーションを構築できることが特徴です。このセクションでは、Echoアプリの全体像と、どのようにしてリクエストを受け取り、レスポンスを返すのかを見ていきます。

基本的な構成

Echoを使ったアプリケーションの典型的な構成は以下のようになります。

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, Echo!")
	})

	e.Start(":8080")
}

各要素の解説

構成要素 説明
echo.New() Echoインスタンスを生成します。ルーティングやミドルウェア設定に使われます。
e.GET(...) HTTPメソッドとパスに応じたルートハンドラを定義します。他に POSTPUTDELETE なども使用可能です。
c echo.Context リクエストやレスポンスにアクセスするためのコンテキストです。Echoの特徴的なインターフェースです。
c.String(...) クライアントにテキストレスポンスを返します。他にも c.JSON()c.HTML() などの返し方があります。
e.Start(...) サーバーを起動し、指定されたポートでリクエストを受け付けます。

シンプルかつ強力なルーティング

Echoのルーティングはとても直感的です。ルートの設定はメソッドチェーンのように積み上げていけるため、小規模から中規模のアプリには特に適しています。

リクエスト処理の基本

Echoでは、リクエストデータの取得がとてもシンプルです。クエリパラメータ・パスパラメータ・JSONボディなど、さまざまな入力方法に対応しており、Echo独自のContextを通じて取得できます。

クエリパラメータの取得

main.go
+ e.GET("/search", func(c echo.Context) error {
+ 	keyword := c.QueryParam("q")
+ 	return c.String(http.StatusOK, "query: "+keyword)
+ })

以下のURLにアクセスします。

curl 'http://localhost:8080/search?q=golang'

レスポンス

"query": "golang"

パスパラメータの取得

パスに埋め込まれた値を取得するには、:param という形でルーティングを定義します。

main.go
+ e.GET("/users/:id", func(c echo.Context) error {
+ 	id := c.Param("id")
+ 	return c.String(http.StatusOK, "user_id: "+id)
+ })

以下のURLにアクセスします。

curl http://localhost:8080/users/123

レスポンス

"user_id": "123"

フォームデータの取得

フォームから送られるデータは c.FormValue で取得します。

main.go
+ e.POST("/login", func(c echo.Context) error {
+ 	username := c.FormValue("username")
+ 	password := c.FormValue("password")
+ 	return c.String(http.StatusOK, "user_name: "+username+", password: "+password)
+ })
curl -X POST -d "name=Gopher&password=p1234567" http://localhost:8080/login

レスポンス

user_name: , password:p1234567

JSONの取得と構造体へのバインド

クライアントからJSON形式でデータが送られてくる場合、c.Bind で構造体にバインドします。

main.go
+ type Message struct {
+ 	Title   string `json:"title"`
+ 	Content string `json:"content"`
+ }

func main() {
	r := gin.Default()

※一部省略

+ e.POST("/messages", func(c echo.Context) error {
+ 	msg := new(Message)
+ 	if err := c.Bind(msg); err != nil {
+ 		return err
+ 	}
+ 	return c.JSON(http.StatusOK, msg)
+ })
}

curlでPOSTリクエストを送ります。

curl -X POST http://localhost:8080/messages \
  -H "Content-Type: application/json" \
  -d '{"title": "Hello", "content": "This is a test message"}'

レスポンス

{"title":"Hello","content":"This is a test message"}

レスポンスの書き方

Echoでは、JSON・HTML・文字列など様々な形式でレスポンスを返すことができます。ここでは代表的なレスポンスの書き方を紹介します。

JSONを返す

構造体を定義してJSONで返すことができます。

e.GET("/json", func(c echo.Context) error {
	msg := Message{
		Title:   "Hello",
		Content: "This is a JSON response",
	}
	return c.JSON(http.StatusOK, msg)
})

curlで/jsonにアクセスします。

 curl http://localhost:8080/json

レスポンス

{"title":"Hello","content":"This is a JSON response"}
  • c.JSON(status, obj) は obj をJSONにエンコードして返します。

HTMLを返す

Echoではテンプレートエンジンを使ってHTMLを返すことも可能ですが、まずはシンプルにHTML文字列を返す方法です。

e.GET("/html", func(c echo.Context) error {
	html := `<h1>Welcome</h1><p>This is HTML content.</p>`
	return c.HTML(http.StatusOK, html)
})

ブラウザでアクセスして確認します。

Image from Gyazo

任意のステータスコードを使う

例えば、データが見つからなかったときに404を返すこともできます。

e.GET("/notfound", func(c echo.Context) error {
	return c.String(http.StatusNotFound, "Not Found")
})

curlで/notfoundにアクセスします。

curl -i http://localhost:8080/notfound

レスポンス

HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=UTF-8
Date: Mon, 16 Jun 2025 00:31:15 GMT
Content-Length: 9

Not Found

ミドルウェアの活用

Echoはミドルウェアを簡単に導入できる仕組みが整っており、ログ出力、リカバリ(panic処理)、CORS、Basic認証など、Webアプリで頻出の機能を“薄いコード”で導入できます。

ミドルウェアとは?

ミドルウェアとは、リクエストとレスポンスの「間」に挟まる処理のことです。全てのリクエストに対して共通の処理を実行するのに適しています。

たとえば:

  • リクエストログを記録する
  • 認証・認可を行う
  • panicが起きた際に500エラーを返すようにする
  • CORSヘッダーを追加する

標準ミドルウェアの使い方

Echoには最初からいくつかの便利なミドルウェアが用意されています。

mian.go
import (
	"net/http"

	"github.com/labstack/echo/v4"
+	"github.com/labstack/echo/v4/middleware"
)

e := echo.New()

+ e.Use(middleware.Logger())
+ e.Use(middleware.Recover())

※一部省略

+	e.GET("/panic", func(c echo.Context) error {
+		panic("something went wrong")
+	})

curlでアクセスするとログが出力されます。

{"time":"2025-06-16T09:46:37.223514+09:00","id":"","remote_ip":"::1","host":"localhost:8080","method":"GET","uri":"/notfound","user_agent":"curl/8.7.1","status":404,"error":"","latency":9333,"latency_human":"9.333µs","bytes_in":0,"bytes_out":9}

また/panicにアクセスすると下記のメッセージがクライアント側で出力されます。

curl http://localhost:8080/panic

レスポンス

{"message":"Internal Server Error"}

独自ミドルウェアの定義

独自のミドルウェアを定義することもできます。
このように next を挟んで前後に処理を入れることで、処理の前後関係を制御できます。

func myMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		// 前処理
		println("Before handler")

		// ハンドラー本体の呼び出し
		err := next(c)

		// 後処理
		println("After handler")
		return err
	}
}

e.Use(myMiddleware)

ミドルウェアをルート単位で適用することも可能です。

main.go
+	g := e.Group("")
+	g.Use(myMiddleware)
-	e.GET("/notfound", func(c echo.Context) error {
+	g.GET("/notfound", func(c echo.Context) error {
		return c.String(http.StatusNotFound, "Not Found")
	})

curlで/notfoundにアクセスします。

curl -i http://localhost:8080/notfound

サーバー側で特定のルートだけログが表示されていることが確認できます。

Image from Gyazo

さいごに

本記事では、Go × Echo を使った Web サーバの構築手順を、基本構造からミドルウェアの活用まで丁寧に解説しました。

Echo は学習コストが低く、柔軟で高速な Web フレームワークです。Gin と比べても、よりシンプルなコードで直感的に書ける点が魅力です。初学者はもちろん、中規模なプロジェクトにも十分に対応できるポテンシャルがあります。

この記事を通じて、Echo の書き方や考え方が少しでもつかめたなら幸いです。ぜひ自身のプロジェクトや学習に取り入れて、手を動かしながら感覚を深めていってください。

次回以降では、Echoを使ったより実践的なAPI設計や、テスト、構成管理、デプロイ手法にも触れていく予定です。

Xでシェア
Facebookでシェア
LinkedInでシェア

記事に関するお問い合わせ📝

記事の内容に関するご質問、ご意見などは、下記よりお気軽にお問い合わせください。
ご質問フォームへ

技術支援などお仕事に関するお問い合わせ📄

技術支援やお仕事のご依頼に関するお問い合わせは、下記よりお気軽にお問い合わせください。
お問い合わせフォームへ

関連する技術ブログ

弊社の技術支援サービス

お問い合わせ

経営と現場をつなぐ“共創型”の技術支援。
成果に直結するチーム・技術・プロセスを共に整えます。

お問い合わせ