diff --git a/README.md b/README.md index 7125e9d014..93f5f83ee8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ discord

------- +---

Web page | Docs @@ -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 MarianoMiguel Tech4Money haseeb-heaven - - diff --git a/examples/hackathon-submissions/main.wasp b/examples/hackathon-submissions/main.wasp index d3aa9a08eb..9be911286c 100644 --- a/examples/hackathon-submissions/main.wasp +++ b/examples/hackathon-submissions/main.wasp @@ -1,6 +1,6 @@ app hackathonBetaSubmissions { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "Hackathon Submissions" } diff --git a/examples/streaming/main.wasp b/examples/streaming/main.wasp index 3eaad63f69..d6a5c64b93 100644 --- a/examples/streaming/main.wasp +++ b/examples/streaming/main.wasp @@ -1,6 +1,6 @@ app streaming { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "streaming" } diff --git a/examples/thoughts/main.wasp b/examples/thoughts/main.wasp index 5c31aba4e7..1d0050576d 100644 --- a/examples/thoughts/main.wasp +++ b/examples/thoughts/main.wasp @@ -1,6 +1,6 @@ app Thoughts { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "Thoughts", db: { diff --git a/examples/todo-typescript/main.wasp b/examples/todo-typescript/main.wasp index 365f1d3ad6..9febee3abf 100644 --- a/examples/todo-typescript/main.wasp +++ b/examples/todo-typescript/main.wasp @@ -1,6 +1,6 @@ app TodoTypescript { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "ToDo TypeScript", diff --git a/examples/tutorials/TodoApp/main.wasp b/examples/tutorials/TodoApp/main.wasp index 748dcebd71..4b7f6308ad 100644 --- a/examples/tutorials/TodoApp/main.wasp +++ b/examples/tutorials/TodoApp/main.wasp @@ -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: { diff --git a/examples/tutorials/TodoAppTs/main.wasp b/examples/tutorials/TodoAppTs/main.wasp index 5a6eb80a39..58abcae07e 100644 --- a/examples/tutorials/TodoAppTs/main.wasp +++ b/examples/tutorials/TodoAppTs/main.wasp @@ -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: { diff --git a/examples/waspello/main.wasp b/examples/waspello/main.wasp index 02ecf8d217..b3c91f8945 100644 --- a/examples/waspello/main.wasp +++ b/examples/waspello/main.wasp @@ -1,6 +1,6 @@ app waspello { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "Waspello", diff --git a/examples/waspleau/main.wasp b/examples/waspleau/main.wasp index 0d2f5aa33e..682f4e91cc 100644 --- a/examples/waspleau/main.wasp +++ b/examples/waspleau/main.wasp @@ -1,6 +1,6 @@ app waspleau { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "Waspleau", diff --git a/examples/websockets-realtime-voting/main.wasp b/examples/websockets-realtime-voting/main.wasp index 9d9207bc6f..1b79e1a8ab 100644 --- a/examples/websockets-realtime-voting/main.wasp +++ b/examples/websockets-realtime-voting/main.wasp @@ -1,6 +1,6 @@ app whereDoWeEat { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "where-do-we-eat", client: { diff --git a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates.hs b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates.hs index 4c2f1124ee..0c44950e11 100644 --- a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates.hs +++ b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates.hs @@ -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 diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/main.wasp b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/main.wasp index 94891d4615..558931eaa0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/main.wasp +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/main.wasp @@ -1,6 +1,6 @@ app waspBuild { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, title: "waspBuild" } diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/main.wasp b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/main.wasp index 109925d543..ff726b6370 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/main.wasp +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/main.wasp @@ -1,6 +1,6 @@ app waspCompile { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, title: "waspCompile" } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/main.wasp b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/main.wasp index 2a1cc49656..41be57f420 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/main.wasp +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/main.wasp @@ -1,6 +1,6 @@ app waspComplexTest { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, auth: { userEntity: User, diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/main.wasp b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/main.wasp index cfde7303cb..550fcdff25 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/main.wasp +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/main.wasp @@ -1,6 +1,6 @@ app waspJob { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, title: "waspJob" } diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/main.wasp b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/main.wasp index 5b1bd22700..f252b02110 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/main.wasp +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/main.wasp @@ -1,6 +1,6 @@ app waspMigrate { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, title: "waspMigrate" } diff --git a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/main.wasp b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/main.wasp index e3555de634..dc65b1c369 100644 --- a/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/main.wasp +++ b/waspc/e2e-test/test-outputs/waspNew-golden/waspNew/main.wasp @@ -1,6 +1,6 @@ app waspNew { wasp: { - version: "^0.14.2" + version: "^0.15.0" }, title: "waspNew" } diff --git a/waspc/examples/crud-testing/main.wasp b/waspc/examples/crud-testing/main.wasp index 4bb52e3586..98faae8f53 100644 --- a/waspc/examples/crud-testing/main.wasp +++ b/waspc/examples/crud-testing/main.wasp @@ -1,6 +1,6 @@ app crudTesting { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, head: [ "" diff --git a/waspc/examples/pg-vector-example/main.wasp b/waspc/examples/pg-vector-example/main.wasp index 400684b421..928d123126 100644 --- a/waspc/examples/pg-vector-example/main.wasp +++ b/waspc/examples/pg-vector-example/main.wasp @@ -1,6 +1,6 @@ app pgVectorExample { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "PG Vector Example", client: { diff --git a/waspc/examples/todo-typescript/main.wasp b/waspc/examples/todo-typescript/main.wasp index 251ba71949..5dfe2230b6 100644 --- a/waspc/examples/todo-typescript/main.wasp +++ b/waspc/examples/todo-typescript/main.wasp @@ -1,6 +1,6 @@ app TodoTypescript { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "ToDo TypeScript", diff --git a/waspc/examples/todoApp/main.wasp b/waspc/examples/todoApp/main.wasp index 0043e397b4..9cd2f4b1c6 100644 --- a/waspc/examples/todoApp/main.wasp +++ b/waspc/examples/todoApp/main.wasp @@ -1,6 +1,6 @@ app todoApp { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "ToDo App", // head: [], diff --git a/waspc/headless-test/examples/todoApp/todoApp.wasp b/waspc/headless-test/examples/todoApp/todoApp.wasp index a3bbbd4aad..457d46d560 100644 --- a/waspc/headless-test/examples/todoApp/todoApp.wasp +++ b/waspc/headless-test/examples/todoApp/todoApp.wasp @@ -1,6 +1,6 @@ app todoApp { wasp: { - version: "^0.14.0" + version: "^0.15.0" }, title: "ToDo App", auth: { diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index f4e73f246c..058aca0162 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -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 homepage: https://github.com/wasp-lang/wasp/waspc#readme bug-reports: https://github.com/wasp-lang/wasp/issues diff --git a/web/docs/auth/auth-hooks.md b/web/docs/auth/auth-hooks.md index 75b711b2cd..b66450b38e 100644 --- a/web/docs/auth/auth-hooks.md +++ b/web/docs/auth/auth-hooks.md @@ -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, diff --git a/web/docs/auth/email.md b/web/docs/auth/email.md index 90feb806ce..5da5bfc69f 100644 --- a/web/docs/auth/email.md +++ b/web/docs/auth/email.md @@ -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 { }, } ``` + ```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 { }, } ``` + @@ -132,6 +135,7 @@ model User { // ... } ``` + @@ -144,12 +148,12 @@ model User { // ... } ``` + - ### 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", } ``` + @@ -218,6 +223,7 @@ page EmailVerificationPage { component: import { EmailVerification } from "@src/pages/auth.tsx", } ``` + @@ -254,11 +260,10 @@ export function Login() {
- Forgot your password? reset it - . + Forgot your password? reset it. - ); + ) } export function Signup() { @@ -270,7 +275,7 @@ export function Signup() { I already have an account (go to login). - ); + ) } export function EmailVerification() { @@ -282,7 +287,7 @@ export function EmailVerification() { If everything is okay, go to login - ); + ) } export function RequestPasswordReset() { @@ -290,7 +295,7 @@ export function RequestPasswordReset() { - ); + ) } export function PasswordReset() { @@ -302,22 +307,23 @@ export function PasswordReset() { If everything is okay, go to login - ); + ) } // A layout component to center the content export function Layout({ children }) { return ( -

-
-
+
+
+
{children}
- ); + ) } ``` + @@ -341,11 +347,10 @@ export function Login() {
- Forgot your password? reset it - . + Forgot your password? reset it. - ); + ) } export function Signup() { @@ -357,7 +362,7 @@ export function Signup() { I already have an account (go to login). - ); + ) } export function EmailVerification() { @@ -369,7 +374,7 @@ export function EmailVerification() { If everything is okay, go to login - ); + ) } export function RequestPasswordReset() { @@ -377,7 +382,7 @@ export function RequestPasswordReset() { - ); + ) } export function PasswordReset() { @@ -389,22 +394,23 @@ export function PasswordReset() { If everything is okay, go to login - ); + ) } // A layout component to center the content export function Layout({ children }: { children: React.ReactNode }) { return ( -
-
-
+
+
+
{children}
- ); + ) } ``` + @@ -430,6 +436,7 @@ app myApp { } } ``` + @@ -442,6 +449,7 @@ app myApp { } } ``` + @@ -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, } ``` + @@ -515,10 +525,11 @@ emailVerification: { clientRoute: EmailVerificationRoute, } ``` + -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, } ``` + @@ -563,6 +576,7 @@ passwordReset: { clientRoute: PasswordResetRoute, } ``` + @@ -665,7 +679,7 @@ export const signup = async (args, _context) => { } catch (e: unknown) { console.error("Failed to send email verification email:", e); throw new HttpError(500, "Failed to send email verification email."); - } + } } } catch (e) { return { @@ -683,6 +697,7 @@ export const signup = async (args, _context) => { } } ``` + @@ -718,7 +733,10 @@ type CustomSignupOutput = { message: string } -export const signup: CustomSignup = async (args, _context) => { +export const signup: CustomSignup< + CustomSignupInput, + CustomSignupOutput +> = async (args, _context) => { ensureValidEmail(args) ensurePasswordIsPresent(args) ensureValidPassword(args) @@ -728,46 +746,49 @@ export const signup: CustomSignup = 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: `

Click the link below to verify your email

Verify email `, - } - ); + }) } 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) { return { @@ -785,6 +806,7 @@ export const signup: CustomSignup = async } } ``` +
@@ -875,6 +897,7 @@ model User { id Int @id @default(autoincrement()) } ``` + @@ -914,6 +937,7 @@ app myApp { // ... } ``` + @@ -946,6 +970,7 @@ app myApp { // ... } ``` + @@ -953,19 +978,23 @@ app myApp { -Read more about the `userSignupFields` function [here](./overview#1-defining-extra-fields). +Read more about the `userSignupFields` function [here](./overview#1-defining-extra-fields). #### `fromField: EmailFromField` -`fromField` is a dict that specifies the name and e-mail address of the sender of the e-mails sent by your app. + +`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 #### `emailVerification: EmailVerificationConfig` + `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. 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 }); ``` + @@ -986,11 +1016,12 @@ It has the following fields: ... await verifyEmail({ token }); ``` + :::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: `, }) ``` + @@ -1027,19 +1059,21 @@ It has the following fields: `, }) ``` + This is the default content of the e-mail, you can customize it to your liking. - #### `passwordReset: PasswordResetConfig` -`passwordReset` is a dict that specifies the password reset process. + +`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. - 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. @@ -1055,6 +1089,7 @@ It has the following fields: ... await resetPassword({ password, token }) ``` + @@ -1069,6 +1104,7 @@ It has the following fields: ... await resetPassword({ password, token }) ``` + @@ -1093,6 +1129,7 @@ It has the following fields: `, }) ``` + @@ -1110,6 +1147,7 @@ It has the following fields: `, }) ``` + diff --git a/web/docs/auth/entities/entities.md b/web/docs/auth/entities/entities.md index 634659fac1..f24dd9247d 100644 --- a/web/docs/auth/entities/entities.md +++ b/web/docs/auth/entities/entities.md @@ -190,7 +190,7 @@ We'll mention the `auth` and the `identities` relations which we will explain in :::caution Be careful about sensitive data -You'll need to include the `auth` and the `identities` relations to get the full auth data about the user. However, you should keep in mind that the `providerData` field in the `identities` can contain sensitive data like the user's hashed password (in case of email or username auth), so you will likely want to exclude it if you are returning those values to the client. +You'll need to include the `auth` and the `identities` relations to get the full auth data about the user. However, you should keep in mind that the `providerData` field in the `identities` can contain sensitive data like the user's hashed password (in case of email or username auth), so you will likely want to exclude it if you are returning those values to the client. ::: @@ -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. @@ -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: { @@ -490,9 +490,9 @@ model User { } ``` -The `User` entity is a "business logic user" which represents a user of your app. +The `User` entity is a "business logic user" which represents a user of your app. -You can use this entity to store any information about the user that you want to store. For example, you might want to store the user's name or address. +You can use this entity to store any information about the user that you want to store. For example, you might want to store the user's name or address. You can also use the user entity to define the relations between users and other entities in your app. For example, you might want to define a relation between a user and the tasks that they have created. diff --git a/web/docs/auth/social-auth/discord.md b/web/docs/auth/social-auth/discord.md index 5615ead0c0..ac2ba7edc8 100644 --- a/web/docs/auth/social-auth/discord.md +++ b/web/docs/auth/social-auth/discord.md @@ -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 ( -
-
-
+
+
+
{children}
@@ -241,9 +241,9 @@ export function Login() { // A layout component to center the content export function Layout({ children }: { children: React.ReactNode }) { return ( -
-
-
+
+
+
{children}
@@ -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: { @@ -556,5 +555,5 @@ The `discord` dict has the following properties: - #### `userSignupFields: ExtImport` - + Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields). diff --git a/web/docs/auth/social-auth/github.md b/web/docs/auth/social-auth/github.md index bdb54c83ed..6da7b4b39d 100644 --- a/web/docs/auth/social-auth/github.md +++ b/web/docs/auth/social-auth/github.md @@ -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 ( -
-
-
+
+
+
{children}
@@ -241,9 +241,9 @@ export function Login() { // A layout component to center the content export function Layout({ children }: { children: React.ReactNode }) { return ( -
-
-
+
+
+
{children}
@@ -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: { @@ -568,5 +568,5 @@ The `gitHub` dict has the following properties: - #### `userSignupFields: ExtImport` - + Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields). diff --git a/web/docs/auth/social-auth/google.md b/web/docs/auth/social-auth/google.md index 69a7fbeaac..a596f7c288 100644 --- a/web/docs/auth/social-auth/google.md +++ b/web/docs/auth/social-auth/google.md @@ -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 ( -
-
-
+
+
+
{children}
@@ -280,9 +280,9 @@ export function Login() { // A layout component to center the content export function Layout({ children }: { children: React.ReactNode }) { return ( -
-
-
+
+
+
{children}
@@ -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: { @@ -594,5 +594,5 @@ The `google` dict has the following properties: - #### `userSignupFields: ExtImport` - + Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields). diff --git a/web/docs/auth/social-auth/keycloak.md b/web/docs/auth/social-auth/keycloak.md index ef241a1d96..1820f149a2 100644 --- a/web/docs/auth/social-auth/keycloak.md +++ b/web/docs/auth/social-auth/keycloak.md @@ -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 ( -
-
-
+
+
+
{children}
@@ -248,9 +248,9 @@ export function Login() { // A layout component to center the content export function Layout({ children }: { children: React.ReactNode }) { return ( -
-
-
+
+
+
{children}
@@ -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: { @@ -553,5 +553,5 @@ The `keycloak` dict has the following properties: - #### `userSignupFields: ExtImport` - - Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields). \ No newline at end of file + + Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields). diff --git a/web/docs/auth/social-auth/overview.md b/web/docs/auth/social-auth/overview.md index 7abdac288f..dc0ed52f75 100644 --- a/web/docs/auth/social-auth/overview.md +++ b/web/docs/auth/social-auth/overview.md @@ -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: { diff --git a/web/docs/auth/username-and-pass.md b/web/docs/auth/username-and-pass.md index d530440930..459d99c220 100644 --- a/web/docs/auth/username-and-pass.md +++ b/web/docs/auth/username-and-pass.md @@ -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: { diff --git a/web/docs/data-model/crud.md b/web/docs/data-model/crud.md index a2d62de2df..29ac2b0937 100644 --- a/web/docs/data-model/crud.md +++ b/web/docs/data-model/crud.md @@ -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", diff --git a/web/docs/data-model/prisma-file.md b/web/docs/data-model/prisma-file.md index c919c4b185..a91c633b4c 100644 --- a/web/docs/data-model/prisma-file.md +++ b/web/docs/data-model/prisma-file.md @@ -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", } diff --git a/web/docs/introduction/introduction.md b/web/docs/introduction/introduction.md index 43da634178..601203a815 100644 --- a/web/docs/introduction/introduction.md +++ b/web/docs/introduction/introduction.md @@ -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. @@ -32,9 +33,10 @@ At the core is the Wasp compiler which takes the Wasp config and your Javascript caption="How the magic happens 🌈" /> -The cool thing about having a compiler that understands your code is that it can do a lot of things for you. +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
Loading...
; + return
Loading...
} return (

Recipes

    - {recipes ? recipes.map((recipe) => ( -
  • -
    {recipe.title}
    -
    {recipe.description}
    -
  • - )) : 'No recipes defined yet!'} + {recipes + ? recipes.map((recipe) => ( +
  • +
    {recipe.title}
    +
    {recipe.description}
    +
  • + )) + : 'No recipes defined yet!'}
- ); + ) } ``` @@ -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. diff --git a/web/docs/project/customizing-app.md b/web/docs/project/customizing-app.md index 31c262f8e6..c774c2d970 100644 --- a/web/docs/project/customizing-app.md +++ b/web/docs/project/customizing-app.md @@ -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: [ diff --git a/web/docs/tutorial/02-project-structure.md b/web/docs/tutorial/02-project-structure.md index ae21e583c2..0f98a57f51 100644 --- a/web/docs/tutorial/02-project-structure.md +++ b/web/docs/tutorial/02-project-structure.md @@ -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! } diff --git a/web/docs/tutorial/03-pages.md b/web/docs/tutorial/03-pages.md index f6fa2fa473..4e1ef827fe 100644 --- a/web/docs/tutorial/03-pages.md +++ b/web/docs/tutorial/03-pages.md @@ -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" } diff --git a/web/docs/tutorial/07-auth.md b/web/docs/tutorial/07-auth.md index d4736bf0d5..815c793381 100644 --- a/web/docs/tutorial/07-auth.md +++ b/web/docs/tutorial/07-auth.md @@ -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.