# Scripts

Source: https://apigenie.pl/docs/reference/server/scripts
Part of the reference of [apigenie-server](https://apigenie.pl/docs/reference/server.md).
Its other pages: [Responses](https://apigenie.pl/docs/reference/server/responses.md), [Scenarios and extensions](https://apigenie.pl/docs/reference/server/scenarios.md), [Document store](https://apigenie.pl/docs/reference/server/store.md), [Several services](https://apigenie.pl/docs/reference/server/services.md), [Validation and security](https://apigenie.pl/docs/reference/server/validation.md), [Contract testing (validating proxy)](https://apigenie.pl/docs/reference/server/contract-testing.md), [Session recording and replay](https://apigenie.pl/docs/reference/server/recording.md), [AsyncAPI](https://apigenie.pl/docs/reference/server/asyncapi.md), [Protobuf / gRPC](https://apigenie.pl/docs/reference/server/grpc.md), [GraphQL](https://apigenie.pl/docs/reference/server/graphql.md), [Configuration](https://apigenie.pl/docs/reference/server/configuration.md), [Dashboard and admin API](https://apigenie.pl/docs/reference/server/dashboard.md), [TLS](https://apigenie.pl/docs/reference/server/tls.md).

A script is an ES module whose default export has any of five hooks:

```js
import { limit } from './settings.mjs';

export default {
  // before the document's operations: a response answers the request
  onRequest(req, ctx) {
    if (req.path === '/pets/13') {
      return ctx.response(404, { title: 'No such pet' });
    }
  },
  // the response about to be sent: change it in place
  onResponse(req, res, ctx) {
    if (req.operationId === 'listPets') {
      res.body = res.body.slice(0, limit);
      res.headers['x-total-count'] = '3';
      res.delay = 250;
    }
  },
  // AsyncAPI messages, in and out: change msg.payload, or return false to drop the message
  onMessage(msg, ctx) {
    if (msg.direction === 'out') msg.payload.sentAt = ctx.now();
  },
  // GraphQL operations (each subscription event too): a result answers, its data completed
  onGraphQL(operation, ctx) {
    if (operation.name === 'GetUser') {
      return { data: { user: { id: operation.variables.id, name: ctx.fake('person.fullName') } } };
    }
  },
  // after the scripts were loaded, at the start and after each reload
  onReload(ctx) {},
};
```

- `req` has `method`, `path`, `query` (an object; repeated parameters are arrays), `headers`
  (lower-case names), `body` (parsed JSON, or the text), `rawBody`, `operationId` and `route`
  (in `onResponse`), `remote` and `connectionId`.
- `res` has `status`, `headers`, `body`, `source`, and `delay` (milliseconds) and `drop` (`reset`,
  `close`, `timeout`, `truncate`) to set. A response returned by `onRequest` has the same fields.
- `msg` has `direction` (`in` or `out`), `channel`, `operation`, `message`, `contentType` and
  `payload`.
- `operation` has `name`, `type` (`query`, `mutation`, `subscription`), `query`, `variables` and
  `rootFields` (`["Query.user"]`). The result's data is cut to the selection set and completed
  with generated values, like an example's.
- `ctx` has `state` (`get`, `set`, `delete` and `keys` of JSON values kept between requests, the
  admin API's `/state`), `db` (collections of documents, see [Document store](/docs/reference/server/store)),
  `log(level, ...values)`, `random()` (seeded by `--seed`), `now()`,
  `response(status, body, headers)`, `fake(name, ...arguments)` (the faker providers of `x-faker`,
  with arguments by position and a last object by name: `fake('string.alpha', {length: 4})`)
  and `scenarios` (`activate`, `deactivate`, `active`).

Scripts run in a sandbox: the language only, modules next to the script, no file system,
network or timers. Each call has `scripting.timeout_ms` (1000) and `scripting.memory_limit_mb`
(64); a hook that throws or runs out of time is logged, and answered with 500 when
`scripting.fail_mode` is `error`. `--watch` reloads changed scripts, and a broken script keeps
the previous version.
