1. 一覧が total で包まれる
  2. 絞り込み
  3. 0件でも items は空配列
  4. 詳細は変えない
  5. 登録していないパス
  6. 出品は変えない
コース一覧
Go入門
仕様互換を仕上げる

Go入門

変数とゼロ値、Go唯一のループであるfor、複数戻り値とエラー処理、スライスとマップ、構造体とメソッド、ゴルーチンによる並行処理、net/httpによるAPI開発まで、Goの基礎を、毎レッスン実際にコードを書き、フリマアプリのAPIをGoで作りながら学びます。

1
Hello, Go
01. Goとは15分
02. 実行してみる15分
03. packageとmain15分
04. fmtで出力する15分
05. go buildと1バイナリ15分
06. つくる: 起動バナー20分
07. 第1章クイズ10分
2
変数と型
01. varで宣言する15分
02. := の短縮宣言15分
03. ゼロ値(Goならでは)15分
04. 基本の型15分
05. 暗黙変換なし15分
06. 定数とiota15分
07. つくる: 商品データ表示20分
08. 第2章クイズ10分
3
制御構文(forしかない世界)
01. Goのif15分
02. forしかない世界15分
03. forの七変化15分
04. Goのswitch15分
05. 定番: FizzBuzz15分
06. ループで集計15分
07. つくる: 商品集計20分
08. 第3章クイズ10分
4
関数とエラー処理
01. funcで関数を作る15分
02. 複数戻り値(Goならでは)15分
03. if err != nil(Goの心臓)15分
04. エラーを作る15分
05. deferで後始末15分
06. つくる: 検証つき関数群20分
07. 第4章クイズ10分
5
スライスとマップ
01. 配列15分
02. スライス15分
03. appendで育てる15分
04. rangeで走査する15分
05. マップ15分
06. カンマokイディオム15分
07. つくる: 検索と集計20分
08. 第5章クイズ10分
6
構造体とメソッド
01. 構造体15分
02. 初期化とフィールド15分
03. メソッド15分
04. ポインタレシーバ20分
05. New関数の慣習15分
06. 構造体のスライス15分
07. つくる: Itemで再設計20分
08. 第6章クイズ10分
7
ゴルーチンと並行処理のさわり
01. ゴルーチン15分
02. チャネル20分
03. 待ち合わせ20分
04. 並行の落とし穴20分
05. つくる: 並行集計20分
06. 第7章クイズ10分
8
Web API(net/http)
01. サーバーを立てる20分
02. ハンドラ関数20分
03. JSONを返す20分
04. ルーティング20分
05. 一覧API20分
06. 詳細APIと40420分
07. 出品API20分
08. つくる: API主要部完成25分
09. 第8章クイズ10分
9
総合制作
01. 仕様互換を仕上げる25分
02. Node版と差し替える25分
03. 1バイナリでデプロイ25分
04. 自由拡張25分
05. 完成と次のステップ20分

仕様互換を仕上げる

同じ仕様を、別の言語で満たす

第9章でやるのは、実務でよくある仕事です。動いている API を、別の言語で作り直す。

作り直すとき、中身は自由に書き換えられます。ですが外から見た振る舞いは1ミリも変えられません。使っている側は API の内側を知らないので、応答が少しでも違えば壊れます。

プレーンテキスト

変えてよい 使う言語、内部の構造、性能 変えられない URL、メソッド、応答の形、ステータスコード

仕様書が正

fleama API の仕様は次のとおりです。

プレーンテキスト

GET /items 200 {"total":N,"items":[...]} GET /items/{id} 200 {"id":..,"name":..,"price":..} 404 {"error":"not found"} 400 {"error":"invalid id"} POST /items 201 作成した商品 400 {"error":"理由"} それ以外のパス 404 {"error":"not found"}

第8章で作ったものと1か所だけ違います。一覧が配列そのままではなく、total で包まれた形です。

プレーンテキスト

第8章 [{...},{...}] 仕様書 {"total":2,"items":[{...},{...}]}

第8章で「どちらが正しいということはない、ただし揃えること」と書きました。仕様書がある以上、こちらに合わせます。好みは関係ありません。

包む形を作る

専用の構造体を用意するのが素直です。

Go

type ListResponse struct { Total int `json:"total"` Items []Item `json:"items"` }

応答の形そのものを型にしておくと、キー名の付け忘れが起こりません。

ここでも Items を nil のままにしないでください。0件のときに "items":null になります。仕様は [] です。

登録していないパスも揃える

第8章で触れたとおり、ServeMux の既定の 404 は page not found という文言です。仕様は JSON なので、これも揃えます。

Go

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { writeError(w, 404, "not found") })

/ は前方一致なので、どのパスにも当てはまらなかったものがここへ来ます。より具体的な登録が優先されるので、/items などは奪われません。

互換かどうかを測る

「たぶん同じ」では作り直しになりません。同じリクエストを両方に投げて、応答が1文字単位で一致するかを確かめます。

この演習のテストが、その突合そのものです。仕様書に書かれた応答を期待値に置いてあります。

要件

  1. 一覧の応答を {"total":N,"items":[...]} の形にする
  2. 0件でも "items":[] を返す
  3. 登録していないパスに 404 {"error":"not found"} を返す
  4. 詳細と出品の振る舞いは変えない

入出力例

callAPI("GET", "/items", "") → "200 {"total":2,"items":[{"id":1,"name":"きのこ図鑑","price":1200},{"id":2,"name":"中古スマホ","price":24800}]}" callAPI("GET", "/items?keyword=中古スマホ", "") → "200 {"total":1,"items":[{"id":2,"name":"中古スマホ","price":24800}]}" callAPI("GET", "/items?keyword=謎の箱", "") → "200 {"total":0,"items":[]}" callAPI("GET", "/items/1", "") → "200 {"id":1,"name":"きのこ図鑑","price":1200}" callAPI("GET", "/unknown", "") → "404 {"error":"not found"}" callAPI("POST", "/items", "{"name":"新商品","price":500}") → "201 {"id":0,"name":"新商品","price":500}"

ヒント

`ListResponse` 構造体を定義し、`Total` と `Items` にタグを付けます

`listHandler` の `json.Marshal(result)` を `json.Marshal(ListResponse{Total: len(result), Items: result})` に変えます

`mux.HandleFunc("/", ...)` を足すと、どの登録にも当てはまらないパスがそこへ来ます

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/10

関連レッスン

  • Node版と差し替える

    同じ仕様の別実装に置き換えられることを確かめ、実務のリプレイスを疑似体験します。

  • 1バイナリでデプロイ

    go buildで作った1つのバイナリをサーバーに置き、常駐させる手順を説明できるようになります。

  • 自由拡張

    欲しいAPIを自分で設計して実装し、並行処理を活かした集計APIを1本作れるようになります。

  • 完成と次のステップ

    fleama-goの完成を確かめ、次に学ぶものを自分で選べるようになります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • エンドポイント「この URL を叩くとこの機能が動く」入口の住所
  • APIアプリ間でデータをやり取りする窓口
  • 配列サイズ固定の同型データの集まり
  • applicationJSON 本文を送るときの Content-Type
  • リクエストWeb 通信の基本単位、ブラウザの問い合わせとサーバーの返答
  • テストバグを見つける工程
main.go
学習モード
エディタを読み込んでいます

メモ

仕様互換を仕上げる

⌘S で保存