# Upgrading to v0.4

Source: https://docs.anetos.dev/upgrade/v0.4/

v0.4 adds the API stack. Most apps upgrade without code changes: update
the modules (`go get anetos.dev/anetos@v0.4.0`, and `cli`, `admin` and
the drivers you use to their v0.4.0), run the tests, and read the
changes below that apply.

## Routes can't be compared with ==

`web.RouteInfo` has a slice field (`Middleware`) and `web.Route` an
unexported one, so neither compiles in an `==` comparison. Compare the
fields you need:

```go
// illustrative
// before
routes[0] == web.RouteInfo{Method: "GET", Pattern: "/posts", Name: "posts.index"}
// after
routes[0].Method == "GET" && routes[0].Pattern == "/posts" && routes[0].Name == "posts.index"
```

## Middleware that returns a documented handler

`auth.Auth.Require`, `TokenMiddleware`, `rbac.Require`, `rbac.RequireIn`
and `ratelimit.Middleware` return the handler `web.Documented` makes,
which carries what the middleware asks for and may answer, rather than
an `http.HandlerFunc`. They serve the same; only code that asserts the
handler's type fails (`h.(http.HandlerFunc)` panics, the two-value form
is false). Use the `http.Handler` instead.

## JSON bodies with values of the wrong type

A JSON body whose values have the wrong type (`"title": 5`), or that a
field's type refuses (`"due": "tomorrow"` for an `anetos.Date`), is
answered with 400 "The request has invalid values." and every such
field in `errors`, under the key the client sent:

```json
{"type": "about:blank", "title": "Bad Request", "status": 400,
 "detail": "The request has invalid values.",
 "errors": {"due": "must be a date", "title": "must be text"}}
```

A bad date was "The request body is not valid JSON." without `errors`,
and only the first wrong type was listed. Body that isn't JSON at all
keeps "The request body is not valid JSON.". Update tests and clients
that expect the old answers.

## Tests stop when .env.testing is missing

`anetostest.New` stops a test when `.env` names a database server
(`DB_CONNECTION=postgres` or `mysql`) and the test settings name no
`DB_CONNECTION`, with no database named either: such tests ran on an
in-memory SQLite database, not the app's. Add `.env.testing` next to
`go.mod`:

```sh
DB_CONNECTION=postgres
DB_URL=postgres://app@localhost/app_test?sslmode=disable
```

or `DB_CONNECTION=sqlite` to keep testing on SQLite. See
[Use PostgreSQL or MySQL](https://docs.anetos.dev/guides/database/index.md).

## Smaller changes

| Change | What to do |
|---|---|
| `HTTP_CORS_EXPOSE` defaults to `X-Request-ID,Location,Retry-After,X-RateLimit-Limit,X-RateLimit-Remaining` (was `X-Request-ID`) | Nothing, unless browsers must not read those headers: set `HTTP_CORS_EXPOSE` |
| `auth.Require` sends `WWW-Authenticate: Bearer` with its 401 behind `TokenMiddleware` on routes that also have sessions | Nothing, unless tests expect no header |
| `plugins/postmark` v0.4.0 requires Anetos `>= v0.2.0, < v0.5.0` | Update it with Anetos |
| `anetos new` pins templ v0.3.1070 (was v0.3.1020) | Optional in existing apps: `go get -tool github.com/a-h/templ/cmd/templ@v0.3.1070`, then `go generate ./...` |

## Describing an existing API

Nothing in v0.4's API stack is required of an app made before it; to
use it on an existing API:

1. Put `web.JSONErrors` on the API's group (or `r.UseGlobal` in an app
   serving only JSON), so its errors are problem details whatever the
   client accepts ([Handlers](https://docs.anetos.dev/guides/handlers/index.md)).
2. Make the API's handlers typed (`web.H`) and give each route the
   status it answers: `.Status(http.StatusCreated)` on the ones that
   create, `web.Empty` for answers without a body
   ([Routing](https://docs.anetos.dev/guides/routing/index.md)).
3. Add an `openapi.Config` and `openapi.ForApp`, run `go run . openapi`,
   commit `openapi.json`, and add a test calling `openapi.Check`
   ([Describe an API with OpenAPI](https://docs.anetos.dev/guides/openapi/index.md)).

`make:auth` in an existing web app writes what it wrote in v0.3; its
API version is for projects made with `anetos new --stack=api`.
