Skip to content

Commit b88d8d0

Browse files
committed
Add placeholder API handler
1 parent 5b53800 commit b88d8d0

4 files changed

Lines changed: 115 additions & 0 deletions

File tree

backend/web/api/handler.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package api
2+
3+
import (
4+
"io/fs"
5+
"net/http"
6+
7+
"github.com/theandrew168/bloggulus/backend/command"
8+
"github.com/theandrew168/bloggulus/backend/query"
9+
)
10+
11+
func Handler(
12+
public fs.FS,
13+
cmd *command.Command,
14+
qry *query.Query,
15+
) http.Handler {
16+
mux := http.NewServeMux()
17+
mux.Handle("GET /articles", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18+
w.WriteHeader(200)
19+
w.Write([]byte("Articles API Endpoint"))
20+
}))
21+
return mux
22+
}

backend/web/api/jsonutil/read.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package jsonutil
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"io"
8+
)
9+
10+
// Let's Go Further - Chapter 4.2
11+
func Read(r io.Reader, data any) error {
12+
// Decode the request body into the target destination.
13+
err := json.NewDecoder(r).Decode(data)
14+
if err != nil {
15+
// If there is an error during decoding, start the triage...
16+
var syntaxError *json.SyntaxError
17+
var unmarshalTypeError *json.UnmarshalTypeError
18+
var invalidUnmarshalError *json.InvalidUnmarshalError
19+
20+
switch {
21+
// Use the errors.As() function to check whether the error has the type
22+
// *json.SyntaxError. If it does, then return a plain-english error message
23+
// which includes the location of the problem.
24+
case errors.As(err, &syntaxError):
25+
return fmt.Errorf("body contains badly-formed JSON (at character %d)", syntaxError.Offset)
26+
27+
// In some circumstances Decode() may also return an io.ErrUnexpectedEOF error
28+
// for syntax errors in the JSON. So we check for this using errors.Is() and
29+
// return a generic error message. There is an open issue regarding this at
30+
// https://github.com/golang/go/issues/25956.
31+
case errors.Is(err, io.ErrUnexpectedEOF):
32+
return errors.New("body contains badly-formed JSON")
33+
34+
// Likewise, catch any *json.UnmarshalTypeError errors. These occur when the
35+
// JSON value is the wrong type for the target destination. If the error relates
36+
// to a specific field, then we include that in our error message to make it
37+
// easier for the client to debug.
38+
case errors.As(err, &unmarshalTypeError):
39+
if unmarshalTypeError.Field != "" {
40+
return fmt.Errorf("body contains incorrect JSON type for field %q", unmarshalTypeError.Field)
41+
}
42+
return fmt.Errorf("body contains incorrect JSON type (at character %d)", unmarshalTypeError.Offset)
43+
44+
// An io.EOF error will be returned by Decode() if the request body is empty.
45+
// We check for this with errors.Is() and return a plain-english error message
46+
// instead.
47+
case errors.Is(err, io.EOF):
48+
return errors.New("body must not be empty")
49+
50+
// A json.InvalidUnmarshalError error will be returned if we pass something
51+
// that is not a non-nil pointer to Decode(). We catch this and panic,
52+
// rather than returning an error to our handler.
53+
case errors.As(err, &invalidUnmarshalError):
54+
panic(err)
55+
56+
// For anything else, return the error message as-is.
57+
default:
58+
return err
59+
}
60+
}
61+
62+
return nil
63+
}

backend/web/api/jsonutil/write.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package jsonutil
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
)
7+
8+
// Let's Go Further - Chapter 3.2
9+
func Write(w http.ResponseWriter, status int, data any) error {
10+
// Encode the data to JSON, returning the error if there was one.
11+
js, err := json.Marshal(data)
12+
if err != nil {
13+
return err
14+
}
15+
16+
// Append a newline to make it easier to view in terminal applications.
17+
js = append(js, '\n')
18+
19+
// Add the "Content-Type: application/json" header, then write the
20+
// status code and JSON response.
21+
w.Header().Set("Content-Type", "application/json")
22+
w.WriteHeader(status)
23+
w.Write(js)
24+
25+
return nil
26+
}

backend/web/handler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/theandrew168/bloggulus/backend/job"
1616
"github.com/theandrew168/bloggulus/backend/query"
1717
"github.com/theandrew168/bloggulus/backend/repository"
18+
"github.com/theandrew168/bloggulus/backend/web/api"
1819
"github.com/theandrew168/bloggulus/backend/web/middleware"
1920
"github.com/theandrew168/bloggulus/backend/web/util"
2021
)
@@ -93,6 +94,9 @@ func Handler(
9394
// The main application routes start here.
9495
mux.Handle("GET /{$}", HandleIndexPage(qry))
9596

97+
apiHandler := api.Handler(public, cmd, qry)
98+
mux.Handle("GET /api/v1/", http.StripPrefix("/api/v1", apiHandler))
99+
96100
// Check if the debug auth method should be enabled.
97101
enableDebugAuth := os.Getenv("ENABLE_DEBUG_AUTH") != ""
98102
if enableDebugAuth {

0 commit comments

Comments
 (0)