# Codegen (/docs/code/codegen)

---
title: Codegen
description: Codegen scans your convention directories and writes one typed app into .generated/. Run it after adding a file, and the app object, the routes and the types all match what is on disk.
kind: guide
package: questpie
---

You drop a file in `collections/`. Nothing imports it. It still turns up on
`app.collections`. Codegen is what closes that gap. This page is how it works,
and the commands that drive it.

## Run it

```bash
questpie generate
```

The command reads `questpie.config.ts` and scans the directories beside it.
Codegen follows a root config that only re-exports a deeper one. The starters do
that, so they scan beside `src/questpie/server/questpie.config.ts`. The
`#questpie` and `#questpie/factories` imports in your code resolve into
`.generated/`. Before the first run they do not exist. That is why a fresh clone
and a CI build both start here.

Write a file:

```ts title="src/questpie/server/collections/invoices.ts"
import { collection } from "#questpie/factories";

export const invoices = collection("invoices").fields(({ f }) => ({
	number: f.text(40).required(),
	total: f.number(),
}));
```

Generate, then use it:

```bash
questpie generate
```

```ts
import { app } from "#questpie";

const { docs } = await app.collections.invoices.find({});
```

No import list. No manifest. The directory is the registry.

## What it reads

Each of these is a directory beside `questpie.config.ts`. Put a file in one and
it is found on the next run.

| Directory               | Lands on                       | Rule                              |
| ----------------------- | ------------------------------ | --------------------------------- |
| `collections/`          | `app.collections`              | the file must call `collection()` |
| `globals/`              | `app.globals`                  | the file must call `global()`     |
| `channels/`             | `ctx.channels`                 | the file must call `channel()`    |
| `routes/`, `functions/` | `app.routes`, an HTTP endpoint | subfolders are scanned too        |
| `jobs/`                 | `ctx.queue.<key>`              | one job per file                  |
| `services/`             | `ctx.services.<key>`           | one service per file              |
| `emails/`               | `ctx.email.sendTemplate`       | one template per file             |
| `fields/`               | `f.<name>` in `.fields()`      | the file must call `fieldType()`  |
| `messages/`             | backend translations           | one file per locale, `en.ts`      |
| `migrations/`, `seeds/` | the lists the CLI runs         | one per file                      |

A handful of single files are read the same way: `modules.ts`, `env.ts`,
`env.client.ts`, `fields.ts`, `config/app.ts` and `config/auth.ts`. Modules add
their own, so `@questpie/admin` also brings `views/`, `blocks/`, `components/`
and `config/admin.ts`.

## What it writes

| File                         | What it holds                                                    |
| ---------------------------- | ---------------------------------------------------------------- |
| `.generated/index.ts`        | The shared `app` instance, `env` and the public types            |
| `.generated/app-factory.ts`  | `createAppForRuntime()`, one fresh app per call                  |
| `.generated/factories.ts`    | `collection()` and `global()`, wired to your enabled field types |
| `.generated/entities.gen.ts` | The flat category maps, `AppCollections` and friends             |
| `.generated/context.gen.ts`  | The `AppContext` shape and the session types                     |
| `.generated/names.gen.ts`    | The key registries that make relation targets autocomplete       |

`app-factory.ts` is for tests. `createAppForRuntime(runtime)` builds a new app
from the runtime config you pass it. `index.ts` shares one app, so every
import of `#questpie` gets the same one. Import from `#questpie/app-factory`.

Import `collection` and `global` from `#questpie/factories`. Import everything
else from `#questpie`. Run codegen after a schema change and commit the result.
`.generated/` belongs in git. It is build output, but your code imports it, so a
teammate, CI and your editor all resolve it with no build step first.

<Callout type="warn" title="Codegen owns the output directory">
	Each run that writes deletes `.generated/` and recreates it. A file you put
	there by hand is gone on the next generate. Each file is written to a temp
	path and renamed, so a killed run never leaves a truncated file behind.
</Callout>

## What becomes the key

