Merge pull request #105 from syumai/support-sql-open-on-d1

support sql.Open for D1
This commit is contained in:
syumai 2024-04-17 02:10:24 +09:00 committed by GitHub
commit 482b28195a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 36 additions and 31 deletions

View File

@ -15,6 +15,6 @@ deploy:
init-db: init-db:
wrangler d1 execute d1-blog-server --file=./schema.sql wrangler d1 execute d1-blog-server --file=./schema.sql
.PHONY: init-db-preview .PHONY: init-db-local
init-db-preview: init-db-local:
wrangler d1 execute d1-blog-server-preview --file=./schema.sql wrangler d1 execute d1-blog-server --file=./schema.sql --local

View File

@ -65,9 +65,9 @@ This project requires these tools to be installed globally.
``` ```
# development # development
make init-db-preview # initialize preview DB (remove all rows) make init-db-local # initialize local DB (remove all rows)
make dev # run dev server make dev # run dev server
make build # build Go Wasm binary make build # build Go Wasm binary
# production # production
make init-db # initialize production DB (remove all rows) make init-db # initialize production DB (remove all rows)

View File

@ -10,34 +10,27 @@ import (
"time" "time"
"github.com/syumai/workers/_examples/d1-blog-server/app/model" "github.com/syumai/workers/_examples/d1-blog-server/app/model"
"github.com/syumai/workers/cloudflare/d1"
_ "github.com/syumai/workers/cloudflare/d1" // register driver
) )
type articleHandler struct{} type articleHandler struct {
db *sql.DB
}
var _ http.Handler = (*articleHandler)(nil) var _ http.Handler = (*articleHandler)(nil)
func NewArticleHandler() http.Handler { func NewArticleHandler(db *sql.DB) http.Handler {
return &articleHandler{} return &articleHandler{
db: db,
}
} }
func (h *articleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (h *articleHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// initialize DB.
// D1 connector requires request's context to initialize DB.
c, err := d1.OpenConnector("BlogDB")
if err != nil {
h.handleErr(w, http.StatusInternalServerError, fmt.Sprintf("failed to initialize DB: %v", err))
}
// use sql.OpenDB instead of sql.Open.
db := sql.OpenDB(c)
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
h.listArticles(w, req, db) h.listArticles(w, req)
return return
case http.MethodPost: case http.MethodPost:
h.createArticle(w, req, db) h.createArticle(w, req)
return return
} }
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
@ -51,7 +44,7 @@ func (h *articleHandler) handleErr(w http.ResponseWriter, status int, msg string
w.Write([]byte(msg)) w.Write([]byte(msg))
} }
func (h *articleHandler) createArticle(w http.ResponseWriter, req *http.Request, db *sql.DB) { func (h *articleHandler) createArticle(w http.ResponseWriter, req *http.Request) {
var createArticleReq model.CreateArticleRequest var createArticleReq model.CreateArticleRequest
if err := json.NewDecoder(req.Body).Decode(&createArticleReq); err != nil { if err := json.NewDecoder(req.Body).Decode(&createArticleReq); err != nil {
h.handleErr(w, http.StatusBadRequest, h.handleErr(w, http.StatusBadRequest,
@ -66,7 +59,7 @@ func (h *articleHandler) createArticle(w http.ResponseWriter, req *http.Request,
CreatedAt: uint64(now), CreatedAt: uint64(now),
} }
result, err := db.Exec(` result, err := h.db.Exec(`
INSERT INTO articles (title, body, created_at) INSERT INTO articles (title, body, created_at)
VALUES (?, ?, ?) VALUES (?, ?, ?)
`, article.Title, article.Body, article.CreatedAt) `, article.Title, article.Body, article.CreatedAt)
@ -95,12 +88,13 @@ VALUES (?, ?, ?)
} }
} }
func (h *articleHandler) listArticles(w http.ResponseWriter, req *http.Request, db *sql.DB) { func (h *articleHandler) listArticles(w http.ResponseWriter, req *http.Request) {
rows, err := db.Query(` rows, err := h.db.Query(`
SELECT id, title, body, created_at FROM articles SELECT id, title, body, created_at FROM articles
ORDER BY created_at DESC; ORDER BY created_at DESC;
`) `)
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err)
h.handleErr(w, http.StatusInternalServerError, h.handleErr(w, http.StatusInternalServerError,
"failed to load article") "failed to load article")
return return

View File

@ -1,13 +1,20 @@
package main package main
import ( import (
"database/sql"
"log"
"net/http" "net/http"
"github.com/syumai/workers" "github.com/syumai/workers"
"github.com/syumai/workers/_examples/d1-blog-server/app" "github.com/syumai/workers/_examples/d1-blog-server/app"
_ "github.com/syumai/workers/cloudflare/d1" // register driver
) )
func main() { func main() {
http.Handle("/articles", app.NewArticleHandler()) db, err := sql.Open("d1", "BlogDB")
if err != nil {
log.Fatalf("error opening DB: %s", err.Error())
}
http.Handle("/articles", app.NewArticleHandler(db))
workers.Serve(nil) // use http.DefaultServeMux workers.Serve(nil) // use http.DefaultServeMux
} }

View File

@ -1,6 +1,6 @@
name = "d1-blog-server" name = "d1-blog-server"
main = "./build/worker.mjs" main = "./build/worker.mjs"
compatibility_date = "2023-01-09" compatibility_date = "2024-04-15"
[build] [build]
command = "make build" command = "make build"

View File

@ -1,9 +1,9 @@
package d1 package d1
import ( import (
"context"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"errors"
) )
func init() { func init() {
@ -16,6 +16,10 @@ var (
_ driver.Driver = (*Driver)(nil) _ driver.Driver = (*Driver)(nil)
) )
func (d *Driver) Open(string) (driver.Conn, error) { func (d *Driver) Open(name string) (driver.Conn, error) {
return nil, errors.New("d1: Open is not supported. use d1.OpenConnector and sql.OpenDB instead") connector, err := OpenConnector(name)
if err != nil {
return nil, err
}
return connector.Connect(context.Background())
} }