Bumps Wasp to 0.15.0 (#2321)

This commit is contained in:
Mihovil Ilakovac 2024-10-04 13:53:34 +02:00 committed by GitHub
parent 451251b52c
commit 05dfcc860e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 274 additions and 224 deletions

View File

@ -11,7 +11,7 @@
<a href="https://discord.gg/rzdnErX"><img alt="discord" src="https://img.shields.io/discord/686873244791210014?label=chat%20@%20discord"/></a>
</p>
------
---
<p align="center">
<a href="https://wasp-lang.dev">Web page</a> | <a href="https://wasp-lang.dev/docs">Docs</a>
@ -23,22 +23,25 @@ Wasp (**W**eb **A**pplication **Sp**ecification) is a Rails-like framework for R
Build your app in a day and deploy it with a single CLI command!
### Why is Wasp awesome
- 🚀 **Quick start**: Due to its expressiveness, you can create and deploy a production-ready web app from scratch with very few lines of concise, consistent, declarative code.
- 😌 **No boilerplate**: By abstracting away complex full-stack features, there is less boilerplate code. That means less code to maintain and understand! It also means easier upgrades.
- 🔓 **No lock-in**: You can deploy the Wasp app anywhere you like. There is no lock-in into specific providers; you have full control over the code (and can actually check it out in .wasp/ dir if you are interested ).
### Features
🔒 Full-stack Auth, 🖇️ RPC (Client <-> Server), 🚀 Simple Deployment, ⚙ Jobs, ✉️ Email Sending, 🛟 Full-stack Type Safety, ...
🔒 Full-stack Auth, 🖇️ RPC (Client <-> Server), 🚀 Simple Deployment, ⚙ Jobs, ✉️ Email Sending, 🛟 Full-stack Type Safety, ...
### Code example
Simple Wasp config file in which you describe the high-level details of your web app:
```js
// file: main.wasp
app todoApp {
title: "ToDo App", // visible in the browser tab
wasp: { version: "^0.14.0" },
wasp: { version: "^0.15.0" },
auth: { // full-stack auth out-of-the-box
userEntity: User, methods: { email: {...} }
}
@ -81,9 +84,11 @@ For more information about Wasp, check [**docs**](https://wasp-lang.dev/docs).
# Get started
Run
```
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
```
to install Wasp on OSX/Linux/WSL(Win). From there, just follow the instructions to run your first app in less than a minute!
For more details, check out [the docs](https://wasp-lang.dev/docs).
@ -114,9 +119,10 @@ The core of Wasp is built in Haskell, but there are also a lot of non-Haskell pa
Even if you don't plan to submit any code, just joining the discussion on discord [![Discord](https://img.shields.io/discord/686873244791210014?label=chat%20on%20discord)](https://discord.gg/rzdnErX) and giving your feedback is already great and helps a lot (motivates us and helps us figure out how to shape Wasp)!
You can also:
- :star: Star this repo to show your interest/support.
- :mailbox: Stay updated by subscribing to our [email list](https://wasp-lang.dev#signup).
- :speech_balloon: Join the discussion at https://github.com/wasp-lang/wasp/discussions .
- :star: Star this repo to show your interest/support.
- :mailbox: Stay updated by subscribing to our [email list](https://wasp-lang.dev#signup).
- :speech_balloon: Join the discussion at https://github.com/wasp-lang/wasp/discussions .
# Careers
@ -143,5 +149,3 @@ Check our [careers](https://wasp-lang.notion.site/Wasp-Careers-59fd1682c80d446f9
<a href="https://github.com/MarianoMiguel"><img src="https://github.com/MarianoMiguel.png" width="50px" alt="MarianoMiguel" /></a>
<a href="https://github.com/Tech4Money"><img src="https://github.com/Tech4Money.png" width="50px" alt="Tech4Money" /></a>
<a href="https://github.com/haseeb-heaven"><img src="https://github.com/haseeb-heaven.png" width="50px" alt="haseeb-heaven" /></a>

View File

@ -1,6 +1,6 @@
app hackathonBetaSubmissions {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "Hackathon Submissions"
}

View File

@ -1,6 +1,6 @@
app streaming {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "streaming"
}

View File

@ -1,6 +1,6 @@
app Thoughts {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "Thoughts",
db: {

View File

@ -1,6 +1,6 @@
app TodoTypescript {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo TypeScript",

View File

@ -1,6 +1,6 @@
app TodoApp {
wasp: {
version: "^0.14.0" // Pins the version of Wasp to use.
version: "^0.15.0" // Pins the version of Wasp to use.
},
title: "TodoApp", // Used as the browser tab title. Note that all strings in Wasp are double quoted!
auth: {

View File

@ -1,6 +1,6 @@
app TodoApp {
wasp: {
version: "^0.14.0" // Pins the version of Wasp to use.
version: "^0.15.0" // Pins the version of Wasp to use.
},
title: "TodoApp", // Used as the browser tab title. Note that all strings in Wasp are double quoted!
auth: {

View File

@ -1,6 +1,6 @@
app waspello {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "Waspello",

View File

@ -1,6 +1,6 @@
app waspleau {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "Waspleau",

View File

@ -1,6 +1,6 @@
app whereDoWeEat {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "where-do-we-eat",
client: {

View File

@ -226,7 +226,7 @@ waspGhOrgName = "wasp-lang"
-- By tagging templates for each version of Wasp CLI, we ensure that each release of
-- Wasp CLI uses correct version of templates, that work with it.
waspVersionTemplateGitTag :: String
waspVersionTemplateGitTag = "wasp-v0.14-template"
waspVersionTemplateGitTag = "wasp-v0.15-template"
findTemplateByString :: [StarterTemplate] -> String -> Maybe StarterTemplate
findTemplateByString templates query = find ((== query) . show) templates

View File

@ -1,6 +1,6 @@
app waspBuild {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
title: "waspBuild"
}

View File

@ -1,6 +1,6 @@
app waspCompile {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
title: "waspCompile"
}

View File

@ -1,6 +1,6 @@
app waspComplexTest {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
auth: {
userEntity: User,

View File

@ -1,6 +1,6 @@
app waspJob {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
title: "waspJob"
}

View File

@ -1,6 +1,6 @@
app waspMigrate {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
title: "waspMigrate"
}

View File

@ -1,6 +1,6 @@
app waspNew {
wasp: {
version: "^0.14.2"
version: "^0.15.0"
},
title: "waspNew"
}

View File

@ -1,6 +1,6 @@
app crudTesting {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
head: [
"<link rel=\"stylesheet\" href=\"https://unpkg.com/mvp.css@1.12/mvp.css\">"

View File

@ -1,6 +1,6 @@
app pgVectorExample {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "PG Vector Example",
client: {

View File

@ -1,6 +1,6 @@
app TodoTypescript {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo TypeScript",

View File

@ -1,6 +1,6 @@
app todoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo App",
// head: [],

View File

@ -1,6 +1,6 @@
app todoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo App",
auth: {

View File

@ -6,7 +6,7 @@ cabal-version: 2.4
-- Consider using hpack, or maybe even hpack-dhall.
name: waspc
version: 0.14.2
version: 0.15.0
description: Please see the README on GitHub at <https://github.com/wasp-lang/wasp/waspc#readme>
homepage: https://github.com/wasp-lang/wasp/waspc#readme
bug-reports: https://github.com/wasp-lang/wasp/issues

View File

@ -55,7 +55,7 @@ To use auth hooks, you must first declare them in the Wasp file:
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
auth: {
userEntity: User,
@ -77,7 +77,7 @@ app myApp {
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
auth: {
userEntity: User,
@ -583,7 +583,7 @@ If you want to refresh the token periodically, use a [Wasp Job](../advanced/jobs
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
auth: {
userEntity: User,
@ -605,7 +605,7 @@ app myApp {
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
auth: {
userEntity: User,

View File

@ -19,6 +19,7 @@ Wasp supports e-mail authentication out of the box, along with email verificatio
## Setting Up Email Authentication
We'll need to take the following steps to set up email authentication:
1. Enable email authentication in the Wasp file
1. Add the `User` entity
1. Add the auth routes and pages
@ -49,7 +50,7 @@ Let's start with adding the following to our `main.wasp` file:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -77,13 +78,14 @@ app myApp {
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -111,6 +113,7 @@ app myApp {
},
}
```
</TabItem>
</Tabs>
@ -132,6 +135,7 @@ model User {
// ...
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -144,12 +148,12 @@ model User {
// ...
}
```
</TabItem>
</Tabs>
<ReadMoreAboutAuthEntities />
### 3. Add the Routes and Pages
Next, we need to define the routes and pages for the authentication pages.
@ -187,6 +191,7 @@ page EmailVerificationPage {
component: import { EmailVerification } from "@src/pages/auth.jsx",
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -218,6 +223,7 @@ page EmailVerificationPage {
component: import { EmailVerification } from "@src/pages/auth.tsx",
}
```
</TabItem>
</Tabs>
@ -254,11 +260,10 @@ export function Login() {
</span>
<br />
<span className="text-sm font-medium text-gray-900">
Forgot your password? <Link to="/request-password-reset">reset it</Link>
.
Forgot your password? <Link to="/request-password-reset">reset it</Link>.
</span>
</Layout>
);
)
}
export function Signup() {
@ -270,7 +275,7 @@ export function Signup() {
I already have an account (<Link to="/login">go to login</Link>).
</span>
</Layout>
);
)
}
export function EmailVerification() {
@ -282,7 +287,7 @@ export function EmailVerification() {
If everything is okay, <Link to="/login">go to login</Link>
</span>
</Layout>
);
)
}
export function RequestPasswordReset() {
@ -290,7 +295,7 @@ export function RequestPasswordReset() {
<Layout>
<ForgotPasswordForm />
</Layout>
);
)
}
export function PasswordReset() {
@ -302,22 +307,23 @@ export function PasswordReset() {
If everything is okay, <Link to="/login">go to login</Link>
</span>
</Layout>
);
)
}
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
</div>
);
)
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -341,11 +347,10 @@ export function Login() {
</span>
<br />
<span className="text-sm font-medium text-gray-900">
Forgot your password? <Link to="/request-password-reset">reset it</Link>
.
Forgot your password? <Link to="/request-password-reset">reset it</Link>.
</span>
</Layout>
);
)
}
export function Signup() {
@ -357,7 +362,7 @@ export function Signup() {
I already have an account (<Link to="/login">go to login</Link>).
</span>
</Layout>
);
)
}
export function EmailVerification() {
@ -369,7 +374,7 @@ export function EmailVerification() {
If everything is okay, <Link to="/login">go to login</Link>
</span>
</Layout>
);
)
}
export function RequestPasswordReset() {
@ -377,7 +382,7 @@ export function RequestPasswordReset() {
<Layout>
<ForgotPasswordForm />
</Layout>
);
)
}
export function PasswordReset() {
@ -389,22 +394,23 @@ export function PasswordReset() {
If everything is okay, <Link to="/login">go to login</Link>
</span>
</Layout>
);
)
}
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
</div>
);
)
}
```
</TabItem>
</Tabs>
@ -430,6 +436,7 @@ app myApp {
}
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -442,6 +449,7 @@ app myApp {
}
}
```
</TabItem>
</Tabs>
@ -462,21 +470,22 @@ Running `wasp db migrate-dev` and then `wasp start` should give you a working ap
![Auth UI](/img/authui/signup.png)
Some of the behavior you get out of the box:
1. Rate limiting
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
2. Preventing user email leaks
If somebody tries to signup with an email that already exists and it's verified, we _pretend_ that the account was created instead of saying it's an existing account. This is done to prevent leaking the user's email address.
If somebody tries to signup with an email that already exists and it's verified, we _pretend_ that the account was created instead of saying it's an existing account. This is done to prevent leaking the user's email address.
3. Allowing registration for unverified emails
If a user tries to register with an existing but **unverified** email, we'll allow them to do that. This is done to prevent bad actors from locking out other users from registering with their email address.
If a user tries to register with an existing but **unverified** email, we'll allow them to do that. This is done to prevent bad actors from locking out other users from registering with their email address.
4. Password validation
Read more about the default password validation rules and how to override them in [auth overview docs](../auth/overview).
Read more about the default password validation rules and how to override them in [auth overview docs](../auth/overview).
## Email Verification Flow
@ -505,6 +514,7 @@ emailVerification: {
clientRoute: EmailVerificationRoute,
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -515,10 +525,11 @@ emailVerification: {
clientRoute: EmailVerificationRoute,
}
```
</TabItem>
</Tabs>
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `EmailVerificationRoute` route we defined in the `main.wasp` file.
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `EmailVerificationRoute` route we defined in the `main.wasp` file.
The content of the e-mail can be customized, read more about it [here](#emailverification-emailverificationconfig-).
@ -533,13 +544,14 @@ We defined our email verification page in the `auth.{jsx,tsx}` file.
Users can request a password and then they'll receive an e-mail with a link to reset their password.
Some of the behavior you get out of the box:
1. Rate limiting
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
2. Preventing user email leaks
If somebody requests a password reset with an unknown email address, we'll give back the same response as if the user requested a password reset successfully. This is done to prevent leaking information.
If somebody requests a password reset with an unknown email address, we'll give back the same response as if the user requested a password reset successfully. This is done to prevent leaking information.
Our setup in `main.wasp` looks like this:
@ -553,6 +565,7 @@ passwordReset: {
clientRoute: PasswordResetRoute,
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -563,6 +576,7 @@ passwordReset: {
clientRoute: PasswordResetRoute,
}
```
</TabItem>
</Tabs>
@ -683,6 +697,7 @@ export const signup = async (args, _context) => {
}
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -718,7 +733,10 @@ type CustomSignupOutput = {
message: string
}
export const signup: CustomSignup<CustomSignupInput, CustomSignupOutput> = async (args, _context) => {
export const signup: CustomSignup<
CustomSignupInput,
CustomSignupOutput
> = async (args, _context) => {
ensureValidEmail(args)
ensurePasswordIsPresent(args)
ensureValidPassword(args)
@ -728,45 +746,48 @@ export const signup: CustomSignup<CustomSignupInput, CustomSignupOutput> = async
const existingAuthIdentity = await findAuthIdentity(providerId)
if (existingAuthIdentity) {
const providerData = deserializeAndSanitizeProviderData<'email'>(existingAuthIdentity.providerData)
const providerData = deserializeAndSanitizeProviderData<'email'>(
existingAuthIdentity.providerData
)
// Your custom code here
} else {
// sanitizeAndSerializeProviderData will hash the user's password
const newUserProviderData = await sanitizeAndSerializeProviderData<'email'>({
const newUserProviderData =
await sanitizeAndSerializeProviderData<'email'>({
hashedPassword: args.password,
isEmailVerified: false,
emailVerificationSentAt: null,
passwordResetSentAt: null,
})
})
await createUser(
providerId,
providerData,
// Any additional data you want to store on the User entity
{},
{}
)
// Verification link links to a client route e.g. /email-verification
const verificationLink = await createEmailVerificationLink(args.email, '/email-verification');
const verificationLink = await createEmailVerificationLink(
args.email,
'/email-verification'
)
try {
await sendEmailVerificationEmail(
args.email,
{
from: {
name: "My App Postman",
email: "hello@itsme.com",
},
to: args.email,
subject: "Verify your email",
text: `Click the link below to verify your email: ${verificationLink}`,
html: `
await sendEmailVerificationEmail(args.email, {
from: {
name: 'My App Postman',
email: 'hello@itsme.com',
},
to: args.email,
subject: 'Verify your email',
text: `Click the link below to verify your email: ${verificationLink}`,
html: `
<p>Click the link below to verify your email</p>
<a href="${verificationLink}">Verify email</a>
`,
}
);
})
} catch (e: unknown) {
console.error("Failed to send email verification email:", e);
throw new HttpError(500, "Failed to send email verification email.");
console.error('Failed to send email verification email:', e)
throw new HttpError(500, 'Failed to send email verification email.')
}
}
} catch (e) {
@ -785,6 +806,7 @@ export const signup: CustomSignup<CustomSignupInput, CustomSignupOutput> = async
}
}
```
</TabItem>
</Tabs>
@ -875,6 +897,7 @@ model User {
id Int @id @default(autoincrement())
}
```
</TabItem>
</Tabs>
@ -914,6 +937,7 @@ app myApp {
// ...
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -946,6 +970,7 @@ app myApp {
// ...
}
```
</TabItem>
</Tabs>
@ -956,16 +981,20 @@ app myApp {
Read more about the `userSignupFields` function [here](./overview#1-defining-extra-fields).
#### `fromField: EmailFromField` <Required />
`fromField` is a dict that specifies the name and e-mail address of the sender of the e-mails sent by your app.
It has the following fields:
- `name`: name of the sender
- `email`: e-mail address of the sender <Required />
#### `emailVerification: EmailVerificationConfig` <Required />
`emailVerification` is a dict that specifies the details of the e-mail verification process.
It has the following fields:
- `clientRoute: Route`: a route that is used for the user to verify their e-mail address. <Required />
Client route should handle the process of taking a token from the URL and sending it to the server to verify the e-mail address. You can use our `verifyEmail` action for that.
@ -978,6 +1007,7 @@ It has the following fields:
...
await verifyEmail({ token });
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -986,11 +1016,12 @@ It has the following fields:
...
await verifyEmail({ token });
```
</TabItem>
</Tabs>
:::note
We used Auth UI above to avoid doing this work of sending the token to the server manually.
We used Auth UI above to avoid doing this work of sending the token to the server manually.
:::
- `getEmailContentFn: ExtImport`: a function that returns the content of the e-mail that is sent to the user.
@ -1010,6 +1041,7 @@ It has the following fields:
`,
})
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -1027,19 +1059,21 @@ It has the following fields:
`,
})
```
</TabItem>
</Tabs>
<small>This is the default content of the e-mail, you can customize it to your liking.</small>
#### `passwordReset: PasswordResetConfig` <Required />
`passwordReset` is a dict that specifies the password reset process.
It has the following fields:
- `clientRoute: Route`: a route that is used for the user to reset their password. <Required />
Client route should handle the process of taking a token from the URL and a new password from the user and sending it to the server. You can use our `requestPasswordReset` and `resetPassword` actions to do that.
Client route should handle the process of taking a token from the URL and a new password from the user and sending it to the server. You can use our `requestPasswordReset` and `resetPassword` actions to do that.
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
@ -1055,6 +1089,7 @@ It has the following fields:
...
await resetPassword({ password, token })
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -1069,6 +1104,7 @@ It has the following fields:
...
await resetPassword({ password, token })
```
</TabItem>
</Tabs>
@ -1093,6 +1129,7 @@ It has the following fields:
`,
})
```
</TabItem>
<TabItem value="ts" label="TypeScript">
@ -1110,6 +1147,7 @@ It has the following fields:
`,
})
```
</TabItem>
</Tabs>

View File

@ -410,7 +410,7 @@ function MainPage() {
#### `getFirstProviderUserId`
It returns the first user ID that it finds for the user. For example if the user has signed up with email, it will return the email. If the user has signed up with Google, it will return the Google ID. The `user` object needs to have the `auth` and the `identities` relations included.
It returns the first user ID that it finds for the user. For example if the user has signed up with email, it will return the email. If the user has signed up with Google, it will return the Google ID. The `user` object needs to have the `auth` and the `identities` relations included.
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
@ -470,7 +470,7 @@ For example, you might set it to `User`:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -11,10 +11,9 @@ import WaspFileStructureNote from './\_wasp-file-structure-note.md';
import GetUserFieldsType from './\_getuserfields-type.md';
import ApiReferenceIntro from './\_api-reference-intro.md';
import UserSignupFieldsExplainer from '../\_user-signup-fields-explainer.md';
import DiscordData from '../entities/_discord-data.md';
import DiscordData from '../entities/\_discord-data.md';
import AccessingUserDataNote from '../\_accessing-user-data-note.md';
Wasp supports Discord Authentication out of the box.
Letting your users log in using their Discord accounts turns the signup process into a breeze.
@ -43,7 +42,7 @@ Let's start by properly configuring the Auth object:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -68,7 +67,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -137,8 +136,9 @@ width="400px"
/>
4. Go to the **OAuth2** tab on the sidebar and click **Add Redirect**
- For development, put: `http://localhost:3001/auth/discord/callback`.
- Once you know on which URL your API server will be deployed, you can create a new app with that URL instead e.g. `https://your-server-url.com/auth/discord/callback`.
- For development, put: `http://localhost:3001/auth/discord/callback`.
- Once you know on which URL your API server will be deployed, you can create a new app with that URL instead e.g. `https://your-server-url.com/auth/discord/callback`.
4. Hit **Save Changes**.
5. Hit **Reset Secret**.
@ -213,9 +213,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -241,9 +241,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -263,7 +263,6 @@ Yay, we've successfully set up Discord Auth! 🎉
![Discord Auth](/img/auth/discord.png)
Running `wasp db migrate-dev` and `wasp start` should now give you a working app with authentication.
To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview).
@ -277,7 +276,7 @@ Add `discord: {}` to the `auth.methods` dictionary to use it with default settin
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -297,7 +296,7 @@ app myApp {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -362,7 +361,7 @@ For an up to date info about the data received from Discord, please refer to the
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -394,12 +393,12 @@ model User {
export const userSignupFields = {
username: (data) => data.profile.global_name,
avatarUrl: (data) => data.profile.avatar,
};
}
export function getConfig() {
return {
scopes: ['identify'],
};
}
}
```
@ -409,7 +408,7 @@ export function getConfig() {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -477,7 +476,7 @@ When you receive the `user` object [on the client or the server](../overview.md#
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -501,7 +500,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -11,7 +11,7 @@ import WaspFileStructureNote from './\_wasp-file-structure-note.md';
import GetUserFieldsType from './\_getuserfields-type.md';
import ApiReferenceIntro from './\_api-reference-intro.md';
import UserSignupFieldsExplainer from '../\_user-signup-fields-explainer.md';
import GithubData from '../entities/_github-data.md';
import GithubData from '../entities/\_github-data.md';
import AccessingUserDataNote from '../\_accessing-user-data-note.md';
Wasp supports Github Authentication out of the box.
@ -43,7 +43,7 @@ Let's start by properly configuring the Auth object:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -68,7 +68,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -213,9 +213,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -241,9 +241,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -276,7 +276,7 @@ Add `gitHub: {}` to the `auth.methods` dictionary to use it with default setting
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -296,7 +296,7 @@ app myApp {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -339,7 +339,7 @@ The data we receive from GitHub on the `/user` endpoint looks something this:
"id": 1,
"name": "monalisa octocat",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"gravatar_id": ""
// ...
}
```
@ -374,7 +374,7 @@ For an up to date info about the data received from GitHub, please refer to the
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -404,14 +404,14 @@ model User {
```js title=src/auth/github.js
export const userSignupFields = {
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data) => data.profile.name,
};
}
export function getConfig() {
return {
scopes: ['user'],
};
}
}
```
@ -421,7 +421,7 @@ export function getConfig() {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -453,7 +453,7 @@ model User {
import { defineUserSignupFields } from 'wasp/server/auth'
export const userSignupFields = defineUserSignupFields({
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data: any) => data.profile.name,
})
@ -489,7 +489,7 @@ When you receive the `user` object [on the client or the server](../overview.md#
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -513,7 +513,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -11,7 +11,7 @@ import WaspFileStructureNote from './\_wasp-file-structure-note.md';
import GetUserFieldsType from './\_getuserfields-type.md';
import ApiReferenceIntro from './\_api-reference-intro.md';
import UserSignupFieldsExplainer from '../\_user-signup-fields-explainer.md';
import GoogleData from '../entities/_google-data.md';
import GoogleData from '../entities/\_google-data.md';
import AccessingUserDataNote from '../\_accessing-user-data-note.md';
Wasp supports Google Authentication out of the box.
@ -43,7 +43,7 @@ Let's start by properly configuring the Auth object:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -66,7 +66,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -252,9 +252,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -280,9 +280,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -317,7 +317,7 @@ Add `google: {}` to the `auth.methods` dictionary to use it with default setting
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -337,7 +337,7 @@ app myApp {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -400,7 +400,7 @@ For an up to date info about the data received from Google, please refer to the
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -430,7 +430,7 @@ model User {
```js title=src/auth/google.js
export const userSignupFields = {
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data) => data.profile.name,
}
@ -447,7 +447,7 @@ export function getConfig() {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -479,7 +479,7 @@ model User {
import { defineUserSignupFields } from 'wasp/server/auth'
export const userSignupFields = defineUserSignupFields({
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data: any) => data.profile.name,
})
@ -515,7 +515,7 @@ When you receive the `user` object [on the client or the server](../overview.md#
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -539,7 +539,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -11,7 +11,7 @@ import WaspFileStructureNote from './\_wasp-file-structure-note.md';
import GetUserFieldsType from './\_getuserfields-type.md';
import ApiReferenceIntro from './\_api-reference-intro.md';
import UserSignupFieldsExplainer from '../\_user-signup-fields-explainer.md';
import KeycloakData from '../entities/_keycloak-data.md';
import KeycloakData from '../entities/\_keycloak-data.md';
import AccessingUserDataNote from '../\_accessing-user-data-note.md';
Wasp supports Keycloak Authentication out of the box.
@ -42,7 +42,7 @@ Let's start by properly configuring the Auth object:
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -65,7 +65,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -125,19 +125,19 @@ model User {
1. Log into your Keycloak admin console.
1. Under **Clients**, click on **Create Client**.
![Keycloak Screenshot 1](/img/auth/keycloak/1-keycloak.png)
![Keycloak Screenshot 1](/img/auth/keycloak/1-keycloak.png)
1. Fill in the **Client ID** and choose a name for the client.
![Keycloak Screenshot 2](/img/auth/keycloak/2-keycloak.png)
![Keycloak Screenshot 2](/img/auth/keycloak/2-keycloak.png)
1. In the next step, enable **Client Authentication**.
![Keycloak Screenshot 3](/img/auth/keycloak/3-keycloak.png)
![Keycloak Screenshot 3](/img/auth/keycloak/3-keycloak.png)
1. Under **Valid Redirect URIs**, add `http://localhost:3001/auth/keycloak/callback` for local development.
![Keycloak Screenshot 4](/img/auth/keycloak/4-keycloak.png)
![Keycloak Screenshot 4](/img/auth/keycloak/4-keycloak.png)
- Once you know on which URL(s) your API server will be deployed, also add those URL(s).
- For example: `https://my-server-url.com/auth/keycloak/callback`.
@ -145,7 +145,7 @@ model User {
1. Click **Save**.
1. In the **Credentials** tab, copy the **Client Secret** value, which we'll use in the next step.
![Keycloak Screenshot 5](/img/auth/keycloak/5-keycloak.png)
![Keycloak Screenshot 5](/img/auth/keycloak/5-keycloak.png)
### 4. Adding Environment Variables
@ -220,9 +220,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -248,9 +248,9 @@ export function Login() {
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div className="h-full w-full bg-white">
<div className="flex min-h-[75vh] min-w-full items-center justify-center">
<div className="h-full w-full max-w-sm bg-white p-5">
<div>{children}</div>
</div>
</div>
@ -283,7 +283,7 @@ Add `keycloak: {}` to the `auth.methods` dictionary to use it with default setti
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -303,7 +303,7 @@ app myApp {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -359,7 +359,7 @@ For up-to-date info about the data received from Keycloak, please refer to the [
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -389,7 +389,7 @@ model User {
```js title=src/auth/keycloak.js
export const userSignupFields = {
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data) => data.profile.name,
}
@ -406,7 +406,7 @@ export function getConfig() {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -438,7 +438,7 @@ model User {
import { defineUserSignupFields } from 'wasp/server/auth'
export const userSignupFields = defineUserSignupFields({
username: () => "hardcoded-username",
username: () => 'hardcoded-username',
displayName: (data: any) => data.profile.name,
})
@ -474,7 +474,7 @@ When you receive the `user` object [on the client or the server](../overview.md#
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -498,7 +498,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -36,7 +36,7 @@ Here's what the full setup looks like:
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -63,7 +63,7 @@ model User {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -148,7 +148,7 @@ Declare an import under `app.auth.methods.google.userSignupFields` (the example
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -180,7 +180,7 @@ export const userSignupFields = {
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -45,7 +45,7 @@ Let's start with adding the following to our `main.wasp` file:
```wasp title="main.wasp" {11}
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -66,7 +66,7 @@ app myApp {
```wasp title="main.wasp" {11}
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -624,7 +624,7 @@ When you receive the `user` object [on the client or the server](./overview.md#a
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -649,7 +649,7 @@ model User {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -681,7 +681,7 @@ model User {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {
@ -703,7 +703,7 @@ app myApp {
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
auth: {

View File

@ -73,7 +73,7 @@ We can start by running `wasp new tasksCrudApp` and then adding the following to
```wasp title="main.wasp"
app tasksCrudApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "Tasks Crud App",

View File

@ -66,7 +66,7 @@ Finally, Prisma models become Wasp Entities which can be then used in the `main.
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
}

View File

@ -17,6 +17,7 @@ It is an opinionated way of building **full-stack web applications**. It takes c
major parts of a web application: **client** (front-end), **server** (back-end) and **database**.
### Works well with your existing stack
Wasp is not trying to do everything at once but rather focuses on the complexity that arises from connecting all the parts of the stack (client, server, database, deployment).
Wasp is using **React**, **Node.js** and **Prisma** under the hood and relies on them to define web components and server queries and actions.
@ -35,6 +36,7 @@ At the core is the Wasp compiler which takes the Wasp config and your Javascript
The cool thing about having a compiler that understands your code is that it can do a lot of things for you.
Define your app in the Wasp config and get:
- login and signup with Auth UI components,
- full-stack type safety,
- e-mail sending,
@ -52,10 +54,11 @@ Let's say you want to build a web app that allows users to **create and share th
Let's start with the `main.wasp` file: it is the central file of your app, where you describe the app from the high level.
Let's give our app a title and let's immediately turn on the full-stack authentication via username and password:
```wasp title="main.wasp"
app RecipeApp {
title: "My Recipes",
wasp: { version: "^0.14.0" },
wasp: { version: "^0.15.0" },
auth: {
methods: { usernameAndPassword: {} },
onAuthFailedRedirectTo: "/login",
@ -90,6 +93,7 @@ We do that by defining Operations, in this case, a Query `getRecipes` and Action
which are in their essence Node.js functions that execute on the server and can, thanks to Wasp, very easily be called from the client.
First, we define these Operations in our `main.wasp` file, so Wasp knows about them and can "beef them up":
```wasp title="main.wasp"
// Queries have automatic cache invalidation and are type-safe.
query getRecipes {
@ -125,6 +129,7 @@ Now we can very easily use these in our React components!
For the end, let's create a home page of our app.
First, we define it in `main.wasp`:
```wasp title="main.wasp"
...
@ -138,30 +143,32 @@ page HomePage {
and then implement it as a React component in JS/TS (that calls the Operations we previously defined):
```tsx title="src/pages/HomePage.tsx"
import { useQuery, getRecipes } from "wasp/client/operations";
import { type User } from "wasp/entities";
import { useQuery, getRecipes } from 'wasp/client/operations'
import { type User } from 'wasp/entities'
export function HomePage({ user }: { user: User }) {
// Due to full-stack type safety, `recipes` will be of type `Recipe[]` here.
const { data: recipes, isLoading } = useQuery(getRecipes); // Calling our query here!
const { data: recipes, isLoading } = useQuery(getRecipes) // Calling our query here!
if (isLoading) {
return <div>Loading...</div>;
return <div>Loading...</div>
}
return (
<div>
<h1>Recipes</h1>
<ul>
{recipes ? recipes.map((recipe) => (
<li key={recipe.id}>
<div>{recipe.title}</div>
<div>{recipe.description}</div>
</li>
)) : 'No recipes defined yet!'}
{recipes
? recipes.map((recipe) => (
<li key={recipe.id}>
<div>{recipe.title}</div>
<div>{recipe.description}</div>
</li>
))
: 'No recipes defined yet!'}
</ul>
</div>
);
)
}
```
@ -174,14 +181,17 @@ Above we skipped defining `/login` and `/signup` pages to keep the example a bit
:::
## When to use Wasp
Wasp addresses the same core problems that typical web app frameworks are addressing, and it in big part [looks, swims and quacks](https://en.wikipedia.org/wiki/Duck_test) like a web app framework.
### Best used for
- building full-stack web apps (like e.g. Airbnb or Asana)
- quickly starting a web app with industry best practices
- to be used alongside modern web dev stack (React and Node.js are currently supported)
### Avoid using Wasp for
- building static/presentational websites
- to be used as a no-code solution
- to be a solve-it-all tool in a single language
@ -196,7 +206,7 @@ Wasp does not match typical expectations of a web app framework: it is not a set
Wasp is a programming language, but a specific kind: it is specialized for a single purpose: **building modern web applications**. We call such languages *DSL*s (Domain Specific Language).
Other examples of *DSL*s that are often used today are e.g. *SQL* for databases and *HTML* for web page layouts.
Other examples of *DSL*s that are often used today are e.g. _SQL_ for databases and _HTML_ for web page layouts.
The main advantage and reason why *DSL*s exist is that they need to do only one task (e.g. database queries)
so they can do it well and provide the best possible experience for the developer.

View File

@ -9,7 +9,7 @@ Each Wasp project can have only one `app` type declaration. It is used to config
```wasp
app todoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo App",
head: [
@ -27,7 +27,7 @@ You may want to change the title of your app, which appears in the browser tab,
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "BookFace"
}
@ -42,7 +42,7 @@ An example of adding extra style sheets and scripts:
```wasp
app myApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "My App",
head: [ // optional
@ -58,7 +58,7 @@ app myApp {
```wasp
app todoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "ToDo App",
head: [

View File

@ -93,7 +93,7 @@ The default `main.wasp` file generated with `wasp new` on the previous page look
```wasp title="main.wasp"
app TodoApp {
wasp: {
version: "^0.14.0" // Pins the version of Wasp to use.
version: "^0.15.0" // Pins the version of Wasp to use.
},
title: "TodoApp" // Used as the browser tab title. Note that all strings in Wasp are double quoted!
}
@ -113,7 +113,7 @@ page MainPage {
```wasp title="main.wasp"
app TodoApp {
wasp: {
version: "^0.14.0" // Pins the version of Wasp to use.
version: "^0.15.0" // Pins the version of Wasp to use.
},
title: "TodoApp" // Used as the browser tab title. Note that all strings in Wasp are double quoted!
}

View File

@ -194,7 +194,7 @@ Your Wasp file should now look like this:
```wasp title="main.wasp"
app TodoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "TodoApp"
}
@ -211,7 +211,7 @@ page MainPage {
```wasp title="main.wasp"
app TodoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
title: "TodoApp"
}

View File

@ -38,7 +38,7 @@ Next, tell Wasp to use full-stack [authentication](../auth/overview):
```wasp title="main.wasp"
app TodoApp {
wasp: {
version: "^0.14.0"
version: "^0.15.0"
},
// highlight-start
title: "TodoApp",
@ -275,7 +275,6 @@ src={useBaseUrl('img/wasp_user_in_db.gif')}
style={{ border: "1px solid black" }}
/>
You'll notice that we now have a `User` entity in the database alongside the `Task` entity.
However, you will notice that if you try logging in as different users and creating some tasks, all users share the same tasks. That's because you haven't yet updated the queries and actions to have per-user tasks. Let's do that next.