GoDasturchi
The Basic CRUD Handlers: Create & Read

The Basic CRUD Handlers: Create & Read

Asosiy CRUD handlerlar: Yaratish va O'qish

Kutubxona kartotekasini tasavvur qiling: yangi kitob kelganda, kutubxonachi UNGA yangi KARTOCHKA yozadi va katalogga qo'shadi (Create) — mijoz esa kartochka raqamini aytib, o'sha kitob haqida ma'lumot so'raydi (Read). Bizning Bookstore API'da ham xuddi shu ikki amal — POST /books (yaratish) va GET /books/{id} (o'qish) — birinchi ishlaydigan qismlar bo'ladi.

example.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strconv"
	"strings"
	"sync"
)

type Book struct {
	ID     string  `json:"id"`
	Title  string  `json:"title"`
	Author string  `json:"author"`
	Price  float64 `json:"price"`
}

type BookStore struct {
	mu     sync.Mutex
	books  map[string]*Book
	nextID int
}

func NewBookStore() *BookStore {
	return &BookStore{books: make(map[string]*Book)}
}

func (s *BookStore) Create(title, author string, price float64) *Book {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.nextID++
	id := strconv.Itoa(s.nextID)
	book := &Book{ID: id, Title: title, Author: author, Price: price}
	s.books[id] = book
	return book
}

func (s *BookStore) Get(id string) (*Book, bool) {
	s.mu.Lock()
	defer s.mu.Unlock()
	book, ok := s.books[id]
	return book, ok
}

func main() {
	store := NewBookStore()
	mux := http.NewServeMux()

	mux.HandleFunc("POST /books", func(w http.ResponseWriter, r *http.Request) {
		var req struct {
			Title  string  `json:"title"`
			Author string  `json:"author"`
			Price  float64 `json:"price"`
		}
		json.NewDecoder(r.Body).Decode(&req)
		book := store.Create(req.Title, req.Author, req.Price)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(book)
	})

	mux.HandleFunc("GET /books/{id}", func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		book, ok := store.Get(id)
		if !ok {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(book)
	})

	createBody := strings.NewReader(`{"title":"Go bilan tanishuv","author":"A. Karimov","price":45000}`)
	createReq := httptest.NewRequest("POST", "/books", createBody)
	createRec := httptest.NewRecorder()
	mux.ServeHTTP(createRec, createReq)
	fmt.Println("POST /books ->", createRec.Code)

	getReq := httptest.NewRequest("GET", "/books/1", nil)
	getRec := httptest.NewRecorder()
	mux.ServeHTTP(getRec, getReq)
	fmt.Println("GET /books/1 ->", getRec.Code)
	fmt.Println(getRec.Body.String())
}

http.NewServeMux() va "POST /books", "GET /books/{id}" shablonlari — "Routing and Path Parameters" darsida ko'rgan METOD+YO'L naqshi: bitta mux ICHIDA, har bir (metod, yo'l) juftligi O'ZINING handler'iga ega. BookStoresync.Mutex bilan himoyalangan, chunki HAQIQIY serverda ko'plab so'rovlar BIR VAQTDA kelishi mumkin ("Mutex" darsidagi tamoyil): ikkita so'rov bir vaqtda nextIDni oshirsa, mu.Lock()/Unlock() ular BIR-BIRINI KUTISHINI ta'minlaydi.

httptest.NewRequest + httptest.NewRecorder + mux.ServeHTTP(rec, req) — "HTTP Testing with httptest" darsida ko'rgan naqsh: HAQIQIY tarmoq portini ochmasdan, handler'ni to'g'ridan-to'g'ri chaqirib sinaymiz. json.NewEncoder(w).Encode(book)Book struct'ini JAVOB TANASIGA to'g'ridan-to'g'ri JSON qilib yozadi; maydonlar JSON'da ANIQ struct'da yozilgan TARTIBDA chiqadi (id, title, author, price), shuning uchun natija har doim BASHORAT QILINADIGAN.

>_ Exercise

BookStore'ga barcha kitoblarni RO'YXAT qilib qaytaruvchi endpoint qo'shing.

  • BookStore'ga List() []*Book metodini qo'shing: barcha kitoblarni ID bo'yicha SONLI tartibda (sort.Slice va strconv.Atoi bilan) saralab qaytaring — xarita tartibi TASODIFIY bo'lgani uchun ("Iterating over Maps" darsini eslang)
  • "GET /books" handlerini yozing: store.List() natijasini JSON massiv sifatida javob tanasiga yozing
  • ikkita kitob yaratib (store.Create orqali), GET /books ga so'rov yuboring va javob tanasini chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Create (POST) va Read (GET) — CRUD'ning birinchi ikki amali; ro'yxat qaytarishda xarita tartibi TASODIFIY bo'lgani uchun, natijani har doim ANIQ bir mezon (masalan ID) bo'yicha saralash kerak.

NEXT UP

The Basic CRUD Handlers: Update & Delete

OUTPUT

$ go run main.go
Kodingizni ishga tushiring

The Basic CRUD Handlers: Create & Read

Asosiy CRUD handlerlar: Yaratish va O'qish

Kutubxona kartotekasini tasavvur qiling: yangi kitob kelganda, kutubxonachi UNGA yangi KARTOCHKA yozadi va katalogga qo'shadi (Create) — mijoz esa kartochka raqamini aytib, o'sha kitob haqida ma'lumot so'raydi (Read). Bizning Bookstore API'da ham xuddi shu ikki amal — POST /books (yaratish) va GET /books/{id} (o'qish) — birinchi ishlaydigan qismlar bo'ladi.

