# server phoenix

Source: https://apigenie.pl/techdocs/reference/cli-generate_server_phoenix

Generate a [Phoenix](https://www.phoenixframework.org) 1.7 application from an OpenAPI document.

From an AsyncAPI document, the command generates Phoenix channels and Kafka pipelines instead; see
[apigenie-generator server phoenix (AsyncAPI)](/techdocs/reference/cli-generate_server_phoenix_asyncapi).

## Usage

```sh
apigenie-generator server phoenix [...OPTIONS] INPUT_FILE
```

## Description

The generator writes a Mix project (Elixir 1.18 or later) depending on Phoenix 1.7, Bandit and
Jason, without Ecto, HTML, assets or a mailer. Its code passes `mix format --check-formatted`,
`mix compile --warnings-as-errors` and `mix credo --strict`, and `mix phx.server` serves it.

| File | Contents |
| ---- | -------- |
| `lib/<app>_web/router.ex` | the routes of the operations, under the path of the first server |
| `lib/<app>_web/api/<tag>.ex` | a behaviour per tag (`Default` for operations without tags), with a callback per operation |
| `lib/<app>_web/controllers/<tag>_controller.ex` | an action per operation, calling the implementation of the behaviour |
| `lib/<app>/models/*.ex` | a module per schema: structs, enums, unions and aliases, with `new/1`, `new!/1` and `to_map/1` |
| `lib/<app>_web/runtime/*.ex` | the checks of credentials, the decoding of parameters and bodies, the encoding of responses |
| `lib/<app>_web/authenticator.ex` | the behaviour checking credentials |
| `lib/<app>_web/endpoint.ex`, `error_json.ex`, `lib/<app>/application.ex` | the endpoint, the errors Phoenix renders, the application |
| `config/*.exs`, `mix.exs`, `.formatter.exs`, `.credo.exs`, `.gitignore`, `README.md` | the project |

```sh
mix deps.get
PORT=8080 mix phx.server
```

### Routes

Every operation has a route in `<App>Web.Router`, in the scope of the path of the first server
(`/v2` for `https://api.example.com/v2`, or the `base_path` option). Methods without a Phoenix macro,
such as `QUERY` and the `COPY` of `additionalOperations`, are routed with `match`. Static segments
come before parameters (`/shipments/search` before `/shipments/{shipmentId}`), as Phoenix tries the
routes in order. A segment holding parameters is captured whole and split by the runtime with its
template, so `/labels/{labelId}.{extension}` works.

### Behaviours and implementations

The operations of each tag are the callbacks of a behaviour, `<App>Web.Api.<Tag>`. A callback
receives the connection, the parameters and the body:

```elixir
defmodule MyApp.Shipments do
  @behaviour CarpetExpressWeb.Api.Shipments

  @impl true
  def get_shipment(_conn, %{shipment_id: id}, nil) do
    case MyApp.Store.fetch(id) do
      {:ok, shipment} -> {200, shipment}
      :error -> {404, %{"type" => "about:blank", "title" => "Not Found", "status" => 404}}
    end
  end
end
```

and is configured as the implementation:

```elixir
config :carpet_express, CarpetExpressWeb.Api.Shipments, impl: MyApp.Shipments
```

Operations without an implementation answer 501 Not Implemented. A callback returns
`{status, body}`, `{status, body, headers}`, or the connection after sending a response itself
(e.g. a chunked stream). The body is encoded in the media type the document gives the status (the
exact code, then its range, then `default`): JSON with the type of the response, so a model struct
or a plain map; text and bytes as they are; nothing for responses without content.

### Parameters and bodies

The actions check the request before calling the implementation, and answer a problem document
(RFC 9457, `application/problem+json`) when it doesn't match the description:

- 401 when the request has none of the credentials of the operation's security requirements;
- 400 when a parameter is missing or invalid (the `errors` list the parameters), or the body is
  missing, isn't JSON or doesn't match its schema;
- 415 for a media type the operation doesn't accept.

Parameters are decoded from their styles into a map with atom keys (`%{shipment_id: "...",
limit: 20}`): `simple`, `label` and `matrix` path parameters, `form`, `spaceDelimited`,
`pipeDelimited` and `deepObject` query parameters, headers, cookies, and parameters described with
`content` as JSON. Integers, numbers and booleans are converted from text, models and enums
decoded with their schemas.

| Media type | Body |
| ---------- | ---- |
| JSON (`application/json`, `*+json`) | the model, a list or the value its schema describes |
| `application/x-www-form-urlencoded` | the map of the fields |
| `multipart/form-data` | the map of the parts, files as `Plug.Upload` structs |
| `text/*` | a string |
| anything else | the bytes |

`Plug.Parsers` reads JSON, form and multipart bodies of up to 20 MB; the runtime parses the JSON
and form bodies of methods Plug.Parsers leaves alone, such as `QUERY`.

### Security

The first security requirement of an operation whose credentials the request presents is checked:
API keys in headers, the query or cookies, HTTP bearer and basic, and OAuth2 and OpenID Connect
tokens as bearer tokens. Without an implementation of `<App>Web.Authenticator`, every presented
credential is accepted; with one,

```elixir
config :carpet_express, CarpetExpressWeb.Authenticator, impl: MyApp.Authenticator
```

its `authenticate(conn, scheme, credential, scopes)` returns `{:ok, principal}`, assigned to the
connection as `:principal`, or `{:error, reason}`, answered with 401.

### Types

The models are the modules of [the Elixir models](/techdocs/reference/cli-generate_client_elixir#types),
shared with `client elixir`: a struct per object (with `allOf` flattened), atoms for string enums,
a module dispatching on the discriminator for `oneOf` and `anyOf`, `Date`, `DateTime`,
`NaiveDateTime` and `Time` for dates and times, binaries for `format: byte` (decoded from base64)
and `format: binary`, `additional_properties` maps, and `| nil` for nullable values.

### Extensions

| Extension | Effect |
| --------- | ------ |
| `x-apigenie.codegen.name` | name of the module of a component schema, unless `module_overrides` names it |
| `x-apigenie.codegen.action-name` | name of the action and callback of an operation without an `operationId` (with the default `action_naming`, which lists `id` first) |
| `x-apigenie.codegen.disabled` | `true` on an operation or its path item leaves the operation out |
| `x-internal` | `true` on an operation or its path item leaves the operation out, unless `internal_operations` is set |
| `x-defaultMapping` | `defaultMapping` of a discriminator in OpenAPI 3.0 and 3.1 documents: the schema of the values whose discriminator value isn't mapped |

The extensions can live in an overlay for this target instead of in the document, e.g.
`phoenix.overlay.yaml`:

```yaml
overlay: 1.0.0
info:
  title: Elixir names
  version: 1.0.0
actions:
  - target: $.components.schemas.Pet
    update:
      x-apigenie:
        codegen:
          name: Animal
  - target: $.paths['/admin']
    update:
      x-internal: true
```

```sh
apigenie-generator server phoenix openapi.yaml --overlay phoenix.overlay.yaml -o out
```

### Limitations

- `text/event-stream` and JSON Lines responses are sent whole unless the implementation sends
  the response itself.
- The models don't validate `minimum`, `maximum`, `pattern` and the other constraints, and ignore
  `prefixItems`, `patternProperties` and conditional schemas.
- `readOnly` and `writeOnly` properties are fields of the same struct.
- Every operation is served under the base path; operation and path servers are ignored.
- Callbacks, webhooks and links aren't generated.
- The endpoint serves HTTP; mutual TLS needs an `https` configuration of the endpoint.

## Options

### -c, --config

Path to the configuration file to use. See the "Configuration" section for more information.

### --overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the "Extensions" section. Can be repeated: the overlays apply in order, after those
listed under `commands.generate.server_phoenix.overlay` in the configuration file (with paths
relative to it). Warnings and errors about a value an overlay set point to the overlay.

### --strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (`OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.`).

### -o, --output

Directory where the project is generated.

### --package-name

Name of the Mix application, converted to snake_case (`carpet-express` gives `carpet_express`,
the root module `CarpetExpress` and the web modules `CarpetExpressWeb`). Defaults to the name of
the output directory.

### --package-version

Version of the Mix project; `0.1.0` by default.

### -p, --property NAME=VALUE

Sets a generator option (see below), e.g. `-p base_path=/api`. Options given on the command line
take precedence over the configuration file. Lists are comma-separated (`-p include_tags=pets,store`)
and mappings too (`-p module_overrides=pet-owner=Owner`).

## Configuration

```yaml
version: '1.0'
commands:
  generate:
    server_phoenix:
      app: carpet_express
      base_path: /v2
      exclude_tags: [internal]
```

| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `app` | string | name of the output directory | Name of the Mix application (snake_case, the directory under `lib/`); also set by `--package-name`. |
| `namespace` | string | the application in PascalCase | Root module of the generated modules, e.g. `CarpetExpress`; the web modules are in `CarpetExpressWeb`. |
| `base_path` | string | the path of the first server | Path the routes are mounted at, e.g. `/v2`; `/` mounts them at the root. |
| `include_tags` | list of strings | | Generates only the operations with one of these tags. |
| `exclude_tags` | list of strings | | Skips the operations with one of these tags. |
| `skip_prune` | boolean | `false` | Keeps the component schemas that no generated operation uses. |
| `internal_operations` | boolean | `false` | Also generates the operations marked `x-internal: true`. |
| `dev_deps` | boolean | `true` | Lists credo as a dependency of `mix.exs` (`only: [:dev, :test]`). |
| `action_naming` | list of `id`, `extension`, `url` | `id, extension, url` | Where the names of actions and callbacks come from, in order of preference. |
| `field_prefix` | string | `field_` | Prefix of struct fields whose property names give no valid name. |
| `module_overrides` | map of strings | | Module names of component schemas, e.g. `pet-owner: Owner`. |
