# server express

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

Generate an Express server stub in TypeScript from an OpenAPI document.

## Usage

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

## Description

The input must be an OpenAPI 3.0, 3.1 or 3.2 document; bundle documents referencing other files
with `apigenie bundle` first. The generated code runs on Node.js 20 or later with Express 5.

The generator writes typed handler interfaces and a router serving them. Your code implements
the handlers in files of its own, so regenerating the code never overwrites it:

```ts
import express from "express";
import { BASE_PATH, createRouter, HttpError, type PetsHandlers } from "pet-store-server";

const pets: Partial<PetsHandlers> = {
  async listPets(request) {
    const pets = await db.pets.list({ limit: request.query.limit });
    return { status: 200, body: pets, headers: { "X-Total-Count": pets.length } };
  },
  async showPetById(request) {
    const pet = await db.pets.find(request.params.petId);
    if (pet === undefined) {
      throw new HttpError(404, "No such pet");
    }
    return { status: 200, body: pet };
  },
};

const app = express();
app.use(
  BASE_PATH,
  createRouter(
    { pets },
    {
      handleNotFound: true,
      security: { api_key: ({ credential }) => users.byApiKey(credential) },
    },
  ),
);
app.listen(8080);
```

Operations are grouped into handler interfaces by their first tag (`PetsHandlers`); operations
without tags belong to the `default` group. A handler is named after the `operationId` of its
operation, or after its method and path. It receives:

- the request: the decoded and validated path, query, header and cookie parameters (`params`,
  `query`, `headers`, `cookies`, by name) and the body with its declared media type
  (`contentType`), all typed from the document
- the context: the Express request and response (`req`, `res`), the description of the
  operation and the principal returned by the security handlers

It returns the response: a status, a body, headers and optionally a content type, typed per
status from the document. A handler which answers on its own through `context.res` returns
nothing.

For every request, the router:

1. checks the security requirements of the operation: any of them, with all the schemes of a
   requirement, must be satisfied. A security handler receives the credential (the API key, the
   bearer token or the decoded `user:password` of basic authentication) and the required scopes,
   and returns the principal, or `undefined` to reject the request with 401 Unauthorized. It may
   throw an `HttpError`, e.g. 403 Forbidden for missing scopes. Schemes without handlers reject
   every request.
2. decodes the parameters in all OpenAPI styles (`simple`, `label`, `matrix`, `form`,
   `spaceDelimited`, `pipeDelimited` and `deepObject`, exploded or not) and converts them to the
   types of their schemas; parameters with `content` are parsed as JSON. Absent parameters get
   their default values.
3. reads the body: JSON, `application/x-www-form-urlencoded` (converted to the types of its
   schema), text and binary bodies are parsed; `multipart/form-data` bodies are left to
   middleware such as multer, whose `req.body` is used. A media type that isn't declared is
   answered with 415 Unsupported Media Type, a missing required body with 400.
4. validates the parameters and the body against their schemas (see `validation`) and answers
   invalid requests with 400 Bad Request.
5. calls the handler, or answers with 501 Not Implemented when there's none.
6. writes the response, optionally validating it first (`validateResponses`).