example.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strconv"
	"strings"
	"sync"
)

type Book struct {
	ID     string  `json:"id"`
	Title  string  `json:"title"`
	Author string  `json:"author"`
	Price  float64 `json:"price"`
}

type BookStore struct {
	mu     sync.Mutex
	books  map[string]*Book
	nextID int
}

func NewBookStore() *BookStore {
	return &BookStore{books: make(map[string]*Book)}
}

func (s *BookStore) Create(title, author string, price float64) *Book {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.nextID++
	id := strconv.Itoa(s.nextID)
	book := &Book{ID: id, Title: title, Author: author, Price: price}
	s.books[id] = book
	return book
}

func (s *BookStore) Get(id string) (*Book, bool) {
	s.mu.Lock()
	defer s.mu.Unlock()
	book, ok := s.books[id]
	return book, ok
}

func main() {
	store := NewBookStore()
	mux := http.NewServeMux()

	mux.HandleFunc("POST /books", func(w http.ResponseWriter, r *http.Request) {
		var req struct {
			Title  string  `json:"title"`
			Author string  `json:"author"`
			Price  float64 `json:"price"`
		}
		json.NewDecoder(r.Body).Decode(&req)
		book := store.Create(req.Title, req.Author, req.Price)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(book)
	})

	mux.HandleFunc("GET /books/{id}", func(w http.ResponseWriter, r *http.Request) {
		id := r.PathValue("id")
		book, ok := store.Get(id)
		if !ok {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(book)
	})

	createBody := strings.NewReader(`{"title":"Go bilan tanishuv","author":"A. Karimov","price":45000}`)
	createReq := httptest.NewRequest("POST", "/books", createBody)
	createRec := httptest.NewRecorder()
	mux.ServeHTTP(createRec, createReq)
	fmt.Println("POST /books ->", createRec.Code)

	getReq := httptest.NewRequest("GET", "/books/1", nil)
	getRec := httptest.NewRecorder()
	mux.ServeHTTP(getRec, getReq)
	fmt.Println("GET /books/1 ->", getRec.Code)
	fmt.Println(getRec.Body.String())
}

http.NewServeMux() va "POST /books", "GET /books/{id}" shablonlari — "Routing and Path Parameters" darsida ko'rgan METOD+YO'L naqshi: bitta mux ICHIDA, har bir (metod, yo'l) juftligi O'ZINING handler'iga ega. BookStoresync.Mutex bilan himoyalangan, chunki HAQIQIY serverda ko'plab so'rovlar BIR VAQTDA kelishi mumkin ("Mutex" darsidagi tamoyil): ikkita so'rov bir vaqtda nextIDni oshirsa, mu.Lock()/Unlock() ular BIR-BIRINI KUTISHINI ta'minlaydi.

httptest.NewRequest + httptest.NewRecorder + mux.ServeHTTP(rec, req) — "HTTP Testing with httptest" darsida ko'rgan naqsh: HAQIQIY tarmoq portini ochmasdan, handler'ni to'g'ridan-to'g'ri chaqirib sinaymiz. json.NewEncoder(w).Encode(book)Book struct'ini JAVOB TANASIGA to'g'ridan-to'g'ri JSON qilib yozadi; maydonlar JSON'da ANIQ struct'da yozilgan TARTIBDA chiqadi (id, title, author, price), shuning uchun natija har doim BASHORAT QILINADIGAN.

>_ Exercise

BookStore'ga barcha kitoblarni RO'YXAT qilib qaytaruvchi endpoint qo'shing.

  • BookStore'ga List() []*Book metodini qo'shing: barcha kitoblarni ID bo'yicha SONLI tartibda (sort.Slice va strconv.Atoi bilan) saralab qaytaring — xarita tartibi TASODIFIY bo'lgani uchun ("Iterating over Maps" darsini eslang)
  • "GET /books" handlerini yozing: store.List() natijasini JSON massiv sifatida javob tanasiga yozing
  • ikkita kitob yaratib (store.Create orqali), GET /books ga so'rov yuboring va javob tanasini chop eting

Stuck? Reveal a hint to help you.

Hints (0/3)

Key Takeaway

Key Takeaway:

Create (POST) va Read (GET) — CRUD'ning birinchi ikki amali; ro'yxat qaytarishda xarita tartibi TASODIFIY bo'lgani uchun, natijani har doim ANIQ bir mezon (masalan ID) bo'yicha saralash kerak.

NEXT UP

The Basic CRUD Handlers: Update & Delete