Add Blog Posts CRUD with SQLite
- Add modernc.org/sqlite (pure Go, no CGO) - Create models package with Post struct - Implement SQLite connection and schema auto-creation - Add CRUD methods to database package - Create post handlers with JSON API - Register API routes: GET/POST/PUT/DELETE /api/v1/posts - Set default DBURL to file:./data.db with WAL mode
This commit is contained in:
137
internal/handlers/posts.go
Normal file
137
internal/handlers/posts.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.eeqj.de/sneak/gohttpserver/internal/models"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (s *Handlers) HandleListPosts() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
posts, err := s.params.Database.ListPosts(r.Context())
|
||||
if err != nil {
|
||||
s.log.Error("failed to list posts", "error", err)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if posts == nil {
|
||||
posts = []*models.Post{}
|
||||
}
|
||||
|
||||
s.respondJSON(w, r, posts, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Handlers) HandleGetPost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "invalid id"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
post, err := s.params.Database.GetPost(r.Context(), id)
|
||||
if err != nil {
|
||||
s.log.Error("failed to get post", "error", err, "id", id)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if post == nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "not found"}, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, r, post, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Handlers) HandleCreatePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req models.CreatePostRequest
|
||||
if err := s.decodeJSON(w, r, &req); err != nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "invalid request body"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Title == "" || req.Body == "" || req.Author == "" {
|
||||
s.respondJSON(w, r, map[string]string{"error": "title, body, and author are required"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
post, err := s.params.Database.CreatePost(r.Context(), &req)
|
||||
if err != nil {
|
||||
s.log.Error("failed to create post", "error", err)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, r, post, http.StatusCreated)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Handlers) HandleUpdatePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "invalid id"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UpdatePostRequest
|
||||
if err := s.decodeJSON(w, r, &req); err != nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "invalid request body"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
post, err := s.params.Database.UpdatePost(r.Context(), id, &req)
|
||||
if err != nil {
|
||||
s.log.Error("failed to update post", "error", err, "id", id)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if post == nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "not found"}, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, r, post, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Handlers) HandleDeletePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "invalid id"}, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
post, err := s.params.Database.GetPost(r.Context(), id)
|
||||
if err != nil {
|
||||
s.log.Error("failed to get post for delete", "error", err, "id", id)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if post == nil {
|
||||
s.respondJSON(w, r, map[string]string{"error": "not found"}, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.params.Database.DeletePost(r.Context(), id); err != nil {
|
||||
s.log.Error("failed to delete post", "error", err, "id", id)
|
||||
s.respondJSON(w, r, map[string]string{"error": "internal server error"}, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.respondJSON(w, r, nil, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user