--- title: Wasp Spec (main.wasp.ts) --- import { CardLink } from "@site/src/components/CardLink"; You define and configure the high level of your app (pages, routes, queries, actions, auth, ...) in a `main.wasp.ts` file in the root of your project. We call this file the **Wasp Spec**. You write the Wasp Spec in TypeScript, so you get out-of-the-box support in all editors, full type checking, and the flexibility of a real programming language while configuring your app. :::info Coming from an older version of Wasp? The Wasp Spec replaces two older ways of configuring a Wasp app: - The **Wasp DSL** (`main.wasp`). - The **TS Config** (`main.wasp.ts`, with the class-based `new App(...)` API). If you're upgrading from Wasp `0.23.X` to `0.24.X`, start with the [migration guide](../migration-guide.md). Then pick the conversion guide matching your old config: - **Wasp DSL** → [Migrating from the Wasp DSL](../guides/legacy/wasp-dsl.md) - **TS Config** → [Migrating from the TS Config](../guides/legacy/wasp-ts-config.md) ::: ## A quick example ```ts title="main.wasp.ts" import { app, page, query, route } from "@wasp.sh/spec"; import { MainPage } from "./src/MainPage" with { type: "ref" }; import { getTasks } from "./src/queries" with { type: "ref" }; export default app({ name: "todoApp", wasp: { version: "^0.24.0" }, title: "ToDo App", head: [""], spec: [ route("MainRoute", "/", page(MainPage, { authRequired: true })), query(getTasks, { entities: ["Task"] }), ], }); ``` You build your app by: 1. Importing the building blocks (`app`, `page`, `route`, `query`, ...) from `@wasp.sh/spec`. 2. Importing your own components and functions adding the import attribute `with { type: "ref" }`. 3. Calling `app({ ... })` with your app's configuration, listing all the pages, routes, queries, actions, etc. in the `spec` property. 4. Exporting the result as the **default export** of the file. `spec` is short for specification: the pages, routes, queries, actions, APIs, jobs and CRUDs that make up your app. ## `wasp install` The `@wasp.sh/spec` package doesn't exist on npm, but it is generated by Wasp per project, so we can customize it to fit the needs of your app. We do this through the `wasp install` command, which installs your app's dependencies and sets up the generated Wasp Spec package. You'll have to run it at least once after creating a new Wasp project, but you might also need to run it again later on when the generated spec needs to be updated. If the Spec needs to be regenerated, Wasp will tell you to run `wasp install` before being able to start the app. Usually, this might happen when upgrading Wasp versions, running `wasp clean`, or removing the `node_modules` folder. ## Referencing your app's code Anywhere the Wasp Spec expects your app's function or component (like a page's `component` or a query's `fn`), you can provide it in one of two ways: ### Reference imports {#reference-imports} **Recommended** Import the value with the regular syntax, adding `with { type: "ref" }`. Use it when importing components or functions from `src/` so Wasp can connect them to pages, actions, queries, and other specifications. ```ts title="main.wasp.ts" import { MainPage } from "./src/MainPage" with { type: "ref" }; import { getTasks } from "./src/queries" with { type: "ref" }; export default app({ spec: [page(MainPage), query(getTasks)], }); ``` The import paths are relative to the `*.wasp.ts` file they're written in (see [multiple spec files](#splitting-your-spec-into-multiple-files)): ```ts title="src/auth/auth.wasp.ts" import { LoginPage } from "./LoginPage" with { type: "ref" }; export const auth = [page(LoginPage)]; ``` :::note Limitations Reference imports have some limitations: - They only work from `*.wasp.ts` files. - The referenced files must be inside the `src` directory. - You can't re-export something as a reference import (`export { X } from "./X" with { type: "ref" }`). Import it first, then re-export it if needed. - Namespace imports (`import * as something from './src/something' with { type: "ref" }`) aren't supported. Use named or default imports instead. The vast majority of Wasp apps won't run into these limitations, so we recommend using reference imports by default. ::: ### `ref` helper {#reference-objects} Use `ref(...)` when a direct reference import is not practical. Import `ref` from `@wasp.sh/spec`, then pass it an import object with `import` (or `importDefault`) and `from`: ```ts title="main.wasp.ts" import { ref } from "@wasp.sh/spec"; export default app({ // ... spec: [ page(ref({ importDefault: "MainPage", from: "./src/MainPage" })), query(ref({ import: "getTasks", from: "./src/queries" })), // You can rename a named import with `alias`: query(ref({ import: "getTasks", alias: "getAllTasks", from: "./src/queries" })), ], }); ``` The `from` path is relative to the `*.wasp.ts` file where you call `ref(...)` and must resolve inside your project's `src` directory. :::note Limitation You can't re-export `ref` from `@wasp.sh/spec` (`export { ref } from "@wasp.sh/spec"`). Import it first, then re-export it if needed. ::: ## Useful patterns ### Splitting your spec into multiple files For larger apps you don't have to keep everything in `main.wasp.ts`. You can move related specifications into their own `*.wasp.ts` files and combine them in `main.wasp.ts`. This works well for vertical slices, like keeping a feature's page, route, query, and action specifications in that feature's folder. Each feature file exports it's own `Spec`: ```ts title="src/auth/auth.wasp.ts" import { page, route, type Spec } from "@wasp.sh/spec"; import { LoginPage } from "./LoginPage" with { type: "ref" }; import { SignupPage } from "./SignupPage" with { type: "ref" }; export const authSpec: Spec = [ route("SignupRoute", "/signup", page(SignupPage)), route("LoginRoute", "/login", page(LoginPage)), ]; ``` The `Spec` annotation gives TypeScript enough information to validate the specification in its own file before it's added to the `main.wasp.ts`. Then `main.wasp.ts` imports it and joins in into the `spec`: ```ts title="main.wasp.ts" import { app, page, route } from "@wasp.sh/spec"; import { MainPage } from "./src/MainPage" with { type: "ref" }; import { authSpec } from "./src/auth/auth.wasp"; export default app({ name: "todoApp", wasp: { version: "^0.24.0" }, title: "ToDo App", head: [""], spec: [ route("MainRoute", "/", page(MainPage, { authRequired: true })), authSpec, ], }); ``` All spec files should have the `.wasp.ts` extension, so they are included in the `tsconfig.wasp.json` and type-checked. ### Detecting production mode Wasp sets the `NODE_ENV` environment variable based on which command you use to run Wasp: - `"development"` during `wasp start` (and some other commands that compile the project, like `wasp db migrate-dev`). - `"production"` during `wasp build`. Because the Wasp Spec is just TypeScript, you can read this variable to switch config values per environment: ```ts title="main.wasp.ts" const isProd = process.env.NODE_ENV === "production"; export default app({ //... emailSender: { provider: isProd ? "SMTP" : "Dummy", defaultFrom: { email: "hi@example.com" }, }, }); ``` ## Reference