ข้ามไปยังเนื้อหา

JSON Response

ใน Express คุณเรียก res.status(200).json(payload) ส่วนใน Echo ใช้ c.JSON(statusCode, payload) และ Echo ตั้ง header Content-Type: application/json ให้อัตโนมัติ

TypeScript
// Express
app.get('/books/:id', (req, res) => {
const book = { id: req.params.id, title: 'Go in Action' };
res.status(200).json(book);
});
// 404
res.status(404).json({ message: 'not found' });
Go
// Echo
func getBook(c echo.Context) error {
book := map[string]string{
"id": c.Param("id"),
"title": "Go in Action",
}
return c.JSON(http.StatusOK, book)
}
// 404 (ผ่าน HTTPError — ดูในบท Error Handling)
return echo.NewHTTPError(http.StatusNotFound, "not found")

การใช้ map[string]string สะดวกสำหรับตัวอย่าง แต่โค้ด production ควรใช้ typed response struct เพื่อให้ compiler จับ typo ของชื่อ field ได้ และทำให้ API surface ถูก document ไว้ในโค้ดเอง

TypeScript
// TypeScript — response type
interface BookResponse {
id: string;
title: string;
author: string;
publishedAt: string; // ISO 8601
}
const toResponse = (b: Book): BookResponse => ({
id: b.id,
title: b.title,
author: b.author,
publishedAt: b.createdAt.toISOString(),
});
res.json(toResponse(book));
Go
// Go — response struct
type BookResponse struct {
ID string `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
PublishedAt string `json:"publishedAt"` // RFC 3339 / ISO 8601
}
func toBookResponse(b Book) BookResponse {
return BookResponse{
ID: b.ID,
Title: b.Title,
Author: b.Author,
PublishedAt: b.CreatedAt.UTC().Format(time.RFC3339),
}
}
return c.JSON(http.StatusOK, toBookResponse(book))

Echo re-export constant จาก net/http ของ Go ใช้ชื่อ constant แทนตัวเลขดิบ

http.StatusOK // 200
http.StatusCreated // 201
http.StatusNoContent // 204
http.StatusBadRequest // 400
http.StatusUnauthorized // 401
http.StatusForbidden // 403
http.StatusNotFound // 404
http.StatusConflict // 409
http.StatusUnprocessableEntity // 422
http.StatusInternalServerError // 500

encoding/json คือ engine ที่อยู่เบื้องหลัง c.JSON ลองใช้โดยตรง:

package main
import (
"encoding/json"
"fmt"
"time"
)
type BookResponse struct {
ID string `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
PublishedAt time.Time `json:"publishedAt"`
}
func main() {
book := BookResponse{
ID: "abc-123",
Title: "The Go Programming Language",
Author: "Donovan & Kernighan",
PublishedAt: time.Date(2015, 10, 26, 0, 0, 0, 0, time.UTC),
}
// Marshal เป็น JSON (สิ่งที่ c.JSON ทำภายใน)
data, err := json.MarshalIndent(book, "", " ")
if err != nil {
fmt.Println("marshal error:", err)
return
}
fmt.Println(string(data))
// Unmarshal กลับ
var decoded BookResponse
if err := json.Unmarshal(data, &decoded); err != nil {
fmt.Println("unmarshal error:", err)
return
}
fmt.Printf("\nDecoded title: %s\n", decoded.Title)
fmt.Printf("Published: %s\n", decoded.PublishedAt.Format(time.RFC3339))
}

อย่า return JSON array เปล่า ๆ ที่ top level — ห่อด้วย object เสมอ เพื่อให้เพิ่ม metadata ทีหลังได้โดยไม่ต้อง break API

TypeScript
// Express — paginated list
res.json({
data: books,
total: count,
page: Number(req.query.page ?? 1),
limit: Number(req.query.limit ?? 10),
});
Go
// Echo — paginated list
type PaginatedBooks struct {
Data []BookResponse `json:"data"`
Total int `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}
return c.JSON(http.StatusOK, PaginatedBooks{
Data: responses,
Total: total,
Page: page,
Limit: limit,
})
c.JSON(http.StatusOK, payload) ใน Echo ทำอะไร?
method ไหนของ Echo ที่ส่ง response ไม่มี body และ status 204?
ทำไม list endpoint ควรห่อ array ด้วย object เช่น { data: [...] }?