`collection("invoices")` is keyed as `invoices`. The string you pass the factory
is the key, whatever the file is called. Hyphens become camelCase. Underscores
stay put. So `collection("barber_services")` in `collections/barber-services.ts`
keys as `barber_services`.

Files without a factory call are keyed off the file name instead.
`jobs/send-newsletter.ts` is `sendNewsletter`, default export or named.

Codegen skips `index.ts`, `*.d.ts`, test and spec files, and anything starting
with an underscore. So a `_helpers.ts` sitting next to your collections is
invisible.

## Keep it running

```bash
questpie dev
```

Watch mode. It regenerates when a file is added or removed, and when the config
changes. Editing the body of a collection does nothing. The generated file
imports your module by path, and that import line does not change when the
file's contents do. Both commands take `-c` for a config path and `--verbose`.
`generate` also takes `--dry-run` to print the output instead of writing it.

## Scaffold a file

```bash
questpie add collection invoice   # writes collections/invoice.ts, then generates
questpie add --list               # every type, and which target provides it
```

The built-in types are `collection`, `global`, `channel`, `job`, `service`,
`email`, `route`, `seed` and `migration`. Modules add more. When two targets
declare the same type, `questpie add` writes a file in both. So
`questpie add block hero` gives you the server definition and the admin
renderer at once. Pass `--target` to pick one. An existing file is skipped with
a warning.

## Modules bring their own directories

`modules.ts` lists the packages your app depends on. Codegen reads it before
anything else, because a module can carry its own codegen plugin. That plugin is
what adds `views/`, `blocks/` and `config/admin.ts` when you install
`@questpie/admin`.

```ts title="src/questpie/server/modules.ts"
import { adminModule } from "@questpie/admin/modules/admin";
import { openApiModule } from "@questpie/openapi";

const modules = [adminModule, openApiModule] as const;
export default modules;
```

You do not register the plugin. Installing the module is enough. The plugin key
is read at codegen time and dropped before the runtime merge, so it never
reaches the app.

## Types you import from `#questpie`

```ts
import type { CollectionDoc, CollectionWhere, App, AppConfig } from "#questpie";

type Invoice = CollectionDoc<"invoices">;
type InvoiceFilter = CollectionWhere<"invoices">;
```

| Type                 | Use it for                                         |
| -------------------- | -------------------------------------------------- |
| `CollectionDoc<K>`   | One row of collection `K`                          |
| `CollectionWhere<K>` | A `where` clause you build before calling `find`   |
| `GlobalDoc<K>`       | One row of global `K`                              |
| `AppConfig`          | The client APIs, as in `createClient<AppConfig>()` |
| `App`                | The typed app instance, `typeof app`               |

`#questpie` also exports `createContext()`. It builds a typed `AppContext` for
scripts and tests. `AppSession` and `AppSessionUser` come from there too.

<Callout type="warn" title="Generate before you migrate, seed or push">
	A CLI command resolves `.generated/index.ts` for the real app instance. When
	that file is missing it tells you to run `questpie generate` first. A stale
	`.generated/` is the usual reason a new collection is not on
	`app.collections`.
</Callout>

## Where each topic lives

| Topic                                               | Page                                             |
| --------------------------------------------------- | ------------------------------------------------ |
| Every rule that turns a file into an entity         | [Discovery](/docs/code/codegen/discovery)        |
| The plugin contract that adds a convention          | [Plugins](/docs/code/codegen/plugins)            |
| Writing a plugin, start to finish                   | [Building a plugin](/docs/guides/build-a-plugin) |
| Generating a module inside an npm package           | [Publishing](/docs/code/modules/publishing)      |
| `questpie.config.ts`, `config/app.ts`, `modules.ts` | [Configuration](/docs/ship/configuration)        |
| `env.ts` and `env.client.ts`                        | [Environment](/docs/ship/environment)            |

## Next

**[Modules](/docs/code/modules)** is the bundle side of this. A module ships
collections, routes and jobs, and codegen folds them in before it scans a single
directory of yours.
