Go Beginner Guide

Go for beginnersGo สำหรับผู้เริ่มต้น

Go Beginner Guide

From your first program to a tested JSON APIตั้งแต่โปรแกรมแรกจนถึง JSON API ที่มีเทสต์

Go is a small, fast language made at Google. It runs Docker, Kubernetes, Terraform and much of the cloud. This guide teaches it from the first line: variables, strings, slices, maps, functions, structs, interfaces, errors and goroutines, then a real web server with tests. The examples on this page run in your browser. Change the code, press Run, and see what happens. Go เป็นภาษาที่เล็กและเร็ว สร้างโดย Google และเป็นภาษาที่ใช้สร้าง Docker, Kubernetes, Terraform และระบบคลาวด์อีกมาก คู่มือนี้สอนตั้งแต่บรรทัดแรก: ตัวแปร ข้อความ slice, map ฟังก์ชัน struct, interface, error และ goroutine แล้วจบด้วยเว็บเซิร์ฟเวอร์จริงที่มีเทสต์ ตัวอย่างในหน้านี้รันได้ในเบราว์เซอร์ ลองแก้โค้ด กด Run แล้วดูว่าเกิดอะไรขึ้น

Each example first shows the output it printed with the real Go compiler (go run, Go 1.25.4). Before this page was published, every example was also run through the browser runner and gave the same output. New to programming? Start with Logic & Loops. ทุกตัวอย่างแสดงผลลัพธ์ที่ได้จากคอมไพเลอร์ Go ตัวจริงไว้ก่อน (go run, Go 1.25.4) และก่อนเผยแพร่หน้านี้ ทุกตัวอย่างถูกรันผ่านตัวรันในเบราว์เซอร์แล้วได้ผลลัพธ์ตรงกัน ถ้ายังใหม่กับการเขียนโปรแกรม ให้เริ่มจาก Logic & Loops ก่อน

Shortcut: pressคีย์ลัด: กด Ctrl+Enter (⌘+Enter Mac) inside a code box to run it. The first run downloads the Go runner (about 3 MB).ในกล่องโค้ดเพื่อรันทันที การรันครั้งแรกจะดาวน์โหลดตัวรัน Go (ประมาณ 3 MB)

  • Recorded withบันทึกด้วย Go 1.25.4
  • Runs in browserรันในเบราว์เซอร์ Yaegi (WebAssembly)
  • Testsเทสต์ go test

Lesson 1 · Setupบทที่ 1 · ติดตั้ง

Getting startedเริ่มต้นใช้งาน

Go is a compiled language: the go tool turns your code into a single program file that runs on its own, with nothing else to install on the server. Install Go, then check it works: Go เป็นภาษาแบบคอมไพล์ เครื่องมือ go จะแปลงโค้ดเป็นไฟล์โปรแกรมไฟล์เดียวที่รันได้ด้วยตัวเอง โดยไม่ต้องติดตั้งอะไรเพิ่มบนเซิร์ฟเวอร์ ติดตั้ง Go แล้วตรวจว่าใช้งานได้:

SystemระบบInstallวิธีติดตั้ง
macOSbrew install go
Windowswinget install GoLang.Go or the installer from go.dev/dlหรือตัวติดตั้งจาก go.dev/dl
Linuxthe archive from go.dev/dl (distro packages are often old)ไฟล์ archive จาก go.dev/dl (แพ็กเกจของ distro มักเป็นเวอร์ชันเก่า)

Every Go program lives in a module, a folder with a go.mod file. Create one, write main.go, and run it: โปรแกรม Go ทุกตัวอยู่ใน module ซึ่งก็คือโฟลเดอร์ที่มีไฟล์ go.mod สร้าง module เขียน main.go แล้วรัน:

Terminalเทอร์มินัลgo1.25.4 · macOS
$ go version
go version go1.25.4 darwin/arm64

$ mkdir hello && cd hello

$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

$ go run .
Hello, Go!

$ go build && ls
go.mod
hello
main.go

$ ./hello
Hello, Go!

