---
# System prepended metadata

title: Introduction to reactor package

---

# Introduction to reactor package

## Code Walkthrough
:question: Why I choose to implement package reactor?

* Because I need an object that I can pass it to the layer which  needs to send / receive request, response.
* I need to deal with internal server error in the same place, and do some logging.


```mermaid
sequenceDiagram
    participant rc.HandlerWrapper
    participant api.Healthcheck
    rc.HandlerWrapper->>api.Healthcheck: reactor.Context
    Note right of api.Healthcheck: send response using reactor.Context
    api.Healthcheck->>rc.HandlerWrapper: internal server error
    Note left of rc.HandlerWrapper: log the error using logger stored in rc.Reactor
```


Example:
At commit [`002524`](https://github.com/unknowntpo/todos/commit/0025248cec922dea9614e3e213f908d1bc8d9e4b)
in file [`./internal/healthcheck/delivery/api/healthcheck_api.go`](https://github.com/unknowntpo/todos/blob/0025248cec922dea9614e3e213f908d1bc8d9e4b/internal/healthcheck/delivery/api/healthcheck_api.go)
```go
// Put reactor at structure that holds all the handler
type healthcheckAPI struct {
	version string
	env     string
	rc      *reactor.Reactor
}

type HealthcheckResponse struct {
	Status      string `json: "status"`
	Environment string `json: "environment"`
	Version     string `json: "version"`
}

// NewHealthcheckAPI registers all handlers in /v1/healcheck to the router.
func NewHealthcheckAPI(router *httprouter.Router, version, env string, rc *reactor.Reactor) {
	api := &healthcheckAPI{version: version, env: env, rc: rc}
	router.Handler(http.MethodGet, "/v1/healthcheck", rc.HandlerWrapper(api.Healthcheck))
}

// Healthcheck shows status of service.
func (h *healthcheckAPI) Healthcheck(c *reactor.Context) error {
	return c.WriteJSON(http.StatusOK, &HealthcheckResponse{
		Status:      "available",
		Version:     h.version,
		Environment: h.env,
	})
}
```

in [`internal/reactor/reactor.go`](https://github.com/unknowntpo/todos/blob/0025248cec922dea9614e3e213f908d1bc8d9e4b/internal/reactor/reactor.go#L7)

```go=44
func (rc *Reactor) HandlerWrapper(h HandlerFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Use ctxPool to reduce allocation and gc.
		c := ctxPool.Get().(*Context)
		defer ctxPool.Put(c)
		c.w = w
		c.r = r

		err := h(c)
		// Only Internal Server Error will come to here.
		// TODO: Use kindIs to check , if failed, panic
		if err != nil {
			rc.Logger.PrintError(err, nil)
			if e := c.ServerErrorResponse(); e != nil {
				// Something goes wrong during sending server error response.
				// So we write the message directly.
				rc.Logger.PrintError(e, nil)
				c.w.WriteHeader(http.StatusInternalServerError)
				msg := `{"error":"the server encountered a problem and could not process your request"}`
				c.w.Write([]byte(msg))
				return
			}
			return
		}
	})
}
```
:question: How does `*reactor.Reactor.HandlerWrapper` work with `Healthcheck` handler ?

The callgraph:
1. `(*Reactor).HandlerWrapper.func1` is type `http.HandlerFunc`, so it has a method `ServeHTTP(w, r)`
2. httprouter select the URL `/v1/healthcheck` and calls `(*Reactor).HandlerWrapper.func1.ServeHTTP(w, r)`
3. `(*Reactor).HandlerWrapper.func1.ServeHTTP(w, r)` will call `(*Reactor).HandlerWrapper.func1`
4. line47: Attempt to get new `reactor.Context` instance, if reactor.Context is not in `sync.Pool`, `sync.Pool` will allocate a new one for us.
5. Store `w, r` inside reactor.Context
6. line52: call `api.Healthcheck(c)`
7. line55: if `api.Healthcheck` returns any error, we treat it as internal server error, log it and send response.

## Why I remove the `reactor.Context` mechanism now ?
