web/versioned_docs/version-0.23/general/wasp-ts-config.md
import { DiscordLink } from '@site/blog/components/DiscordLink';
:::caution Early preview This feature is currently in early preview and we are actively working on it. :::
:::caution Running wasp ts-setup
Whenever you run run wasp clean or remove node_modules on your own, you must rerun wasp ts-setup! We will remove this requirement in future versions. Read more about it below.
:::
In Wasp, you normally define/configure the high level of your app (pages, routes, queries, actions, auth, ...) in a main.wasp file in the root of your project. In main.wasp you write in Wasp's DSL (domain-specific language), which is a simple configuration language similar to JSON but smarter.
Wasp recently introduced the Wasp TS config, an alternative way to define the high level of your app via main.wasp.ts! Although it looks similar to how you would do it in main.wasp, the difference is that you write in TypeScript, not in Wasp's DSL.
Wasp TS config is an early preview feature, meaning it is a little rough and not yet where it could be, but it does work. We think it's pretty cool already, and you can try it out now. If you do, please share your feedback and ideas with us on our GitHub or <DiscordLink />. This is crucial for us to be able to shape this feature in the best possible way!
Go into the Wasp project you want to switch to the Wasp TS config (or create a new Wasp project if you just want to try it out).
Rename tsconfig.json file to tsconfig.src.json
Create a new tsconfig.json file with the following content:
{
"files": [],
"references": [
{ "path": "./tsconfig.src.json" },
{ "path": "./tsconfig.wasp.json" }
]
}
Create a new tsconfig.wasp.json file with the following content:
{
"compilerOptions": {
"skipLibCheck": true,
"target": "ES2022",
"isolatedModules": true,
"moduleDetection": "force",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"module": "NodeNext",
"noEmit": true,
"lib": ["ES2023"]
},
"include": ["main.wasp.ts"]
}
Add "type": "module" to the top level of your package.json, if you don't have it yet:
{
"type": "module",
...
}
Rename the main.wasp file to main.wasp.old. You'll want to use it as a reference while writing main.wasp.ts.
Run wasp clean and rm package-lock.json. This ensures you start from a clean state.
Run wasp ts-setup. This command will add the wasp-config package to your package.json's devDependencies.
IMPORTANT: Every time you run wasp clean or delete your node_modules, you must follow it up with wasp ts-setup. This is a temporary meassure until we improve the feature.
Create an empty main.wasp.ts file and rewrite your main.wasp.old in it but in TypeScript.
Check out the reference main.wasp.ts file below for details on what the TypeScript API for configuring Wasp looks like. In short, you'll have to:
App from wasp-configapp object with new App().app object to define parts of your web app like auth, pages, query, api...app from your file using a default export.You can manually do the rewrite using the reference file and TS types as guides (IDE support should work for you in main.wasp.ts), or you can (and we recommend it!) give the reference main.wasp.ts file to the LLM of your choice and tell it to rewrite your main.wasp while following the format in the reference file: we had great results with this!
Run wasp start to run your app! If you got everything right, your app should work exactly like it did before. The only difference is that it's now reading the Wasp config from main.wasp.ts instead of main.wasp.
:::tip
Don't forget, during wasp start, to have the database running or do the db migrations if needed, as you would normally when running your app in development.
:::
That is it, you are now using Wasp TS config! You can delete main.wasp.old file now if you still have it around.
:::caution
If you run wasp clean or remove node_modules on your own, you will have to rerun wasp ts-setup! This is a temporary workaround, we will remove it in future versions.
:::
Got stuck on any of these steps? Let us know in our <DiscordLink /> and we will help!
Play with the Wasp TS config, get the feel of it, and see if you can find ways to improve it. Here are some ideas you can experiment with:
main.wasp.ts file? Helper functions, loops?Whatever you end up doing, we would love it if you would let us know how it was and show us what you did.
We do have some immediate ideas of our own about what we want to improve, but we want to hear what you thought of, what you liked or disliked, or what you came up with. Even if you just found it all good, or just a single thing you didn't or did like, that is also valuable feedback and we would love to hear it!
Let us know on our GitHub or, even better, in our <DiscordLink />.
import { App } from 'wasp-config'
const app = new App('todoApp', {
title: 'ToDo App',
wasp: { version: '{latestWaspVersion}' },
// head: []
});
app.webSocket({
fn: { import: 'webSocketFn', from: '@src/webSocket' },
// autoConnect: false
});
app.auth({
userEntity: 'User',
methods: {
discord: {
configFn: { import: 'config', from: '@src/auth/discord' },
userSignupFields: { import: 'userSignupFields', from: '@src/auth/discord' }
},
google: {
configFn: { import: 'config', from: '@src/auth/google' },
userSignupFields: { import: 'userSignupFields', from: '@src/auth/google' }
},
gitHub: {
configFn: { import: 'config', from: '@src/auth/github.js' },
userSignupFields: { import: 'userSignupFields', from: '@src/auth/github.js' }
},
// keycloak: {},
// email: {
// userSignupFields: { import: 'userSignupFields', from: '@src/auth/email' },
// fromField: {
// name: 'ToDO App',
// email: '[email protected]'
// },
// emailVerification: {
// getEmailContentFn: { import: 'getVerificationEmailContent', from: '@src/auth/email' },
// clientRoute: 'EmailVerificationRoute',
// },
// passwordReset: {
// getEmailContentFn: { import: 'getPasswordResetEmailContent', from: '@src/auth/email' },
// clientRoute: 'PasswordResetRoute'
// }
// },
},
onAuthFailedRedirectTo: '/login',
onAuthSucceededRedirectTo: '/profile',
onBeforeSignup: { import: 'onBeforeSignup', from: '@src/auth/hooks.js' },
onAfterSignup: { import: 'onAfterSignup', from: '@src/auth/hooks.js' },
onAfterEmailVerified: { import: 'onAfterEmailVerified', from: "@src/auth/hooks.ts" },
onBeforeOAuthRedirect: { import: 'onBeforeOAuthRedirect', from: '@src/auth/hooks.js' },
onBeforeLogin: { import: 'onBeforeLogin', from: '@src/auth/hooks.js' },
onAfterLogin: { import: 'onAfterLogin', from: '@src/auth/hooks.js' }
});
app.server({
setupFn: { importDefault: 'setup', from: '@src/serverSetup' },
middlewareConfigFn: { import: 'serverMiddlewareFn', from: '@src/serverSetup' },
});
app.client({
rootComponent: { import: 'App', from: '@src/App' },
setupFn: { importDefault: 'setup', from: '@src/clientSetup' }
});
app.db({
seeds: [
{ import: 'devSeedSimple', from: '@src/dbSeeds' },
]
});
app.emailSender({
provider: 'SMTP',
defaultFrom: { email: '[email protected]' }
});
const loginPage = app.page('LoginPage', {
component: { importDefault: 'Login', from: '@src/pages/auth/Login' }
});
app.route('LoginRoute', { path: '/login', to: loginPage });
app.query('getTasks', {
fn: { import: 'getTasks', from: '@src/queries' },
entities: ['Task']
});
app.action('createTask', {
fn: { import: 'createTask', from: '@src/actions' },
entities: ['Task']
});
app.apiNamespace('bar', {
middlewareConfigFn: { import: 'barNamespaceMiddlewareFn', from: '@src/apis' },
path: '/bar'
});
app.api('barBaz', {
fn: { import: 'barBaz', from: '@src/apis' },
auth: false,
entities: ['Task'],
httpRoute: {
method: 'GET',
route: '/bar/baz',
},
});
app.job('mySpecialJob', {
executor: 'PgBoss',
perform: {
fn: { import: 'foo', from: '@src/jobs/bar' },
executorOptions: {
pgBoss: { retryLimit: 1 }
}
},
entities: ['Task']
});
export default app;