# HTTP Handler Style Guide

### HTTP Handler
* Each handler should implement `http.Handler`
  - This can be done by embedding a [`httprouter.Router`](https://github.com/julienschmidt/httprouter)
  (a light weight HTTP router that supports variables in the routing pattern and matches against the request method)
* Required services should be exported on the struct

```go
// ThingHandler represents an HTTP API handler for things.
type ThingHandler struct {
	// embedded httprouter.Router as a lazy way to implement http.Handler
	*httprouter.Router

	ThingService         platform.ThingService
	AuthorizationService platform.AuthorizationService

	Logger               *zap.Logger
}
```

### HTTP Handler Constructor

* Routes should be declared in the constructor

```go
// NewThingHandler returns a new instance of ThingHandler.
func NewThingHandler() *ThingHandler {
	h := &ThingHandler{
		Router: httprouter.New(),
		Logger: zap.Nop(),
	}

	h.HandlerFunc("POST", "/api/v2/things", h.handlePostThing)
	h.HandlerFunc("GET", "/api/v2/things", h.handleGetThings)

	return h
}
```

### Route handlers (`http.HandlerFunc`s)

* Each route handler should have an associated request struct and decode function
* The decode function should take a `context.Context` and an `*http.Request` and return the associated route request struct

```go
type postThingRequest struct {
	Thing *platform.Thing
}

func decodePostThingRequest(ctx context.Context, r *http.Request) (*postThingRequest, error) {
	t := &platform.Thing{}
	if err := json.NewDecoder(r.Body).Decode(t); err != nil {
		return nil, err
	}

	return &postThingRequest{
		Thing: t,
	}, nil
}
```

* Route `http.HandlerFuncs` should separate the decoding and encoding of HTTP requests/response from actual handler logic

```go
// handlePostThing is the HTTP handler for the POST /api/v2/things route.
func (h *ThingHandler) handlePostThing(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	req, err := decodePostThingRequest(ctx, r)
	if err != nil {
		EncodeError(ctx, err, w)
		return
	}

	// Do stuff here
	if err := h.ThingService.CreateThing(ctx, req.Thing); err != nil {
		EncodeError(ctx, err, w)
		return
	}

	if err := encodeResponse(ctx, w, http.StatusCreated, req.Thing); err != nil {
		h.Logger.Info("encoding response failed", zap.Error(err))
		return
	}
}
```

* `http.HandlerFunc`'s that require particular encoding of http responses should implement an encode response function