Wasp now lets you write your full-stack spec in 100% TypeScript

matijash1 pts0 comments

Wasp now lets you write your full-stack logic as a spec in TypeScript | Wasp

Skip to main content<br>🦋 Launch Week #12 — TS Spec · Wasp goes fully TypeScript-native. Kickoff Mon, Jun 15 →

Wasp is an opinionated, batteries-included full-stack JS/TS web app framework, built around the concept of an explicit spec for defining full-stack features of your app.

Today is a big day: we dropped our custom spec language and replaced it with a new, TypeScript-native way to define your full-stack app at a high level: the TypeScript Spec!

Read on for the why and the what, or if you want to try it out right now, go to the quick start guide.

Quick recap of the last 5 years​

Wasp is an opinionated, batteries-included , truly full-stack, spec-driven JS/TS web app framework, in spirit similar to RoR/Laravel/Django/Meteor, but reimagined from scratch for modern JS.

“Truly full-stack” because Wasp covers frontend, backend, and the database; Wasp’s goal is to capture the web app development end-to-end, in its entirety, as one cohesive whole.

“Spec-driven” because of Wasp’s unique, central concept of the spec (specification). It's in the name: Web App SPecification == Wasp.

Spec is a dedicated logic layer where you define your app at the full-stack level, that ties all parts of the stack together, and then you implement it all in the underlying technologies you are used to: React, Node, Prisma, … .<br>It is the place where you are not writing frontend, backend, or database logic, you are writing "full-stack" logic.

So far, the spec layer in Wasp was implemented as a standalone language (DSL), with our own custom compiler and tooling. Turns out that was more trouble than it was worth, and it started blocking us in further growing Wasp into what we want it to become, so by popular demand, we switched to spec in TypeScript!

Read on to learn more about it, how it works and what it unlocks.

The Wasp TypeScript Spec​

You write the Wasp TypeScript Spec (TS Spec) in *.wasp.ts files by constructing the app spec object.

You do so by using the spec constructors from @wasp.sh/spec: page, route, query, job, api, … . That is your "vocabulary" that you use to specify the parts of your web app, and then compose them all together into one big app spec object.

import { app, page, query, route } from "@wasp.sh/spec";

import { MainPage } from "./src/MainPage" with { type: "ref" }; // React

import { getTasks } from "./src/tasks" with { type: "ref" }; // Node.js

export default app({

wasp: { version: "^0.24.0" },

title: "ToDo App",

auth: {

userEntity: "User",

methods: { google: {}, gitHub: {}, email: {} },

onAuthFailedRedirectTo: "/login"

},

spec: [

route("RootRoute", "/", page(MainPage, {

authRequired: true

})),

query(getTasks, { entities: ["Task"] })

});

Wasp takes the spec and the rest of your code (React, Node.js, schema.prisma, …), and builds them into a full-stack app.

In the example above, this app will have:

Full-stack auth working out of the box (supporting Google, GitHub, or email & password auth).

The src/MainPage.tsx React component mounted as the home page at /.

The getTasks function from src/tasks.ts made callable from the client (e.g. from MainPage.tsx) via RPC, either directly or via TanStack Query.

đź’ˇ If you used the old Wasp DSL, you might be thinking at this point "hey this looks the same as before, maybe even a bit more verbose". True, but this is where the similarities stop, and exciting new possibilities start: read on.

While writing the spec, you are just writing TypeScript.

You can use third-party libraries, you can split the spec into multiple TypeScript files, you can define your own helper functions, use for loops, read from the disk, inspect env vars, you can organize your app vertically by features together with their spec, launch a rocket, … .

The only thing Wasp requires from you is that you export an app spec object from the main.wasp.ts file.

main.wasp.ts<br>import { readFile } from "fs/promises";

import { app, page, route } from "@wasp.sh/spec";

import { authSpec } from "./src/auth/auth.wasp";

import { cardsSpec } from "./src/cards/cards.wasp";

import MainPage from "./src/cards/MainPage" with { type: "ref" };

import Layout from "./src/Layout" with { type: "ref" };

export default app({

name: "waspello", // A Trello clone app made with Wasp!

wasp: { version: "^0.24.0" },

title: (await readFile("appTitle.txt", "utf-8")).trim(), // Reading from disk.

auth: {

userEntity: "User",

methods: {

usernameAndPassword: {},

google: {},

},

onAuthFailedRedirectTo: "/login",

},

client: {

rootComponent: Layout,

},

spec: [

route("MainRoute", "/", page(MainPage, { authRequired: true })),

// Parts of specification extracted into their own files.

authSpec,

cardsSpec,

],

});

src/cards/cards.wasp.ts<br>// A specification for the "cards" functionality.

import { action, query, type Spec } from "@wasp.sh/spec";

import { withFields } from "../utils.wasp"; // Custom helper...

wasp spec from stack full import

Related Articles