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

การจัดการ Error

TypeScript ใช้ throw กับ try/catch ส่วน Go ถือว่า error เป็น return value ธรรมดา: ทุกฟังก์ชันที่อาจ fail จะคืน (result, error) คุณต้องเช็ค error อย่างชัดเจน — compiler ไม่ยอมให้เพิกเฉยแบบเงียบ ๆ Echo ต่อยอดแนวคิดนี้แบบธรรมชาติ: handler ที่คืน error ที่ไม่เป็น nil จะ trigger error handler ของ Echo

TypeScript
// TypeScript / Express — throw-based
async function getBook(id: string): Promise<Book> {
const book = await db.findOne(id);
if (!book) throw new NotFoundException('book not found');
return book;
}
// Express error middleware ท้ายสุด
app.use((err, req, res, next) => {
const status = err.status ?? 500;
res.status(status).json({ message: err.message });
});
Go
// Go / Echo — return-based
func findBook(ctx context.Context, id string) (Book, error) {
book, err := store.FindByID(ctx, id)
if err != nil {
return Book{}, fmt.Errorf("findBook: %w", err)
}
return book, nil
}
// Handler — ส่งต่อ error ไปยัง error handler ของ Echo
func getBook(c echo.Context) error {
book, err := findBook(c.Request().Context(), c.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, err.Error())
}
return c.JSON(http.StatusOK, book)
}

echo.NewHTTPError(statusCode, message) สร้าง *echo.HTTPError เมื่อ handler คืนค่านี้ default error handler ของ Echo จะ serialize เป็น {"message": "..."} พร้อม HTTP status code ที่ถูกต้อง สำหรับ error ธรรมดา Echo จะคืน 500 Internal Server Error

// 400
return echo.NewHTTPError(http.StatusBadRequest, "invalid id format")
// 401
return echo.NewHTTPError(http.StatusUnauthorized, "missing token")
// 404
return echo.NewHTTPError(http.StatusNotFound, "book not found")
// 409
return echo.NewHTTPError(http.StatusConflict, "book already exists")

ห่อ error ด้วย fmt.Errorf("context: %w", err) เสมอ เพื่อให้ error ต้นฉบับถูกเก็บไว้สำหรับการตรวจสอบด้วย errors.Is / errors.As และสำหรับ structured logging

TypeScript
// TypeScript — wrapping with cause
throw new Error('failed to save book', { cause: originalError });
// NestJS — HTTP-specific
throw new InternalServerErrorException('failed to save book');
Go
// Go — wrapping ด้วย %w
func (s *BookService) Create(ctx context.Context, req CreateBookRequest) (Book, error) {
book, err := s.store.Insert(ctx, req)
if err != nil {
// %w เก็บ error ต้นฉบับไว้สำหรับ errors.Is / errors.As
return Book{}, fmt.Errorf("BookService.Create: %w", err)
}
return book, nil
}
// Handler — ตรวจสอบชนิด error
func createBook(c echo.Context) error {
book, err := svc.Create(c.Request().Context(), req)
if err != nil {
// errors.Is เดิน wrap chain
if errors.Is(err, store.ErrDuplicate) {
return echo.NewHTTPError(http.StatusConflict, "book already exists")
}
return echo.NewHTTPError(http.StatusInternalServerError, "failed to create book")
}
return c.JSON(http.StatusCreated, book)
}

Echo ให้คุณแทน default error handler ด้วย e.HTTPErrorHandler ตรงนี้คือจุดที่คุณจะ log error, แปลง domain error เป็น HTTP code และทำให้ response envelope สม่ำเสมอ

TypeScript
// Express — error middleware (4 argument, ต้องอยู่ท้ายสุด)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err);
if (err instanceof HttpException) {
return res.status(err.status).json({ message: err.message });
}
res.status(500).json({ message: 'internal server error' });
});
Go
// Echo — custom HTTP error handler
func customErrorHandler(err error, c echo.Context) {
var he *echo.HTTPError
if errors.As(err, &he) {
// Echo HTTPError — ใช้ status code ของตัวเอง
_ = c.JSON(he.Code, map[string]any{
"message": he.Message,
})
return
}
// Error ที่ไม่รู้จัก — log และคืน 500
c.Logger().Errorf("unhandled error: %v", err)
_ = c.JSON(http.StatusInternalServerError, map[string]any{
"message": "internal server error",
})
}
// ลงทะเบียนใน main():
e.HTTPErrorHandler = customErrorHandler

รันบนเครื่องของคุณ — ต้องการ Echo module และ network port

เกิดอะไรขึ้นเมื่อ Echo handler คืน bare error (ที่ไม่ใช่ HTTPError)?
format string verb ใดที่เก็บ error ต้นฉบับไว้สำหรับการตรวจสอบด้วย errors.Is?
ลงทะเบียน custom central error handler ใน Echo อย่างไร?