Errors are answered with [problem details](https://www.rfc-editor.org/rfc/rfc9457)
(`application/problem+json`). Validation errors list the failed checks:

```json
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "The request is invalid",
  "errors": [
    { "in": "query", "name": "limit", "path": "", "keyword": "maximum", "message": "must be <= 50" },
    { "in": "body", "path": "/name", "keyword": "required", "message": "must have required property 'name'" }
  ]
}
```

Routes are registered so that literal path segments win over parameters (`/pets/mine` before
`/pets/{petId}`). Deprecated operations are marked `@deprecated`, and operations disabled with
the `x-apigenie` extension (`codegen.disabled`) are left out. Callbacks and webhooks are not
generated, and operation and path servers are ignored: every route is served under `BASE_PATH`.

The generated package contains:

- `src/types.ts`: the types of the component schemas, and the request and response types of
  every operation
- `src/handlers.ts`: the handler interfaces, `Handlers`, `SecurityHandlers` and
  `createRouter(handlers, options)`
- `src/metadata.ts`: the operations (`OPERATIONS`) and security schemes described for the
  runtime, and the port (`DEFAULT_PORT`) and path (`BASE_PATH`) of the first server
- `src/validation.ts` and, depending on `validation`, `src/schemas.ts` and `src/ajv_engine.ts`,
  or `src/validators.ts`
- `src/runtime.ts`: the request handling code, which doesn't depend on the document
- `src/document.ts`: the OpenAPI document, with `copy_document`
- `src/index.ts`, `package.json`, `tsconfig.json` and `README.md`
- `src/server.ts`: an example server with stub handlers, with `scaffold`

`README.md` and `server.ts` are written only if they don't exist; the other files are
overwritten.

## Extensions

| Extension | Effect |
| --------- | ------ |
| `x-apigenie.codegen.name` | name of the type of a component schema |
| `x-apigenie.codegen.controller-name` | on an operation or its path item: the handler interface of the operation, when `grouping` lists `extension` |
| `x-apigenie.codegen.action-name` | name of the handler of an operation, when `action_naming` lists `extension` |
| `x-apigenie.codegen.disabled` | `true` on an operation or its path item leaves the operation out, unless `skip_disabled` is `false` |
| `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.
`express.overlay.yaml`:

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

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

## 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_express.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 package is generated.

### --package-name

Name of the generated package. Defaults to the title of the API followed by `-server`.

### --package-version

Version of the generated package. Defaults to the version of the API.

## Configuration

```yaml
version: '1.0'
commands:
  generate:
    server_express:
      validation: ajv
      validate_responses: true
      scaffold: true
```

| Option | Description |
| ------ | ----------- |
| `layout` | `package` (default): an npm package with sources in `src/`; `sources`: the sources only |
| `import_extension` | Extension of relative imports: `.js` (default, Node.js) or an empty string (bundlers) |
| `validation` | `ajv` (default): JSON Schemas validated with [Ajv](https://ajv.js.org); `generated`: generated TypeScript validators, without dependencies; `none` |
| `validate_responses` | Generate what's needed to validate responses, enabled by the `validateResponses` router option (default: false) |
| `grouping` | How operations are grouped into handler interfaces: `tag` (default), `extension` (`x-apigenie` `codegen.controller-name`) or `url`, or a list of them, in order of preference |
| `action_naming` | Sources of handler names: `id`, `extension` (`x-apigenie` `codegen.action-name`) or `url`, or a list of them (default: `[id, url]`) |
| `skip_deprecated` | Leave deprecated operations out (default: false) |
| `skip_disabled` | Leave out operations disabled with `x-apigenie` (default: true) |
| `copy_document` | Include the document in the package, so that the router can serve it (default: false) |
| `scaffold` | Write an example server, `src/server.ts`, unless it exists (default: false) |
| `security` | Check the security requirements of operations (default: true) |

Both validation modes report the same issues with the same messages, and both convert OAS 3.0
schemas (`nullable`, boolean `exclusiveMinimum` and `exclusiveMaximum`) and leave `readOnly`
properties out of the required properties of requests and `writeOnly` ones out of responses.
The generated validators don't check `if`/`then`/`else`, `dependentRequired`,
`dependentSchemas`, `unevaluatedProperties`, `unevaluatedItems`, `contains`, `propertyNames`,
`contentSchema` and `$dynamicRef`; the generator warns about them. Formats are checked as JSON
Schema defines them, the generated validators knowing the common ones (`date-time`, `date`, `time`,
`duration`, `email`, `hostname`, `ipv4`, `ipv6`, `uri`, `uri-reference`, `uuid`, `byte`,
`int32` and `int64`).

## Router options

| Option | Description |
| ------ | ----------- |
| `security` | Security handlers, by scheme name |
| `validation` | A validation engine replacing the generated one, e.g. `noValidation` |
| `validateResponses` | Validate the bodies returned by handlers; invalid ones are answered with 500 |
| `handleNotFound` | Answer requests to unknown paths with 404 and unknown methods with 405 |
| `notImplemented` | `"501"` (default) or `"next"`: pass requests to operations without handlers to the next middleware |
| `exposeErrors` | Put the messages of unexpected errors into 500 responses |
| `onError` | Called with unexpected errors, which are logged with `console.error` otherwise |
| `bodyLimit` | Maximum size of request bodies, e.g. `"1mb"` |
| `serveDocument` | Serve the document as JSON at this path, e.g. `"/openapi.json"` (needs `copy_document`) |
| `caseSensitive`, `strict` | Options of the Express router |