go run . compiles and runs in one step. go build leaves a program file, hello, that you can copy to a server and run there without Go installed.go run . คอมไพล์และรันในขั้นตอนเดียว ส่วน go build จะได้ไฟล์โปรแกรมชื่อ hello ที่คัดลอกไปรันบนเซิร์ฟเวอร์ได้โดยไม่ต้องติดตั้ง Go

  • package main and func main() mark the program's starting point.package main และ func main() คือจุดเริ่มต้นของโปรแกรม
  • import "fmt" loads the formatting package. Go refuses to compile if you import something and don't use it.import "fmt" โหลด package สำหรับจัดรูปแบบข้อความ Go จะไม่ยอมคอมไพล์ถ้า import แล้วไม่ได้ใช้
  • No semicolons, and the opening { must stay on the same line as func, if or for.ไม่ต้องมีเซมิโคลอน และ { ต้องอยู่บรรทัดเดียวกับ func, if หรือ for
About Runเกี่ยวกับ Run

Run uses Yaegi, a Go interpreter, compiled to WebAssembly. Normal programs behave the same as with go run, but Yaegi skips some compiler checks (an unused variable is allowed) and words some errors differently. For those cases, trust the recorded output and lesson 16.ปุ่ม Run ใช้ Yaegi ซึ่งเป็นตัวแปลภาษา Go ที่คอมไพล์เป็น WebAssembly โปรแกรมทั่วไปทำงานเหมือน go run แต่ Yaegi ข้ามการตรวจบางอย่างของคอมไพเลอร์ (เช่น ยอมให้มีตัวแปรที่ไม่ได้ใช้) และใช้ถ้อยคำในข้อความผิดพลาดต่างออกไป ในกรณีเหล่านั้นให้เชื่อผลลัพธ์ที่บันทึกไว้และบทที่ 16

Lesson 2 · The basicsบทที่ 2 · พื้นฐาน

Variables & typesตัวแปรและชนิดข้อมูล

Every value in Go has a fixed type, checked before the program runs. Inside a function, := declares a variable and lets Go work out its type. A variable you declare without a value gets its type's zero value, so there's no "undefined" in Go. ทุกค่าใน Go มีชนิดข้อมูล (type) ที่ตายตัวและถูกตรวจก่อนโปรแกรมทำงาน ภายในฟังก์ชัน := ใช้ประกาศตัวแปรโดยให้ Go หาชนิดเอง ตัวแปรที่ประกาศโดยไม่ให้ค่าจะได้ค่าศูนย์ (zero value) ของชนิดนั้น Go จึงไม่มีค่า "undefined"

TypeชนิดHoldsเก็บอะไรZero valueค่าศูนย์
intwhole numberจำนวนเต็ม0
float64decimal numberเลขทศนิยม0
stringtextข้อความ""
booltrue or falseจริงหรือเท็จfalse
[]T, map[K]V, pointersพอยน์เตอร์lessons 6–8บทที่ 6–8nil
Tipเคล็ดลับ

Use := inside functions; it's what you'll see in almost all Go code. var is for package-level variables and for when you want the zero value. Go never converts between types for you: age * 1.5 is a compile error until you write float64(age).ใช้ := ภายในฟังก์ชัน ซึ่งเป็นสิ่งที่เห็นในโค้ด Go เกือบทั้งหมด ส่วน var ใช้กับตัวแปรระดับ package และเมื่อต้องการค่าศูนย์ Go ไม่แปลงชนิดให้เองเลย age * 1.5 จะคอมไพล์ไม่ผ่านจนกว่าจะเขียน float64(age)

Lesson 3 · Textบทที่ 3 · ข้อความ

Strings & runesข้อความและ rune

The strings package has a function for almost every text job. Strings can't be changed in place, so these functions return a new string. package strings มีฟังก์ชันสำหรับงานข้อความแทบทุกอย่าง string แก้ไขในตัวเองไม่ได้ ฟังก์ชันเหล่านี้จึงคืน string ใหม่

Bytes, runes and Thai textไบต์ rune และข้อความภาษาไทย

A Go string is a sequence of bytes in UTF-8. len() counts bytes, and a Thai letter takes three. A rune is one Unicode character. range over a string gives you runes, and []rune(s) lets you cut by character instead of by byte. string ใน Go คือลำดับของไบต์ในรูปแบบ UTF-8 len() นับเป็นไบต์ และตัวอักษรไทยใช้สามไบต์ rune คืออักขระ Unicode หนึ่งตัว การใช้ range กับ string จะได้ rune ออกมา และ []rune(s) ช่วยให้ตัดเป็นตัวอักษรแทนการตัดเป็นไบต์

Noteหมายเหตุ

The count is 6, not 5, because vowel and tone marks such as ั and ี are separate runes. The positions jump by 3 because each Thai letter is 3 bytes. The � is half a letter that can't be drawn.นับได้ 6 ไม่ใช่ 5 เพราะสระและวรรณยุกต์ เช่น ั และ ี เป็น rune แยกกัน ตำแหน่งกระโดดทีละ 3 เพราะตัวอักษรไทยแต่ละตัวมี 3 ไบต์ ส่วน � คือตัวอักษรครึ่งตัวที่แสดงไม่ได้

Lesson 4 · Selectionบทที่ 4 · การเลือก

Decisionsการตัดสินใจ

if needs no parentheses, but always needs braces. It can start with a short statement, which is how Go code checks errors in one line. switch stops after the matching case, so there's no break to forget. if ไม่ต้องมีวงเล็บ แต่ต้องมีวงเล็บปีกกาเสมอ และเริ่มด้วยคำสั่งสั้นๆ ได้ ซึ่งเป็นวิธีที่โค้ด Go ใช้ตรวจ error ในบรรทัดเดียว ส่วน switch จะหยุดหลัง case ที่ตรง จึงไม่มี break ให้ลืม

WriteเขียนMeansความหมาย
== !=equal / not equal (always strict: both sides must be the same type)เท่ากับ / ไม่เท่ากับ (เข้มงวดเสมอ: ทั้งสองฝั่งต้องเป็นชนิดเดียวกัน)
> < >= <=bigger, smaller, or equalมากกว่า น้อยกว่า หรือเท่ากับ
&& || !and, or, notและ หรือ ไม่
Go is strictGo เข้มงวด

There is no ? : operator, no "truthy" values, and if x only works when x is a bool. Write the comparison out: if count > 0.Go ไม่มีตัวดำเนินการ ? : ไม่มีค่าที่ "นับเป็นจริง" และ if x ใช้ได้เฉพาะเมื่อ x เป็น bool ต้องเขียนการเปรียบเทียบให้ชัด เช่น if count > 0

Lesson 5 · Repetitionบทที่ 5 · การวนซ้ำ

Loopsการวนซ้ำ

Go has one loop keyword: for. It counts, it works like while when you give it only a condition, and with range it walks through slices, maps and strings. Go มีคำสั่งวนซ้ำเพียงคำเดียวคือ for ใช้นับรอบได้ ทำงานเหมือน while เมื่อให้แค่เงื่อนไข และเมื่อใช้กับ range จะเดินผ่าน slice, map และ string

Go 1.22+Go 1.22+

Newer Go can also count with for i := range 3 (0, 1, 2). The browser runner doesn't support that form yet, so the labs use the classic loop, which works everywhere. Try deleting left-- and pressing Run: the page stops the endless loop after 6 seconds.Go รุ่นใหม่นับรอบด้วย for i := range 3 (ได้ 0, 1, 2) ได้ด้วย แต่ตัวรันในเบราว์เซอร์ยังไม่รองรับรูปแบบนี้ แล็บจึงใช้ลูปแบบคลาสสิกที่ใช้ได้ทุกที่ ลองลบ left-- แล้วกด Run หน้านี้จะหยุดลูปไม่รู้จบให้หลัง 6 วินาที

Lesson 6 · Dataบทที่ 6 · ข้อมูล

Slices & mapsSlice และ map

A slice ([]int) is a list that can grow. append adds to it and returns the new slice, which you must store. Cutting a slice with s[a:b] doesn't copy: both slices share the same underlying memory. slice ([]int) คือรายการที่ขยายได้ append ใช้เพิ่มข้อมูลและคืน slice ใหม่ ซึ่งต้องเก็บไว้เสมอ การตัด slice ด้วย s[a:b] ไม่ได้คัดลอก ทั้งสอง slice ใช้หน่วยความจำชุดเดียวกัน

A map (map[string]int) looks values up by key. Reading a missing key gives the zero value, so use the "comma ok" form when you need to know whether it was there. Map order is random on purpose: sort the keys when the order matters. map (map[string]int) ใช้ค้นหาค่าด้วยคีย์ การอ่านคีย์ที่ไม่มีอยู่จะได้ค่าศูนย์ จึงต้องใช้รูปแบบ "comma ok" เมื่ออยากรู้ว่ามีคีย์นั้นหรือไม่ ลำดับใน map ถูกสุ่มโดยตั้งใจ ถ้าลำดับสำคัญให้เรียงคีย์ก่อน

Codeโค้ดDoesทำอะไร
s := []int{1, 2}a slice with valuesslice ที่มีค่า
s = append(s, 3)add to the endเพิ่มต่อท้าย
len(s)number of itemsจำนวนสมาชิก
s[1:3]items 1 and 2 (shares memory)สมาชิกตัวที่ 1 และ 2 (ใช้หน่วยความจำร่วม)
slices.Contains(s, 3)is this value in the slice?มีค่านี้ใน slice ไหม
m := map[string]int{}an empty map, ready to usemap ว่างที่พร้อมใช้
v, ok := m["k"]read, and know if the key existsอ่านค่า และรู้ว่ามีคีย์นี้หรือไม่
delete(m, "k")remove a keyลบคีย์

Lesson 7 · Reuseบทที่ 7 · การนำกลับมาใช้ซ้ำ

Functionsฟังก์ชัน

Types come after names: func add(a, b int) int. A Go function can return several values, which is how it reports errors: a result plus an error that is nil when everything went fine. Functions are values too, so you can store them and return them. ชนิดข้อมูลเขียนหลังชื่อ เช่น func add(a, b int) int ฟังก์ชัน Go คืนค่าได้หลายค่า ซึ่งเป็นวิธีรายงาน error: คืนผลลัพธ์พร้อม error ที่เป็น nil เมื่อทุกอย่างเรียบร้อย ฟังก์ชันก็เป็นค่าเช่นกัน จึงเก็บไว้ในตัวแปรหรือคืนออกมาได้

deferdefer

defer schedules a call for when the surrounding function returns, even if it returns early. It's how Go closes files and unlocks locks: you write the clean-up right next to the thing that needs it.defer นัดให้เรียกคำสั่งตอนที่ฟังก์ชันรอบนอกจบ แม้จะจบก่อนกำหนดก็ตาม เป็นวิธีที่ Go ใช้ปิดไฟล์และปลดล็อก โดยเขียนการเก็บกวาดไว้ข้างๆ สิ่งที่ต้องเก็บกวาด

Lesson 8 · Your own typesบทที่ 8 · ชนิดข้อมูลของเราเอง

Structs & methodsStruct และ method

Go has no classes. A struct groups fields, and methods are functions attached to a type. A name that starts with a capital letter is visible to other packages; a lower-case name is private to its package. A method with a pointer receiver (*Account) can change the struct; one with a value receiver works on a copy. Go ไม่มีคลาส struct ใช้รวมฟิลด์เข้าด้วยกัน และ method คือฟังก์ชันที่ผูกกับชนิดข้อมูล ชื่อที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ package อื่นมองเห็นได้ ส่วนชื่อตัวพิมพ์เล็กใช้ได้เฉพาะใน package ของมัน method ที่มี pointer receiver (*Account) แก้ไข struct ได้ ส่วน value receiver ทำงานกับสำเนา

WriteเขียนMeansความหมาย
type Point struct{ X, Y int }a new struct typeชนิด struct ใหม่
p := Point{X: 1, Y: 2}a value with named fieldsค่าพร้อมระบุชื่อฟิลด์
&Point{}a pointer to a new valueพอยน์เตอร์ไปยังค่าใหม่
func (p *Point) Move()a method that can change pmethod ที่แก้ไข p ได้
Name / nameexported / private to the packagepackage อื่นใช้ได้ / ใช้ได้เฉพาะใน package

Lesson 9 · Behaviourบทที่ 9 · พฤติกรรม

InterfacesInterface

An interface lists methods. Any type that has those methods satisfies it automatically; there's no implements keyword. That lets a function accept "anything with an Area()" without knowing the concrete types. interface คือรายการของ method ชนิดข้อมูลใดที่มี method ครบก็เป็น interface นั้นโดยอัตโนมัติ ไม่ต้องมีคำว่า implements ทำให้ฟังก์ชันรับ "อะไรก็ได้ที่มี Area()" โดยไม่ต้องรู้ชนิดที่แท้จริง

Tipเคล็ดลับ

Keep interfaces small. The most used ones in Go have a single method: error (Error() string), fmt.Stringer (String() string) and io.Reader (Read(p []byte) (int, error)).ทำ interface ให้เล็ก interface ที่ใช้บ่อยที่สุดใน Go มี method เดียว ได้แก่ error (Error() string), fmt.Stringer (String() string) และ io.Reader (Read(p []byte) (int, error))

Lesson 10 · When things go wrongบทที่ 10 · เมื่อเกิดปัญหา

ErrorsError

Go has no exceptions for everyday problems. A function that can fail returns an error as its last result, and the caller checks it straight away with if err != nil. You'll write that line a lot; it keeps every failure visible right where it happens. Go ไม่ใช้ exception กับปัญหาทั่วไป ฟังก์ชันที่อาจล้มเหลวจะคืน error เป็นค่าสุดท้าย และผู้เรียกตรวจทันทีด้วย if err != nil จะได้เขียนบรรทัดนี้บ่อยมาก เพราะทำให้เห็นทุกความล้มเหลวตรงจุดที่เกิด

Codeโค้ดDoesทำอะไร
errors.New("msg")a simple error valueค่า error แบบง่าย
fmt.Errorf("doing x: %w", err)add context and keep the originalเพิ่มบริบทและเก็บ error เดิมไว้
errors.Is(err, ErrNotFound)is this (or a wrapped) error that one?error นี้ (หรือที่ถูกห่อไว้) ใช่ตัวนั้นไหม
panic(...)stop the program: only for bugs that should never happenหยุดโปรแกรม: ใช้กับบั๊กที่ไม่ควรเกิดขึ้นเท่านั้น
PanicsPanic

Reading past the end of a slice or writing to a nil map makes Go panic: the program stops and prints a stack trace. Lesson 16 shows the real messages. Treat a panic as a bug to fix, not as an error to handle.การอ่านเกินขอบ slice หรือเขียนลง map ที่เป็น nil จะทำให้ Go panic คือโปรแกรมหยุดและพิมพ์ stack trace บทที่ 16 แสดงข้อความจริง ให้ถือว่า panic คือบั๊กที่ต้องแก้ ไม่ใช่ error ที่ต้องจัดการ

Lesson 11 · Doing many things at onceบทที่ 11 · ทำหลายอย่างพร้อมกัน

Goroutines & channelsGoroutine และ channel

Put go in front of a function call and it runs in a goroutine, alongside the rest of your program. Goroutines are cheap; a server can run thousands. A channel passes values between them and makes the receiver wait until a value arrives. ใส่ go หน้าการเรียกฟังก์ชัน ฟังก์ชันนั้นจะทำงานใน goroutine ไปพร้อมกับส่วนอื่นของโปรแกรม goroutine ใช้ทรัพยากรน้อยมาก เซิร์ฟเวอร์หนึ่งตัวรันได้เป็นพันๆ channel ใช้ส่งค่าระหว่าง goroutine และทำให้ฝั่งรับรอจนกว่าจะมีค่ามาถึง

The three downloads took 600 ms of work but finished in about 300 ms, in the order they completed. When goroutines share a variable, protect it with a sync.Mutex, and use a sync.WaitGroup to wait for all of them: การดาวน์โหลดสามงานรวมกันใช้เวลาทำงาน 600 ms แต่เสร็จในเวลาประมาณ 300 ms และเรียงตามลำดับที่เสร็จ เมื่อหลาย goroutine ใช้ตัวแปรร่วมกัน ให้ป้องกันด้วย sync.Mutex และใช้ sync.WaitGroup เพื่อรอให้ทุกตัวเสร็จ:

Carefulระวัง

Without the mutex, two goroutines can read and write total at the same moment and lose updates. The result is sometimes wrong, which makes the bug hard to find. go run -race and go test -race detect this for you.ถ้าไม่มี mutex สอง goroutine อาจอ่านและเขียน total ในเวลาเดียวกันจนค่าที่บวกหายไป ผลลัพธ์จะผิดเป็นบางครั้ง ทำให้หาบั๊กยาก go run -race และ go test -race ช่วยตรวจหาปัญหานี้ให้

Lesson 12 · Organising codeบทที่ 12 · จัดระเบียบโค้ด

Packages & modulesPackage และ module

The rest of this guide builds one small program in a real terminal: a task list, then a JSON API for it. A module is the project (go.mod); a package is one folder of .go files. The tasks package keeps the data; main uses it. ส่วนที่เหลือของคู่มือนี้สร้างโปรแกรมเล็กๆ หนึ่งตัวในเทอร์มินัลจริง เริ่มจากรายการงาน แล้วต่อด้วย JSON API ของมัน module คือตัวโปรเจกต์ (go.mod) ส่วน package คือโฟลเดอร์หนึ่งที่มีไฟล์ .go package tasks เก็บข้อมูล และ main เป็นผู้ใช้

  • Import a package by its module path plus folder: "example.com/taskapi/tasks". Then use it as tasks.NewStore().import package ด้วยพาธของ module ต่อด้วยโฟลเดอร์ เช่น "example.com/taskapi/tasks" แล้วเรียกใช้เป็น tasks.NewStore()
  • Store, Add and Task start with a capital letter, so main can use them. The fields mu, tasks and nextID don't, so only the tasks package can touch them.Store, Add และ Task ขึ้นต้นด้วยตัวพิมพ์ใหญ่ main จึงใช้ได้ ส่วนฟิลด์ mu, tasks และ nextID ไม่ใช่ จึงมีแต่ package tasks ที่แตะต้องได้
  • A comment right above an exported name is its documentation; go doc and editors show it.คอมเมนต์ที่อยู่เหนือชื่อที่ export คือเอกสารของมัน go doc และตัวแก้โค้ดจะแสดงให้เห็น
Terminalเทอร์มินัลgo1.25.4 · macOS
$ go mod init example.com/taskapi
go: creating new go.mod: module example.com/taskapi

$ go run .
error: title is required
1. Learn Go (done: true)
2. Build an API (done: false)

$ go vet ./...

go vet printed nothing: it found no suspicious code. ./... means "this package and every package below it".go vet ไม่พิมพ์อะไรเลย แปลว่าไม่พบโค้ดที่น่าสงสัย ./... หมายถึง "package นี้และทุก package ที่อยู่ข้างใต้"

The tools catch mistakes for youเครื่องมือช่วยจับข้อผิดพลาดให้

To see that, I added two bad lines to main.go: one prints a string with %d, the other reads the private field store.tasks. เพื่อให้เห็นภาพ ผมเพิ่มบรรทัดที่ผิดสองบรรทัดใน main.go บรรทัดแรกพิมพ์ string ด้วย %d อีกบรรทัดอ่านฟิลด์ส่วนตัว store.tasks

Terminalเทอร์มินัลgo1.25.4 · macOS
$ go build ./...
# example.com/taskapi
./main.go:18:20: store.tasks undefined (cannot refer to unexported field tasks)

$ go vet ./...   # after removing the store.tasks line
# example.com/taskapi
# [example.com/taskapi]
./main.go:17:14: fmt.Printf format %d has arg "three" of wrong type string

Go also has one official code style, applied by gofmt (or go fmt). Nobody argues about tabs or brace position; most editors run it every time you save. Go มีรูปแบบการจัดโค้ดอย่างเป็นทางการแบบเดียว ซึ่ง gofmt (หรือ go fmt) จัดให้ จึงไม่มีใครต้องเถียงเรื่องแท็บหรือตำแหน่งวงเล็บ ตัวแก้โค้ดส่วนใหญ่รันให้ทุกครั้งที่บันทึก

gofmt on a messy filegofmt กับไฟล์ที่จัดไม่เรียบร้อยgo1.25.4 · macOS
$ gofmt -l .
main.go

$ gofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -1,5 +1,7 @@
 package main
+
 import "fmt"
-func main(){
-fmt.Println("hi")
-    }
+
+func main() {
+	fmt.Println("hi")
+}

-l lists files that need formatting, -d shows the changes, and -w writes them.-l แสดงไฟล์ที่ต้องจัดรูปแบบ -d แสดงสิ่งที่จะเปลี่ยน และ -w เขียนการเปลี่ยนแปลงลงไฟล์

Lesson 13 · The webบทที่ 13 · เว็บ

A JSON web serverเว็บเซิร์ฟเวอร์ JSON

Go's standard library includes a production-ready web server, net/http. No framework needed. Replace main.go with a small JSON API around the same tasks package: ไลบรารีมาตรฐานของ Go มีเว็บเซิร์ฟเวอร์ที่พร้อมใช้งานจริงอยู่แล้ว คือ net/http ไม่ต้องใช้เฟรมเวิร์ก แทนที่ main.go ด้วย JSON API เล็กๆ ที่ใช้ package tasks ตัวเดิม:

  • "GET /tasks" and "PATCH /tasks/{id}/done" are route patterns with a method, built into Go since 1.22. r.PathValue("id") reads the {id} part."GET /tasks" และ "PATCH /tasks/{id}/done" คือรูปแบบ route ที่ระบุ method ได้ ซึ่งมีในตัว Go ตั้งแต่ 1.22 r.PathValue("id") ใช้อ่านส่วน {id}
  • The tags on Task (`json:"title"`) choose the JSON names. json.NewDecoder(r.Body).Decode(&body) reads the request; json.NewEncoder(w).Encode(v) writes the response.tag บน Task (`json:"title"`) กำหนดชื่อใน JSON ส่วน json.NewDecoder(r.Body).Decode(&body) อ่านคำขอ และ json.NewEncoder(w).Encode(v) เขียนคำตอบ
  • Each request runs in its own goroutine, so several can hit Store at once. That's why Store uses a sync.Mutex (lesson 11).แต่ละคำขอทำงานใน goroutine ของตัวเอง หลายคำขอจึงอาจเข้าถึง Store พร้อมกัน นี่คือเหตุผลที่ Store ใช้ sync.Mutex (บทที่ 11)
Terminal 1 · serverเทอร์มินัล 1 · เซิร์ฟเวอร์go1.25.4 · macOS
$ go build -o taskapi . && ./taskapi
2026/10/05 23:23:35 listening on http://localhost:8080
Terminal 2 · requestsเทอร์มินัล 2 · คำขอgo1.25.4 · macOS
$ curl -s localhost:8080/tasks
[]

$ curl -si -X POST localhost:8080/tasks -d '{"title":"Learn Go"}'
HTTP/1.1 201 Created
Content-Type: application/json

{"id":1,"title":"Learn Go","done":false}

$ curl -s -X POST localhost:8080/tasks -d '{"title":"  Build an API  "}'
{"id":2,"title":"Build an API","done":false}

$ curl -s -w "%{http_code}\n" -X POST localhost:8080/tasks -d '{"title":"   "}'
{"error":"title is required"}
422

$ curl -s -w "%{http_code}\n" -X POST localhost:8080/tasks -d '{"title":'
{"error":"invalid JSON"}
400

$ curl -s -X PATCH localhost:8080/tasks/1/done
{"id":1,"title":"Learn Go","done":true}

$ curl -s -w "%{http_code}\n" -X PATCH localhost:8080/tasks/99/done
{"error":"task not found"}
404

$ curl -s -w "%{http_code}\n" -X DELETE localhost:8080/tasks
Method Not Allowed
405

$ curl -s localhost:8080/tasks
[{"id":1,"title":"Learn Go","done":true},{"id":2,"title":"Build an API","done":false}]

Requests also sent -H "Content-Type: application/json", and the date and length headers are left out of the -i output. Note the 422 for a title of only spaces, the 400 for broken JSON, and the 405 Go returns by itself for a method no route handles.คำขอส่ง -H "Content-Type: application/json" ไปด้วย และส่วนหัววันที่กับความยาวถูกตัดออกจากผลลัพธ์ของ -i สังเกตว่าได้ 422 เมื่อชื่องานมีแค่ช่องว่าง ได้ 400 เมื่อ JSON เสีย และ Go ตอบ 405 ให้เองเมื่อไม่มี route ใดรับ method นั้น

Lesson 14 · Confidenceบทที่ 14 · ความมั่นใจ

Testingการทดสอบ

Testing is built into Go: no library to install. Tests live next to the code in files ending in _test.go, and go test runs them. Go code usually tests with a table: a list of inputs and expected results, checked in one loop. Here's the idea in a runnable form. Delete the TrimSpace line and press Run. การทดสอบมีมาในตัว Go ไม่ต้องติดตั้งไลบรารีเพิ่ม เทสต์อยู่ข้างๆ โค้ดในไฟล์ที่ลงท้ายด้วย _test.go และ go test เป็นตัวรัน โค้ด Go มักทดสอบด้วยตาราง คือรายการข้อมูลเข้าและผลลัพธ์ที่คาดไว้ แล้วตรวจในลูปเดียว นี่คือแนวคิดในแบบที่รันได้ ลองลบบรรทัด TrimSpace แล้วกด Run

The real tests for the task API: a table-driven test for CleanTitle, and two tests that call the HTTP handler directly with httptest, with no server or network. เทสต์จริงของ task API ได้แก่เทสต์แบบตารางสำหรับ CleanTitle และเทสต์สองตัวที่เรียก HTTP handler โดยตรงด้วย httptest ไม่ต้องมีเซิร์ฟเวอร์หรือเครือข่าย

Red: a planted bugแดง: บั๊กที่ตั้งใจใส่ไว้

To show a failure, I deleted the strings.TrimSpace line from CleanTitle. Three checks catch it, in both packages: เพื่อให้เห็นความล้มเหลว ผมลบบรรทัด strings.TrimSpace ออกจาก CleanTitle มีการตรวจสามจุดที่จับได้ ในทั้งสอง package:

Run 1 · with the bugรันครั้งที่ 1 · มีบั๊กgo1.25.4 · macOS
$ go test ./...
--- FAIL: TestBlankTitleIsRejected (0.00s)
    main_test.go:35: status 201, want 422
FAIL
FAIL	example.com/taskapi	1.212s
--- FAIL: TestCleanTitle (0.00s)
    --- FAIL: TestCleanTitle/trims_spaces (0.00s)
        tasks_test.go:28: CleanTitle("  Write tests  ") = "  Write tests  ", want "Write tests"
    --- FAIL: TestCleanTitle/only_spaces (0.00s)
        tasks_test.go:25: CleanTitle("   ") error = <nil>, want title is required
FAIL
FAIL	example.com/taskapi/tasks	0.653s
FAIL

Green: put the line backเขียว: ใส่บรรทัดนั้นกลับคืน

Run 2 · after the fixรันครั้งที่ 2 · หลังแก้go1.25.4 · macOS
$ go test ./...
ok  	example.com/taskapi	0.519s
ok  	example.com/taskapi/tasks	1.051s

$ go test -v ./tasks
=== RUN   TestCleanTitle
=== RUN   TestCleanTitle/plain_title
=== RUN   TestCleanTitle/trims_spaces
=== RUN   TestCleanTitle/empty
=== RUN   TestCleanTitle/only_spaces
--- PASS: TestCleanTitle (0.00s)
    --- PASS: TestCleanTitle/plain_title (0.00s)
    --- PASS: TestCleanTitle/trims_spaces (0.00s)
    --- PASS: TestCleanTitle/empty (0.00s)
    --- PASS: TestCleanTitle/only_spaces (0.00s)
=== RUN   TestMarkDone
--- PASS: TestMarkDone (0.00s)
PASS
ok  	example.com/taskapi/tasks	0.300s

$ go test -cover ./...
ok  	example.com/taskapi	0.577s	coverage: 53.3% of statements
ok  	example.com/taskapi/tasks	1.112s	coverage: 83.3% of statements

$ go test -run 'TestCleanTitle/only' -v ./tasks
=== RUN   TestCleanTitle
=== RUN   TestCleanTitle/only_spaces
--- PASS: TestCleanTitle (0.00s)
    --- PASS: TestCleanTitle/only_spaces (0.00s)
PASS
ok  	example.com/taskapi/tasks	0.323s

-v lists every test and sub-test, -cover shows how much of the code the tests ran, and -run picks tests by name.-v แสดงทุกเทสต์และเทสต์ย่อย -cover แสดงว่าเทสต์รันโค้ดไปกี่เปอร์เซ็นต์ และ -run เลือกเทสต์ตามชื่อ

Lesson 15 · Your turnบทที่ 15 · ถึงตาคุณแล้ว

Practiceแบบฝึกหัด

Write the code, press Run, and compare with the solution when you're done. Every solution was run with go run, and its output is shown under it. เขียนโค้ด กด Run แล้วเทียบกับเฉลยเมื่อเสร็จ ทุกเฉลยถูกรันด้วย go run และแสดงผลลัพธ์ไว้ด้านล่าง

1. Total of the even numbers1. ผลรวมของเลขคู่

Add up only the even numbers. n%2 == 0 is true for even numbers.รวมเฉพาะเลขคู่ n%2 == 0 เป็นจริงเมื่อ n เป็นเลขคู่

2. Count the words2. นับคำ

Count how often each word appears, using a map[string]int and strings.Fields.นับว่าแต่ละคำปรากฏกี่ครั้ง โดยใช้ map[string]int และ strings.Fields

3. FizzBuzz3. FizzBuzz

For 1 to 15, print Fizz for multiples of 3, Buzz for multiples of 5, FizzBuzz for both, and the number otherwise. A switch with no value fits well.สำหรับ 1 ถึง 15 ให้พิมพ์ Fizz เมื่อหารด้วย 3 ลงตัว Buzz เมื่อหารด้วย 5 ลงตัว FizzBuzz เมื่อหารลงตัวทั้งคู่ และพิมพ์ตัวเลขในกรณีอื่น switch ที่ไม่มีค่าใช้ได้ดีมาก

4. Reverse text, Thai included4. กลับข้อความ รวมถึงภาษาไทย

Reverse "Hello โลก" so it prints กลโ olleH. Reversing bytes would break the Thai letters; reverse runes instead.กลับข้อความ "Hello โลก" ให้พิมพ์เป็น กลโ olleH ถ้ากลับเป็นไบต์ ตัวอักษรไทยจะเสีย ให้กลับเป็น rune แทน

5. Add up in parallel5. บวกเลขแบบขนาน

Split the slice in two, add each half in its own goroutine, send both sums over a channel, and print the total.แบ่ง slice เป็นสองครึ่ง ให้แต่ละครึ่งบวกกันใน goroutine ของตัวเอง ส่งผลรวมทั้งสองผ่าน channel แล้วพิมพ์ยอดรวม

Lesson 16 · Debuggingบทที่ 16 · การดีบัก

Common mistakesข้อผิดพลาดที่พบบ่อย

The comments show what the real Go compiler and runtime printed (go run, Go 1.25.4). Several of these are compile errors, which the browser runner doesn't always report, so learn the messages here. คอมเมนต์แสดงสิ่งที่คอมไพเลอร์และ runtime ของ Go ตัวจริงพิมพ์ออกมา (go run, Go 1.25.4) หลายข้อเป็น compile error ซึ่งตัวรันในเบราว์เซอร์ไม่ได้แจ้งทุกครั้ง จึงควรจำข้อความจากที่นี่

An unused variable or importตัวแปรหรือ import ที่ไม่ได้ใช้

Go refuses to compile code that declares something and never uses it. It feels strict at first, but it keeps dead code out. Use it, delete it, or assign it to _.Go ไม่ยอมคอมไพล์โค้ดที่ประกาศอะไรไว้แล้วไม่ได้ใช้ ตอนแรกอาจรู้สึกว่าเข้มงวด แต่ช่วยกันไม่ให้มีโค้ดที่ไม่ได้ใช้ค้างอยู่ ให้ใช้มัน ลบทิ้ง หรือกำหนดให้ _

✗ Wrong✗ ผิด
✓ Right✓ ถูก

:= outside a functionใช้ := นอกฟังก์ชัน

:= only works inside functions. At package level, use var.:= ใช้ได้เฉพาะในฟังก์ชัน ถ้าอยู่ระดับ package ให้ใช้ var

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Writing to a nil mapเขียนลง map ที่เป็น nil

var m map[string]int is nil: you can read from it, but writing panics. Create it with map[string]int{} or make.var m map[string]int เป็น nil อ่านได้ แต่ถ้าเขียนจะ panic ให้สร้างด้วย map[string]int{} หรือ make

✗ Wrong✗ ผิด
✓ Right✓ ถูก

A value receiver that should change the structvalue receiver ที่ควรแก้ไข struct

A value receiver gets a copy, so the change is lost. If a method modifies its struct, give it a pointer receiver.value receiver ได้รับสำเนา การแก้ไขจึงหายไป ถ้า method ต้องแก้ struct ของตัวเอง ให้ใช้ pointer receiver

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Ignoring an error with _ทิ้ง error ด้วย _

When a call fails, the other results are usually zero values. Ignore the error and you carry on with wrong data, silently.เมื่อการเรียกล้มเหลว ค่าอื่นที่คืนมามักเป็นค่าศูนย์ ถ้าทิ้ง error ไป โปรแกรมจะทำงานต่อด้วยข้อมูลที่ผิดโดยไม่มีใครรู้

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Index out of rangeตำแหน่งเกินขอบเขต

Indexes run from 0 to len-1. Go checks every index and panics instead of reading random memory. The message says the index and the length.ตำแหน่งเริ่มจาก 0 ถึง len-1 Go ตรวจทุกครั้งและจะ panic แทนการอ่านหน่วยความจำมั่วๆ ข้อความบอกทั้งตำแหน่งและความยาว

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Lesson 17 · Referenceบทที่ 17 · อ้างอิง

Glossaryคำศัพท์

The English terms are what you'll see in Go's documentation and error messages, so it pays to know both. คำศัพท์ภาษาอังกฤษคือสิ่งที่จะเจอในเอกสารและข้อความแจ้งข้อผิดพลาดของ Go จึงควรรู้ทั้งสองภาษา

EnglishไทยMeaningความหมาย
moduleโมดูลA project, defined by go.modโปรเจกต์หนึ่งตัว กำหนดด้วย go.mod
packageแพ็กเกจOne folder of Go files that belong togetherโฟลเดอร์หนึ่งที่มีไฟล์ Go ที่อยู่ด้วยกัน
exportedส่งออก (exported)A name starting with a capital letter, usable from other packagesชื่อที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ ใช้จาก package อื่นได้
zero valueค่าศูนย์The value a variable has before you set it: 0, "", false, nilค่าที่ตัวแปรมีก่อนกำหนดค่า: 0, "", false, nil
sliceสไลซ์A list that can grow, written []Tรายการที่ขยายได้ เขียนว่า []T
mapแมปKey–value lookup, written map[K]Vการค้นหาแบบคีย์–ค่า เขียนว่า map[K]V
runeรูน (rune)One Unicode characterอักขระ Unicode หนึ่งตัว
structสตรักต์A type that groups named fieldsชนิดข้อมูลที่รวมฟิลด์ที่มีชื่อเข้าด้วยกัน
method / receiverเมธอด / รีซีฟเวอร์A function attached to a type / the value it's called onฟังก์ชันที่ผูกกับชนิดข้อมูล / ค่าที่ method ถูกเรียกใช้
pointerพอยน์เตอร์The address of a value; &x makes one, *T is its typeที่อยู่ของค่า &x ใช้สร้าง และ *T คือชนิดของมัน
interfaceอินเทอร์เฟซA set of methods; any type with them satisfies itชุดของ method ชนิดใดที่มี method ครบก็เป็น interface นั้น
errorข้อผิดพลาด (error)A value describing a failure, returned as the last resultค่าที่บอกความล้มเหลว คืนเป็นค่าสุดท้าย
panicแพนิกAn unrecoverable stop, usually from a bugการหยุดทำงานที่กู้คืนไม่ได้ มักเกิดจากบั๊ก
deferdeferRun this call when the function returnsให้เรียกคำสั่งนี้ตอนที่ฟังก์ชันจบ
goroutineกอรูทีนA function running alongside the rest of the programฟังก์ชันที่ทำงานไปพร้อมกับส่วนอื่นของโปรแกรม
channelแชนเนลA pipe for passing values between goroutinesท่อส่งค่าระหว่าง goroutine
mutexมิวเท็กซ์A lock so only one goroutine uses shared data at a timeกุญแจที่ทำให้มีเพียง goroutine เดียวใช้ข้อมูลร่วมได้ในแต่ละครั้ง
table-driven testเทสต์แบบตารางA test that loops over a list of inputs and expected resultsเทสต์ที่วนผ่านรายการข้อมูลเข้าและผลลัพธ์ที่คาดไว้