Skip to main content
Version: 0.24

Wasp Spec (main.wasp.ts)

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.

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. Then pick the conversion guide matching your old config:

A quick exampleโ€‹

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: ["<link rel='icon' href='/favicon.ico' />"],
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โ€‹

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.

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):

src/auth/auth.wasp.ts
import { LoginPage } from "./LoginPage" with { type: "ref" };

export const auth = [page(LoginPage)];
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โ€‹

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:

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.

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:

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:

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: ["<link rel='icon' href='/favicon.ico' />"],
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:

main.wasp.ts
const isProd = process.env.NODE_ENV === "production";

export default app({
//...
emailSender: {
provider: isProd ? "SMTP" : "Dummy",
defaultFrom: { email: "hi@example.com" },
},
});

Referenceโ€‹

API reference

@wasp.sh/spec ยป

A complete overview of all the available configuration options in the Wasp Spec.