1. 正しい出品
  2. 商品名が空
  3. 価格が0
  4. price を送ってこない場合もゼロ値で弾かれる
  5. 壊れたJSON
コース一覧
Go入門
出品API

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分

出品API

本文を読む

ここまでは読み取りだけでした。今度は送られてきたデータを受け取ります。

Go

func createHandler(w http.ResponseWriter, r *http.Request) { var item Item err := json.NewDecoder(r.Body).Decode(&item) if err != nil { w.WriteHeader(400) return } }

Decode は、届いた JSON を構造体に流し込みます。Marshal の逆向きです。

&item の & は何か

Go

Decode(&item) // 居場所を渡す Decode(item) // これでは書き込めない

第6章のポインタレシーバと同じ話です。Decode は item の中身を書き換えるので、コピーではなく居場所を渡す必要があります。

& を書き忘れると、Go が教えてくれます。

プレーンテキスト

json: Unmarshal(non-pointer main.Item)

知らないキーは無視される

JSON

{"name":"きのこ図鑑","price":1200,"unknown":"何か"}

unknown に対応するフィールドが無くても、エラーにはなりません。黙って捨てられます。

逆に、送られてこなかったフィールドはゼロ値のままです。

JSON

{"name":"きのこ図鑑"}

Price は 0 になります。第2章のゼロ値がここでも効いてきます。

「0円で送ってきた」と「price を送ってこなかった」は区別できません。 どちらも 0 です。だから、受け取ったあとに検証が要ります。

検証してから受け付ける

第4章と第6章で書いた検証が、そのまま使えます。

Go

if err := validate(item); err != nil { w.WriteHeader(400) w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error()))) return }

%q は、文字列をダブルクォートで囲んで埋め込みます。JSON の値として書くときに便利です。

Go

fmt.Sprintf(`{"error":%q}`, "価格が0以下です") // {"error":"価格が0以下です"}

201 を返す

作成が成功したときは、200 ではなく 201 を使います。

プレーンテキスト

200 OK 取得できた 201 Created 新しく作った 400 Bad Request 送られてきた内容がおかしい

201 は「作った」という意味です。200 でも動きますが、何が起きたのかが応答から読み取れるほうが親切です。

このあたりの使い分けは、決まりというより慣習です。チームで揃っていることのほうが、個々の選択より大切になります。

要件

  1. json.NewDecoder(r.Body).Decode(&item) で本文を読む
  2. 読み取りに失敗したら 400 と {"error":"不正なJSONです"}
  3. validate に通し、エラーなら 400 とその理由
  4. 成功したら 201 と受け取った商品の JSON

入出力例

callCreate("{"name":"きのこ図鑑","price":1200}") → "201 {"name":"きのこ図鑑","price":1200}" callCreate("{"name":"","price":1200}") → "400 {"error":"商品名が空です"}" callCreate("{"name":"本","price":0}") → "400 {"error":"価格が0以下です"}" callCreate("{"name":"本"}") → "400 {"error":"価格が0以下です"}" callCreate("{壊れている") → "400 {"error":"不正なJSONです"}"

ヒント

`if err := json.NewDecoder(r.Body).Decode(&item); err != nil {` の形で読み取ります。`&` を忘れないでください

検証は `if err := validate(item); err != nil {` です。エラーの理由は `err.Error()` で取れます

`fmt.Sprintf("{\"error\":%q}", ...)` の `%q` が、文字列をダブルクォートで囲んで埋め込みます

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

関連レッスン

  • つくる: API主要部完成

    第8章の知識でエンドポイント一式を実装し、fleama API仕様との突合を通せるようになります。

  • 第8章クイズ

    net/http、ハンドラ、JSON、ルーティング、ステータスコードについての理解を確認します。

  • 仕様互換を仕上げる

    ステータスコードとエラー形式を仕様に揃え、互換のあるAPIとして仕上げられるようになります。

main.go
学習モード
エディタを読み込んでいます

メモ

出品API

⌘S で保存