From 7f91081bd8accc517257502e2ce3a44264666a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0o=C5=A1i=C4=87?= Date: Fri, 12 Jan 2024 17:42:53 +0100 Subject: [PATCH] Fix broken documentation links & remove old docs. * Removed old docs leftovers. * Fixed broken links in the docs. * fix --- .../cronjobs-analytics-waspleau/README.md | 2 +- examples/trello-clone-waspello/README.md | 4 +- .../templates/react-app/src/actions/index.ts | 6 +- web/blog/2023-03-02-wasp-beta-update-feb.md | 2 +- ...-voting-app-websockets-react-typescript.md | 4 +- web/docs/OldDocsNote.tsx | 25 - web/docs/advanced/apis.md | 8 +- .../_addExternalAuthEnvVarsReminder.md | 2 +- web/docs/advanced/deployment/manually.md | 6 +- web/docs/advanced/deployment/overview.md | 2 +- web/docs/advanced/jobs.md | 4 +- web/docs/advanced/middleware-config.md | 2 +- web/docs/auth/email.md | 14 +- web/docs/auth/overview.md | 26 +- .../auth/social-auth/_api-reference-intro.md | 2 +- web/docs/auth/social-auth/_using-auth-note.md | 2 +- web/docs/auth/social-auth/github.md | 8 +- web/docs/auth/social-auth/google.md | 8 +- web/docs/auth/social-auth/overview.md | 4 +- web/docs/auth/ui.md | 2 +- web/docs/auth/username-and-pass.md | 12 +- web/docs/data-model/backends.md | 4 +- web/docs/data-model/crud.md | 10 +- web/docs/data-model/entities.md | 4 +- web/docs/data-model/operations/actions.md | 8 +- web/docs/data-model/operations/overview.md | 2 +- web/docs/data-model/operations/queries.md | 8 +- web/docs/examples.md | 31 - web/docs/general/cli.md | 6 +- web/docs/introduction/editor-setup.md | 2 +- .../introduction/introduction.md} | 4 +- .../introduction/quick-start.md} | 4 +- web/docs/language/features.md | 2372 ----------------- web/docs/project/client-config.md | 2 +- web/docs/project/css-frameworks.md | 2 +- web/docs/project/customizing-app.md | 14 +- web/docs/project/env-vars.md | 2 +- web/docs/project/server-config.md | 4 +- web/docs/project/testing.md | 4 +- web/docs/tutorial/01-create.md | 2 +- web/docs/tutorial/02-project-structure.md | 2 +- web/docs/tutorial/03-pages.md | 2 +- web/docs/tutorial/04-entities.md | 2 +- web/docs/tutorial/05-queries.md | 6 +- web/docs/tutorial/07-auth.md | 12 +- web/docs/typescript.md | 521 ---- web/docusaurus.config.js | 6 +- web/sidebars.js | 4 +- .../version-0.11.8/OldDocsNote.tsx | 25 - .../version-0.11.8/advanced/apis.md | 8 +- .../_addExternalAuthEnvVarsReminder.md | 2 +- .../advanced/deployment/manually.md | 6 +- .../advanced/deployment/overview.md | 2 +- .../version-0.11.8/advanced/jobs.md | 4 +- .../advanced/middleware-config.md | 2 +- .../version-0.11.8/auth/email.md | 14 +- .../version-0.11.8/auth/overview.md | 26 +- .../auth/social-auth/_api-reference-intro.md | 2 +- .../auth/social-auth/_using-auth-note.md | 2 +- .../version-0.11.8/auth/social-auth/github.md | 8 +- .../version-0.11.8/auth/social-auth/google.md | 8 +- .../auth/social-auth/overview.md | 4 +- web/versioned_docs/version-0.11.8/auth/ui.md | 2 +- .../version-0.11.8/auth/username-and-pass.md | 12 +- .../version-0.11.8/data-model/backends.md | 4 +- .../version-0.11.8/data-model/crud.md | 10 +- .../version-0.11.8/data-model/entities.md | 4 +- .../data-model/operations/actions.md | 8 +- .../data-model/operations/overview.md | 2 +- .../data-model/operations/queries.md | 8 +- web/versioned_docs/version-0.11.8/examples.md | 31 - .../version-0.11.8/general/cli.md | 6 +- .../introduction/editor-setup.md | 2 +- .../introduction/introduction.md} | 4 +- .../introduction/quick-start.md} | 4 +- .../version-0.11.8/language/features.md | 2372 ----------------- .../version-0.11.8/project/client-config.md | 2 +- .../version-0.11.8/project/css-frameworks.md | 2 +- .../version-0.11.8/project/customizing-app.md | 14 +- .../version-0.11.8/project/env-vars.md | 2 +- .../version-0.11.8/project/server-config.md | 4 +- .../version-0.11.8/project/testing.md | 4 +- .../version-0.11.8/tutorial/01-create.md | 2 +- .../tutorial/02-project-structure.md | 2 +- .../version-0.11.8/tutorial/03-pages.md | 2 +- .../version-0.11.8/tutorial/04-entities.md | 2 +- .../version-0.11.8/tutorial/05-queries.md | 6 +- .../version-0.11.8/tutorial/07-auth.md | 12 +- .../version-0.11.8/typescript.md | 521 ---- .../version-0.11.8-sidebars.json | 4 +- 90 files changed, 222 insertions(+), 6124 deletions(-) delete mode 100644 web/docs/OldDocsNote.tsx delete mode 100644 web/docs/examples.md rename web/{versioned_docs/version-0.11.8/introduction/what-is-wasp.md => docs/introduction/introduction.md} (98%) rename web/{versioned_docs/version-0.11.8/introduction/getting-started.md => docs/introduction/quick-start.md} (96%) delete mode 100644 web/docs/language/features.md delete mode 100644 web/docs/typescript.md delete mode 100644 web/versioned_docs/version-0.11.8/OldDocsNote.tsx delete mode 100644 web/versioned_docs/version-0.11.8/examples.md rename web/{docs/introduction/what-is-wasp.md => versioned_docs/version-0.11.8/introduction/introduction.md} (98%) rename web/{docs/introduction/getting-started.md => versioned_docs/version-0.11.8/introduction/quick-start.md} (96%) delete mode 100644 web/versioned_docs/version-0.11.8/language/features.md delete mode 100644 web/versioned_docs/version-0.11.8/typescript.md diff --git a/examples/cronjobs-analytics-waspleau/README.md b/examples/cronjobs-analytics-waspleau/README.md index cd7cd2b0a..8e9779267 100644 --- a/examples/cronjobs-analytics-waspleau/README.md +++ b/examples/cronjobs-analytics-waspleau/README.md @@ -1,7 +1,7 @@ # Waspleau Welcome to the Waspleau example! This is a small Wasp project that tracks status of wasp-lang/wasp repo via a nice looking dashboard. -It pulls in data via [Jobs](https://wasp-lang.dev/docs/language/features#jobs) and stores them in the database. +It pulls in data via [Jobs](https://wasp-lang.dev/docs/advanced/jobs) and stores them in the database. This example project can serve as a good starting point for building your own dashboard with Wasp, that regularly pulls in external data by using Jobs Wasp feature. diff --git a/examples/trello-clone-waspello/README.md b/examples/trello-clone-waspello/README.md index 395cd3903..5fb8c11ea 100644 --- a/examples/trello-clone-waspello/README.md +++ b/examples/trello-clone-waspello/README.md @@ -10,9 +10,9 @@ The backend is hosted on Fly.io at https://waspello.fly.dev. # Development ### Database -Wasp needs the Postgres database running. Check out the docs for details on [how to setup PostgreSQL](https://wasp-lang.dev/docs/language/features#postgresql) +Wasp needs the Postgres database running. -You can use `wasp start db` to start a PostgreSQL locally using Docker. +Easiest way to do this is to use `wasp start db` to start a PostgreSQL locally using Docker. ### Env variables Copy `env.server` to `.env.server` and fill in the values. diff --git a/waspc/data/Generator/templates/react-app/src/actions/index.ts b/waspc/data/Generator/templates/react-app/src/actions/index.ts index 5e4dfedd1..7fb2de2f9 100644 --- a/waspc/data/Generator/templates/react-app/src/actions/index.ts +++ b/waspc/data/Generator/templates/react-app/src/actions/index.ts @@ -42,7 +42,7 @@ export type UpdateQuery = (item: ActionInput, oldData: /** * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * https://wasp-lang.dev/docs/data-model/operations/actions#the-useaction-hook-and-optimistic-updates */ export type QuerySpecifier = [Query, ...any[]] @@ -116,7 +116,7 @@ type InternalAction = Action & { * * @param publicOptimisticUpdateDefinition An optimistic update definition * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * https://wasp-lang.dev/docs/data-model/operations/actions#the-useaction-hook-and-optimistic-updates * @returns An internally-used optimistic update definition object. */ function translateToInternalDefinition( @@ -260,7 +260,7 @@ function getOptimisticUpdateDefinitionForSpecificItem( * Translates a Wasp query specifier to a query cache key used by React Query. * * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * https://wasp-lang.dev/docs/data-model/operations/actions#the-useaction-hook-and-optimistic-updates * @returns A cache key React Query internally uses for addressing queries. */ function getRqQueryKeyFromSpecifier(querySpecifier: QuerySpecifier): QueryKey { diff --git a/web/blog/2023-03-02-wasp-beta-update-feb.md b/web/blog/2023-03-02-wasp-beta-update-feb.md index fa59b9684..ca23a5f36 100644 --- a/web/blog/2023-03-02-wasp-beta-update-feb.md +++ b/web/blog/2023-03-02-wasp-beta-update-feb.md @@ -60,7 +60,7 @@ This is one of the features we are most excited about! Now, when you define an e This feature beautifully showcases the power of the Wasp language approach and how much it can cut down on the boilerplate. And we're just getting started! -For more details, [check out our docs on reusing entity types on both a client and a server](/docs/typescript#entity-types). +For more details, [check out our entity docs](/docs/data-model/entities). ## πŸ—“ We set a date for the next launch - April 11th! πŸš€ diff --git a/web/blog/2023-08-09-build-real-time-voting-app-websockets-react-typescript.md b/web/blog/2023-08-09-build-real-time-voting-app-websockets-react-typescript.md index f7f23187a..6274394a5 100644 --- a/web/blog/2023-08-09-build-real-time-voting-app-websockets-react-typescript.md +++ b/web/blog/2023-08-09-build-real-time-voting-app-websockets-react-typescript.md @@ -710,7 +710,7 @@ You should see a login screen this time. Go ahead and first register a user, the Once logged in, you’ll see the same hardcoded poll data as in the previous example, because, again, we haven’t set up the [Socket.IO](http://Socket.IO) client on the frontend. But this time it should be much easier. -Why? Well, besides less configuration, another nice benefit of working with [TypeScript with Wasp](/docs/typescript#websocket-full-stack-type-support), is that you just have to define payload types with matching event names on the server, and those types will get exposed automatically on the client! +Why? Well, besides less configuration, another nice benefit of working with [TypeScript with Wasp](/docs/advanced/web-sockets), is that you just have to define payload types with matching event names on the server, and those types will get exposed automatically on the client! Let’s take a look at how that works now. @@ -885,4 +885,4 @@ And if you know of a better, cooler, sleeker way of implementing WebSockets into \ No newline at end of file +/> diff --git a/web/docs/OldDocsNote.tsx b/web/docs/OldDocsNote.tsx deleted file mode 100644 index 008035a5e..000000000 --- a/web/docs/OldDocsNote.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Admonition from "@theme/Admonition"; -import Link from "@docusaurus/Link"; -import React from "react"; - -export default function OldDocsNote() { - return ( -
- - This page is part of a previous documentation version and is no longer - actively maintained. The content is likely out of date and may no longer - be relevant to current releases. -
-
- Go to the current documentation for updated - content. -
-
- ); -} diff --git a/web/docs/advanced/apis.md b/web/docs/advanced/apis.md index 125499e34..f058b92b1 100644 --- a/web/docs/advanced/apis.md +++ b/web/docs/advanced/apis.md @@ -5,7 +5,7 @@ title: Custom HTTP API Endpoints import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers' import { Required } from '@site/src/components/Required' -In Wasp, the default client-server interaction mechanism is through [Operations](/docs/data-model/operations/overview). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`. Best of all, they should look and feel very familiar. +In Wasp, the default client-server interaction mechanism is through [Operations](../data-model/operations/overview). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`. Best of all, they should look and feel very familiar. ## How to Create an API @@ -231,11 +231,11 @@ export const apiMiddleware: MiddlewareConfigFn = (config) => { We are returning the default middleware which enables CORS for all APIs under the `/foo` path. -For more information about middleware configuration, please see: [Middleware Configuration](/docs/advanced/middleware-config) +For more information about middleware configuration, please see: [Middleware Configuration](../advanced/middleware-config) ## Using Entities in APIs -In many cases, resources used in APIs will be [Entities](/docs/data-model/entities.md). +In many cases, resources used in APIs will be [Entities](../data-model/entities.md). To use an Entity in your API, add it to the `api` declaration in Wasp: @@ -340,4 +340,4 @@ The `api` declaration has the following fields: - `middlewareConfigFn: ServerImport` - The import statement to an Express middleware config function for this API. See more in [middleware section](/docs/advanced/middleware-config) of the docs. \ No newline at end of file + The import statement to an Express middleware config function for this API. See more in [middleware section](../advanced/middleware-config) of the docs. \ No newline at end of file diff --git a/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md b/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md index 52e0b68a3..10a105318 100644 --- a/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md +++ b/web/docs/advanced/deployment/_addExternalAuthEnvVarsReminder.md @@ -1,4 +1,4 @@ :::tip Using an external auth method? -If your app is using an external authentication method(s) supported by Wasp (such as [Google](/docs/auth/social-auth/google#4-adding-environment-variables) or [GitHub](/docs/auth/social-auth/github#4-adding-environment-variables)), make sure to set the necessary environment variables. +If your app is using an external authentication method(s) supported by Wasp (such as [Google](../../auth/social-auth/google#4-adding-environment-variables) or [GitHub](../../auth/social-auth/github#4-adding-environment-variables)), make sure to set the necessary environment variables. ::: diff --git a/web/docs/advanced/deployment/manually.md b/web/docs/advanced/deployment/manually.md index e2d53e0f6..7767ab8a1 100644 --- a/web/docs/advanced/deployment/manually.md +++ b/web/docs/advanced/deployment/manually.md @@ -34,7 +34,7 @@ wasp build :::caution PostgreSQL in production You won't be able to build the app if you are using SQLite as a database (which is the default database). -You'll have to [switch to PostgreSQL](/docs/data-model/backends#migrating-from-sqlite-to-postgresql) before deploying to production. +You'll have to [switch to PostgreSQL](../../data-model/backends#migrating-from-sqlite-to-postgresql) before deploying to production. ::: ### 2. Deploying the API Server (backend) @@ -92,7 +92,7 @@ We'll cover a few different deployment providers below: ## Fly.io :::tip We automated this process for you -If you want to do all of the work below with one command, you can use the [Wasp CLI](/docs/advanced/deployment/cli#flyio). +If you want to do all of the work below with one command, you can use the [Wasp CLI](../../advanced/deployment/cli#flyio). Wasp CLI deploys the server, deploys the client, and sets up a database. It also gives you a way to redeploy (update) your app with a single command. @@ -547,7 +547,7 @@ heroku logs --tail --app :::note Using `pg-boss` with Heroku -If you wish to deploy an app leveraging [Jobs](/docs/advanced/jobs) that use `pg-boss` as the executor to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. +If you wish to deploy an app leveraging [Jobs](../../advanced/jobs) that use `pg-boss` as the executor to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. Read more: https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js ::: diff --git a/web/docs/advanced/deployment/overview.md b/web/docs/advanced/deployment/overview.md index 23f841bbf..c4750284e 100644 --- a/web/docs/advanced/deployment/overview.md +++ b/web/docs/advanced/deployment/overview.md @@ -11,7 +11,7 @@ Wasp apps are full-stack apps that consist of: You can deploy each part **anywhere** where you can usually deploy Node.js apps or static apps. For example, you can deploy your client on [Netlify](https://www.netlify.com/), the server on [Fly.io](https://fly.io/), and the database on [Neon](https://neon.tech/). -To make deploying as smooth as possible, Wasp also offers a single-command deployment through the **Wasp CLI**. Read more about deploying through the CLI [here](/docs/advanced/deployment/cli). +To make deploying as smooth as possible, Wasp also offers a single-command deployment through the **Wasp CLI**. Read more about deploying through the CLI [here](../../advanced/deployment/cli). diff --git a/web/docs/advanced/jobs.md b/web/docs/advanced/jobs.md index 3fe783a14..16889daca 100644 --- a/web/docs/advanced/jobs.md +++ b/web/docs/advanced/jobs.md @@ -94,7 +94,7 @@ Let's write an example Job that will print a message to the console and return a `MySpecialJob` is a generic type Wasp generates to help you correctly type the Job's worker function, ensuring type information about the function's arguments and return value. Read more about type-safe jobs in the [Javascript API section](#javascript-api). -3. After successfully defining the job, you can submit work to be done in your [Operations](/docs/data-model/operations/overview) or [setupFn](/docs/project/server-config#setup-function) (or any other NodeJS code): +3. After successfully defining the job, you can submit work to be done in your [Operations](../data-model/operations/overview) or [setupFn](../project/server-config#setup-function) (or any other NodeJS code): @@ -333,7 +333,7 @@ The Job declaration has the following fields: - `entities: [Entity]` - A list of entities you wish to use inside your Job (similar to [Queries and Actions](/docs/data-model/operations/queries#using-entities-in-queries)). + A list of entities you wish to use inside your Job (similar to [Queries and Actions](../data-model/operations/queries#using-entities-in-queries)). ### JavaScript API diff --git a/web/docs/advanced/middleware-config.md b/web/docs/advanced/middleware-config.md index fe2ce9f0b..859df3d6d 100644 --- a/web/docs/advanced/middleware-config.md +++ b/web/docs/advanced/middleware-config.md @@ -19,7 +19,7 @@ Wasp's Express server has the following middleware by default: - [express.json](https://expressjs.com/en/api.html#express.json) (which uses [body-parser](https://github.com/expressjs/body-parser#bodyparserjsonoptions)): parses incoming request bodies in a middleware before your handlers, making the result available under the `req.body` property. :::note - JSON middlware is required for [Operations](/docs/data-model/operations/overview) to function properly. + JSON middlware is required for [Operations](../data-model/operations/overview) to function properly. ::: - [express.urlencoded](https://expressjs.com/en/api.html#express.urlencoded) (which uses [body-parser](https://expressjs.com/en/resources/middleware/body-parser.html#bodyparserurlencodedoptions)): returns middleware that only parses urlencoded bodies and only looks at requests where the `Content-Type` header matches the type option. - [cookieParser](https://github.com/expressjs/cookie-parser#readme): parses Cookie header and populates `req.cookies` with an object keyed by the cookie names. diff --git a/web/docs/auth/email.md b/web/docs/auth/email.md index 0916966d7..22fdaad48 100644 --- a/web/docs/auth/email.md +++ b/web/docs/auth/email.md @@ -240,7 +240,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 4. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -418,7 +418,7 @@ export function Layout({ children }: { children: React.ReactNode }) { -We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](../auth/ui). ### 5. Set up an Email Sender @@ -461,15 +461,15 @@ app myApp { SENDGRID_API_KEY= ``` -If you are not sure how to get a SendGrid API key, read more [here](/docs/advanced/email#getting-the-api-key). +If you are not sure how to get a SendGrid API key, read more [here](../advanced/email#getting-the-api-key). -Read more about setting up email senders in the [sending emails docs](/docs/advanced/email). +Read more about setting up email senders in the [sending emails docs](../advanced/email). ### Conclusion That's it! We have set up email authentication in our app. πŸŽ‰ -Running `wasp db migrate-dev` and then `wasp start` should give you a working app with email authentication. If you want to put some of the pages behind authentication, read the [using auth docs](/docs/auth/overview). +Running `wasp db migrate-dev` and then `wasp start` should give you a working app with email authentication. If you want to put some of the pages behind authentication, read the [using auth docs](../auth/overview). ## Login and Signup Flows @@ -500,7 +500,7 @@ Some of the behavior you get out of the box: 4. Password validation - Read more about the default password validation rules and how to override them in [using auth docs](/docs/auth/overview). + Read more about the default password validation rules and how to override them in [using auth docs](../auth/overview). ## Email Verification Flow @@ -597,7 +597,7 @@ The content of the e-mail can be customized, read more about it [here](#password ## Using The Auth -To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](/docs/auth/overview). +To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](../auth/overview). ## API Reference diff --git a/web/docs/auth/overview.md b/web/docs/auth/overview.md index c9b21ce0f..dfd660c63 100644 --- a/web/docs/auth/overview.md +++ b/web/docs/auth/overview.md @@ -78,11 +78,11 @@ Wasp supports the following auth methods: -Let's say we enabled the [Username & password](/docs/auth/username-and-pass) authentication. +Let's say we enabled the [Username & password](../auth/username-and-pass) authentication. -We get an auth backend with signup and login endpoints. We also get the `user` object in our [Operations](/docs/data-model/operations/overview) and we can decide what to do based on whether the user is logged in or not. +We get an auth backend with signup and login endpoints. We also get the `user` object in our [Operations](../data-model/operations/overview) and we can decide what to do based on whether the user is logged in or not. -We would also get the [Auth UI](/docs/auth/ui) generated for us. We can set up our login and signup pages where our users can **create their account** and **login**. We can then protect certain pages by setting `authRequired: true` for them. This will make sure that only logged-in users can access them. +We would also get the [Auth UI](../auth/ui) generated for us. We can set up our login and signup pages where our users can **create their account** and **login**. We can then protect certain pages by setting `authRequired: true` for them. This will make sure that only logged-in users can access them. We will also have access to the `user` object in our frontend code, so we can show different UI to logged-in and logged-out users. For example, we can show the user's name in the header alongside a **logout button** or a login button if the user is not logged in. @@ -301,7 +301,7 @@ Since the `user` prop is only available in a page's React component: use the `us #### Using the `context.user` object -When authentication is enabled, all [queries and actions](/docs/data-model/operations/overview) have access to the `user` object through the `context` argument. `context.user` contains all User entity's fields, except for the password. +When authentication is enabled, all [queries and actions](../data-model/operations/overview) have access to the `user` object through the `context` argument. `context.user` contains all User entity's fields, except for the password. @@ -361,7 +361,7 @@ export const createTask: CreateTask = async ( To implement access control in your app, each operation must check `context.user` and decide what to do. For example, if `context.user` is `undefined` inside a private operation, the user's access should be denied. -When using WebSockets, the `user` object is also available on the `socket.data` object. Read more in the [WebSockets section](/docs/advanced/web-sockets#websocketfn-function). +When using WebSockets, the `user` object is also available on the `socket.data` object. Read more in the [WebSockets section](../advanced/web-sockets#websocketfn-function). ## User entity @@ -419,7 +419,7 @@ Default validations depend on the auth method you use. #### Username & password -If you use [Username & password](/docs/auth/username-and-pass) authentication, the default validations are: +If you use [Username & password](../auth/username-and-pass) authentication, the default validations are: - The `username` must not be empty - The `password` must not be empty, have at least 8 characters, and contain a number @@ -428,7 +428,7 @@ Note that `username`s are stored in a **case-sensitive** manner. #### Email -If you use [Email](/docs/auth/email) authentication, the default validations are: +If you use [Email](../auth/email) authentication, the default validations are: - The `email` must not be empty and a valid email address - The `password` must not be empty, have at least 8 characters, and contain a number @@ -726,7 +726,7 @@ Now that we defined the fields, Wasp knows how to: 1. Validate the data sent from the client 2. Save the data to the database -Next, let's see how to customize [Auth UI](/docs/auth/ui) to include those fields. +Next, let's see how to customize [Auth UI](../auth/ui) to include those fields. ### 2. Customizing the Signup Component @@ -736,8 +736,8 @@ If you are not using Wasp's Auth UI, you can skip this section. Just make sure t Read more about using the signup actions for: -- email auth [here](/docs/auth/email#fields-in-the-email-dict) -- username & password auth [here](/docs/auth/username-and-pass#customizing-the-auth-flow) +- email auth [here](../auth/email#fields-in-the-email-dict) +- username & password auth [here](../auth/username-and-pass#customizing-the-auth-flow) ::: If you are using Wasp's Auth UI, you can customize the `SignupForm` component by passing the `additionalFields` prop to it. It can be either a list of extra fields or a render function. @@ -1046,7 +1046,7 @@ psl=} The same `externalAuthEntity` can be used across different social login providers (e.g., both GitHub and Google can use the same entity). ::: -See [Google docs](/docs/auth/social-auth/google) and [GitHub docs](/docs/auth/social-auth/github) for more details. +See [Google docs](../auth/social-auth/google) and [GitHub docs](../auth/social-auth/github) for more details. #### `methods: dict` @@ -1057,7 +1057,7 @@ A dictionary of auth methods enabled for the app. #### `onAuthFailedRedirectTo: String` The route to which Wasp should redirect unauthenticated user when they try to access a private page (i.e., a page that has `authRequired: true`). -Check out these [essentials docs on auth](/docs/tutorial/auth#adding-auth-to-the-project) to see an example of usage. +Check out these [essentials docs on auth](../tutorial/auth#adding-auth-to-the-project) to see an example of usage. #### `onAuthSucceededRedirectTo: String` @@ -1065,7 +1065,7 @@ The route to which Wasp will send a successfully authenticated after a successfu The default value is `"/"`. :::note -Automatic redirect on successful login only works when using the Wasp-provided [Auth UI](/docs/auth/ui). +Automatic redirect on successful login only works when using the Wasp-provided [Auth UI](../auth/ui). ::: #### `signup: SignupOptions` diff --git a/web/docs/auth/social-auth/_api-reference-intro.md b/web/docs/auth/social-auth/_api-reference-intro.md index 3fb34065d..ed977b84b 100644 --- a/web/docs/auth/social-auth/_api-reference-intro.md +++ b/web/docs/auth/social-auth/_api-reference-intro.md @@ -5,6 +5,6 @@ Provider-specific behavior comes down to implementing two functions. The reference shows how to define both. -For behavior common to all providers, check the general [API Reference](/docs/auth/overview.md#api-reference). +For behavior common to all providers, check the general [API Reference](../../auth/overview.md#api-reference). diff --git a/web/docs/auth/social-auth/_using-auth-note.md b/web/docs/auth/social-auth/_using-auth-note.md index 8ddc13058..7fe740be1 100644 --- a/web/docs/auth/social-auth/_using-auth-note.md +++ b/web/docs/auth/social-auth/_using-auth-note.md @@ -1,3 +1,3 @@ -To read more about how to set up the logout button and get access to the logged-in user in both client and server code, read the docs on [using auth](/docs/auth/overview). +To read more about how to set up the logout button and get access to the logged-in user in both client and server code, read the docs on [using auth](../../auth/overview). diff --git a/web/docs/auth/social-auth/github.md b/web/docs/auth/social-auth/github.md index 67c067688..5af5c7018 100644 --- a/web/docs/auth/social-auth/github.md +++ b/web/docs/auth/social-auth/github.md @@ -159,7 +159,7 @@ psl=} -`externalAuthEntity` and `userEntity` are explained in [the social auth overview](/docs/auth/social-auth/overview#social-login-entity). +`externalAuthEntity` and `userEntity` are explained in [the social auth overview](../../auth/social-auth/overview#social-login-entity). ### 3. Creating a GitHub OAuth App @@ -231,7 +231,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 6. Creating the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -295,7 +295,7 @@ export function Layout({ children }: { children: React.ReactNode }) { -We imported the generated Auth UI component and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI component and used them in our pages. Read more about the Auth UI components [here](../../auth/ui). ### Conclusion @@ -304,7 +304,7 @@ Yay, we've successfully set up Github Auth! πŸŽ‰ ![Github Auth](/img/auth/github.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](/docs/auth/overview). +To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview). ## Default Behaviour diff --git a/web/docs/auth/social-auth/google.md b/web/docs/auth/social-auth/google.md index 0d38c1d83..d49750bde 100644 --- a/web/docs/auth/social-auth/google.md +++ b/web/docs/auth/social-auth/google.md @@ -96,7 +96,7 @@ app myApp { -`externalAuthEntity` and `userEntity` are explained in [the social auth overview](/docs/auth/social-auth/overview#social-login-entity). +`externalAuthEntity` and `userEntity` are explained in [the social auth overview](../../auth/social-auth/overview#social-login-entity). ### 2. Adding the Entities @@ -271,7 +271,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 6. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../../project/css-frameworks). ::: Let's now create a `auth.{jsx,tsx}` file in the `client/pages`. @@ -337,7 +337,7 @@ export function Layout({ children }: { children: React.ReactNode }) { :::info Auth UI -Our pages use an automatically-generated Auth UI component. Read more about Auth UI components [here](/docs/auth/ui). +Our pages use an automatically-generated Auth UI component. Read more about Auth UI components [here](../../auth/ui). ::: ### Conclusion @@ -347,7 +347,7 @@ Yay, we've successfully set up Google Auth! πŸŽ‰ ![Google Auth](/img/auth/google.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](/docs/auth/overview). +To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview). ## Default Behaviour diff --git a/web/docs/auth/social-auth/overview.md b/web/docs/auth/social-auth/overview.md index 9a89101e5..9faaa3543 100644 --- a/web/docs/auth/social-auth/overview.md +++ b/web/docs/auth/social-auth/overview.md @@ -258,7 +258,7 @@ export const getUserFields: GetUserFieldsFn = async (_context, _args) => { #### 3. Showing the Correct State on the Client -You can query the user's `isSignupComplete` flag on the client with the [`useAuth()`](/docs/auth/overview) hook. +You can query the user's `isSignupComplete` flag on the client with the [`useAuth()`](../../auth/overview) hook. Depending on the flag's value, you can redirect users to the appropriate signup step. For example: @@ -317,7 +317,7 @@ Each provider has their own rules for defining the `getUserFieldsFn` and `config ## UI Helpers :::tip Use Auth UI -[Auth UI](/docs/auth/ui) is a common name for all high-level auth forms that come with Wasp. +[Auth UI](../../auth/ui) is a common name for all high-level auth forms that come with Wasp. These include fully functional auto-generated login and signup forms with working social login buttons. If you're looking for the fastest way to get your auth up and running, that's where you should look. diff --git a/web/docs/auth/ui.md b/web/docs/auth/ui.md index 2070a9ed3..d1fae262d 100644 --- a/web/docs/auth/ui.md +++ b/web/docs/auth/ui.md @@ -218,7 +218,7 @@ export function SignupPage() { It will automatically show the correct authentication providers based on your `main.wasp` file. -Read more about customizing the signup process like adding additional fields or extra UI in the [Using Auth](/docs/auth/overview#customizing-the-signup-process) section. +Read more about customizing the signup process like adding additional fields or extra UI in the [Using Auth](../auth/overview#customizing-the-signup-process) section. ### Forgot Password Form diff --git a/web/docs/auth/username-and-pass.md b/web/docs/auth/username-and-pass.md index c2422f4de..fbb65a530 100644 --- a/web/docs/auth/username-and-pass.md +++ b/web/docs/auth/username-and-pass.md @@ -157,7 +157,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 4. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -255,19 +255,19 @@ export function Layout({ children }: { children: React.ReactNode }) { -We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](../auth/ui). ### Conclusion That's it! We have set up username authentication in our app. πŸŽ‰ -Running `wasp db migrate-dev` and then `wasp start` should give you a working app with username authentication. If you want to put some of the pages behind authentication, read the [using auth docs](/docs/auth/overview). +Running `wasp db migrate-dev` and then `wasp start` should give you a working app with username authentication. If you want to put some of the pages behind authentication, read the [using auth docs](../auth/overview). ## Customizing the Auth Flow The login and signup flows are pretty standard: they allow the user to sign up and then log in with their username and password. The signup flow validates the username and password and then creates a new user entity in the database. -Read more about the default username and password validation rules and how to override them in the [using auth docs](/docs/auth/overview). +Read more about the default username and password validation rules and how to override them in the [using auth docs](../auth/overview). If you require more control in your authentication flow, you can achieve that in the following ways: 1. Create your UI and use `signup` and `login` actions. @@ -538,7 +538,7 @@ export const signUp: SignupUser = async (args, context) => ## Using Auth -To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](/docs/auth/overview). +To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](../auth/overview). ## API Reference @@ -649,4 +649,4 @@ app myApp { `usernameAndPassword` dict doesn't have any options at the moment. ::: -You can read about the rest of the `auth` options in the [using auth](/docs/auth/overview) section of the docs. +You can read about the rest of the `auth` options in the [using auth](../auth/overview) section of the docs. diff --git a/web/docs/data-model/backends.md b/web/docs/data-model/backends.md index acfb036a2..a06416a82 100644 --- a/web/docs/data-model/backends.md +++ b/web/docs/data-model/backends.md @@ -4,7 +4,7 @@ title: Databases import { Required } from '@site/src/components/Required' -[Entities](/docs/data-model/entities.md), [Operations](/docs/data-model/operations/overview) and [Automatic CRUD](/docs/data-model/crud.md) together make a high-level interface for working with your app's data. Still, all that data has to live somewhere, so let's see how Wasp deals with databases. +[Entities](../data-model/entities.md), [Operations](../data-model/operations/overview) and [Automatic CRUD](../data-model/crud.md) together make a high-level interface for working with your app's data. Still, all that data has to live somewhere, so let's see how Wasp deals with databases. ## Supported Database Backends @@ -77,7 +77,7 @@ Also, make sure that: If you want to spin up your own dev database (or connect to an external one), you can tell Wasp about it using the `DATABASE_URL` environment variable. Wasp will use the value of `DATABASE_URL` as a connection string. -The easiest way to set the necessary `DATABASE_URL` environment variable is by adding it to the [.env.server](/docs/project/env-vars) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). +The easiest way to set the necessary `DATABASE_URL` environment variable is by adding it to the [.env.server](../project/env-vars) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). Alternatively, you can set it inline when running `wasp` (this applies to all environment variables): diff --git a/web/docs/data-model/crud.md b/web/docs/data-model/crud.md index 884d2b25f..98927f5e0 100644 --- a/web/docs/data-model/crud.md +++ b/web/docs/data-model/crud.md @@ -10,7 +10,7 @@ If you have a lot of experience writing full-stack apps, you probably ended up d Wasp makes handling these boring bits easy by offering a higher-level concept called Automatic CRUD. -With a single declaration, you can tell Wasp to automatically generate server-side logic (i.e., Queries and Actions) for creating, reading, updating and deleting [Entities](/docs/data-model/entities). As you update definitions for your Entities, Wasp automatically regenerates the backend logic. +With a single declaration, you can tell Wasp to automatically generate server-side logic (i.e., Queries and Actions) for creating, reading, updating and deleting [Entities](../data-model/entities). As you update definitions for your Entities, Wasp automatically regenerates the backend logic. :::caution Early preview This feature is currently in early preview and we are actively working on it. Read more about [our plans](#future-of-crud-operations-in-wasp) for CRUD operations. @@ -62,7 +62,7 @@ Keep reading for an example of Automatic CRUD in action, or skip ahead for the [ ## Example: A Simple TODO App -Let's create a full-app example that uses automatic CRUD. We'll stick to using the `Task` entity from the previous example, but we'll add a `User` entity and enable [username and password](/docs/auth/username-and-pass) based auth. +Let's create a full-app example that uses automatic CRUD. We'll stick to using the `Task` entity from the previous example, but we'll add a `User` entity and enable [username and password](../auth/username-and-pass) based auth. @@ -328,7 +328,7 @@ export const MainPage = () => { -And here are the login and signup pages, where we are using Wasp's [Auth UI](/docs/auth/ui) components: +And here are the login and signup pages, where we are using Wasp's [Auth UI](../auth/ui) components: @@ -692,7 +692,7 @@ export const getAllOverride: GetAllQuery = async ( -For a usage example, check the [example guide](/docs/data-model/crud#adding-crud-to-the-task-entity-). +For a usage example, check the [example guide](../data-model/crud#adding-crud-to-the-task-entity-). #### Using the CRUD operations in client code @@ -742,7 +742,7 @@ const deleteAction = Tasks.delete.useAction() -All CRUD operations are implemented with [Queries and Actions](/docs/data-model/operations/overview) under the hood, which means they come with all the features you'd expect (e.g., automatic SuperJSON serialization, full-stack type safety when using TypeScript) +All CRUD operations are implemented with [Queries and Actions](../data-model/operations/overview) under the hood, which means they come with all the features you'd expect (e.g., automatic SuperJSON serialization, full-stack type safety when using TypeScript) --- diff --git a/web/docs/data-model/entities.md b/web/docs/data-model/entities.md index 78a15c8ba..23843da17 100644 --- a/web/docs/data-model/entities.md +++ b/web/docs/data-model/entities.md @@ -62,11 +62,11 @@ Let's see how you can define and work with Wasp Entities: 1. Create/update some Entities in your `.wasp` file. 2. Run `wasp db migrate-dev`. This command syncs the database model with the Entity definitions in your `.wasp` file. It does this by creating migration scripts. 3. Migration scripts are automatically placed in the `migrations/` folder. Make sure to commit this folder into version control. -4. Use Wasp's JavasScript API to work with the database when implementing Operations (we'll cover this in detail when we talk about [operations](/docs/data-model/operations/overview)). +4. Use Wasp's JavasScript API to work with the database when implementing Operations (we'll cover this in detail when we talk about [operations](../data-model/operations/overview)). #### Using Entities in Operations -Most of the time, you will be working with Entities within the context of [Operations (Queries & Actions)](/docs/data-model/operations/overview). We'll see how that's done on the next page. +Most of the time, you will be working with Entities within the context of [Operations (Queries & Actions)](../data-model/operations/overview). We'll see how that's done on the next page. #### Using Entities directly diff --git a/web/docs/data-model/operations/actions.md b/web/docs/data-model/operations/actions.md index 13f9cffe9..7084548ea 100644 --- a/web/docs/data-model/operations/actions.md +++ b/web/docs/data-model/operations/actions.md @@ -8,7 +8,7 @@ import SuperjsonNote from './\_superjson-note.md'; We'll explain what Actions are and how to use them. If you're looking for a detailed API specification, skip ahead to the [API Reference](#api-reference). -Actions are quite similar to [Queries](/docs/data-model/operations/queries.md), but with a key distinction: Actions are designed to modify and add data, while Queries are solely for reading data. Examples of Actions include adding a comment to a blog post, liking a video, or updating a product's price. +Actions are quite similar to [Queries](../../data-model/operations/queries.md), but with a key distinction: Actions are designed to modify and add data, while Queries are solely for reading data. Examples of Actions include adding a comment to a blog post, liking a video, or updating a product's price. Actions and Queries work together to keep data caches up-to-date. @@ -373,7 +373,7 @@ export const createTask: CreateTask = async (args, context) => { ### Using Entities in Actions -In most cases, resources used in Actions will be [Entities](/docs/data-model/entities.md). +In most cases, resources used in Actions will be [Entities](../../data-model/entities.md). To use an Entity in your Action, add it to the `action` declaration in Wasp: @@ -626,7 +626,7 @@ Since both arguments are positional, you can name the parameters however you wan 2. `context` (type depends on the Action) - An additional context object **passed into the Action by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Actions](#using-entities-in-actions) to see how to use the entities field on the `context` object, or the [auth section](/docs/auth/overview#using-the-contextuser-object) to see how to use the `user` object. + An additional context object **passed into the Action by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Actions](#using-entities-in-actions) to see how to use the entities field on the `context` object, or the [auth section](../../auth/overview#using-the-contextuser-object) to see how to use the `user` object. @@ -704,7 +704,7 @@ In this case, the Action expects to receive an object with a `bar` field of type ### The `useAction` Hook and Optimistic Updates -Make sure you understand how [Queries](/docs/data-model/operations/queries.md) and [Cache Invalidation](#cache-invalidation) work before reading this chapter. +Make sure you understand how [Queries](../../data-model/operations/queries.md) and [Cache Invalidation](#cache-invalidation) work before reading this chapter. When using Actions in components, you can enhance them with the help of the `useAction` hook. This hook comes bundled with Wasp, and is used for decorating Wasp Actions. In other words, the hook returns a function whose API matches the original Action while also doing something extra under the hood (depending on how you configure it). diff --git a/web/docs/data-model/operations/overview.md b/web/docs/data-model/operations/overview.md index 42d933a5a..2f1a37567 100644 --- a/web/docs/data-model/operations/overview.md +++ b/web/docs/data-model/operations/overview.md @@ -6,7 +6,7 @@ import { Required } from '@site/src/components/Required'; While Entities enable help you define your app's data model and relationships, Operations are all about working with this data. -There are two kinds of Operations: [Queries](/docs/data-model/operations/queries.md) and [Actions](/docs/data-model/operations/actions.md). As their names suggest, +There are two kinds of Operations: [Queries](../../data-model/operations/queries.md) and [Actions](../../data-model/operations/actions.md). As their names suggest, Queries are meant for reading data, and Actions are meant for changing it (either by updating existing entries or creating new ones). Keep reading to find out all there is to know about Operations in Wasp. diff --git a/web/docs/data-model/operations/queries.md b/web/docs/data-model/operations/queries.md index 8d25f6aa6..6a70050ed 100644 --- a/web/docs/data-model/operations/queries.md +++ b/web/docs/data-model/operations/queries.md @@ -15,7 +15,7 @@ Fetching all comments on a blog post, a list of users that liked a video, inform Queries are fairly similar to Actions in terms of their API. Therefore, if you're already familiar with Actions, you might find reading the entire guide repetitive. -We instead recommend skipping ahead and only reading [the differences between Queries and Actions](/docs/data-model/operations/actions#differences-between-queries-and-actions), and consulting the [API Reference](#api-reference) as needed. +We instead recommend skipping ahead and only reading [the differences between Queries and Actions](../../data-model/operations/actions#differences-between-queries-and-actions), and consulting the [API Reference](#api-reference) as needed. ::: ## Working with Queries @@ -395,7 +395,7 @@ To prevent information leakage, the server won't forward these fields for any ot ### Using Entities in Queries -In most cases, resources used in Queries will be [Entities](/docs/data-model/entities.md). +In most cases, resources used in Queries will be [Entities](../../data-model/entities.md). To use an Entity in your Query, add it to the `query` declaration in Wasp: @@ -552,7 +552,7 @@ Since both arguments are positional, you can name the parameters however you wan 2. `context` (type depends on the Query) - An additional context object **passed into the Query by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Queries](#using-entities-in-queries) to see how to use the entities field on the `context` object, or the [auth section](/docs/auth/overview#using-the-contextuser-object) to see how to use the `user` object. + An additional context object **passed into the Query by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Queries](#using-entities-in-queries) to see how to use the entities field on the `context` object, or the [auth section](../../auth/overview#using-the-contextuser-object) to see how to use the `user` object. @@ -651,6 +651,6 @@ Wasp's `useQuery` hook accepts three arguments: [the default behavior](https://react-query.tanstack.com/guides/important-defaults) for this particular Query. If you want to change the global defaults, you can do - so in the [client setup function](/docs/project/client-config.md#overriding-default-behaviour-for-queries). + so in the [client setup function](../../project/client-config.md#overriding-default-behaviour-for-queries). For an example of usage, check [this section](#the-usequery-hook). diff --git a/web/docs/examples.md b/web/docs/examples.md deleted file mode 100644 index 8bd07753c..000000000 --- a/web/docs/examples.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Examples ---- - -import useBaseUrl from '@docusaurus/useBaseUrl'; - -We have a constantly growing collection of fully-functioning example apps, which you can use to learn more about Wasp's features. - -The full list of examples can be found [here](https://github.com/wasp-lang/wasp/tree/release/examples/). Here is a few of them: - -## Todo App - - **Features**: Auth ([username/password](language/features#authentication--authorization)), [Queries & Actions](language/features#queries-and-actions-aka-operations), [Entities](language/features#entity), [Routes](language/features#route) - - JS source code: [GitHub](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/TodoApp) - - TS source code: [GitHub](https://github.com/wasp-lang/wasp/tree/release/examples/todo-typescript) - - in-browser dev environment: [GitPod](https://gitpod.io/#https://github.com/wasp-lang/gitpod-template) - -## Waspello (Trello Clone) - - **Features**: Auth ([Google](language/features#social-login-providers-oauth-20), [username/password](language/features#authentication--authorization)), [Optimistic Updates](language/features#the-useaction-hook), [Tailwind CSS integration](/docs/project/css-frameworks) - - Source code: [GitHub](https://github.com/wasp-lang/wasp/tree/main/examples/waspello) - - Hosted at [https://waspello-demo.netlify.app](https://waspello-demo.netlify.app/login) -

- -

- -## Waspleau (Realtime Statistics Dashboard) - - **Features**: Cron [Jobs](language/features#jobs), [Server Setup](language/features#server-configuration) - - Source code: [GitHub](https://github.com/wasp-lang/wasp/tree/main/examples/waspleau) - - Hosted at [https://waspleau-app-client.fly.dev/](https://waspleau-app-client.fly.dev/) -

- -

\ No newline at end of file diff --git a/web/docs/general/cli.md b/web/docs/general/cli.md index b478487a7..46414825f 100644 --- a/web/docs/general/cli.md +++ b/web/docs/general/cli.md @@ -5,7 +5,7 @@ This guide provides an overview of the Wasp CLI commands, arguments, and options ## Overview -Once [installed](/docs/quick-start), you can use the wasp command from your command line. +Once [installed](../quick-start), you can use the wasp command from your command line. If you run the `wasp` command without any arguments, it will show you a list of available commands and their descriptions: @@ -96,13 +96,13 @@ Newsletter: https://wasp-lang.dev/#signup Deleted .wasp/ directory. ``` - - `wasp build` generates the complete web app code, which is ready for [deployment](/docs/advanced/deployment/overview). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. + - `wasp build` generates the complete web app code, which is ready for [deployment](../advanced/deployment/overview). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. - `wasp deploy` makes it easy to get your app hosted on the web. Currently, Wasp offers support for [Fly.io](https://fly.io). If you prefer a different hosting provider, feel free to let us know on Discord or submit a PR by updating [this TypeScript app](https://github.com/wasp-lang/wasp/tree/main/waspc/packages/deploy). - Read more about automatic deployment [here](/docs/advanced/deployment/cli). + Read more about automatic deployment [here](../advanced/deployment/cli). - `wasp telemetry` displays the status of [telemetry](https://wasp-lang.dev/docs/telemetry). diff --git a/web/docs/introduction/editor-setup.md b/web/docs/introduction/editor-setup.md index 15e49ef01..f025eccd3 100644 --- a/web/docs/introduction/editor-setup.md +++ b/web/docs/introduction/editor-setup.md @@ -4,7 +4,7 @@ slug: /editor-setup --- :::note -This page assumes you have already installed Wasp. If you do not have Wasp installed yet, check out the [Quick Start](/docs/quick-start) guide. +This page assumes you have already installed Wasp. If you do not have Wasp installed yet, check out the [Quick Start](./quick-start.md) guide. ::: Wasp comes with the Wasp language server, which gives supported editors powerful support and integration with the language. diff --git a/web/versioned_docs/version-0.11.8/introduction/what-is-wasp.md b/web/docs/introduction/introduction.md similarity index 98% rename from web/versioned_docs/version-0.11.8/introduction/what-is-wasp.md rename to web/docs/introduction/introduction.md index 1d1f4778d..fa05b7082 100644 --- a/web/versioned_docs/version-0.11.8/introduction/what-is-wasp.md +++ b/web/docs/introduction/introduction.md @@ -6,7 +6,7 @@ slug: / import ImgWithCaption from '@site/blog/components/ImgWithCaption' :::note -If you are looking for the installation instructions, check out the [Quick Start](/docs/quick-start) section. +If you are looking for the installation instructions, check out the [Quick Start](./quick-start.md) section. ::: We will give a brief overview of what Wasp is, how it works on a high level and when to use it. @@ -170,7 +170,7 @@ export function HomePage({ user }: { user: User }) { And voila! We are listing all the recipes in our app πŸŽ‰ -This was just a quick example to give you a taste of what Wasp is. For step by step tour through the most important Wasp features, check out the [Todo app tutorial](/docs/tutorial/create). +This was just a quick example to give you a taste of what Wasp is. For step by step tour through the most important Wasp features, check out the [Todo app tutorial](../tutorial/01-create.md). :::note Above we skipped defining /login and /signup pages to keep the example a bit shorter, but those are very simple to do by using Wasp's Auth UI feature. diff --git a/web/versioned_docs/version-0.11.8/introduction/getting-started.md b/web/docs/introduction/quick-start.md similarity index 96% rename from web/versioned_docs/version-0.11.8/introduction/getting-started.md rename to web/docs/introduction/quick-start.md index 09364d647..6d382227a 100644 --- a/web/versioned_docs/version-0.11.8/introduction/getting-started.md +++ b/web/docs/introduction/quick-start.md @@ -43,8 +43,8 @@ Check [More Details](#more-details) section below if anything went wrong, or if ### What next? - - [ ] πŸ‘‰ **Check out the [Todo App tutorial](/docs/tutorial/create), which will take you through all the core features of Wasp!** πŸ‘ˆ - - [ ] [Setup your editor](/docs/editor-setup) for working with Wasp. + - [ ] πŸ‘‰ **Check out the [Todo App tutorial](../tutorial/01-create.md), which will take you through all the core features of Wasp!** πŸ‘ˆ + - [ ] [Setup your editor](./editor-setup.md) for working with Wasp. - [ ] Join us on [Discord](https://discord.gg/rzdnErX)! Any feedback or questions you have, we are there for you. - [ ] Follow Wasp development by subscribing to our newsletter: https://wasp-lang.dev/#signup . We usually send 1 per month, and Matija does his best to unleash his creativity to make them engaging and fun to read :D! diff --git a/web/docs/language/features.md b/web/docs/language/features.md deleted file mode 100644 index 43a2046cc..000000000 --- a/web/docs/language/features.md +++ /dev/null @@ -1,2372 +0,0 @@ ---- -title: Features ---- - -import SendingEmailsInDevelopment from '../_sendingEmailsInDevelopment.md' -import OldDocsNote from '@site/docs/OldDocsNote' - - - -## App - -There can be only one declaration of `app` type per Wasp project. -It serves as a starting point and defines global properties of your app. - -```wasp -app todoApp { - wasp: { - version: "^0.6.0" - }, - title: "ToDo App", - head: [ // optional - "" - ] -} -``` - -### Fields - -#### `wasp: dict` (required) -Wasp compiler configuration. It is a dictionary with a single field: -- `version: string` (required) - version declares the compatible Wasp versions for the app. It should contain a valid [SemVer range](https://github.com/npm/node-semver#ranges). - -:::info -For now, the version field only supports caret ranges (i.e., `^x.y.z`). Support for the full specification will come in a future version of Wasp -::: - -#### `title: string` (required) -Title of your app. It will be displayed in the browser tab, next to the favicon. - -#### `head: [string]` (optional) -Head of your HTML Document. Your app's metadata (styles, links, etc) can be added here. - -#### `auth: dict` (optional) -Authentication and authorization configuration. -Check [`app.auth`](/docs/language/features#authentication--authorization) for more details. - -#### `client: dict` (optional) -Client configuration. -Check [`app.client`](/docs/language/features#client-configuration) for more details. - -#### `server: dict` (optional) -Server configuration. -Check [`app.server`](/docs/language/features#server-configuration) for more details. - -#### `db: dict` (optional) -Database configuration. -Check [`app.db`](/docs/language/features#database-configuration) for more details. - -#### `dependencies: [(string, string)]` (optional) -List of dependencies (external libraries). -Check [`app.dependencies`](/docs/language/features#dependencies) for more details. - -#### `emailSender: dict` (optional) -Email sender configuration. -Check [`app.emailSender`](/docs/language/features#email-sender) for more details. - -#### `webSocket: dict` (optional) -WebSocket configuration. -Check out the [`WebSocket guide`](/docs/guides/websockets) for more details. - -## Page - -`page` declaration is the top-level layout abstraction. Your app can have multiple pages. - -```wasp -page MainPage { - component: import Main from "@client/pages/Main", - authRequired: false // optional -} -``` - -Normally you will also want to associate `page` with a `route`, otherwise it won't be accessible in the app. - -### Fields - -#### `component: ClientImport` (required) -Import statement of the React element that implements the page component. - -#### `authRequired: bool` (optional) -Can be specified only if [`app.auth`](/docs/language/features#authentication--authorization) is defined. - -If set to `true`, only authenticated users will be able to access this page. Unauthenticated users will be redirected to a route defined by `onAuthFailedRedirectTo` property within `app.auth`. - -If `authRequired` is set to `true`, the React component of a page (specified by `component` property) will be provided `user` object as a prop. - -Check out this [section of our Todo app tutorial](/docs/tutorial/auth#update-the-main-page-to-require-auth) for an example of usage. - -## Route - -`route` declaration provides top-level routing functionality in Wasp. - -```wasp -route AboutRoute { path: "/about", to: AboutPage } -``` - -### Fields - -#### `path: string` (required) -URL path of the route. Route path can be parametrised and follows the same conventions as -[React Router](https://reactrouter.com/web/). - -#### `to: page` (required) -Name of the `page` to which the path will lead. -Referenced page must be defined somewhere in `.wasp` file. - -### Example - parametrised URL path -```wasp -route TaskRoute { path: "/task/:id", to: TaskPage } -``` -For details on URL path format check [React Router](https://reactrouter.com/web/) -documentation. - -### Accessing route parameters in a page component - -Since Wasp under the hood generates code with [React Router](https://reactrouter.com/web/), -the same rules apply when accessing URL params in your React components. Here is an example just to get you -started: - -```wasp title="todoApp.wasp" -// ... -route TaskRoute { path: "/task/:id", to: TaskPage } -page TaskPage { - component: import Task from "@client/pages/Task" -} -``` - -```jsx title="pages/Task.js" -import React from 'react' - -const Task = (props) => { - return ( -
- I am showing a task with id: {props.match.params.id}. -
- ) -} - -export default Task -``` -### Navigating between routes - -Navigation can be performed from the React code via `` component, also using the functionality of -[React Router](https://reactrouter.com/web/): - -```wasp title="todoApp.wasp" -// ... -route HomeRoute { path: "/home", to: HomePage } -page HomePage { - component: import Home from "@client/pages/Home" -} -``` - -```jsx title="src/client/pages/OtherPage.js" -import React from 'react' -import { Link } from "react-router-dom" - -const OtherPage = (props) => { - return ( - Go to homepage - ) -} -``` - -## Entity - -`entity` declaration represents a database model. -Wasp uses [Prisma](https://www.prisma.io/) to implement database functionality and currently provides only a thin layer above it. - -Each `Entity` declaration corresponds 1-to-1 to Prisma data model and is defined in a following way: - -```wasp -entity Task {=psl - id Int @id @default(autoincrement()) - description String - isDone Boolean @default(false) -psl=} -``` - -### `{=psl ... psl=}: PSL` -Definition of entity fields in *Prisma Schema Language* (PSL). See -[here for intro and examples](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema) -and [here for a more exhaustive language specification](https://github.com/prisma/specs/tree/master/schema). - -### Using Entities - -Entity-system in Wasp is based on [Prisma](http://www.prisma.io), and currently Wasp provides only a thin layer -on top of it. The workflow is as follows: - -1. Wasp developer creates/updates some of the entities in `.wasp` file. -2. Wasp developer runs `wasp db migrate-dev`. -3. Migration data is generated in `migrations/` folder (and should be committed). -4. Wasp developer uses Prisma JS API to work with the database when in Operations. - -#### Using Entities in Operations - -Most of the time in Wasp you will be working with entities in the context of Operations (Queries & Actions), so check their part of docs for more info on how to use entities in Operations. - -#### Using Entities directly - -If needed, you can also interact with entities directly via [Prisma Client(https://www.prisma.io/docs/concepts/components/prisma-client/crud) (although we recommend using them via injected `entities` when in Operations). - -To import Prisma Client in your Wasp server code, do `import prismaClient from '@wasp/dbClient'`. - -## Queries and Actions (aka Operations) - -In Wasp, the client and the server interact with each other through Operations. -Wasp currently supports two kinds of Operations: **Queries** and **Actions**. - -### Query - -Queries are used to fetch data from the server. They do not modify the server's state. - -Queries are implemented in NodeJS and executed within the server's context. -Wasp generates the code that lets you call the Query from anywhere in your code (client or server) using the same interface. -In other words, you won't have to worry about building an HTTP API for the Query, handling the request on the server, or even handling and caching the responses on the client. -Instead, simply focus on the business logic inside your Query and let Wasp take care of the rest! - -To create a Wasp Query, you must: -1. Define the Query's NodeJS implementation -2. Declare the Query in Wasp using the `query` declaration - -After completing these two steps, you'll be able to use the Query from any point in your code. - - -#### Defining the Query's NodeJS implementation -The Query's implementation is a NodeJS function that takes two arguments (it can be an `async` function but doesn't have to). -Since both arguments are positional, you can name the parameters however you want, but we'll stick with `args` and `context`: -1. `args`: An object containing all the arguments (i.e., payload) **passed to the Query by the caller** (e.g., filtering conditions). -Take a look at [the examples of usage](#using-the-query) to see how to pass this object to the Query. -3. `context`: An additional context object **injected into the Query by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in queries](#using-entities-in-queries). - -Here's an example of three simple Queries: -```js title="src/server/queries.js" -// our "database" -const tasks = [ - { id: 1, description: "Buy some eggs", isDone: true }, - { id: 2, description: "Make an omelette", isDone: false }, - { id: 3, description: "Eat breakfast", isDone: false } -] - -// You don't need to use the arguments if you don't need them -export const getAllTasks = () => { - return tasks; -} - -// The 'args' object is something sent by the caller (most often from the client) -export const getFilteredTasks = (args) => { - const { isDone } = args; - return tasks.filter(task => task.isDone === isDone) -} - -// Query implementations can be async functions and use await. -export const getTasksWithDelay = async () => { - const result = await sleep(1000) - return tasks -} -``` - -#### Declaring a Query in Wasp -After implementing your Queries in NodeJS, all that's left to do before using them is tell Wasp about it! -You can easily do this with the `query` declaration, which supports the following fields: -- `fn: ServerImport` (required) - The import statement of the Query's NodeJs implementation. -- `entities: [Entity]` (optional) - A list of entities you wish to use inside your Query. -We'll leave this option aside for now. You can read more about it [here](#using-entities-in-queries). - -Wasp Queries and their implementations don't need to (but can) have the same name, so we will keep the names different to avoid confusion. -With that in mind, this is how you might declare the Queries that use the implementations from the previous step: -```wasp title="pages/main.wasp" -// ... - -// Again, it most likely makes sense to name the Wasp Query after -// its implementation. We're changing the name to emphasize the difference. - -query fetchAllTasks { - fn: import { getAllTasks } from "@server/queries.js" -} - -query fetchFilteredTasks { - fn: import { getFilteredTasks } from "@server/queries.js" -} -``` - -After declaring a NodeJS function as a Wasp Query, two crucial things happen: -- Wasp **generates a client-side JavaScript function** that shares its name with the Query (e.g., `fetchFilteredTasks`). -This function takes a single optional argument - an object containing any serializable data you wish to use inside the Query. -Wasp will pass this object to the Query's implementation as its first positional argument (i.e., `args` from the previous step). -Such an abstraction works thanks to an HTTP API route handler Wasp generates on the server, which calls the Query's NodeJS implementation under the hood. -- Wasp **generates a server-side NodeJS function** that shares its name with the Query. This function's interface is identical to the client-side function from the previous point. - -Generating two such functions ensures a uniform calling interface across the entire app (both client and server). - - -#### Using the Query -To use the Query, you can import it from `@wasp` and call it directly. As mentioned, the usage is the same regardless of whether you're on the server or the client: -```javascript -import fetchAllTasks from '@wasp/queries/fetchAllTasks.js' -import fetchFilteredTasks from '@wasp/queries/fetchFilteredTasks.js' - -// ... - -const allTasks = await fetchAllTasks(); -const doneTasks = await fetchFilteredTasks({isDone: true}) -``` - -**NOTE**: Wasp will not stop you from importing a Query's NodeJS implementation from `./queries.js` and calling it directly. However, we advise against this, as you'll lose all the useful features a Wasp Query provides (e.g., entity injection). - -#### The `useQuery` hook -When using Queries on the client, you can make them reactive with the help of the `useQuery` hook. -This hook comes bundled with Wasp and is a thin wrapper around the `useQuery` hook from [_react-query_](https://github.com/tannerlinsley/react-query). - -Wasp's `useQuery` hook accepts three arguments: -- `queryFn` (required): A Wasp query declared in the previous step or, in other words, the client-side query function generated by Wasp based on a `query` declaration. -- `queryFnArgs` (optional): The arguments object (payload) you wish to pass into the Query. The Query's NodeJS implementation will receive this object as its first positional argument. -- `options` (optional): A _react-query_ `options` object. Use this to change - [the default - behaviour](https://react-query.tanstack.com/guides/important-defaults) for - this particular query. If you want to change the global defaults, you can do - so in the [client setup function](#overriding-default-behaviour-for-queries). - -Wasp's `useQuery` hook behaves mostly the same as [_react-query_'s `useQuery` hook](https://react-query.tanstack.com/docs/api#usequery), the only difference being in not having to supply the key (Wasp does this automatically under the hood). - -Here's an example of calling the Queries using the `useQuery` hook: -```jsx -import React from 'react' -import { useQuery } from '@wasp/queries' - -import fetchAllTasks from '@wasp/queries/fetchAllTasks' -import fetchFilteredTasks from '@wasp/queries/fetchFilteredTasks' - -const MainPage = () => { - const { - data: allTasks, - error: error1 - } = useQuery(fetchAllTasks) - - const { - data: doneTasks, - error: error2 - } = useQuery(fetchFilteredTasks, { isDone: true }) - - return ( -
-

All Tasks

- {allTasks ? allTasks.map(task => ) : error1} - -

Finished Tasks

- {doneTasks ? doneTasks.map(task => ) : error2} -
- ) -} - -const Task = ({ description, isDone }) => { - return ( -
-

Description: { description }

-

Is done: { isDone ? 'Yes' : 'No' }

-
- ) -} - - -export default MainPage -``` - -#### Error Handling -For security reasons, all exceptions thrown in the Query's NodeJS implementation are sent to the client as responses with the HTTP status code `500`, with all other details removed. -Hiding error details by default helps against accidentally leaking possibly sensitive information over the network. - -If you do want to pass additional error information to the client, you can construct and throw an appropriate `HttpError` in your NodeJS Query function: -```js title=src/server/queries.js -import HttpError from '@wasp/core/HttpError.js' - -export const getTasks = async (args, context) => { - const statusCode = 403 - const message = 'You can\'t do this!' - const data = { foo: 'bar' } - throw new HttpError(statusCode, message, data) -} -``` - -If the status code is `4xx`, the client will receive a response object with the corresponding `.message` and `.data` fields and rethrow the error (with these fields included). -To prevent information leakage, the server won't forward these fields for any other HTTP status codes. - -#### Using Entities in Queries -In most cases, resources used in Queries will be [Entities](#entity). -To use an Entity in your Query, add it to the query declaration in Wasp: - -```wasp {4,9} title="main.wasp" - -query fetchAllTasks { - fn: import { getAllTasks } from "@server/queries.js", - entities: [Task] -} - -query fetchFilteredTasks { - fn: import { getFilteredTasks } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp will inject the specified Entity into the Query's `context` argument, giving you access to the Entity's Prisma API: -```js title="src/server/queries.js" -export const getAllTasks = async (args, context) => { - return context.entities.Task.findMany({}) -} - -export const getFilteredTasks = async (args, context) => { - return context.entities.Task.findMany({ - where: { isDone: args.isDone } - }) -} -``` - -The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/crud). - - -### Action - -Actions are very similar to Queries. So similar, in fact, we will only list the differences: -1. They can (and most often should) modify the server's state, while Queries are only allowed to read it. -2. Actions don't need to be reactive so you can call them directly. Still, Wasp does provide a `useAction` React hook for adding extra behavior to the Action (e.g., optimistic updates). -Read more about the [`useAction` hook](#the-useaction-hook) below. -3. `action` declarations in Wasp are mostly identical to `query` declarations. The only difference is in the declaration's name. - -Here's an implementation of a simple Action: - -```js title=src/server/actions.js -export const sayHi = async () => { - console.log('The client said Hi!') -} -``` -Its corresponding declaration in Wasp: - -```wasp title="main.wasp" -// ... - -action sayHi { - fn: import { sayHi } from "@server/actions.js" -} -``` -And an example of how to import and call the declared Action: - -```js -import sayHi from '@wasp/actions/sayHi' - -// ... - -sayHi() -``` - -Here's an example on how you might define a less contrived Action. -```js title=src/server/actions.js -// ... -export const updateTaskIsDone = ({ id, isDone }, context) => { - return context.entities.Task.update({ - where: { id }, - data: { isDone } - }) -} -``` -```wasp title=main.wasp -action updateTaskIsDone { - fn: import { updateTaskIsDone } from "@server/actions.js", - entities: [Task] -} -``` - -And here is how you might use it: -```jsx {4,18} title=src/client/pages/Task.js -import React from 'react' -import { useQuery } from '@wasp/queries' -import fetchTask from '@wasp/queries/fetchTask' -import updateTaskIsDone from '@wasp/actions/updateTaskIsDone' - -const TaskPage = ({ id }) => { - const { data: task } = useQuery(fetchTask, { id }) - - if (!task) { - return

"Loading"

- } - - const { description, isDone } = task - return ( -
-

Description: {description}

-

Is done: {isDone ? 'Yes' : 'No'}

- -
- ) -} -``` - -#### The `useAction` hook -When using Actions in components, you can enhance them with the help of the `useAction` hook. This hook comes bundled with Wasp and decorates Wasp Actions. -In other words, the hook returns a function whose API matches the original Action while also doing something extra under the hood (depending on how you configure it). - -The `useAction` hook accepts two arguments: -- `actionFn` (required) - The Wasp Action (i.e., the client-side query function generated by Wasp based on a query declaration) you wish to enhance. -- `actionOptions` (optional) - An object configuring the extra features you want to add to the given Action. While this argument is technically optional, there is no point in using the `useAction` hook without providing it (it would be the same as using the Action directly). The Action options object supports the following fields: - - `optimisticUpdates` (optional) - An array of objects where each object defines an [optimistic update](https://stackoverflow.com/a/33009713) to perform on the query cache. To define an optimistic update, you must specify the following properties: - - `getQuerySpecifier` (required) - A function returning the query specifier (i.e., a value used to address the query you want to update). A query specifier is an array specifying the query function and arguments. For example, to optimistically update the query used with `useQuery(fetchFilteredTasks, {isDone: true }]`, your `getQuerySpecifier` function would have to return the array `[fetchFilteredTasks, { isDone: true}]`. Wasp will forward the argument you pass into the decorated Action to this function (i.e., you can use the properties of the added/change item to address the query). - - `updateQuery` (required) - The function used to perform the optimistic update. It should return the desired state of the cache. Wasp will call it with the following arguments: - - `item` - The argument you pass into the decorated Action. - - `oldData` - The currently cached value for the query identified by the specifier. - -**NOTE:** The `updateQuery` function must be a pure function. It must return the desired cache value identified by the `getQuerySpecifier` function and _must not_ perform any side effects. Also, make sure you only update the query caches affected by your action causing the optimistic update (Wasp cannot yet verify this). Finally, your implementation of the `updateQuery` function should work correctly regardless of the state of `oldData` (e.g., don't rely on array positioning). If you need to do something else during your optimistic update, you can directly use _react-query_'s lower-level API (read more about it [here](#advanced-usage)). - -Here's an example showing how to configure the Action from the previous example to perform an optimistic update: -```jsx {3,9,10,11,12,13,14,15,16,27} title=src/client/pages/Task.js -import React from 'react' -import { useQuery } from '@wasp/queries' -import { useAction } from '@wasp/actions' -import fetchTask from '@wasp/queries/fetchTask' -import updateTaskIsDone from '@wasp/actions/updateTaskIsDone' - -const TaskPage = ({ id }) => { - const { data: task } = useQuery(fetchTask, { id }) - const updateTaskIsDoneOptimistically = useAction(updateTaskIsDone, { - optimisticUpdates: [ - { - getQuerySpecifier: ({ id }) => [fetchTask, { id }], - updateQuery: ({ isDone }, oldData) => ({ ...oldData, isDone }) - } - ] - }) - - if (!task) { - return

"Loading"

- } - - const { description, isDone } = task - return ( -
-

Description: {description}

-

Is done: {isDone ? 'Yes' : 'No'}

- -
- Back to main page -
-
- ) -} - -export default TaskPage -``` -#### Advanced usage -The `useAction` hook currently only supports specifying optimistic updates. You can expect more features in future versions of Wasp. - -Wasp's optimistic update API is deliberately small and focuses exclusively on updating Query caches (as that's the most common use case). You might need an API that offers more options or a higher level of control. If that's the case, instead of using Wasp's `useAction` hook, you can use _react-query_'s `useMutation` hook and directly work with [their low-level API](https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates). - -If you decide to use _react-query_'s API directly, you will need access to the Query's cache key. Wasp internally uses this key but abstracts it from the programmer. Still, you can easily obtain it by accessing the `queryCacheKey` property on a Query: -```js -import { fetchTasks } from '@wasp/queries' - -const queryKey = fetchTasks.queryCacheKey -``` - -### Cache Invalidation -One of the trickiest parts of managing a web app's state is making sure the data returned by the queries is up to date. -Since Wasp uses _react-query_ for Query management, we must make sure to invalidate Queries (more specifically, their cached results managed by _react-query_) whenever they become stale. - -It's possible to invalidate the caches manually through several mechanisms _react-query_ provides (e.g., refetch, direct invalidation). -However, since manual cache invalidation quickly becomes complex and error-prone, Wasp offers a quicker and a more effective solution to get you started: **automatic Entity-based Query cache invalidation**. -Because Actions can (and most often do) modify the state while Queries read it, Wasp invalidates a Query's cache whenever an Action that uses the same Entity is executed. - -For example, let's assume that Action `createTask` and Query `getTasks` both use Entity `Task`. If `createTask` is executed, `getTasks`'s cached result may no longer be up-to-date. -Wasp will therefore invalidate it, making `getTasks` refetch data from the server, bringing it up to date again. - -In practice, this means that Wasp keeps the queries "fresh" without requiring you to think about cache invalidation. - -On the other hand, this kind of automatic cache invalidation can become wasteful (some updates might not be necessary) and will only work for Entities. If that's an issue, you can use the mechanisms provided by _react-query_ for now, and expect more direct support in Wasp for handling those use cases in a nice, elegant way. - -If you wish to optimistically set cache values after performing an action, you can do so using [optimistic updates](https://stackoverflow.com/a/33009713). Configure them using Wasp's [useAction hook](#the-useaction-hook). This is currently the only manual cache invalidation mechanism Wasps supports natively. For everything else, you can always rely on _react-query_. - -### Prisma Error Helpers -In your Operations, you may wish to handle general Prisma errors with HTTP-friendly responses. We have exposed two helper functions, `isPrismaError`, and `prismaErrorToHttpError`, for this purpose. As of now, we convert two specific Prisma errors (which we will continue to expand), with the rest being `500`. See the [source here](https://github.com/wasp-lang/wasp/blob/main/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/utils.js). - -#### `import statement`: -```js -import { isPrismaError, prismaErrorToHttpError } from '@wasp/utils.js' -``` - -##### Example of usage: -```js -try { - await context.entities.Task.create({...}) -} catch (e) { - if (isPrismaError(e)) { - throw prismaErrorToHttpError(e) - } else { - throw e - } -} -``` - -### CRUD operations on top of entities - -:::caution Early preview -This feature is currently in early preview. It doesn't contain all the planned features. - -In the future iterations of Wasp we plan on supporting: -- **authorization** that will allow you to specify which users can perform which operations -- **validation** of input data (e.g. using Zod schema validation) -::: - -For a specific [Entity](/docs/language/features#entity), you can tell Wasp to automatically instantiate server-side logic ([Queries](/docs/language/features#query) and [Actions](/docs/language/features#action)) for creating, reading, updating and deleting such entities. - -#### Which operations are supported? - -If we create CRUD operations for an entity named `Task`, - -```wasp title="main.wasp" -crud Tasks { // crud name here is "Tasks" - entity: Task, - operations: { - getAll: { - isPublic: true, // optional, defaults to false - }, - get: {}, - create: { - overrideFn: import { createTask } from "@server/tasks.js", // optional - }, - update: {}, - }, -} -``` - -Wasp will give you the following default implementations: - -**getAll** - returns all entities - -```js -// ... - -// If the operation is not public, Wasp checks if an authenticated user -// is making the request. - -return Task.findMany() -``` - -**get** - returns one entity by id field - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.findUnique({ where: { id: args.id } }) -``` - -**create** - creates a new entity - -```js -// ... -return Task.create({ data: args.data }) -``` - -**update** - updates an existing entity - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.update({ where: { id: args.id }, data: args.data }) -``` - -**delete** - deletes an existing entity - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.delete({ where: { id: args.id } }) -``` - -:::info Current Limitations -In the default `create` and `update` implementations, we are saving all of the data that the client sends to the server. This is not always desirable, i.e. in the case when the client should not be able to modify all of the data in the entity. - -[In the future](#/docs/guides/crud#future-of-crud-operations-in-wasp), we are planning to add validation of action input, where only the data that the user is allowed to change will be saved. - -For now, the solution is to provide an override function. You can override the default implementation by using the `overrideFn` option and implementing the validation logic yourself. - -::: - -#### CRUD declaration - -The CRUD declaration works on top of an existing entity declaration. It is declared as follows: - -```wasp title="main.wasp" -crud Tasks { // crud name here is "Tasks" - entity: Task, - operations: { - getAll: { - isPublic: true, // optional, defaults to false - }, - get: {}, - create: { - overrideFn: import { createTask } from "@server/tasks.js", // optional - }, - update: {}, - }, -} -``` - -It has the following fields: -- `entity: Entity` - the entity to which the CRUD operations will be applied. -- `operations: { [operationName]: CrudOperationOptions }` - the operations to be generated. The key is the name of the operation, and the value is the operation configuration. - - The possible values for `operationName` are: - - `getAll` - - `get` - - `create` - - `update` - - `delete` - - `CrudOperationOptions` can have the following fields: - - `isPublic: bool` - Whether the operation is public or not. If it is public, no auth is required to access it. If it is not public, it will be available only to authenticated users. Defaults to `false`. - - `overrideFn: ServerImport` - The import statement of the optional override implementation in Node.js. - -#### Defining the overrides - -Like with actions and queries, you can define the implementation in a Javascript/Typescript file. The overrides are functions that take the following arguments: -- `args` - The arguments of the operation i.e. the data that's sent from the client. -- `context` - Context contains the `user` making the request and the `entities` object containing the entity that's being operated on. - -You can also import types for each of the functions you want to override from `@wasp/crud/{crud name}`. The available types are: -- `GetAllQuery` -- `GetQuery` -- `CreateAction` -- `UpdateAction` -- `DeleteAction` - -If you have a CRUD named `Tasks`, you would import the types like this: -```ts -import type { GetAllQuery, GetQuery, CreateAction, UpdateAction, DeleteAction } from '@wasp/crud/Tasks' - -// Each of the types is a generic type, so you can use it like this: -export const getAllOverride: GetAllQuery = async (args, context) => { - // ... -} -``` - -We are showing an example of an override in the [CRUD guide](/docs/guides/crud). - -#### Using the CRUD operations in client code - -On the client, you import the CRUD operations from `@wasp/crud/{crud name}`. The names of the imports are the same as the names of the operations. For example, if you have a CRUD called `Tasks`, you would import the operations like this: - -```jsx title="SomePage.jsx" -import { Tasks } from '@wasp/crud/Tasks' -``` - -You can then access the operations like this: -```jsx title="SomePage.jsx" -const { data } = Tasks.getAll.useQuery() -const { data } = Tasks.get.useQuery({ id: 1 }) -const createAction = Tasks.create.useAction() -const updateAction = Tasks.update.useAction() -const deleteAction = Tasks.delete.useAction() - -// The CRUD operations are using the existing actions and queries -// under the hood, so all the options are available as before. -``` - -Check out the [CRUD guide](/docs/guides/crud) to see how to use the CRUD operations in client code. - -## APIs - -In Wasp, the default client-server interaction mechanism is through [Operations](#queries-and-actions-aka-operations). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`! Best of all, they should look and feel very familiar. - -### API - -APIs are used to tie a JS function to an HTTP (method, path) pair. They are distinct from Operations and have no client-side helpers (like `useQuery`). - -To create a Wasp API, you must: -1. Define the APIs NodeJS implementation -2. Declare the API in Wasp using the `api` declaration - -After completing these two steps, you'll be able to call the API from client code (via our Axios wrapper), or from the outside world. - -:::note -In order to leverage the benefits of TypeScript and use types in your NodeJS implementation (step 1), you must add your `api` declarations to your `.wasp` file (step 2) _and_ compile the Wasp project. This will enable the Wasp compiler to generate any new types based on your `.wasp`file definitions for use in your implementation files. -::: - -#### Defining the APIs NodeJS implementation -An API should be implemented as a NodeJS function that takes three arguments. -1. `req`: Express Request object -2. `res`: Express Response object -3. `context`: An additional context object **injected into the API by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in APIs](#using-entities-in-apis). - -##### Simple API example -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar : FooBar = (req, res, context) => { - res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware. - res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` }) -} -``` - -##### More complicated TypeScript example -Let's say you wanted to create some `GET` route that would take an email address as a param, and provide them the answer to "Life, the Universe and Everything." :) What would this look like in TypeScript? - -```wasp title="main.wasp" -api fooBar { - fn: import { fooBar } from "@server/apis.js", - entities: [Task], - httpRoute: (GET, "/foo/bar/:email") -} -``` - -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar: FooBar< -{ email: string }, // params -{ answer: number } // response -> = (req, res, _context) => { - console.log(req.params.email) - res.json({ answer: 42 }) -} -``` - -#### Declaring an API in Wasp -After implementing your APIs in NodeJS, all that's left to do before using them is tell Wasp about it! -You can easily do this with the `api` declaration, which supports the following fields: -- `fn: ServerImport` (required) - The import statement of the APIs NodeJs implementation. -- `httpRoute: (HttpMethod, string)` (required) - The HTTP (method, path) pair, where the method can be one of: - - `ALL`, `GET`, `POST`, `PUT` or `DELETE` - - and path is an Express path `string`. -- `entities: [Entity]` (optional) - A list of entities you wish to use inside your API. -We'll leave this option aside for now. You can read more about it [here](#using-entities-in-apis). -- `auth: bool` (optional) - If auth is enabled, this will default to `true` and provide a `context.user` object. If you do not wish to attempt to parse the JWT in the Authorization Header, you may set this to `false`. -- `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/guides/middleware-customization#2-customize-api-specific-middleware). - -Wasp APIs and their implementations don't need to (but can) have the same name. With that in mind, this is how you might declare the API that uses the implementations from the previous step: -```wasp title="pages/main.wasp" -// ... - -api fooBar { - fn: import { fooBar } from "@server/apis.js", - httpRoute: (GET, "/foo/bar") -} -``` - -#### Using the API -To use the API externally, you simply call the endpoint using the method and path you used. For example, if your app is running at `https://example.com` then from the above you could issue a `GET` to `https://example/com/foo/callback` (in your browser, Postman, `curl`, another web service, etc.). - -To use the API from your client, including with auth support, you can import the Axios wrapper from `@wasp/api` and invoke a call. For example: -```ts -import React, { useEffect } from 'react' -import api from '@wasp/api' - -async function fetchCustomRoute() { - const res = await api.get('/foo/bar') - console.log(res.data) -} - -export const Foo = () => { - useEffect(() => { - fetchCustomRoute() - }, []); - - return ( - <> - // ... - - ) -} -``` - -#### Using Entities in APIs -In many cases, resources used in APIs will be [Entities](#entity). -To use an Entity in your API, add it to the `api` declaration in Wasp: - -```wasp {3} title="main.wasp" -api fooBar { - fn: import { fooBar } from "@server/apis.js", - entities: [Task], - httpRoute: (GET, "/foo/bar") -} -``` - -Wasp will inject the specified Entity into the APIs `context` argument, giving you access to the Entity's Prisma API: -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar : FooBar = (req, res, context) => { - res.json({ count: await context.entities.Task.count() }) -} - -``` - -The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/crud). - -### apiNamespace - -An `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path. For example: - -```wasp title="main.wasp" -apiNamespace fooBar { - middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js", - path: "/foo/bar" -} -``` - -For more information about middleware configuration, please see: [Middleware Configuration](/docs/guides/middleware-customization) - -## Jobs - -If you have server tasks that you do not want to handle as part of the normal request-response cycle, Wasp allows you to make that function a `job` and it will gain some "superpowers." Jobs will: - * persist between server restarts - * can be retried if they fail - * can be delayed until the future - * can have a recurring schedule! - -Some examples where you may want to use a `job` on the server include sending an email, making an HTTP request to some external API, or doing some nightly calculations. - -### Job Executors - -Job executors handle the scheduling, monitoring, and execution of our jobs. - -Wasp allows you to choose which job executor will be used to execute a specific job that you define, which affects some of the finer details of how jobs will behave and how they can be further configured. Each job executor has its pros and cons, which we will explain in more detail below, so you can pick the one that best suits your needs. - -Currently, Wasp supports only one type of job executor, which is `PgBoss`, but in the future, it will likely support more. - -#### pg-boss - -We have selected [pg-boss](https://github.com/timgit/pg-boss/) as our first job executor to handle the low-volume, basic job queue workloads many web applications have. By using PostgreSQL (and [SKIP LOCKED](https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/)) as its storage and synchronization mechanism, it allows us to provide many job queue pros without any additional infrastructure or complex management. - -:::info -Keep in mind that pg-boss jobs run alongside your other server-side code, so they are not appropriate for CPU-heavy workloads. Additionally, some care is required if you modify scheduled jobs. Please see pg-boss details below for more information. - -
- pg-boss details - - pg-boss provides many useful features, which can be found [here](https://github.com/timgit/pg-boss/blob/8.4.2/README.md). - - When you add pg-boss to a Wasp project, it will automatically add a new schema to your database called `pgboss` with some internal tracking tables, including `job` and `schedule`. pg-boss tables have a `name` column in most tables that will correspond to your `job` identifier. Additionally, these tables maintain arguments, states, return values, retry information, start and expiration times, and other metadata required by pg-boss. - - If you need to customize the creation of the pg-boss instance, you can set an environment variable called `PG_BOSS_NEW_OPTIONS` to a stringified JSON object containing [these initialization parameters](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#newoptions). **NOTE**: Setting this overwrites all Wasp defaults, so you must include database connection information as well. - - ##### pg-boss considerations - - Wasp starts pg-boss alongside your web server's application, where both are simultaneously operational. This means that jobs running via pg-boss and the rest of the server logic (like Operations) share the CPU, therefore you should avoid running CPU-intensive tasks via jobs. - - Wasp does not (yet) support independent, horizontal scaling of pg-boss-only applications, nor starting them as separate workers/processes/threads. - - The job name/identifier in your `.wasp` file is the same name that will be used in the `name` column of pg-boss tables. If you change a name that had a `schedule` associated with it, pg-boss will continue scheduling those jobs but they will have no handlers associated, and will thus become stale and expire. To resolve this, you can remove the applicable row from the `schedule` table in the `pgboss` schema of your database. - - If you remove a `schedule` from a job, you will need to do the above as well. - - If you wish to deploy to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. -- https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js - -
-::: - -### Basic job definition and usage - -To declare a `job` in Wasp, simply add a declaration with a reference to an `async` function, like the following: - -```wasp title="main.wasp" -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js" - } -} -``` - -Then, in your [Operations](/docs/language/features#queries-and-actions-aka-operations) or [setupFn](/docs/language/features#setupfn-serverimport-optional) (or any other NodeJS code), you can submit work to be done: -```js -import { mySpecialJob } from '@wasp/jobs/mySpecialJob.js' - -const submittedJob = await mySpecialJob.submit({ job: "args" }) -console.log(await submittedJob.pgBoss.details()) - -// Or, if you'd prefer it to execute in the future, just add a .delay(). -// It takes a number of seconds, Date, or ISO date string. -await mySpecialJob.delay(10).submit({ job: "args" }) -``` - -And that is it! Your job will be executed by the job executor (pg-boss, in this case) as if you called `foo({ job: "args" })`. - -Note that in our example, `foo` takes an argument, but this does not always have to be the case. It all depends on how you've implemented your worker function. - -### Recurring jobs - -If you have work that needs to be done on some recurring basis, you can add a `schedule` to your job declaration: - -```wasp {6-9} title="main.wasp" -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js" - }, - schedule: { - cron: "0 * * * *", - args: {=json { "job": "args" } json=} // optional - } -} -``` - -In this example, you do _not_ need to invoke anything in JavaScript. You can imagine `foo({ job: "args" })` getting automatically scheduled and invoked for you every hour. - -### Fully specified example -Both `perform` and `schedule` accept `executorOptions`, which we pass directly to the named job executor when you submit jobs. In this example, the scheduled job will have a `retryLimit` set to 0, as `schedule` overrides any similar property from `perform`. Lastly, we add an entity to pass in via the context argument to `perform.fn`. - -```wasp -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js", - executorOptions: { - pgBoss: {=json { "retryLimit": 1 } json=} - } - }, - schedule: { - cron: "*/5 * * * *", - args: {=json { "foo": "bar" } json=}, - executorOptions: { - pgBoss: {=json { "retryLimit": 0 } json=} - } - }, - entities: [Task], -} -``` - -### Fields - -#### `executor: JobExecutor` (required) -`PgBoss` is currently our only job executor, and is recommended for low-volume production use cases. It requires your `app.db.system` to be `PostgreSQL`. - -#### `perform: dict` (required) - - - ##### `fn: ServerImport` (required) - An `async` JavaScript function of work to be performed. Since Wasp executes jobs on the server, you must import it from `@server`. The function receives a first argument which may be passed when the job is called, as well as the context containing any declared entities as the second (this is passed automatically by Wasp). Here is a sample signature: - - ```js - export async function foo(args, context) { - // Can reference context.entities.Task, for example. - } - ``` - - - ##### `executorOptions: dict` (optional) - Executor-specific default options to use when submitting jobs. These are passed directly through and you should consult the documentation for the job executor. These can be overridden during invocation with `submit()` or in a `schedule`. - - - ##### `pgBoss: JSON` (optional) - See the docs for [pg-boss](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#sendname-data-options). - -#### `schedule: dict` (optional) - - - ##### `cron: string` (required) - A 5-placeholder format cron expression string. See rationale for minute-level precision [here](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#scheduling). - - _If you need help building cron expressions, Check out_ [Crontab guru](https://crontab.guru/#0_*_*_*_*). - - - ##### `args: JSON` (optional) - The arguments to pass to the `perform.fn` function when invoked. - - - ##### `executorOptions: dict` (optional) - Executor-specific options to use when submitting jobs. These are passed directly through and you should consult the documentation for the job executor. The `perform.executorOptions` are the default options, and `schedule.executorOptions` can override/extend those. - - - ##### `pgBoss: JSON` (optional) - See the docs for [pg-boss](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#sendname-data-options). - -#### `entities: [Entity]` (optional) -A list of entities you wish to use inside your Job (similar to Queries and Actions). - -### JavaScript API - -#### Invocation -##### `import` - -```js -import { mySpecialJob } from '@wasp/jobs/mySpecialJob.js' -``` - -##### `submit(jobArgs, executorOptions)` -- ###### `jobArgs: JSON` (optional) -- ###### `executorOptions: JSON` (optional) - -Submits a `job` to be executed by an executor, optionally passing in a JSON job argument your job handler function will receive, and executor-specific submit options. - -```js -const submittedJob = await mySpecialJob.submit({ job: "args" }) -``` - -##### `delay(startAfter)` (optional) -- ###### `startAfter: int | string | Date` (required) - -Delaying the invocation of the job handler. The delay can be one of: -- Integer: number of seconds to delay. [Default 0] -- String: ISO date string to run at. -- Date: Date to run at. - -```js -const submittedJob = await mySpecialJob.delay(10).submit({ job: "args" }, { "retryLimit": 2 }) -``` - -#### Tracking -The return value of `submit()` is an instance of `SubmittedJob`, which minimally contains: -- `jobId`: A getter returning the UUID String ID for the job in that executor. -- `jobName`: A getter returning the name of the job you used in your `.wasp` file. -- `executorName`: A getter returning a Symbol of the name of the job executor. - - For pg-boss, you can import a Symbol from: `import { PG_BOSS_EXECUTOR_NAME } from '@wasp/jobs/core/pgBoss/pgBossJob.js'` if you wish to compare against `executorName`. - -There will also be namespaced, job executor-specific objects. - -- For pg-boss, you may access: `pgBoss` - - **NOTE**: no arguments are necessary, as we already applied the `jobId` in the available functions. - - `details()`: pg-boss specific job detail information. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#getjobbyidid) - - `cancel()`: attempts to cancel a job. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#cancelid) - - `resume()`: attempts to resume a canceled job. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#resumeid) - -## Dependencies - -You can specify additional npm dependencies via `dependencies` field in `app` declaration, in following way: - -```wasp -app MyApp { - title: "My app", - // ... - dependencies: [ - ("redux", "^4.0.5"), - ("react-redux", "^7.1.3") - ] -} -``` - -You will need to re-run `wasp start` after adding a dependency for Wasp to pick it up. - -**NOTE**: In current implementation of Wasp, if Wasp is already internally using certain npm dependency with certain version specified, you are not allowed to define that same npm dependency yourself while specifying different version. -If you do that, you will get an error message telling you which exact version you have to use for that dependency. -This means Wasp dictates exact versions of certain packages, so for example you can't choose version of React you want to use. -In the future, we will add support for picking any version you like, but we have not implemented that yet. Check [issue #59](https://github.com/wasp-lang/wasp/issues/59) to check out the progress or contribute. - - -## Authentication & Authorization - -Wasp provides authentication and authorization support out-of-the-box. Enabling it for your app is optional and can be done by configuring the `auth` field of the `app` declaration: - -```wasp -app MyApp { - title: "My app", - //... - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - usernameAndPassword: {}, // use this or email, not both - email: {}, // use this or usernameAndPassword, not both - google: {}, - gitHub: {}, - }, - onAuthFailedRedirectTo: "/someRoute" - } -} - -//... -``` - -`app.auth` is a dictionary with following fields: - -#### `userEntity: entity` (required) -Entity which represents the user. - -#### `externalAuthEntity: entity` (optional) -Entity which associates a user with some external authentication provider. We currently offer support for Google and GitHub. See the sections on [Social Login Providers](#social-login-providers-oauth-20) for more info. - -#### `methods: dict` (required) -List of authentication methods that Wasp app supports. Currently supported methods are: -* `usernameAndPassword`: authentication with a username and password. See [here](#username-and-password) for more. -* `email`: authentication with a email and password. See [here](#email-authentication) for more. -* `google`: authentication via Google accounts. See [here](#social-login-providers-oauth-20) for more. -* `gitHub`: authentication via GitHub accounts. See [here](#social-login-providers-oauth-20) for more. - -#### `onAuthFailedRedirectTo: String` (required) -Path where an unauthenticated user will be redirected to if they try to access a private page (which is declared by setting `authRequired: true` for a specific page). -Check out this [section of our Todo app tutorial](/docs/tutorial/auth#update-the-main-page-to-require-auth) to see an example of usage. - -#### `onAuthSucceededRedirectTo: String` (optional) -Path where a successfully authenticated user will be sent upon successful login/signup. -Default value is "/". - -:::note -Automatic redirect on successful login only works when using the Wasp provided [`Signup` and `Login` forms](#high-level-api) -::: - -### Username and Password - -`usernameAndPassword` authentication method makes it possible to signup/login into the app by using a username and password. -This method requires that `userEntity` specified in `auth` contains `username: string` and `password: string` fields: - -```wasp -app MyApp { - title: "My app", - //... - - auth: { - userEntity: User, - methods: { - usernameAndPassword: {}, - }, - onAuthFailedRedirectTo: "/someRoute" - } -} - -// Wasp requires the userEntity to have at least the following fields -entity User {=psl - id Int @id @default(autoincrement()) - username String @unique - password String -psl=} -``` - -We provide basic validations out of the box, which you can customize as shown below. Default validations are: -- `username`: non-empty -- `password`: non-empty, at least 8 characters, and contains a number - -Note that `username`s are stored in a case-sensitive manner. - -#### High-level API - -The quickest way to get started is by using the following API generated by Wasp: -- Signup and Login forms at `@wasp/auth/forms/Signup` and `@wasp/auth/forms/Login` routes - - For styling, these default authentication components have form classes associated for both login (`login-form`) and signup (`signup-form`). Additionally, they both share a common class (`auth-form`). -- `logout` function -- `useAuth()` React hook -**NOTE:** If the signup is successful, the Signup form will automatically log in the user. - -Check our [Todo app tutorial](/docs/tutorial/auth) to see how it works. See below for detailed specification of each of these methods. - -#### Lower-level API - -If you require more control in your authentication flow, you can achieve that in the following ways: -- If you don't want to use already generated Signup and Login forms and want to create your own, you can use `signup` and `login` function by invoking them from the client. -- If you want to execute custom code on the server during sign up, create your own sign up action which invokes Prisma client as `context.entities.[USER_ENTITY].create()` function, along with your custom code. - -The code of your custom sign-up action would look like this (your user entity being `User` in this instance): -```js title="src/server/auth/signup.js" -export const signUp = async (args, context) => { - // Your custom code before sign-up. - // ... - - const newUser = context.entities.User.create({ - data: { - username: args.username, - password: args.password // password hashed automatically by Wasp! 🐝 - } - }) - - // Your custom code after sign-up. - // ... - return newUser -} -``` - -:::info -You don't need to worry about hashing the password yourself! Even when you are using Prisma's client directly and calling `create()` with a plain-text password, Wasp's middleware takes care of hashing it before storing it in the database. An additional middleware also performs field validation. -::: - -##### Customizing user entity validations - -To disable/enable default validations, or add your own, you can modify your custom signUp function like so: -```js -const newUser = context.entities.User.create({ - data: { - username: args.username, - password: args.password // password hashed automatically by Wasp! 🐝 - }, - _waspSkipDefaultValidations: false, // can be omitted if false (default), or explicitly set to true - _waspCustomValidations: [ - { - validates: 'password', - message: 'password must contain an uppercase letter', - validator: password => /[A-Z]/.test(password) - }, - ] -}) -``` - -:::info -Validations always run on `create()`, but only when the field mentioned in `validates` is present for `update()`. The validation process stops on the first `validator` to return false. If enabled, default validations run first and validate basic properties of both the `'username'` or `'password'` fields. -::: - -#### Specification - -#### `login()` -An action for logging in the user. -```js -login(username, password) -``` -:::info -When using the exposed `login()` function, make sure to implement your own redirect on successful login logic -::: - -#### `username: string` -Username of the user logging in. - -#### `password: string` -Password of the user logging in. - -#### `import statement`: -```js -import login from '@wasp/auth/login' -``` -Login is a regular action and can be used directly from the frontend. - - -#### `signup()` -An action for signing up the user. This action does not log in the user, you still need to call `login()`. - -```js -signup(userFields) -``` -#### `userFields: object` -Auth-related fields (either `username` or `email` and `password`) of the user entity which was declared in `auth`. - -:::info -Wasp only stores the auth-related fields of the user entity. Adding extra fields to `userFields` will not have any effect. - -If you need to add extra fields to the user entity, we suggest doing it in a separate step after the user logs in for the first time. -::: - -#### `import statement`: -```js -import signup from '@wasp/auth/signup' -``` -Signup is a regular action and can be used directly from the frontend. - -#### `logout()` -An action for logging out the user. -```js -logout() -``` - -#### `import statement`: -```js -import logout from '@wasp/auth/logout' -``` - -##### Example of usage: -```jsx -import logout from '@wasp/auth/logout' - -const SignOut = () => { - return ( - - ) -} -``` - -#### Updating a user's password -If you need to update user's password, you can do it safely via Prisma client, e.g. within an action: -```js -export const updatePassword = async (args, context) => { - return context.entities.User.update({ - where: { id: args.userId }, - data: { - password: 'New pwd which will be hashed automatically!' - } - }) -} -``` -You don't need to worry about hashing the password yourself - if you have an `auth` declaration -in your `.wasp` file, Wasp already set a middleware on Prisma that makes sure whenever password -is created or updated on the user entity, it is also hashed before it is stored to the database. - -### Email authentication - -:::info -We have written a step-by-step guide on how to set up the e-mail authentication with Wasp's included Auth UI. - -Read more in the [email authentication guide](/docs/guides/email-auth). -::: - -:::warning -If a user signs up with Google or Github (and you set it up to save their social provider e-mail info on the `User` entity), they'll be able to reset their password and login with e-mail and password. - -If a user signs up with the e-mail and password and then tries to login with a social provider (Google or Github), they won't be able to do that. - -In the future, we will lift this limitation and enable smarter merging of accounts. -::: - -`email` authentication method makes it possible to signup/login into the app by using an e-mail and a password. - -```wasp title="main.wasp" -app MyApp { - title: "My app", - // ... - - auth: { - userEntity: User, - methods: { - email: { - // we'll deal with `email` below - }, - }, - onAuthFailedRedirectTo: "/someRoute" - }, - // ... -} - -// Wasp requires the userEntity to have at least the following fields -entity User {=psl - id Int @id @default(autoincrement()) - email String? @unique - password String? - isEmailVerified Boolean @default(false) - emailVerificationSentAt DateTime? - passwordResetSentAt DateTime? -psl=} -``` - -This method requires that `userEntity` specified in `auth` contains: - -- optional `email` field of type `String` -- optional `password` field of type `String` -- `isEmailVerified` field of type `Boolean` with a default value of `false` -- optional `emailVerificationSentAt` field of type `DateTime` -- optional `passwordResetSentAt` field of type `DateTime` - -#### Fields in the `email` dict - -```wasp title="main.wasp" -app MyApp { - title: "My app", - // ... - - auth: { - userEntity: User, - methods: { - email: { - fromField: { - name: "My App", - email: "hello@itsme.com" - }, - emailVerification: { - clientRoute: EmailVerificationRoute, - getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js", - }, - passwordReset: { - clientRoute: PasswordResetRoute, - getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js", - }, - allowUnverifiedLogin: false, - }, - }, - onAuthFailedRedirectTo: "/someRoute" - }, - // ... -} -``` - -##### `fromField: EmailFromField` (required) -`fromField` is a dict that specifies the name and e-mail address of the sender of the e-mails sent by Wasp. It is required to be defined. The object has the following fields: -- `name`: name of the sender (optional) -- `email`: e-mail address of the sender - -##### `emailVerification: EmailVerificationConfig` (required) -`emailVerification` is a dict that specifies the e-mail verification process. It is required to be defined. - -The object 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. - -```js title="src/pages/EmailVerificationPage.jsx" -import { verifyEmail } from '@wasp/auth/email/actions'; -... -await verifyEmail({ token }); -``` - -Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth). - -- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional) - -Defining `getEmailContentFn` can be done by defining a Javscript or Typescript file in the `server` directory. - -```ts title="server/email.ts" -import { GetVerificationEmailContentFn } from '@wasp/types' - -export const getVerificationEmailContent: GetVerificationEmailContentFn = ({ - verificationLink, -}) => ({ - 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 - `, -}) -``` - -##### `passwordReset: PasswordResetConfig` (required) -`passwordReset` is a dict that specifies the password reset process. It is required to be defined. The object 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. - -```js title="src/pages/ForgotPasswordPage.jsx" -import { requestPasswordReset } from '@wasp/auth/email/actions'; -... -await requestPasswordReset({ email }); -``` - -```js title="src/pages/PasswordResetPage.jsx" -import { resetPassword } from '@wasp/auth/email/actions'; -... -await resetPassword({ password, token }) -``` - -##### `allowUnverifiedLogin: bool`: a boolean that specifies whether the user can login without verifying their e-mail address. (optional) - -It defaults to `false`. If `allowUnverifiedLogin` is set to `true`, the user can login without verifying their e-mail address, otherwise users will receive a `401` error when trying to login without verifying their e-mail address. - - -Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth). - -- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional) - -Defining `getEmailContentFn` is done by defining a function that looks like this: - -```ts title="server/email.ts" -import { GetPasswordResetEmailContentFn } from '@wasp/types' - -export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({ - passwordResetLink, -}) => ({ - subject: 'Password reset', - text: `Click the link below to reset your password: ${passwordResetLink}`, - html: ` -

Click the link below to reset your password

- Reset password - `, -}) -``` - -#### Email sender for email authentication - -We require that you define an `emailSender`, so that Wasp knows how to send e-mails. Read more about that [here](#email-sender). - -#### Validations - -We provide basic validations out of the box. The validations are: -- `email`: non-empty, valid e-mail address -- `password`: non-empty, at least 8 characters, and contains a number - -Note that `email`s are stored in a case-insensitive manner. - -:::info -You don't need to worry about hashing the password yourself! Even when you are using Prisma's client directly and calling `create()` with a plain-text password, Wasp's middleware takes care of hashing it before storing it in the database. An additional middleware also performs field validation. -::: - - - -### Social Login Providers (OAuth 2.0) -Wasp allows you to easily add social login providers to your app. - -The following is a list of links to guides that will help you get started with the currently supported providers: -- [GitHub](/docs/integrations/github) -- [Google](/docs/integrations/google) - -When using Social Login Providers, Wasp gives you the following options: -- Default settings to get you started quickly -- UI Helpers to make it easy to add social login buttons and actions -- Override settings to customize the behavior of the providers - -#### Default Settings - - - - - -```wasp - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - google: {}, - }, - } -``` - -

Add google: {} to your auth.methods dictionary to use it with default settings

-

By default, Wasp expects you to set two environment variables in order to use Google authentication:

-
    -
  • GOOGLE_CLIENT_ID
  • -
  • GOOGLE_CLIENT_SECRET
  • -
-

These can be obtained in your Google Cloud Console project dashboard. See here for a detailed guide.

- -
- - -```wasp - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - gitHub: {}, - }, - } -``` - -

Add gitHub: {} to your auth.methods dictionary to use it with default settings

-

By default, Wasp expects you to set two environment variables in order to use GitHub authentication:

-
    -
  • GITHUB_CLIENT_ID
  • -
  • GITHUB_CLIENT_SECRET
  • -
-

These can be obtained in your GitHub project dashboard. See here for a detailed guide.

- -
-
- -When a user signs in for the first time, if the `userEntity` has `username` and/or `password` fields Wasp assigns generated values to those fields by default (e.g. `username: nice-blue-horse-14357` and a strong random `password`). This is a historical coupling between auth methods that will be removed over time. If you'd like to change this behavior, these values can be overridden as described below. - -:::tip Overriding Defaults -It is also possible to [override the default](features#overrides-for-social-login-providers) login behaviors that Wasp provides for you. This allows you to create custom setups, such as allowing Users to define a username rather than the default random username assigned by Wasp on initial Login. -::: - -#### `externalAuthEntity` -Anytime an authentication method is used that relies on an external authorization provider, for example, Google, we require an `externalAuthEntity` specified in `auth`, in addition to the `userEntity`, that contains the following configuration: - -```wasp {4,14} -//... - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, -//... - -entity User {=psl - id Int @id @default(autoincrement()) - //... - externalAuthAssociations SocialLogin[] -psl=} - -entity SocialLogin {=psl - id Int @id @default(autoincrement()) - provider String - providerId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - createdAt DateTime @default(now()) - @@unique([provider, providerId, userId]) -psl=} -``` -:::note -the same `externalAuthEntity` can be used across different social login providers (e.g., both GitHub and Google can use the same entity). -::: -#### UI helpers - -Wasp provides sign-in buttons, logos and URLs for your login page: - -```jsx -... -import { SignInButton as GoogleSignInButton, signInUrl as googleSignInUrl, logoUrl as googleLogoUrl } from '@wasp/auth/helpers/Google' -import { SignInButton as GitHubSignInButton, signInUrl as gitHubSignInUrl, logoUrl as gitHubLogoUrl } from '@wasp/auth/helpers/GitHub' - -const Login = () => { - return ( - <> - ... - - - - {/* or */} - Sign in with Google - Sign in with GitHub - - ) -} - -export default Login -``` - -If you need more customization than what the buttons provide, you can create your own custom components using the `signInUrl`s. - -#### Overrides - -When a user signs in for the first time, Wasp will create a new User account and link it to the chosen Auth Provider account for future logins. If the `userEntity` contains a `username` field it will default to a random dictionary phrase that does not exist in the database, such as `nice-blue-horse-27160`. This is a historical coupling between auth methods that will be removed over time. - -If you would like to allow the user to select their own username, or some other sign up flow, you could add a boolean property to your `User` entity indicating the account setup is incomplete. You can then check this user's property on the client with the [`useAuth()`](#useauth) hook and redirect them when appropriate - - e.g. check on homepage if `user.isAuthSetup === false`, redirect them to `EditUserDetailsPage` where they can edit the `username` property. - -Alternatively, you could add a `displayName` property to your User entity and assign it using the details of their provider account. Below is an example of how to do this by using: - - the `getUserFieldsFn` function to configure the user's `username` or `displayName` from their provider account - -We also show you how to customize the configuration of the Provider's settings using: - - the `configFn` function - -```wasp title=main.wasp {9,10,13,14,26} -app Example { - //... - - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - google: { - configFn: import { config } from "@server/auth/google.js", - getUserFieldsFn: import { getUserFields } from "@server/auth/google.js" - }, - gitHub: { - configFn: import { config } from "@server/auth/github.js", - getUserFieldsFn: import { getUserFields } from "@server/auth/github.js" - } - }, - - //... - } -} - -entity User {=psl - id Int @id @default(autoincrement()) - username String @unique - password String - displayName String? - externalAuthAssociations SocialLogin[] -psl=} - -//... - -``` - - -#### `configFn` - -This function should return an object with the following shape: - - - -```js title=src/server/auth/google.js -export function config() { - // ... - return { - clientID, // look up from env or elsewhere, - clientSecret, // look up from env or elsewhere, - scope: ['profile'] // must include at least 'profile' for Google - } -} - -// ... -``` - -

Here is a link to the default implementations as a reference

-
- - -```js title=src/server/auth/github.js -export function config() { - // ... - return { - clientID, // look up from env or elsewhere, - clientSecret, // look up from env or elsewhere, - scope: [] // default is an empty array for GitHub - } -} - -// ... -``` - -

Here is a link to the default implementations as a reference

-
-
- -#### `getUserFieldsFn` - -This function should return the user fields to use when creating a new user upon their first time logging in with a Social Auth Provider. The context contains a User entity for DB access, and the args are what the OAuth provider responds with. Here is how you could generate a username based on the Google display name. In your model, you could choose to add more attributes and set additional information. - ```js title=src/server/auth/google.js - import { generateAvailableUsername } from '@wasp/core/auth.js' - - // ... - - export async function getUserFields(_context, args) { - const username = await generateAvailableUsername(args.profile.displayName.split(' '), { separator: '.' }) - return { username } - } - ``` - - Or you could set the optional `displayName` property on the `User` entity instead: - ```js title=src/server/auth/google.js - import { generateAvailableDictionaryUsername, generateAvailableUsername } from '@wasp/core/auth.js' - - // ... - - export async function getUserFields(_context, args) { - const username = await generateAvailableDictionaryUsername() - const displayName = await generateAvailableUsername(args.profile.displayName.split(' '), { separator: '.' }) - return { username, displayName } - } - ``` - - `generateAvailableUsername` takes an array of Strings and an optional separator and generates a string ending with a random number that is not yet in the database. For example, the above could produce something like "Jim.Smith.3984" for a Google user Jim Smith. - - `generateAvailableDictionaryUsername` generates a random dictionary phrase that is not yet in the database. For example, `nice-blue-horse-27160`. - - -### Validation Error Handling -When creating, updating, or deleting entities, you may wish to handle validation errors. We have exposed a class called `AuthError` for this purpose. This could also be combined with [Prisma Error Helpers](/docs/language/features#prisma-error-helpers). - -#### `import statement`: -```js -import AuthError from '@wasp/core/AuthError.js' -``` - -##### Example of usage: -```js -try { - await context.entities.User.update(...) -} catch (e) { - if (e instanceof AuthError) { - throw new HttpError(422, 'Validation failed', { message: e.message }) - } else { - throw e - } -} -``` - -## Accessing the currently logged in user -When authentication is enabled in a Wasp app, we need a way to tell whether a user is logged in and access its data. -With that, we can further implement access control and decide which content is private and which public. - -#### On the client -On the client, Wasp provides a React hook you can use in functional components - `useAuth`. -This hook is actually a thin wrapper over Wasp's [`useQuery` hook](http://localhost:3002/docs/language/features#the-usequery-hook) and returns data in the same format. - -### `useAuth()` -#### `import statement`: -```js -import useAuth from '@wasp/auth/useAuth' -``` - -##### Example of usage: -```js title="src/client/pages/MainPage.js" -import React from 'react' - -import { Link } from 'react-router-dom' -import useAuth from '@wasp/auth/useAuth' -import logout from '@wasp/auth/logout' -import Todo from '../Todo' -import '../Main.css' - -const Main = () => { - const { data: user } = useAuth() - - if (!user) { - return ( - - Please login or sign up. - - ) - } else { - return ( - <> - - - < /> - ) - } -} - -export default Main -``` - -#### On the server - -### `context.user` - -When authentication is enabled, all operations (actions and queries) will have access to the `user` through the `context` argument. `context.user` will contain all the fields from the user entity except for the password. - -##### Example of usage: -```js title="src/server/actions.js" -import HttpError from '@wasp/core/HttpError.js' - -export const createTask = async (task, context) => { - if (!context.user) { - throw new HttpError(403) - } - - const Task = context.entities.Task - return Task.create({ - data: { - description: task.description, - user: { - connect: { id: context.user.id } - } - } - }) -} -``` -In order to implement access control, each operation is responsible for checking `context.user` and -acting accordingly - e.g. if `context.user` is `undefined` and the operation is private then user -should be denied access to it. - -## Client configuration - -You can configure the client using the `client` field inside the `app` -declaration, - -```wasp -app MyApp { - title: "My app", - // ... - client: { - rootComponent: import Root from "@client/Root.jsx", - setupFn: import mySetupFunction from "@client/myClientSetupCode.js" - } -} -``` - -`app.client` is a dictionary with the following fields: - -#### `rootComponent: ClientImport` (optional) - -`rootComponent` defines the root component of your client application. It is -expected to be a React component, and Wasp will use it to wrap your entire app. -It must render its children, which are the actual pages of your application. - -You can use it to define a common layout for your application: - -```jsx title="src/client/Root.jsx" -export default async function Root({ children }) { - return ( -
-
-

My App

-
- {children} -
-

My App footer

-
-
- ) -} -``` - -You can use it to set up various providers that your application needs: - -```jsx title="src/client/Root.jsx" -import store from './store' -import { Provider } from 'react-redux' - -export default async function Root({ children }) { - return ( - - {children} - - ) -} -``` - -As long as you render the children, you can do whatever you want in your root -component. Here's an example of a root component both sets up a provider and -renders a custom layout: - -```jsx title="src/client/Root.jsx" -import store from './store' -import { Provider } from 'react-redux' - -export default function Root({ children }) { - return ( - - - {children} - - - ) -} - -function Layout({ children }) { - return ( -
-
-

My App

-
- {children} -
-

My App footer

-
-
- ) -} -``` - - -#### `setupFn: ClientImport` (optional) - -`setupFn` declares a JavaScript function that Wasp executes on the client -before everything else. It is expected to be asynchronous, and -Wasp will await its completion before rendering the page. The function takes no -arguments, and its return value is ignored. - -You can use this function to perform any custom setup (e.g., setting up -client-side periodic jobs). - -Here's a dummy example of such a function: - -```js title="src/client/myClientSetupCode.js" -export default async function mySetupFunction() { - let count = 1; - setInterval( - () => console.log(`You have been online for ${count++} hours.`), - 1000 * 60 * 60, - ) -} -``` - -##### Overriding default behaviour for Queries -As mentioned, our `useQuery` hook uses _react-query_'s hook of the same name. -Since _react-query_ comes configured with aggressive but sane default options, -you most likely won't have to change those defaults for all Queries (you can -change them for a single Query using the `options` object, as described -[here](#the-usequery-hook)). - -Still, if you do need the global defaults, you can do so inside client setup -function. Wasp exposes a `configureQueryClient` hook that lets you configure -_react-query_'s `QueryClient` object: - - -```js title="src/client/myClientSetupCode.js" -import { configureQueryClient } from '@wasp/queryClient' - -export default async function mySetupFunction() { - // ... some setup - configureQueryClient({ - defaultOptions: { - queries: { - staleTime: Infinity, - } - } - }) - // ... some more setup -} -``` - -Make sure to pass in an object expected by the `QueryClient`'s constructor, as -explained in -[_react-query_'s docs](https://tanstack.com/query/v4/docs/react/reference/QueryClient). - -## Public static files on the client - -If you wish to override the default `favicon.ico` file or expose any other static files to the client, you can do so by placing them in the `public` directory in the `src/client` folder. - -The contents of this directory will be copied to the `dist/public` directory during the build process. This makes these files available at the root of the domain. For example, if you have a file `favicon.ico` in the `public` directory, it will be available at `https://example.com/favicon.ico`. - -For example, doing this: -``` -src -└── client - β”œβ”€β”€ public - β”‚ └── favicon.ico - └── ... -``` -will result in the following directory structure in the `build` folder: -``` -build -└── public - └── favicon.ico -``` - -:::warning Usage in client code -You **can't import these files** from your client code. They are only exposed at the root of the domain, e.g. `https://example.com/favicon.ico`. -::: - -## Server configuration - -Via `server` field of `app` declaration, you can configure the behavior of the Node.js server (one that is executing wasp operations). - -```wasp -app MyApp { - title: "My app", - // ... - server: { - setupFn: import mySetupFunction from "@server/myServerSetupCode.js" - } -} -``` - -`app.server` is a dictionary with the following fields: - -#### `middlewareConfigFn: ServerImport` (optional) - -The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See [the guide here](/docs/guides/middleware-customization#1-customize-global-middleware). - -#### `setupFn: ServerImport` (optional) - -`setupFn` declares a JS function that will be executed on server start. This function is expected to be async and will be awaited before the server starts accepting any requests. - -It allows you to do any custom setup, e.g. setting up additional database/websockets or starting cron/scheduled jobs. - -The `setupFn` function receives the `express.Application` and the `http.Server` instances as part of its context. They can be useful for setting up any custom server routes or for example, setting up `socket.io`. -```ts -export type ServerSetupFn = (context: ServerSetupFnContext) => Promise - -export type ServerSetupFnContext = { - app: Application, // === express.Application - server: Server, // === http.Server -} -``` - -As an example, adding a custom route would look something like: -```ts title="src/server/myServerSetupCode.ts" -import { ServerSetupFn, Application } from '@wasp/types' - -const mySetupFunction: ServerSetupFn = async ({ app }) => { - addCustomRoute(app) -} - -function addCustomRoute(app: Application) { - app.get('/customRoute', (_req, res) => { - res.send('I am a custom route') - }) -} -``` - -In case you want to store some values for later use, or to be accessed by the Operations, recommended way is to store those in variables in the same module/file where you defined the javascript setup function and then expose additional functions for reading those values, which you can then import directly from Operations and use. This effectively turns your module into a singleton whose construction is performed on server start. - -Dummy example of such function and its usage: - -```js title="src/server/myServerSetupCode.js" -let someResource = undefined - -const mySetupFunction = async () => { - // Let's pretend functions setUpSomeResource and startSomeCronJob - // are implemented below or imported from another file. - someResource = await setUpSomeResource() - startSomeCronJob() -} - -export const getSomeResource = () => someResource - -export default mySetupFunction -``` - -```js title="src/server/queries.js" -import { getSomeResource } from './myServerSetupCode.js' - -... - -export const someQuery = async (args, context) => { - const someResource = getSomeResource() - return queryDataFromSomeResource(args, someResource) -} -``` - - -## .env - -Your project will likely be using environment variables for configuration, typically to define connection to the database, API keys for external services and similar. - -When in production, you will typically define environment variables through mechanisms provided by your hosting provider. - -However, when in development, you might also need to supply certain environment variables, and to avoid doing it "manually", Wasp supports `.env` (dotenv) files where you can define environment variables that will be used during development (they will not be used during production). - -Since environmental variables are usually different for server-side and client apps, in Wasp root directly you can create two files, `.env.server` for server-side of your Wasp project, and `.env.client` for client side (or web app) of Wasp project. - - -`.env.server` and `.env.client` files have to be defined in the root of your Wasp project. - -`.env.server` and `.env.client` files should not be committed to the version control - we already ignore it by default in the .gitignore file we generate when you create a new Wasp project via `wasp new` cli command. - -Variables are defined in `.env.server` or `.env.client` files in the form of `NAME=VALUE`, for example: -``` -DATABASE_URL=postgresql://localhost:5432 -MY_VAR=somevalue -``` - -Any env vars defined in the `.env.server` / `.env.client` files will be forwarded to the server-side / web app of your Wasp project and therefore accessible in your javascript code via `process.env`, for example: -```js -console.log(process.env.DATABASE_URL) -``` - -## Database configuration - -Via `db` field of `app` declaration, you can configure the database used by Wasp. - -```wasp -app MyApp { - title: "My app", - // ... - db: { - system: PostgreSQL, - seeds: [ - import devSeed from "@server/dbSeeds.js" - ], - prisma: { - clientPreviewFeatures: ["extendedWhereUnique"] - } - } -} -``` - -`app.db` is a dictionary with following fields: - -#### - `system: DbSystem` (Optional) -The database system Wasp will use. It can be either `PostgreSQL` or `SQLite`. -If not defined, or even if whole `db` field is not present, default value is `SQLite`. -If you add/remove/modify `db` field, run `wasp db migrate-dev` to apply the changes. - -#### - `seeds: [ServerImport]` (Optional) -Defines seed functions that you can use via `wasp db seed` to seed your database with initial data. -Check out [Seeding](#seeding) section for more details. - -#### - `prisma: [PrismaOptions]` (Optional) -Additional configuration for Prisma. -It currently only supports a single field: - - `clientPreviewFeatures : string` - allows you to define [Prisma client preview features](https://www.prisma.io/docs/concepts/components/preview-features/client-preview-features). - - -### SQLite -Default database is `SQLite`, since it is great for getting started with a new project (needs no configuring), but it can be used only in development - once you want to deploy Wasp to production you will need to switch to `PostgreSQL` and stick with it. -Check below for more details on how to migrate from SQLite to PostgreSQL. - -### PostgreSQL -When using `PostgreSQL` as your database (`app: { db: { system: PostgreSQL } }`), you will need to make sure you have a postgres database running during development (when running `wasp start` or doing `wasp db ...` commands). - -### Using Wasp provided dev database - -Wasp provides `wasp start db` command that starts the default dev db for you. - -Your Wasp app will automatically connect to it once you have it running via `wasp start db`, no additional configuration is needed. This command relies on Docker being installed on your machine. - -### Connecting to existing database - -If instead of using `wasp start db` you would rather spin up your own dev database or connect to some external database, you will need to provide Wasp with `DATABASE_URL` environment variable that Wasp will use to connect to it. - -The easiest way to provide the needed `DATABASE_URL` environment variable is by adding it to the [.env.server](https://wasp-lang.dev/docs/language/features#env) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). - -You can also set it per command by doing `DATABASE_URL= wasp ...` -> this can be useful if you want to run specific `wasp` command on a specific database. -Example: you could do `DATABASE_URL= wasp db seed myProdSeed` to seed data for a fresh staging or production database. - -### Migrating from SQLite to PostgreSQL -To run Wasp app in production, you will need to switch from `SQLite` to `PostgreSQL`. - -1. Set `app.db.system` to `PostgreSQL`. -3. Delete old migrations, since they are SQLite migrations and can't be used with PostgreSQL: `rm -r migrations/`. -3. Run `wasp start db` to start your new db running (or check instructions above if you prefer using your own db). Leave it running, since we need it for the next step. -4. In a different terminal, run `wasp db migrate-dev` to apply new changes and create new, initial migration. -5. That is it, you are all done! - -### Seeding - -**Database seeding** is a term for populating database with some initial data. - -Seeding is most commonly used for two following scenarios: - 1. To put development database into a state convenient for testing / playing with it. - 2. To initialize dev/staging/prod database with some essential data needed for it to be useful, - for example default currencies in a Currency table. - -#### Writing a seed function - -Wasp enables you to define multiple **seed functions** via `app.db.seeds`: - -```wasp -app MyApp { - // ... - db: { - // ... - seeds: [ - import { devSeedSimple } from "@server/dbSeeds.js", - import { prodSeed } from "@server/dbSeeds.js" - ] - } -} -``` - -Each seed function is expected to be an async function that takes one argument, `prismaClient`, which is a [Prisma Client](https://www.prisma.io/docs/concepts/components/prisma-client/crud) instance that you can use to interact with the database. -This is the same instance of Prisma Client that Wasp uses internally, so you e.g. get password hashing for free. - -Since a seed function is part of the server-side code, it can also import other server-side code, so you can and will normally want to import and use Actions to perform the seeding. - -Example of a seed function that imports an Action (+ a helper function to create a user): - -```js -import { createTask } from './actions.js' - -export const devSeedSimple = async (prismaClient) => { - const user = await createUser(prismaClient, { - username: "RiuTheDog", - password: "bark1234" - }) - - await createTask( - { description: "Chase the cat" }, - { user, entities: { Task: prismaClient.task } } - ) -} - -async function createUser (prismaClient, data) { - const { password, ...newUser } = await prismaClient.user.create({ data }) - return newUser -} -``` - -#### Running seed functions - - - `wasp db seed`: If you have just one seed function, it will run it. If you have multiple, it will interactively ask you to choose one to run. - - - `wasp db seed `: It will run the seed function with the specified name, where the name is the identifier you used in its `import` expression in the `app.db.seeds` list. Example: `wasp db seed devSeedSimple`. - -:::tip - Often you will want to call `wasp db seed` right after you ran `wasp db reset`: first you empty your database, then you fill it with some initial data. -::: - - -## Email sender - -#### `provider: EmailProvider` (required) - -We support multiple different providers for sending e-mails: `SMTP`, `SendGrid` and `Mailgun`. - -### SMTP - -SMTP e-mail sender uses your SMTP server to send e-mails. - -Read [our guide](/docs/advanced/email#using-the-smtp-provider) for setting up SMTP for more details. - - -### SendGrid - -SendGrid is a popular service for sending e-mails that provides both API and SMTP methods of sending e-mails. We use their official SDK for sending e-mails. - -Check out [our guide](/docs/advanced/email#using-the-sendgrid-provider) for setting up Sendgrid for more details. - -### Mailgun - -Mailgun is a popular service for sending e-mails that provides both API and SMTP methods of sending e-mails. We use their official SDK for sending e-mails. - -Check out [our guide](/docs/advanced/email#using-the-mailgun-provider) for setting up Mailgun for more details. - -#### `defaultSender: EmailFromField` (optional) - -You can optionally provide a default sender info that will be used when you don't provide it explicitly when sending an e-mail. - -```wasp -app MyApp { - title: "My app", - // ... - emailSender: { - provider: SMTP, - defaultFrom: { - name: "Hello", - email: "hello@itsme.com" - }, - }, -} -``` - -After you set up the email sender, you can use it in your code to send e-mails. For example, you can send an e-mail when a user signs up, or when a user resets their password. - -### Sending e-mails - - - -To send an e-mail, you can use the `emailSender` that is provided by the `@wasp/email` module. - -```ts title="src/actions/sendEmail.js" -import { emailSender } from '@wasp/email/index.js' - -// In some action handler... -const info = await emailSender.send({ - to: 'user@domain.com', - subject: 'Saying hello', - text: 'Hello world', - html: 'Hello world' -}) -``` diff --git a/web/docs/project/client-config.md b/web/docs/project/client-config.md index e7ca198e0..9b9070ef9 100644 --- a/web/docs/project/client-config.md +++ b/web/docs/project/client-config.md @@ -211,7 +211,7 @@ export default async function mySetupFunction(): Promise { ### Overriding Default Behaviour for Queries :::info -You can change the options for a **single** Query using the `options` object, as described [here](/docs/data-model/operations/queries#the-usequery-hook-1). +You can change the options for a **single** Query using the `options` object, as described [here](../data-model/operations/queries#the-usequery-hook-1). ::: Wasp's `useQuery` hook uses `react-query`'s `useQuery` hook under the hood. Since `react-query` comes configured with aggressive but sane default options, you most likely won't have to change those defaults for all Queries. diff --git a/web/docs/project/css-frameworks.md b/web/docs/project/css-frameworks.md index f5ca4ecdf..5fa751aee 100644 --- a/web/docs/project/css-frameworks.md +++ b/web/docs/project/css-frameworks.md @@ -85,7 +85,7 @@ Make sure to use the `.cjs` extension for these config files, if you name them w ### Adding Tailwind Plugins -To add Tailwind plugins, add it to [dependencies](/docs/project/dependencies) in your `main.wasp` file and to the plugins list in your `tailwind.config.cjs` file: +To add Tailwind plugins, add it to [dependencies](../project/dependencies) in your `main.wasp` file and to the plugins list in your `tailwind.config.cjs` file: ```wasp title="./main.wasp" {4-5} app todoApp { diff --git a/web/docs/project/customizing-app.md b/web/docs/project/customizing-app.md index fe3bffea4..08fc73b4b 100644 --- a/web/docs/project/customizing-app.md +++ b/web/docs/project/customizing-app.md @@ -113,28 +113,28 @@ The rest of the fields are covered in dedicated sections of the docs: - `auth: dict` - Authentication configuration. Read more in the [authentication section](/docs/auth/overview) of the docs. + Authentication configuration. Read more in the [authentication section](../auth/overview) of the docs. - `client: dict` - Configuration for the client side of your app. Read more in the [client configuration section](/docs/project/client-config) of the docs. + Configuration for the client side of your app. Read more in the [client configuration section](../project/client-config) of the docs. - `server: dict` - Configuration for the server side of your app. Read more in the [server configuration section](/docs/project/server-config) of the docs. + Configuration for the server side of your app. Read more in the [server configuration section](../project/server-config) of the docs. - `db: dict` - Database configuration. Read more in the [database configuration section](/docs/data-model/backends) of the docs. + Database configuration. Read more in the [database configuration section](../data-model/backends) of the docs. - `dependencies: [(string, string)]` - List of npm dependencies for your app. Read more in the [dependencies section](/docs/project/dependencies) of the docs. + List of npm dependencies for your app. Read more in the [dependencies section](../project/dependencies) of the docs. - `emailSender: dict` - Email sender configuration. Read more in the [email sending section](/docs/advanced/email) of the docs. + Email sender configuration. Read more in the [email sending section](../advanced/email) of the docs. - `webSocket: dict` - WebSocket configuration. Read more in the [WebSocket section](/docs/advanced/web-sockets) of the docs. + WebSocket configuration. Read more in the [WebSocket section](../advanced/web-sockets) of the docs. diff --git a/web/docs/project/env-vars.md b/web/docs/project/env-vars.md index d4157b239..775c75cc3 100644 --- a/web/docs/project/env-vars.md +++ b/web/docs/project/env-vars.md @@ -131,4 +131,4 @@ The way you provide env vars to your Wasp project in production depends on where flyctl secrets set SOME_VAR_NAME=somevalue ``` -You can read a lot more details in the [deployment section](/docs/advanced/deployment/manually) of the docs. We go into detail on how to define env vars for each deployment option. +You can read a lot more details in the [deployment section](../advanced/deployment/manually) of the docs. We go into detail on how to define env vars for each deployment option. diff --git a/web/docs/project/server-config.md b/web/docs/project/server-config.md index 5fe5e60f2..10800d1f5 100644 --- a/web/docs/project/server-config.md +++ b/web/docs/project/server-config.md @@ -90,7 +90,7 @@ function addCustomRoute(app: Application) { ### Storing Some Values for Later Use -In case you want to store some values for later use, or to be accessed by the [Operations](/docs/data-model/operations/overview) you do that in the `setupFn` function. +In case you want to store some values for later use, or to be accessed by the [Operations](../data-model/operations/overview) you do that in the `setupFn` function. Dummy example of such function and its usage: @@ -247,4 +247,4 @@ app MyApp { - #### `middlewareConfigFn: ServerImport` - The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See more in the [configuring middleware section](/docs/advanced/middleware-config#1-customize-global-middleware). + The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See more in the [configuring middleware section](../advanced/middleware-config#1-customize-global-middleware). diff --git a/web/docs/project/testing.md b/web/docs/project/testing.md index b64f2ed0b..5816cd1c2 100644 --- a/web/docs/project/testing.md +++ b/web/docs/project/testing.md @@ -69,7 +69,7 @@ Wasp provides several functions to help you write React tests: const { mockQuery, mockApi } = mockServer(); ``` - - `mockQuery`: Takes a Wasp [query](/docs/data-model/operations/queries) to mock and the JSON data it should return. + - `mockQuery`: Takes a Wasp [query](../data-model/operations/queries) to mock and the JSON data it should return. ```js import getTasks from "@wasp/queries/getTasks"; @@ -81,7 +81,7 @@ Wasp provides several functions to help you write React tests: - Behind the scenes, Wasp uses [`msw`](https://npmjs.com/package/msw) to create a server request handle that responds with the specified data. - Mock are cleared between each test. - - `mockApi`: Similar to `mockQuery`, but for [APIs](/docs/advanced/apis). Instead of a Wasp query, it takes a route containing an HTTP method and a path. + - `mockApi`: Similar to `mockQuery`, but for [APIs](../advanced/apis). Instead of a Wasp query, it takes a route containing an HTTP method and a path. ```js import { HttpMethod } from "@wasp/types"; diff --git a/web/docs/tutorial/01-create.md b/web/docs/tutorial/01-create.md index a735e1667..96d31de7e 100644 --- a/web/docs/tutorial/01-create.md +++ b/web/docs/tutorial/01-create.md @@ -5,7 +5,7 @@ title: 1. Creating a New Project import useBaseUrl from '@docusaurus/useBaseUrl'; :::info -You'll need to have the latest version of Wasp installed locally to follow this tutorial. If you haven't installed it yet, check out the [QuickStart](/docs/quick-start) guide! +You'll need to have the latest version of Wasp installed locally to follow this tutorial. If you haven't installed it yet, check out the [QuickStart](../quick-start) guide! ::: In this section, we'll guide you through the process of creating a simple Todo app with Wasp. In the process, we'll take you through the most important and useful features of Wasp. diff --git a/web/docs/tutorial/02-project-structure.md b/web/docs/tutorial/02-project-structure.md index 0160d5ceb..168e6ac85 100644 --- a/web/docs/tutorial/02-project-structure.md +++ b/web/docs/tutorial/02-project-structure.md @@ -35,7 +35,7 @@ By _your code_, we mean the _"the code you write"_, as opposed to the code gener Many of the other files (`tsconfig.json`, `vite-env.d.ts`, etc.) are used by your IDE to improve your development experience with tools like autocompletion, intellisense, and error reporting. The file `vite.config.ts` is used to configure [Vite](https://vitejs.dev/guide/), Wasp's build tool of choice. -We won't be configuring Vite in this tutorial, so you can safely ignore the file. Still, if you ever end up wanting more control over Vite, you'll find everything you need to know in [custom Vite config docs](/docs/project/custom-vite-config.md). +We won't be configuring Vite in this tutorial, so you can safely ignore the file. Still, if you ever end up wanting more control over Vite, you'll find everything you need to know in [custom Vite config docs](../project/custom-vite-config.md). :::note TypeScript Support Wasp supports TypeScript out of the box, but you are free to choose between or mix JavaScript and TypeScript as you see fit. diff --git a/web/docs/tutorial/03-pages.md b/web/docs/tutorial/03-pages.md index ca0f9944a..7c8052f10 100644 --- a/web/docs/tutorial/03-pages.md +++ b/web/docs/tutorial/03-pages.md @@ -142,7 +142,7 @@ Now you can visit `/hello/johnny` and see "Here's johnny!" :::tip Type-safe links -Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](/docs/advanced/links) for more details. +Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](../advanced/links) for more details. ::: diff --git a/web/docs/tutorial/04-entities.md b/web/docs/tutorial/04-entities.md index 42ee9d68c..2565c9d3d 100644 --- a/web/docs/tutorial/04-entities.md +++ b/web/docs/tutorial/04-entities.md @@ -21,7 +21,7 @@ psl=} :::note Wasp uses [Prisma](https://www.prisma.io) as a way to talk to the database. You define entities by defining [Prisma models](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/data-model/) using the Prisma Schema Language (PSL) between the `{=psl psl=}` tags. -Read more in the [Entities](/docs/data-model/entities) section of the docs. +Read more in the [Entities](../data-model/entities) section of the docs. ::: To update the database schema to include this entity, stop the `wasp start` process, if its running, and run: diff --git a/web/docs/tutorial/05-queries.md b/web/docs/tutorial/05-queries.md index 748de193f..2056a71e8 100644 --- a/web/docs/tutorial/05-queries.md +++ b/web/docs/tutorial/05-queries.md @@ -5,7 +5,7 @@ title: 5. Querying the Database import useBaseUrl from '@docusaurus/useBaseUrl'; import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers'; -We want to know which tasks we need to do, so let's list them! The primary way of interacting with entities in Wasp is by using [queries and actions](/docs/data-model/operations/overview), collectively known as _operations_. +We want to know which tasks we need to do, so let's list them! The primary way of interacting with entities in Wasp is by using [queries and actions](../data-model/operations/overview), collectively known as _operations_. Queries are used to read an entity, while actions are used to create, modify, and delete entities. Since we want to list the tasks, we'll want to use a query. @@ -225,14 +225,14 @@ Most of this code is regular React, the only exception being the two< - `import getTasks from '@wasp/queries/getTasks'` - Imports the client-side query function. -- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](/docs/data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. +- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](../data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. - `import getTasks from '@wasp/queries/getTasks'` - Imports the client-side query function. -- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](/docs/data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. +- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](../data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. - `import { Task } from '@wasp/entities'` - The type for the task entity we defined in `main.wasp`. Notice how you don't need to annotate the type of the query's return value: Wasp uses the types you defined while implementing the query for the generated client-side function. This is **full-stack type safety**: the types on the client always match the types on the server. diff --git a/web/docs/tutorial/07-auth.md b/web/docs/tutorial/07-auth.md index 8e7ebde46..2efeb6d3d 100644 --- a/web/docs/tutorial/07-auth.md +++ b/web/docs/tutorial/07-auth.md @@ -39,7 +39,7 @@ wasp db migrate-dev ## Adding Auth to the Project -Next, we want to tell Wasp that we want to use full-stack [authentication](/docs/auth/overview) in our app: +Next, we want to tell Wasp that we want to use full-stack [authentication](../auth/overview) in our app: ```wasp {7-16} title="main.wasp" app TodoApp { @@ -65,13 +65,13 @@ app TodoApp { By doing this, Wasp will create: -- [Auth UI](/docs/auth/ui) with login and signup forms. +- [Auth UI](../auth/ui) with login and signup forms. - A `logout()` action. - A React hook `useAuth()`. - `context.user` for use in Queries and Actions. :::info -Wasp also supports authentication using [Google](/docs/auth/social-auth/google), [GitHub](/docs/auth/social-auth/github), and [email](/docs/auth/email), with more on the way! +Wasp also supports authentication using [Google](../auth/social-auth/google), [GitHub](../auth/social-auth/github), and [email](../auth/email), with more on the way! ::: ## Adding Login and Signup Pages @@ -216,7 +216,7 @@ export default SignupPage :::tip Type-safe links -Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](/docs/advanced/links) for more details. +Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](../advanced/links) for more details. ::: @@ -515,8 +515,8 @@ You should be ready to learn about more complicated features and go more in-dept Looking for inspiration? -- Get a jump start on your next project with [Starter Templates](/docs/project/starter-templates) -- Make a real-time app with [Web Sockets](/docs/advanced/web-sockets) +- Get a jump start on your next project with [Starter Templates](../project/starter-templates) +- Make a real-time app with [Web Sockets](../advanced/web-sockets) :::note If you notice that some of the features you'd like to have are missing, or have any other kind of feedback, please write to us on [Discord](https://discord.gg/rzdnErX) or create an issue on [Github](https://github.com/wasp-lang/wasp), so we can learn which features to add/improve next πŸ™ diff --git a/web/docs/typescript.md b/web/docs/typescript.md deleted file mode 100644 index 6f04d7277..000000000 --- a/web/docs/typescript.md +++ /dev/null @@ -1,521 +0,0 @@ ---- -title: TypeScript Support ---- - -import OldDocsNote from '@site/docs/OldDocsNote' - -# Using Wasp with TypeScript - - - -TypeScript is a programming language that brings static type analysis to JavaScript. It is a superset of JavaScript (i.e., all valid JavaScript programs are valid TypeScript programs) and compiles to JavaScript before running. TypeScript's type system detects common errors at build time (reducing the chance of runtime errors in production) and enables type-based auto-completion in IDEs. - -This document assumes you are familiar with TypeScript and primarily focuses on how to use it with Wasp. To learn more about TypeScript itself, we recommend reading [the official docs](https://www.typescriptlang.org/docs/). - -The document also assumes a basic understanding of core Wasp features (e.g., Queries, Actions, Entities). You can read more about these features in [our feature docs](/docs/language/features). - -Besides allowing you to write your code in TypeScript, Wasp also supports: - -- Importing and using Wasp Entity types (on both the server and the client). -- Automatic full-stack type support for Queries and Actions - frontend types are automatically inferred from backend definitions. -- Type-safe generic hooks (`useQuery` and `useAction`) with the accompanying type inference. -- Type-safe optimistic update definitions. - -We'll dig into the details of each feature in the following sections. But first, let's see how you can introduce TypeScript to an existing Wasp project. - -:::info -To get the best IDE experience, make sure to leave `wasp start` running in the background. Wasp will track the working directory and ensure the generated code/types are up to date with your changes. - -Your editor may sometimes report type and import errors even while `wasp start` is running. This happens when the TypeScript Language Server gets out of sync with the current code. If you're using VS Code, you can manually restart the language server by opening the command palette and selecting _"TypeScript: Restart TS Server."_ -::: - -## Migrating your project to TypeScript - -Wasp supports TypeScript out of the box! - -Our scaffolding already includes TypeScript, so migrating your project to TypeScript is as simple as changing file extensions and using the language. This approach allows you to gradually migrate your project on a file-by-file basis. - -### Example - -Let's first assume your Wasp file contains the following definitions: - -```wasp title=main.wasp -entity Task {=psl - id Int @id @default(autoincrement()) - description String - isDone Boolean @default(false) -psl=} - -query getTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Let's now assume that your `queries.js` file looks something like this: - -```javascript title="src/server/queries.js" -import HttpError from "@wasp/core/HttpError.js" - -function getInfoMessage(task) { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` -To migrate this file to TypeScript, all you have to do is: - -1. Change the filename from `queries.js` to `queries.ts`. -2. Write some types. - -Let's start by only providing a basic `getInfoMessage` function. We'll see how to properly type the rest of the file in the following sections. - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" - -// highlight-next-line -function getInfoMessage(task: { - isDone: boolean - description: string -}): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` - -You don't need to change anything inside the `.wasp` file. -:::caution - - - -Even when you use TypeScript, and your file is called `queries.ts`, you still need to import it using the `.js` extension: - -```wasp -query getTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp internally uses `esnext` module resolution, which always requires specifying the extension as `.js` (i.e., the extension used in the emitted JS file). This applies to all `@server` imports (and files on the server in general). This quirk does not apply to client files (the transpiler takes care of it). - -Read more about ES modules in TypeScript [here](https://www.typescriptlang.org/docs/handbook/esm-node.html). If you're interested in the discussion and the reasoning behind this, read about it [in this GitHub issue](https://github.com/microsoft/TypeScript/issues/33588). -::: - -## Entity Types - -Instead of manually specifying the types for `isDone` and `description`, we can get them from the `Task` entity type. Wasp will generate types for all entities and let you import them from `"@wasp/entities"`: - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" -// highlight-next-line -import { Task } from "@wasp/entities" - -// highlight-next-line -function getInfoMessage(task: Pick): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` - -By doing this, we've connected the argument type of the `getInfoMessage` function with the `Task` entity. This coupling removes duplication and ensures the function keeps the correct signature even if we change the entity. Of course, the function might throw type errors depending on how we change the entity, but that's precisely what we want! - -Don't worry about typing the query function for now. We'll see how to handle this in the next section. - -Entity types are also available on the client under the same import: - -```tsx title=src/client/Main.jsx -import { Task } from "@wasp/entities" - -export function ExamplePage() {} - const task: Task = { - id: 123, - description: "Some random task", - isDone: false, - } - return
{task.description}
-} - -``` - -The mentioned type safety mechanisms also apply here: changing the task entity in our `.wasp` file changes the imported type, which might throw a type error and warn us that our task definition is outdated. - -## Backend type support for Queries and Actions - -Wasp automatically generates the appropriate types for all Operations (i.e., Actions and Queries) you define inside your `.wasp` file. Assuming your `.wasp` file contains the following definition: - -```wasp title=main.wasp -// ... - -query GetTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp will generate a type called `GetTaskInfo`, which you can use to type the Query's implementation. By assigning the `GetTaskInfo` type to your function, you get the type information for its context. In this case, TypeScript will know the `context.entities` object must include the `Task` entity. If the Query had auth enabled, it would also know that `context` includes user information. - -`GetTaskInfo` can is a generic type that takes two (optional) type arguments: - -1. `Input` - The argument (i.e., payload) received by the query function. -2. `Output` - The query function's return type. - -Suppose you don't care about typing the Query's inputs and outputs. In that case, you can omit both type arguments, and TypeScript will infer the most general types (i.e., `never` for the input, `unknown` for the output.). - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" -import { Task } from "@wasp/entities" -// highlight-next-line -import { GetTaskInfo } from "@wasp/queries/types" - -function getInfoMessage(task: Pick): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -// Use the type parameters to specify the Query's argument and return types. -// highlight-next-line -export const getTaskInfo: GetTaskInfo, string> = async ({ id }, context) => { - // Thanks to the definition in your .wasp file, the compiler knows the type of - // `context` (and that it contains the `Task` entity). - const Task = context.entities.Task - - // Thanks to the first type argument in `GetTaskInfo`, the compiler knows `args` - // is of type `Pick`. - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - - // Thanks to the second type argument in `GetTaskInfo`, the compiler knows the - // function must return a value of type `string`. - return getInfoMessage(task) -} -``` -Everything described above applies to Actions as well. -:::tip - -If don't want to define a new type for the Query's return value, the new `satisfies` keyword will allow TypeScript to infer it automatically: -```typescript -const getFoo = (async (_args, context) => { - const foos = await context.entities.Foo.findMany() - return { - foos, - message: "Here are some foos!", - queriedAt: new Date(), - } -}) satisfies GetFoo -``` -From the snippet above, TypeScript knows: -1. The correct type for `context`. -2. The Query's return type is `{ foos: Foo[], message: string, queriedAt: Date }`. - -If you don't need the context, you can skip specifying the Query's type (and arguments): -```typescript -const getFoo = () => {{ name: 'Foo', date: new Date() }} -``` - -::: - -## Frontend type support for Queries and Actions - -Wasp supports automatic full-stack type safety Γ  la tRPC. You only need to define the Operation's type on the backend, and the frontend will automatically know how to call it. - -### Frontend type support for Queries -The examples assume you've defined the Query `getTaskInfo` from the previous sections: - -```typescript title="src/server/queries.ts" -export const getTaskInfo: GetTaskInfo, string> = - async ({ id }, context) => { - // ... - } -``` - -Wasp will use the type of `getTaskInfo` to infer the Query's types on the frontend: - -```tsx title="src/client/TaskInfo.tsx" -import { useQuery } from "@wasp/queries" -// Wasp knows the type of `getTaskInfo` thanks to your backend definition. -// highlight-next-line -import getTaskInfo from "@wasp/queries/getTaskInfo" - -export const TaskInfo = () => { - const { - // TypeScript knows `taskInfo` is a `string | undefined` thanks to the - // backend definition. - data: taskInfo, - // TypeScript also knows `isError` is a `boolean`. - isError, - // TypeScript knows `error` is of type `Error`. - error, - // TypeScript knows `id` must be a `Task["id"]` (i.e., a number) thanks to - // your backend definition. - // highlight-next-line - } = useQuery(getTaskInfo, { id: 1 }) - - if (isError) { - return
Error during fetching tasks: {error.message || "unknown"}
- } - - // TypeScript forces you to perform this check. - return taskInfo === undefined ? ( -
Waiting for info...
- ) : ( -
{taskInfo}
- ) -} -``` - -### Frontend type support for Actions - -Assuming the following action definition in your `.wasp` file - -```wasp title=main.wasp -action addTask { - fn: import { addTask } from "@server/actions.js" - entities: [Task] -} -``` - -And its corresponding implementation in `src/server/actions.ts`: - -```typescript title=src/server/actions.ts -import { AddTask } from "@wasp/actions/types" - -type TaskPayload = Pick - -const addTask: AddTask = async (args, context) => { - // ... -} -``` - -Here's how to use it on the frontend: -```tsx title=src/client/AddTask.tsx -import { useAction } from "@wasp/actions" -// TypeScript knows `addTask` is a function that expects a value of type -// `TaskPayload` and returns a value of type `Promise`. -import addTask from "@wasp/queries/addTask" - -const AddTask = ({ description }: Pick) => { - return ( -
- - -
- ) -} - -``` -#### Type support for the `useAction` hook -Type inference also works if you decide to use the action via the `useAction` hook: -```typescript -// addTaskFn is of type (args: TaskPayload) => Task -const addTaskFn = useAction(addTask) -``` - -The `useAction` hook also includes support for optimistic updates. Read [the feature docs](/docs/language/features#the-useaction-hook) to understand more about optimistic updates and how to define them in Wasp. - -Here's an example that shows how you can use static type checking in their definitions (the example assumes an appropriate action defined in the `.wasp` file and implemented on the server): - -```tsx title=Task.tsx -import { useQuery } from "@wasp/queries" -import { OptimisticUpdateDefinition, useAction } from "@wasp/actions" -import updateTaskIsDone from "@wasp/actions/updateTaskIsDone" - -type TaskPayload = Pick - -const Task = ({ taskId }: Pick) => { - const updateTaskIsDoneOptimistically = useAction( - updateTaskIsDone, - { - optimisticUpdates: [ - { - getQuerySpecifier: () => [getTask, { id: taskId }], - // This query's cache should should never be empty - updateQuery: ({ isDone }, oldTask) => ({ ...oldTask!, isDone }), - // highlight-next-line - } as OptimisticUpdateDefinition, - { - getQuerySpecifier: () => [getTasks], - updateQuery: (updatedTask, oldTasks) => - oldTasks && - oldTasks.map((task) => - task.id === updatedTask.id ? { ...task, ...updatedTask } : task - ), - // highlight-next-line - } as OptimisticUpdateDefinition, - ], - } - ) - // ... -} -``` - -## Database seeding - -When implementing a seed function in TypeScript, you can import a `DbSeedFn` type via - -```ts -import type { DbSeedFn } from "@wasp/dbSeed/types.js" -``` - -and use it to type your seed function like this: - -```ts -export const devSeedSimple: DbSeedFn = async (prismaClient) => { ... } -``` - -## CRUD operations on entities - -For a specific [Entity](/docs/language/features#entity), you can tell Wasp to automatically instantiate server-side logic ([Queries](/docs/language/features#query) and [Actions](/docs/language/features#action)) for creating, reading, updating and deleting such entities. - -Read more about CRUD operations in Wasp [here](/docs/language/features#crud-operations). - -### Using types for CRUD operations overrides - -If you writing the override implementation in Typescript, you'll have access to generated types. The overrides are functions that take the following arguments: -- `args` - The arguments of the operation i.e. the data that's sent from the client. -- `context` - Context containing the `user` making the request and the `entities` object containing the entity that's being operated on. - -You can types for each of the functions you want to override from `@wasp/crud/{crud name}`. The types that are available are: -- `GetAllQuery` -- `GetQuery` -- `CreateAction` -- `UpdateAction` -- `DeleteAction` - -If you have a CRUD named `Tasks`, you would import the types like this: -```ts -import type { GetAllQuery, GetQuery, CreateAction, UpdateAction, DeleteAction } from '@wasp/crud/Tasks' - -// Each of the types is a generic type, so you can use it like this: -export const getAllOverride: GetAllQuery = async (args, context) => { - // ... -} -``` - -## WebSocket full-stack type support - - -Defining event names with the matching payload types on the server makes those types exposed automatically on the client. This helps you avoid mistakes when emitting events or handling them. - -### Defining the events handler -On the server, you will get Socket.IO `io: Server` argument and `context` for your WebSocket function, which contains all entities you defined in your Wasp app. You can type the `webSocketFn` function like this: - -```ts title=src/server/webSocket.ts -import type { WebSocketDefinition, WaspSocketData } from '@wasp/webSocket' - -// Using the generic WebSocketDefinition type to define the WebSocket function. -type WebSocketFn = WebSocketDefinition< - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData -> - -interface ServerToClientEvents { - // The type for the payload of the "chatMessage" event. - chatMessage: (msg: { id: string, username: string, text: string }) => void; -} - -interface ClientToServerEvents { - // The type for the payload of the "chatMessage" event. - chatMessage: (msg: string) => void; -} - -interface InterServerEvents {} - -interface SocketData extends WaspSocketData {} - -// Use the WebSocketFn to type the webSocketFn function. -export const webSocketFn: WebSocketFn = (io, context) => { - io.on('connection', (socket) => { - socket.on('chatMessage', async (msg) => { - io.emit('chatMessage', { ... }) - }) - }) -} -``` - -### Using the WebSocket on the client - -After you have defined the WebSocket function on the server, you can use it on the client. The `useSocket` hook will give you the `socket` instance and the `isConnected` boolean. The `socket` instance is typed with the types you defined on the server. - -The `useSocketListener` hook will give you a type-safe event handler. The event name and its payload type are defined on the server. - -You can additonally use the `ClientToServerPayload` and `ServerToClientPayload` helper types to get the payload type for a specific event. - -```tsx title=src/client/ChatPage.tsx -import React, { useState } from 'react' -import { - useSocket, - useSocketListener, - ServerToClientPayload, - ClientToServerPayload, -} from '@wasp/webSocket' - -export const ChatPage = () => { - const [messageText, setMessageText] = useState< - // We are using a helper type to get the payload type for the "chatMessage" event. - ClientToServerPayload<'chatMessage'> - >('') - - const [messages, setMessages] = useState< - // We are using a helper type to get the payload type for the "chatMessage" event. - ServerToClientPayload<'chatMessage'>[] - >([]) - - // The "socket" instance is typed with the types you defined on the server. - const { socket, isConnected } = useSocket() - - // This is a type-safe event handler: "chatMessage" event and its payload type - // are defined on the server. - useSocketListener('chatMessage', logMessage) - - function logMessage(msg: ServerToClientPayload<'chatMessage'>) { - // ... - } - - function handleSubmit(e: React.FormEvent) { - e.preventDefault() - // This is a type-safe event emitter: "chatMessage" event and its payload type - // are defined on the server. - socket.emit('chatMessage', messageText) - setMessageText('') - } - - return ( - ... - ) -} -``` \ No newline at end of file diff --git a/web/docusaurus.config.js b/web/docusaurus.config.js index 6e00afe39..985138f7b 100644 --- a/web/docusaurus.config.js +++ b/web/docusaurus.config.js @@ -105,11 +105,7 @@ module.exports = { { label: "Todo app tutorial", to: "docs/tutorial/create", - }, - { - label: "Reference", - to: "docs/language/features", - }, + } ], }, { diff --git a/web/sidebars.js b/web/sidebars.js index f2ae06c26..46cbc03ee 100644 --- a/web/sidebars.js +++ b/web/sidebars.js @@ -6,8 +6,8 @@ module.exports = { collapsed: false, collapsible: false, items: [ - 'introduction/what-is-wasp', - 'introduction/getting-started', + 'introduction/introduction', + 'introduction/quick-start', 'introduction/editor-setup', ], }, diff --git a/web/versioned_docs/version-0.11.8/OldDocsNote.tsx b/web/versioned_docs/version-0.11.8/OldDocsNote.tsx deleted file mode 100644 index 008035a5e..000000000 --- a/web/versioned_docs/version-0.11.8/OldDocsNote.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Admonition from "@theme/Admonition"; -import Link from "@docusaurus/Link"; -import React from "react"; - -export default function OldDocsNote() { - return ( -
- - This page is part of a previous documentation version and is no longer - actively maintained. The content is likely out of date and may no longer - be relevant to current releases. -
-
- Go to the current documentation for updated - content. -
-
- ); -} diff --git a/web/versioned_docs/version-0.11.8/advanced/apis.md b/web/versioned_docs/version-0.11.8/advanced/apis.md index 125499e34..f058b92b1 100644 --- a/web/versioned_docs/version-0.11.8/advanced/apis.md +++ b/web/versioned_docs/version-0.11.8/advanced/apis.md @@ -5,7 +5,7 @@ title: Custom HTTP API Endpoints import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers' import { Required } from '@site/src/components/Required' -In Wasp, the default client-server interaction mechanism is through [Operations](/docs/data-model/operations/overview). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`. Best of all, they should look and feel very familiar. +In Wasp, the default client-server interaction mechanism is through [Operations](../data-model/operations/overview). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`. Best of all, they should look and feel very familiar. ## How to Create an API @@ -231,11 +231,11 @@ export const apiMiddleware: MiddlewareConfigFn = (config) => { We are returning the default middleware which enables CORS for all APIs under the `/foo` path. -For more information about middleware configuration, please see: [Middleware Configuration](/docs/advanced/middleware-config) +For more information about middleware configuration, please see: [Middleware Configuration](../advanced/middleware-config) ## Using Entities in APIs -In many cases, resources used in APIs will be [Entities](/docs/data-model/entities.md). +In many cases, resources used in APIs will be [Entities](../data-model/entities.md). To use an Entity in your API, add it to the `api` declaration in Wasp: @@ -340,4 +340,4 @@ The `api` declaration has the following fields: - `middlewareConfigFn: ServerImport` - The import statement to an Express middleware config function for this API. See more in [middleware section](/docs/advanced/middleware-config) of the docs. \ No newline at end of file + The import statement to an Express middleware config function for this API. See more in [middleware section](../advanced/middleware-config) of the docs. \ No newline at end of file diff --git a/web/versioned_docs/version-0.11.8/advanced/deployment/_addExternalAuthEnvVarsReminder.md b/web/versioned_docs/version-0.11.8/advanced/deployment/_addExternalAuthEnvVarsReminder.md index 52e0b68a3..10a105318 100644 --- a/web/versioned_docs/version-0.11.8/advanced/deployment/_addExternalAuthEnvVarsReminder.md +++ b/web/versioned_docs/version-0.11.8/advanced/deployment/_addExternalAuthEnvVarsReminder.md @@ -1,4 +1,4 @@ :::tip Using an external auth method? -If your app is using an external authentication method(s) supported by Wasp (such as [Google](/docs/auth/social-auth/google#4-adding-environment-variables) or [GitHub](/docs/auth/social-auth/github#4-adding-environment-variables)), make sure to set the necessary environment variables. +If your app is using an external authentication method(s) supported by Wasp (such as [Google](../../auth/social-auth/google#4-adding-environment-variables) or [GitHub](../../auth/social-auth/github#4-adding-environment-variables)), make sure to set the necessary environment variables. ::: diff --git a/web/versioned_docs/version-0.11.8/advanced/deployment/manually.md b/web/versioned_docs/version-0.11.8/advanced/deployment/manually.md index e2d53e0f6..7767ab8a1 100644 --- a/web/versioned_docs/version-0.11.8/advanced/deployment/manually.md +++ b/web/versioned_docs/version-0.11.8/advanced/deployment/manually.md @@ -34,7 +34,7 @@ wasp build :::caution PostgreSQL in production You won't be able to build the app if you are using SQLite as a database (which is the default database). -You'll have to [switch to PostgreSQL](/docs/data-model/backends#migrating-from-sqlite-to-postgresql) before deploying to production. +You'll have to [switch to PostgreSQL](../../data-model/backends#migrating-from-sqlite-to-postgresql) before deploying to production. ::: ### 2. Deploying the API Server (backend) @@ -92,7 +92,7 @@ We'll cover a few different deployment providers below: ## Fly.io :::tip We automated this process for you -If you want to do all of the work below with one command, you can use the [Wasp CLI](/docs/advanced/deployment/cli#flyio). +If you want to do all of the work below with one command, you can use the [Wasp CLI](../../advanced/deployment/cli#flyio). Wasp CLI deploys the server, deploys the client, and sets up a database. It also gives you a way to redeploy (update) your app with a single command. @@ -547,7 +547,7 @@ heroku logs --tail --app :::note Using `pg-boss` with Heroku -If you wish to deploy an app leveraging [Jobs](/docs/advanced/jobs) that use `pg-boss` as the executor to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. +If you wish to deploy an app leveraging [Jobs](../../advanced/jobs) that use `pg-boss` as the executor to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. Read more: https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js ::: diff --git a/web/versioned_docs/version-0.11.8/advanced/deployment/overview.md b/web/versioned_docs/version-0.11.8/advanced/deployment/overview.md index 23f841bbf..c4750284e 100644 --- a/web/versioned_docs/version-0.11.8/advanced/deployment/overview.md +++ b/web/versioned_docs/version-0.11.8/advanced/deployment/overview.md @@ -11,7 +11,7 @@ Wasp apps are full-stack apps that consist of: You can deploy each part **anywhere** where you can usually deploy Node.js apps or static apps. For example, you can deploy your client on [Netlify](https://www.netlify.com/), the server on [Fly.io](https://fly.io/), and the database on [Neon](https://neon.tech/). -To make deploying as smooth as possible, Wasp also offers a single-command deployment through the **Wasp CLI**. Read more about deploying through the CLI [here](/docs/advanced/deployment/cli). +To make deploying as smooth as possible, Wasp also offers a single-command deployment through the **Wasp CLI**. Read more about deploying through the CLI [here](../../advanced/deployment/cli). diff --git a/web/versioned_docs/version-0.11.8/advanced/jobs.md b/web/versioned_docs/version-0.11.8/advanced/jobs.md index 3fe783a14..16889daca 100644 --- a/web/versioned_docs/version-0.11.8/advanced/jobs.md +++ b/web/versioned_docs/version-0.11.8/advanced/jobs.md @@ -94,7 +94,7 @@ Let's write an example Job that will print a message to the console and return a `MySpecialJob` is a generic type Wasp generates to help you correctly type the Job's worker function, ensuring type information about the function's arguments and return value. Read more about type-safe jobs in the [Javascript API section](#javascript-api).
-3. After successfully defining the job, you can submit work to be done in your [Operations](/docs/data-model/operations/overview) or [setupFn](/docs/project/server-config#setup-function) (or any other NodeJS code): +3. After successfully defining the job, you can submit work to be done in your [Operations](../data-model/operations/overview) or [setupFn](../project/server-config#setup-function) (or any other NodeJS code): @@ -333,7 +333,7 @@ The Job declaration has the following fields: - `entities: [Entity]` - A list of entities you wish to use inside your Job (similar to [Queries and Actions](/docs/data-model/operations/queries#using-entities-in-queries)). + A list of entities you wish to use inside your Job (similar to [Queries and Actions](../data-model/operations/queries#using-entities-in-queries)). ### JavaScript API diff --git a/web/versioned_docs/version-0.11.8/advanced/middleware-config.md b/web/versioned_docs/version-0.11.8/advanced/middleware-config.md index fe2ce9f0b..859df3d6d 100644 --- a/web/versioned_docs/version-0.11.8/advanced/middleware-config.md +++ b/web/versioned_docs/version-0.11.8/advanced/middleware-config.md @@ -19,7 +19,7 @@ Wasp's Express server has the following middleware by default: - [express.json](https://expressjs.com/en/api.html#express.json) (which uses [body-parser](https://github.com/expressjs/body-parser#bodyparserjsonoptions)): parses incoming request bodies in a middleware before your handlers, making the result available under the `req.body` property. :::note - JSON middlware is required for [Operations](/docs/data-model/operations/overview) to function properly. + JSON middlware is required for [Operations](../data-model/operations/overview) to function properly. ::: - [express.urlencoded](https://expressjs.com/en/api.html#express.urlencoded) (which uses [body-parser](https://expressjs.com/en/resources/middleware/body-parser.html#bodyparserurlencodedoptions)): returns middleware that only parses urlencoded bodies and only looks at requests where the `Content-Type` header matches the type option. - [cookieParser](https://github.com/expressjs/cookie-parser#readme): parses Cookie header and populates `req.cookies` with an object keyed by the cookie names. diff --git a/web/versioned_docs/version-0.11.8/auth/email.md b/web/versioned_docs/version-0.11.8/auth/email.md index 0916966d7..22fdaad48 100644 --- a/web/versioned_docs/version-0.11.8/auth/email.md +++ b/web/versioned_docs/version-0.11.8/auth/email.md @@ -240,7 +240,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 4. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -418,7 +418,7 @@ export function Layout({ children }: { children: React.ReactNode }) { -We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](../auth/ui). ### 5. Set up an Email Sender @@ -461,15 +461,15 @@ app myApp { SENDGRID_API_KEY= ``` -If you are not sure how to get a SendGrid API key, read more [here](/docs/advanced/email#getting-the-api-key). +If you are not sure how to get a SendGrid API key, read more [here](../advanced/email#getting-the-api-key). -Read more about setting up email senders in the [sending emails docs](/docs/advanced/email). +Read more about setting up email senders in the [sending emails docs](../advanced/email). ### Conclusion That's it! We have set up email authentication in our app. πŸŽ‰ -Running `wasp db migrate-dev` and then `wasp start` should give you a working app with email authentication. If you want to put some of the pages behind authentication, read the [using auth docs](/docs/auth/overview). +Running `wasp db migrate-dev` and then `wasp start` should give you a working app with email authentication. If you want to put some of the pages behind authentication, read the [using auth docs](../auth/overview). ## Login and Signup Flows @@ -500,7 +500,7 @@ Some of the behavior you get out of the box: 4. Password validation - Read more about the default password validation rules and how to override them in [using auth docs](/docs/auth/overview). + Read more about the default password validation rules and how to override them in [using auth docs](../auth/overview). ## Email Verification Flow @@ -597,7 +597,7 @@ The content of the e-mail can be customized, read more about it [here](#password ## Using The Auth -To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](/docs/auth/overview). +To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](../auth/overview). ## API Reference diff --git a/web/versioned_docs/version-0.11.8/auth/overview.md b/web/versioned_docs/version-0.11.8/auth/overview.md index c9b21ce0f..dfd660c63 100644 --- a/web/versioned_docs/version-0.11.8/auth/overview.md +++ b/web/versioned_docs/version-0.11.8/auth/overview.md @@ -78,11 +78,11 @@ Wasp supports the following auth methods: -Let's say we enabled the [Username & password](/docs/auth/username-and-pass) authentication. +Let's say we enabled the [Username & password](../auth/username-and-pass) authentication. -We get an auth backend with signup and login endpoints. We also get the `user` object in our [Operations](/docs/data-model/operations/overview) and we can decide what to do based on whether the user is logged in or not. +We get an auth backend with signup and login endpoints. We also get the `user` object in our [Operations](../data-model/operations/overview) and we can decide what to do based on whether the user is logged in or not. -We would also get the [Auth UI](/docs/auth/ui) generated for us. We can set up our login and signup pages where our users can **create their account** and **login**. We can then protect certain pages by setting `authRequired: true` for them. This will make sure that only logged-in users can access them. +We would also get the [Auth UI](../auth/ui) generated for us. We can set up our login and signup pages where our users can **create their account** and **login**. We can then protect certain pages by setting `authRequired: true` for them. This will make sure that only logged-in users can access them. We will also have access to the `user` object in our frontend code, so we can show different UI to logged-in and logged-out users. For example, we can show the user's name in the header alongside a **logout button** or a login button if the user is not logged in. @@ -301,7 +301,7 @@ Since the `user` prop is only available in a page's React component: use the `us #### Using the `context.user` object -When authentication is enabled, all [queries and actions](/docs/data-model/operations/overview) have access to the `user` object through the `context` argument. `context.user` contains all User entity's fields, except for the password. +When authentication is enabled, all [queries and actions](../data-model/operations/overview) have access to the `user` object through the `context` argument. `context.user` contains all User entity's fields, except for the password. @@ -361,7 +361,7 @@ export const createTask: CreateTask = async ( To implement access control in your app, each operation must check `context.user` and decide what to do. For example, if `context.user` is `undefined` inside a private operation, the user's access should be denied. -When using WebSockets, the `user` object is also available on the `socket.data` object. Read more in the [WebSockets section](/docs/advanced/web-sockets#websocketfn-function). +When using WebSockets, the `user` object is also available on the `socket.data` object. Read more in the [WebSockets section](../advanced/web-sockets#websocketfn-function). ## User entity @@ -419,7 +419,7 @@ Default validations depend on the auth method you use. #### Username & password -If you use [Username & password](/docs/auth/username-and-pass) authentication, the default validations are: +If you use [Username & password](../auth/username-and-pass) authentication, the default validations are: - The `username` must not be empty - The `password` must not be empty, have at least 8 characters, and contain a number @@ -428,7 +428,7 @@ Note that `username`s are stored in a **case-sensitive** manner. #### Email -If you use [Email](/docs/auth/email) authentication, the default validations are: +If you use [Email](../auth/email) authentication, the default validations are: - The `email` must not be empty and a valid email address - The `password` must not be empty, have at least 8 characters, and contain a number @@ -726,7 +726,7 @@ Now that we defined the fields, Wasp knows how to: 1. Validate the data sent from the client 2. Save the data to the database -Next, let's see how to customize [Auth UI](/docs/auth/ui) to include those fields. +Next, let's see how to customize [Auth UI](../auth/ui) to include those fields. ### 2. Customizing the Signup Component @@ -736,8 +736,8 @@ If you are not using Wasp's Auth UI, you can skip this section. Just make sure t Read more about using the signup actions for: -- email auth [here](/docs/auth/email#fields-in-the-email-dict) -- username & password auth [here](/docs/auth/username-and-pass#customizing-the-auth-flow) +- email auth [here](../auth/email#fields-in-the-email-dict) +- username & password auth [here](../auth/username-and-pass#customizing-the-auth-flow) ::: If you are using Wasp's Auth UI, you can customize the `SignupForm` component by passing the `additionalFields` prop to it. It can be either a list of extra fields or a render function. @@ -1046,7 +1046,7 @@ psl=} The same `externalAuthEntity` can be used across different social login providers (e.g., both GitHub and Google can use the same entity). ::: -See [Google docs](/docs/auth/social-auth/google) and [GitHub docs](/docs/auth/social-auth/github) for more details. +See [Google docs](../auth/social-auth/google) and [GitHub docs](../auth/social-auth/github) for more details. #### `methods: dict` @@ -1057,7 +1057,7 @@ A dictionary of auth methods enabled for the app. #### `onAuthFailedRedirectTo: String` The route to which Wasp should redirect unauthenticated user when they try to access a private page (i.e., a page that has `authRequired: true`). -Check out these [essentials docs on auth](/docs/tutorial/auth#adding-auth-to-the-project) to see an example of usage. +Check out these [essentials docs on auth](../tutorial/auth#adding-auth-to-the-project) to see an example of usage. #### `onAuthSucceededRedirectTo: String` @@ -1065,7 +1065,7 @@ The route to which Wasp will send a successfully authenticated after a successfu The default value is `"/"`. :::note -Automatic redirect on successful login only works when using the Wasp-provided [Auth UI](/docs/auth/ui). +Automatic redirect on successful login only works when using the Wasp-provided [Auth UI](../auth/ui). ::: #### `signup: SignupOptions` diff --git a/web/versioned_docs/version-0.11.8/auth/social-auth/_api-reference-intro.md b/web/versioned_docs/version-0.11.8/auth/social-auth/_api-reference-intro.md index 3fb34065d..ed977b84b 100644 --- a/web/versioned_docs/version-0.11.8/auth/social-auth/_api-reference-intro.md +++ b/web/versioned_docs/version-0.11.8/auth/social-auth/_api-reference-intro.md @@ -5,6 +5,6 @@ Provider-specific behavior comes down to implementing two functions. The reference shows how to define both. -For behavior common to all providers, check the general [API Reference](/docs/auth/overview.md#api-reference). +For behavior common to all providers, check the general [API Reference](../../auth/overview.md#api-reference). diff --git a/web/versioned_docs/version-0.11.8/auth/social-auth/_using-auth-note.md b/web/versioned_docs/version-0.11.8/auth/social-auth/_using-auth-note.md index 8ddc13058..7fe740be1 100644 --- a/web/versioned_docs/version-0.11.8/auth/social-auth/_using-auth-note.md +++ b/web/versioned_docs/version-0.11.8/auth/social-auth/_using-auth-note.md @@ -1,3 +1,3 @@ -To read more about how to set up the logout button and get access to the logged-in user in both client and server code, read the docs on [using auth](/docs/auth/overview). +To read more about how to set up the logout button and get access to the logged-in user in both client and server code, read the docs on [using auth](../../auth/overview). diff --git a/web/versioned_docs/version-0.11.8/auth/social-auth/github.md b/web/versioned_docs/version-0.11.8/auth/social-auth/github.md index 67c067688..5af5c7018 100644 --- a/web/versioned_docs/version-0.11.8/auth/social-auth/github.md +++ b/web/versioned_docs/version-0.11.8/auth/social-auth/github.md @@ -159,7 +159,7 @@ psl=} -`externalAuthEntity` and `userEntity` are explained in [the social auth overview](/docs/auth/social-auth/overview#social-login-entity). +`externalAuthEntity` and `userEntity` are explained in [the social auth overview](../../auth/social-auth/overview#social-login-entity). ### 3. Creating a GitHub OAuth App @@ -231,7 +231,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 6. Creating the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -295,7 +295,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
-We imported the generated Auth UI component and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI component and used them in our pages. Read more about the Auth UI components [here](../../auth/ui). ### Conclusion @@ -304,7 +304,7 @@ Yay, we've successfully set up Github Auth! πŸŽ‰ ![Github Auth](/img/auth/github.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](/docs/auth/overview). +To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview). ## Default Behaviour diff --git a/web/versioned_docs/version-0.11.8/auth/social-auth/google.md b/web/versioned_docs/version-0.11.8/auth/social-auth/google.md index 0d38c1d83..d49750bde 100644 --- a/web/versioned_docs/version-0.11.8/auth/social-auth/google.md +++ b/web/versioned_docs/version-0.11.8/auth/social-auth/google.md @@ -96,7 +96,7 @@ app myApp {
-`externalAuthEntity` and `userEntity` are explained in [the social auth overview](/docs/auth/social-auth/overview#social-login-entity). +`externalAuthEntity` and `userEntity` are explained in [the social auth overview](../../auth/social-auth/overview#social-login-entity). ### 2. Adding the Entities @@ -271,7 +271,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 6. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../../project/css-frameworks). ::: Let's now create a `auth.{jsx,tsx}` file in the `client/pages`. @@ -337,7 +337,7 @@ export function Layout({ children }: { children: React.ReactNode }) { :::info Auth UI -Our pages use an automatically-generated Auth UI component. Read more about Auth UI components [here](/docs/auth/ui). +Our pages use an automatically-generated Auth UI component. Read more about Auth UI components [here](../../auth/ui). ::: ### Conclusion @@ -347,7 +347,7 @@ Yay, we've successfully set up Google Auth! πŸŽ‰ ![Google Auth](/img/auth/google.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](/docs/auth/overview). +To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview). ## Default Behaviour diff --git a/web/versioned_docs/version-0.11.8/auth/social-auth/overview.md b/web/versioned_docs/version-0.11.8/auth/social-auth/overview.md index 9a89101e5..9faaa3543 100644 --- a/web/versioned_docs/version-0.11.8/auth/social-auth/overview.md +++ b/web/versioned_docs/version-0.11.8/auth/social-auth/overview.md @@ -258,7 +258,7 @@ export const getUserFields: GetUserFieldsFn = async (_context, _args) => { #### 3. Showing the Correct State on the Client -You can query the user's `isSignupComplete` flag on the client with the [`useAuth()`](/docs/auth/overview) hook. +You can query the user's `isSignupComplete` flag on the client with the [`useAuth()`](../../auth/overview) hook. Depending on the flag's value, you can redirect users to the appropriate signup step. For example: @@ -317,7 +317,7 @@ Each provider has their own rules for defining the `getUserFieldsFn` and `config ## UI Helpers :::tip Use Auth UI -[Auth UI](/docs/auth/ui) is a common name for all high-level auth forms that come with Wasp. +[Auth UI](../../auth/ui) is a common name for all high-level auth forms that come with Wasp. These include fully functional auto-generated login and signup forms with working social login buttons. If you're looking for the fastest way to get your auth up and running, that's where you should look. diff --git a/web/versioned_docs/version-0.11.8/auth/ui.md b/web/versioned_docs/version-0.11.8/auth/ui.md index 2070a9ed3..d1fae262d 100644 --- a/web/versioned_docs/version-0.11.8/auth/ui.md +++ b/web/versioned_docs/version-0.11.8/auth/ui.md @@ -218,7 +218,7 @@ export function SignupPage() { It will automatically show the correct authentication providers based on your `main.wasp` file. -Read more about customizing the signup process like adding additional fields or extra UI in the [Using Auth](/docs/auth/overview#customizing-the-signup-process) section. +Read more about customizing the signup process like adding additional fields or extra UI in the [Using Auth](../auth/overview#customizing-the-signup-process) section. ### Forgot Password Form diff --git a/web/versioned_docs/version-0.11.8/auth/username-and-pass.md b/web/versioned_docs/version-0.11.8/auth/username-and-pass.md index c2422f4de..fbb65a530 100644 --- a/web/versioned_docs/version-0.11.8/auth/username-and-pass.md +++ b/web/versioned_docs/version-0.11.8/auth/username-and-pass.md @@ -157,7 +157,7 @@ We'll define the React components for these pages in the `client/pages/auth.{jsx ### 4. Create the Client Pages :::info -We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](/docs/project/css-frameworks). +We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../project/css-frameworks). ::: Let's create a `auth.{jsx,tsx}` file in the `client/pages` folder and add the following to it: @@ -255,19 +255,19 @@ export function Layout({ children }: { children: React.ReactNode }) { -We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](/docs/auth/ui). +We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](../auth/ui). ### Conclusion That's it! We have set up username authentication in our app. πŸŽ‰ -Running `wasp db migrate-dev` and then `wasp start` should give you a working app with username authentication. If you want to put some of the pages behind authentication, read the [using auth docs](/docs/auth/overview). +Running `wasp db migrate-dev` and then `wasp start` should give you a working app with username authentication. If you want to put some of the pages behind authentication, read the [using auth docs](../auth/overview). ## Customizing the Auth Flow The login and signup flows are pretty standard: they allow the user to sign up and then log in with their username and password. The signup flow validates the username and password and then creates a new user entity in the database. -Read more about the default username and password validation rules and how to override them in the [using auth docs](/docs/auth/overview). +Read more about the default username and password validation rules and how to override them in the [using auth docs](../auth/overview). If you require more control in your authentication flow, you can achieve that in the following ways: 1. Create your UI and use `signup` and `login` actions. @@ -538,7 +538,7 @@ export const signUp: SignupUser = async (args, context) => ## Using Auth -To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](/docs/auth/overview). +To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [using auth docs](../auth/overview). ## API Reference @@ -649,4 +649,4 @@ app myApp { `usernameAndPassword` dict doesn't have any options at the moment. ::: -You can read about the rest of the `auth` options in the [using auth](/docs/auth/overview) section of the docs. +You can read about the rest of the `auth` options in the [using auth](../auth/overview) section of the docs. diff --git a/web/versioned_docs/version-0.11.8/data-model/backends.md b/web/versioned_docs/version-0.11.8/data-model/backends.md index acfb036a2..a06416a82 100644 --- a/web/versioned_docs/version-0.11.8/data-model/backends.md +++ b/web/versioned_docs/version-0.11.8/data-model/backends.md @@ -4,7 +4,7 @@ title: Databases import { Required } from '@site/src/components/Required' -[Entities](/docs/data-model/entities.md), [Operations](/docs/data-model/operations/overview) and [Automatic CRUD](/docs/data-model/crud.md) together make a high-level interface for working with your app's data. Still, all that data has to live somewhere, so let's see how Wasp deals with databases. +[Entities](../data-model/entities.md), [Operations](../data-model/operations/overview) and [Automatic CRUD](../data-model/crud.md) together make a high-level interface for working with your app's data. Still, all that data has to live somewhere, so let's see how Wasp deals with databases. ## Supported Database Backends @@ -77,7 +77,7 @@ Also, make sure that: If you want to spin up your own dev database (or connect to an external one), you can tell Wasp about it using the `DATABASE_URL` environment variable. Wasp will use the value of `DATABASE_URL` as a connection string. -The easiest way to set the necessary `DATABASE_URL` environment variable is by adding it to the [.env.server](/docs/project/env-vars) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). +The easiest way to set the necessary `DATABASE_URL` environment variable is by adding it to the [.env.server](../project/env-vars) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). Alternatively, you can set it inline when running `wasp` (this applies to all environment variables): diff --git a/web/versioned_docs/version-0.11.8/data-model/crud.md b/web/versioned_docs/version-0.11.8/data-model/crud.md index 884d2b25f..98927f5e0 100644 --- a/web/versioned_docs/version-0.11.8/data-model/crud.md +++ b/web/versioned_docs/version-0.11.8/data-model/crud.md @@ -10,7 +10,7 @@ If you have a lot of experience writing full-stack apps, you probably ended up d Wasp makes handling these boring bits easy by offering a higher-level concept called Automatic CRUD. -With a single declaration, you can tell Wasp to automatically generate server-side logic (i.e., Queries and Actions) for creating, reading, updating and deleting [Entities](/docs/data-model/entities). As you update definitions for your Entities, Wasp automatically regenerates the backend logic. +With a single declaration, you can tell Wasp to automatically generate server-side logic (i.e., Queries and Actions) for creating, reading, updating and deleting [Entities](../data-model/entities). As you update definitions for your Entities, Wasp automatically regenerates the backend logic. :::caution Early preview This feature is currently in early preview and we are actively working on it. Read more about [our plans](#future-of-crud-operations-in-wasp) for CRUD operations. @@ -62,7 +62,7 @@ Keep reading for an example of Automatic CRUD in action, or skip ahead for the [ ## Example: A Simple TODO App -Let's create a full-app example that uses automatic CRUD. We'll stick to using the `Task` entity from the previous example, but we'll add a `User` entity and enable [username and password](/docs/auth/username-and-pass) based auth. +Let's create a full-app example that uses automatic CRUD. We'll stick to using the `Task` entity from the previous example, but we'll add a `User` entity and enable [username and password](../auth/username-and-pass) based auth. @@ -328,7 +328,7 @@ export const MainPage = () => { -And here are the login and signup pages, where we are using Wasp's [Auth UI](/docs/auth/ui) components: +And here are the login and signup pages, where we are using Wasp's [Auth UI](../auth/ui) components: @@ -692,7 +692,7 @@ export const getAllOverride: GetAllQuery = async ( -For a usage example, check the [example guide](/docs/data-model/crud#adding-crud-to-the-task-entity-). +For a usage example, check the [example guide](../data-model/crud#adding-crud-to-the-task-entity-). #### Using the CRUD operations in client code @@ -742,7 +742,7 @@ const deleteAction = Tasks.delete.useAction() -All CRUD operations are implemented with [Queries and Actions](/docs/data-model/operations/overview) under the hood, which means they come with all the features you'd expect (e.g., automatic SuperJSON serialization, full-stack type safety when using TypeScript) +All CRUD operations are implemented with [Queries and Actions](../data-model/operations/overview) under the hood, which means they come with all the features you'd expect (e.g., automatic SuperJSON serialization, full-stack type safety when using TypeScript) --- diff --git a/web/versioned_docs/version-0.11.8/data-model/entities.md b/web/versioned_docs/version-0.11.8/data-model/entities.md index 78a15c8ba..23843da17 100644 --- a/web/versioned_docs/version-0.11.8/data-model/entities.md +++ b/web/versioned_docs/version-0.11.8/data-model/entities.md @@ -62,11 +62,11 @@ Let's see how you can define and work with Wasp Entities: 1. Create/update some Entities in your `.wasp` file. 2. Run `wasp db migrate-dev`. This command syncs the database model with the Entity definitions in your `.wasp` file. It does this by creating migration scripts. 3. Migration scripts are automatically placed in the `migrations/` folder. Make sure to commit this folder into version control. -4. Use Wasp's JavasScript API to work with the database when implementing Operations (we'll cover this in detail when we talk about [operations](/docs/data-model/operations/overview)). +4. Use Wasp's JavasScript API to work with the database when implementing Operations (we'll cover this in detail when we talk about [operations](../data-model/operations/overview)). #### Using Entities in Operations -Most of the time, you will be working with Entities within the context of [Operations (Queries & Actions)](/docs/data-model/operations/overview). We'll see how that's done on the next page. +Most of the time, you will be working with Entities within the context of [Operations (Queries & Actions)](../data-model/operations/overview). We'll see how that's done on the next page. #### Using Entities directly diff --git a/web/versioned_docs/version-0.11.8/data-model/operations/actions.md b/web/versioned_docs/version-0.11.8/data-model/operations/actions.md index 13f9cffe9..7084548ea 100644 --- a/web/versioned_docs/version-0.11.8/data-model/operations/actions.md +++ b/web/versioned_docs/version-0.11.8/data-model/operations/actions.md @@ -8,7 +8,7 @@ import SuperjsonNote from './\_superjson-note.md'; We'll explain what Actions are and how to use them. If you're looking for a detailed API specification, skip ahead to the [API Reference](#api-reference). -Actions are quite similar to [Queries](/docs/data-model/operations/queries.md), but with a key distinction: Actions are designed to modify and add data, while Queries are solely for reading data. Examples of Actions include adding a comment to a blog post, liking a video, or updating a product's price. +Actions are quite similar to [Queries](../../data-model/operations/queries.md), but with a key distinction: Actions are designed to modify and add data, while Queries are solely for reading data. Examples of Actions include adding a comment to a blog post, liking a video, or updating a product's price. Actions and Queries work together to keep data caches up-to-date. @@ -373,7 +373,7 @@ export const createTask: CreateTask = async (args, context) => { ### Using Entities in Actions -In most cases, resources used in Actions will be [Entities](/docs/data-model/entities.md). +In most cases, resources used in Actions will be [Entities](../../data-model/entities.md). To use an Entity in your Action, add it to the `action` declaration in Wasp: @@ -626,7 +626,7 @@ Since both arguments are positional, you can name the parameters however you wan 2. `context` (type depends on the Action) - An additional context object **passed into the Action by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Actions](#using-entities-in-actions) to see how to use the entities field on the `context` object, or the [auth section](/docs/auth/overview#using-the-contextuser-object) to see how to use the `user` object. + An additional context object **passed into the Action by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Actions](#using-entities-in-actions) to see how to use the entities field on the `context` object, or the [auth section](../../auth/overview#using-the-contextuser-object) to see how to use the `user` object. @@ -704,7 +704,7 @@ In this case, the Action expects to receive an object with a `bar` field of type ### The `useAction` Hook and Optimistic Updates -Make sure you understand how [Queries](/docs/data-model/operations/queries.md) and [Cache Invalidation](#cache-invalidation) work before reading this chapter. +Make sure you understand how [Queries](../../data-model/operations/queries.md) and [Cache Invalidation](#cache-invalidation) work before reading this chapter. When using Actions in components, you can enhance them with the help of the `useAction` hook. This hook comes bundled with Wasp, and is used for decorating Wasp Actions. In other words, the hook returns a function whose API matches the original Action while also doing something extra under the hood (depending on how you configure it). diff --git a/web/versioned_docs/version-0.11.8/data-model/operations/overview.md b/web/versioned_docs/version-0.11.8/data-model/operations/overview.md index 42d933a5a..2f1a37567 100644 --- a/web/versioned_docs/version-0.11.8/data-model/operations/overview.md +++ b/web/versioned_docs/version-0.11.8/data-model/operations/overview.md @@ -6,7 +6,7 @@ import { Required } from '@site/src/components/Required'; While Entities enable help you define your app's data model and relationships, Operations are all about working with this data. -There are two kinds of Operations: [Queries](/docs/data-model/operations/queries.md) and [Actions](/docs/data-model/operations/actions.md). As their names suggest, +There are two kinds of Operations: [Queries](../../data-model/operations/queries.md) and [Actions](../../data-model/operations/actions.md). As their names suggest, Queries are meant for reading data, and Actions are meant for changing it (either by updating existing entries or creating new ones). Keep reading to find out all there is to know about Operations in Wasp. diff --git a/web/versioned_docs/version-0.11.8/data-model/operations/queries.md b/web/versioned_docs/version-0.11.8/data-model/operations/queries.md index 8d25f6aa6..6a70050ed 100644 --- a/web/versioned_docs/version-0.11.8/data-model/operations/queries.md +++ b/web/versioned_docs/version-0.11.8/data-model/operations/queries.md @@ -15,7 +15,7 @@ Fetching all comments on a blog post, a list of users that liked a video, inform Queries are fairly similar to Actions in terms of their API. Therefore, if you're already familiar with Actions, you might find reading the entire guide repetitive. -We instead recommend skipping ahead and only reading [the differences between Queries and Actions](/docs/data-model/operations/actions#differences-between-queries-and-actions), and consulting the [API Reference](#api-reference) as needed. +We instead recommend skipping ahead and only reading [the differences between Queries and Actions](../../data-model/operations/actions#differences-between-queries-and-actions), and consulting the [API Reference](#api-reference) as needed. ::: ## Working with Queries @@ -395,7 +395,7 @@ To prevent information leakage, the server won't forward these fields for any ot ### Using Entities in Queries -In most cases, resources used in Queries will be [Entities](/docs/data-model/entities.md). +In most cases, resources used in Queries will be [Entities](../../data-model/entities.md). To use an Entity in your Query, add it to the `query` declaration in Wasp: @@ -552,7 +552,7 @@ Since both arguments are positional, you can name the parameters however you wan 2. `context` (type depends on the Query) - An additional context object **passed into the Query by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Queries](#using-entities-in-queries) to see how to use the entities field on the `context` object, or the [auth section](/docs/auth/overview#using-the-contextuser-object) to see how to use the `user` object. + An additional context object **passed into the Query by Wasp**. This object contains user session information, as well as information about entities. Check the [section about using entities in Queries](#using-entities-in-queries) to see how to use the entities field on the `context` object, or the [auth section](../../auth/overview#using-the-contextuser-object) to see how to use the `user` object. @@ -651,6 +651,6 @@ Wasp's `useQuery` hook accepts three arguments: [the default behavior](https://react-query.tanstack.com/guides/important-defaults) for this particular Query. If you want to change the global defaults, you can do - so in the [client setup function](/docs/project/client-config.md#overriding-default-behaviour-for-queries). + so in the [client setup function](../../project/client-config.md#overriding-default-behaviour-for-queries). For an example of usage, check [this section](#the-usequery-hook). diff --git a/web/versioned_docs/version-0.11.8/examples.md b/web/versioned_docs/version-0.11.8/examples.md deleted file mode 100644 index 8bd07753c..000000000 --- a/web/versioned_docs/version-0.11.8/examples.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Examples ---- - -import useBaseUrl from '@docusaurus/useBaseUrl'; - -We have a constantly growing collection of fully-functioning example apps, which you can use to learn more about Wasp's features. - -The full list of examples can be found [here](https://github.com/wasp-lang/wasp/tree/release/examples/). Here is a few of them: - -## Todo App - - **Features**: Auth ([username/password](language/features#authentication--authorization)), [Queries & Actions](language/features#queries-and-actions-aka-operations), [Entities](language/features#entity), [Routes](language/features#route) - - JS source code: [GitHub](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/TodoApp) - - TS source code: [GitHub](https://github.com/wasp-lang/wasp/tree/release/examples/todo-typescript) - - in-browser dev environment: [GitPod](https://gitpod.io/#https://github.com/wasp-lang/gitpod-template) - -## Waspello (Trello Clone) - - **Features**: Auth ([Google](language/features#social-login-providers-oauth-20), [username/password](language/features#authentication--authorization)), [Optimistic Updates](language/features#the-useaction-hook), [Tailwind CSS integration](/docs/project/css-frameworks) - - Source code: [GitHub](https://github.com/wasp-lang/wasp/tree/main/examples/waspello) - - Hosted at [https://waspello-demo.netlify.app](https://waspello-demo.netlify.app/login) -

- -

- -## Waspleau (Realtime Statistics Dashboard) - - **Features**: Cron [Jobs](language/features#jobs), [Server Setup](language/features#server-configuration) - - Source code: [GitHub](https://github.com/wasp-lang/wasp/tree/main/examples/waspleau) - - Hosted at [https://waspleau-app-client.fly.dev/](https://waspleau-app-client.fly.dev/) -

- -

\ No newline at end of file diff --git a/web/versioned_docs/version-0.11.8/general/cli.md b/web/versioned_docs/version-0.11.8/general/cli.md index b478487a7..46414825f 100644 --- a/web/versioned_docs/version-0.11.8/general/cli.md +++ b/web/versioned_docs/version-0.11.8/general/cli.md @@ -5,7 +5,7 @@ This guide provides an overview of the Wasp CLI commands, arguments, and options ## Overview -Once [installed](/docs/quick-start), you can use the wasp command from your command line. +Once [installed](../quick-start), you can use the wasp command from your command line. If you run the `wasp` command without any arguments, it will show you a list of available commands and their descriptions: @@ -96,13 +96,13 @@ Newsletter: https://wasp-lang.dev/#signup Deleted .wasp/ directory. ``` - - `wasp build` generates the complete web app code, which is ready for [deployment](/docs/advanced/deployment/overview). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. + - `wasp build` generates the complete web app code, which is ready for [deployment](../advanced/deployment/overview). Use this command when you're deploying or ejecting. The generated code is stored in the `.wasp/build` folder. - `wasp deploy` makes it easy to get your app hosted on the web. Currently, Wasp offers support for [Fly.io](https://fly.io). If you prefer a different hosting provider, feel free to let us know on Discord or submit a PR by updating [this TypeScript app](https://github.com/wasp-lang/wasp/tree/main/waspc/packages/deploy). - Read more about automatic deployment [here](/docs/advanced/deployment/cli). + Read more about automatic deployment [here](../advanced/deployment/cli). - `wasp telemetry` displays the status of [telemetry](https://wasp-lang.dev/docs/telemetry). diff --git a/web/versioned_docs/version-0.11.8/introduction/editor-setup.md b/web/versioned_docs/version-0.11.8/introduction/editor-setup.md index 15e49ef01..f025eccd3 100644 --- a/web/versioned_docs/version-0.11.8/introduction/editor-setup.md +++ b/web/versioned_docs/version-0.11.8/introduction/editor-setup.md @@ -4,7 +4,7 @@ slug: /editor-setup --- :::note -This page assumes you have already installed Wasp. If you do not have Wasp installed yet, check out the [Quick Start](/docs/quick-start) guide. +This page assumes you have already installed Wasp. If you do not have Wasp installed yet, check out the [Quick Start](./quick-start.md) guide. ::: Wasp comes with the Wasp language server, which gives supported editors powerful support and integration with the language. diff --git a/web/docs/introduction/what-is-wasp.md b/web/versioned_docs/version-0.11.8/introduction/introduction.md similarity index 98% rename from web/docs/introduction/what-is-wasp.md rename to web/versioned_docs/version-0.11.8/introduction/introduction.md index 1d1f4778d..fa05b7082 100644 --- a/web/docs/introduction/what-is-wasp.md +++ b/web/versioned_docs/version-0.11.8/introduction/introduction.md @@ -6,7 +6,7 @@ slug: / import ImgWithCaption from '@site/blog/components/ImgWithCaption' :::note -If you are looking for the installation instructions, check out the [Quick Start](/docs/quick-start) section. +If you are looking for the installation instructions, check out the [Quick Start](./quick-start.md) section. ::: We will give a brief overview of what Wasp is, how it works on a high level and when to use it. @@ -170,7 +170,7 @@ export function HomePage({ user }: { user: User }) { And voila! We are listing all the recipes in our app πŸŽ‰ -This was just a quick example to give you a taste of what Wasp is. For step by step tour through the most important Wasp features, check out the [Todo app tutorial](/docs/tutorial/create). +This was just a quick example to give you a taste of what Wasp is. For step by step tour through the most important Wasp features, check out the [Todo app tutorial](../tutorial/01-create.md). :::note Above we skipped defining /login and /signup pages to keep the example a bit shorter, but those are very simple to do by using Wasp's Auth UI feature. diff --git a/web/docs/introduction/getting-started.md b/web/versioned_docs/version-0.11.8/introduction/quick-start.md similarity index 96% rename from web/docs/introduction/getting-started.md rename to web/versioned_docs/version-0.11.8/introduction/quick-start.md index 09364d647..6d382227a 100644 --- a/web/docs/introduction/getting-started.md +++ b/web/versioned_docs/version-0.11.8/introduction/quick-start.md @@ -43,8 +43,8 @@ Check [More Details](#more-details) section below if anything went wrong, or if ### What next? - - [ ] πŸ‘‰ **Check out the [Todo App tutorial](/docs/tutorial/create), which will take you through all the core features of Wasp!** πŸ‘ˆ - - [ ] [Setup your editor](/docs/editor-setup) for working with Wasp. + - [ ] πŸ‘‰ **Check out the [Todo App tutorial](../tutorial/01-create.md), which will take you through all the core features of Wasp!** πŸ‘ˆ + - [ ] [Setup your editor](./editor-setup.md) for working with Wasp. - [ ] Join us on [Discord](https://discord.gg/rzdnErX)! Any feedback or questions you have, we are there for you. - [ ] Follow Wasp development by subscribing to our newsletter: https://wasp-lang.dev/#signup . We usually send 1 per month, and Matija does his best to unleash his creativity to make them engaging and fun to read :D! diff --git a/web/versioned_docs/version-0.11.8/language/features.md b/web/versioned_docs/version-0.11.8/language/features.md deleted file mode 100644 index 43a2046cc..000000000 --- a/web/versioned_docs/version-0.11.8/language/features.md +++ /dev/null @@ -1,2372 +0,0 @@ ---- -title: Features ---- - -import SendingEmailsInDevelopment from '../_sendingEmailsInDevelopment.md' -import OldDocsNote from '@site/docs/OldDocsNote' - - - -## App - -There can be only one declaration of `app` type per Wasp project. -It serves as a starting point and defines global properties of your app. - -```wasp -app todoApp { - wasp: { - version: "^0.6.0" - }, - title: "ToDo App", - head: [ // optional - "" - ] -} -``` - -### Fields - -#### `wasp: dict` (required) -Wasp compiler configuration. It is a dictionary with a single field: -- `version: string` (required) - version declares the compatible Wasp versions for the app. It should contain a valid [SemVer range](https://github.com/npm/node-semver#ranges). - -:::info -For now, the version field only supports caret ranges (i.e., `^x.y.z`). Support for the full specification will come in a future version of Wasp -::: - -#### `title: string` (required) -Title of your app. It will be displayed in the browser tab, next to the favicon. - -#### `head: [string]` (optional) -Head of your HTML Document. Your app's metadata (styles, links, etc) can be added here. - -#### `auth: dict` (optional) -Authentication and authorization configuration. -Check [`app.auth`](/docs/language/features#authentication--authorization) for more details. - -#### `client: dict` (optional) -Client configuration. -Check [`app.client`](/docs/language/features#client-configuration) for more details. - -#### `server: dict` (optional) -Server configuration. -Check [`app.server`](/docs/language/features#server-configuration) for more details. - -#### `db: dict` (optional) -Database configuration. -Check [`app.db`](/docs/language/features#database-configuration) for more details. - -#### `dependencies: [(string, string)]` (optional) -List of dependencies (external libraries). -Check [`app.dependencies`](/docs/language/features#dependencies) for more details. - -#### `emailSender: dict` (optional) -Email sender configuration. -Check [`app.emailSender`](/docs/language/features#email-sender) for more details. - -#### `webSocket: dict` (optional) -WebSocket configuration. -Check out the [`WebSocket guide`](/docs/guides/websockets) for more details. - -## Page - -`page` declaration is the top-level layout abstraction. Your app can have multiple pages. - -```wasp -page MainPage { - component: import Main from "@client/pages/Main", - authRequired: false // optional -} -``` - -Normally you will also want to associate `page` with a `route`, otherwise it won't be accessible in the app. - -### Fields - -#### `component: ClientImport` (required) -Import statement of the React element that implements the page component. - -#### `authRequired: bool` (optional) -Can be specified only if [`app.auth`](/docs/language/features#authentication--authorization) is defined. - -If set to `true`, only authenticated users will be able to access this page. Unauthenticated users will be redirected to a route defined by `onAuthFailedRedirectTo` property within `app.auth`. - -If `authRequired` is set to `true`, the React component of a page (specified by `component` property) will be provided `user` object as a prop. - -Check out this [section of our Todo app tutorial](/docs/tutorial/auth#update-the-main-page-to-require-auth) for an example of usage. - -## Route - -`route` declaration provides top-level routing functionality in Wasp. - -```wasp -route AboutRoute { path: "/about", to: AboutPage } -``` - -### Fields - -#### `path: string` (required) -URL path of the route. Route path can be parametrised and follows the same conventions as -[React Router](https://reactrouter.com/web/). - -#### `to: page` (required) -Name of the `page` to which the path will lead. -Referenced page must be defined somewhere in `.wasp` file. - -### Example - parametrised URL path -```wasp -route TaskRoute { path: "/task/:id", to: TaskPage } -``` -For details on URL path format check [React Router](https://reactrouter.com/web/) -documentation. - -### Accessing route parameters in a page component - -Since Wasp under the hood generates code with [React Router](https://reactrouter.com/web/), -the same rules apply when accessing URL params in your React components. Here is an example just to get you -started: - -```wasp title="todoApp.wasp" -// ... -route TaskRoute { path: "/task/:id", to: TaskPage } -page TaskPage { - component: import Task from "@client/pages/Task" -} -``` - -```jsx title="pages/Task.js" -import React from 'react' - -const Task = (props) => { - return ( -
- I am showing a task with id: {props.match.params.id}. -
- ) -} - -export default Task -``` -### Navigating between routes - -Navigation can be performed from the React code via `` component, also using the functionality of -[React Router](https://reactrouter.com/web/): - -```wasp title="todoApp.wasp" -// ... -route HomeRoute { path: "/home", to: HomePage } -page HomePage { - component: import Home from "@client/pages/Home" -} -``` - -```jsx title="src/client/pages/OtherPage.js" -import React from 'react' -import { Link } from "react-router-dom" - -const OtherPage = (props) => { - return ( - Go to homepage - ) -} -``` - -## Entity - -`entity` declaration represents a database model. -Wasp uses [Prisma](https://www.prisma.io/) to implement database functionality and currently provides only a thin layer above it. - -Each `Entity` declaration corresponds 1-to-1 to Prisma data model and is defined in a following way: - -```wasp -entity Task {=psl - id Int @id @default(autoincrement()) - description String - isDone Boolean @default(false) -psl=} -``` - -### `{=psl ... psl=}: PSL` -Definition of entity fields in *Prisma Schema Language* (PSL). See -[here for intro and examples](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema) -and [here for a more exhaustive language specification](https://github.com/prisma/specs/tree/master/schema). - -### Using Entities - -Entity-system in Wasp is based on [Prisma](http://www.prisma.io), and currently Wasp provides only a thin layer -on top of it. The workflow is as follows: - -1. Wasp developer creates/updates some of the entities in `.wasp` file. -2. Wasp developer runs `wasp db migrate-dev`. -3. Migration data is generated in `migrations/` folder (and should be committed). -4. Wasp developer uses Prisma JS API to work with the database when in Operations. - -#### Using Entities in Operations - -Most of the time in Wasp you will be working with entities in the context of Operations (Queries & Actions), so check their part of docs for more info on how to use entities in Operations. - -#### Using Entities directly - -If needed, you can also interact with entities directly via [Prisma Client(https://www.prisma.io/docs/concepts/components/prisma-client/crud) (although we recommend using them via injected `entities` when in Operations). - -To import Prisma Client in your Wasp server code, do `import prismaClient from '@wasp/dbClient'`. - -## Queries and Actions (aka Operations) - -In Wasp, the client and the server interact with each other through Operations. -Wasp currently supports two kinds of Operations: **Queries** and **Actions**. - -### Query - -Queries are used to fetch data from the server. They do not modify the server's state. - -Queries are implemented in NodeJS and executed within the server's context. -Wasp generates the code that lets you call the Query from anywhere in your code (client or server) using the same interface. -In other words, you won't have to worry about building an HTTP API for the Query, handling the request on the server, or even handling and caching the responses on the client. -Instead, simply focus on the business logic inside your Query and let Wasp take care of the rest! - -To create a Wasp Query, you must: -1. Define the Query's NodeJS implementation -2. Declare the Query in Wasp using the `query` declaration - -After completing these two steps, you'll be able to use the Query from any point in your code. - - -#### Defining the Query's NodeJS implementation -The Query's implementation is a NodeJS function that takes two arguments (it can be an `async` function but doesn't have to). -Since both arguments are positional, you can name the parameters however you want, but we'll stick with `args` and `context`: -1. `args`: An object containing all the arguments (i.e., payload) **passed to the Query by the caller** (e.g., filtering conditions). -Take a look at [the examples of usage](#using-the-query) to see how to pass this object to the Query. -3. `context`: An additional context object **injected into the Query by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in queries](#using-entities-in-queries). - -Here's an example of three simple Queries: -```js title="src/server/queries.js" -// our "database" -const tasks = [ - { id: 1, description: "Buy some eggs", isDone: true }, - { id: 2, description: "Make an omelette", isDone: false }, - { id: 3, description: "Eat breakfast", isDone: false } -] - -// You don't need to use the arguments if you don't need them -export const getAllTasks = () => { - return tasks; -} - -// The 'args' object is something sent by the caller (most often from the client) -export const getFilteredTasks = (args) => { - const { isDone } = args; - return tasks.filter(task => task.isDone === isDone) -} - -// Query implementations can be async functions and use await. -export const getTasksWithDelay = async () => { - const result = await sleep(1000) - return tasks -} -``` - -#### Declaring a Query in Wasp -After implementing your Queries in NodeJS, all that's left to do before using them is tell Wasp about it! -You can easily do this with the `query` declaration, which supports the following fields: -- `fn: ServerImport` (required) - The import statement of the Query's NodeJs implementation. -- `entities: [Entity]` (optional) - A list of entities you wish to use inside your Query. -We'll leave this option aside for now. You can read more about it [here](#using-entities-in-queries). - -Wasp Queries and their implementations don't need to (but can) have the same name, so we will keep the names different to avoid confusion. -With that in mind, this is how you might declare the Queries that use the implementations from the previous step: -```wasp title="pages/main.wasp" -// ... - -// Again, it most likely makes sense to name the Wasp Query after -// its implementation. We're changing the name to emphasize the difference. - -query fetchAllTasks { - fn: import { getAllTasks } from "@server/queries.js" -} - -query fetchFilteredTasks { - fn: import { getFilteredTasks } from "@server/queries.js" -} -``` - -After declaring a NodeJS function as a Wasp Query, two crucial things happen: -- Wasp **generates a client-side JavaScript function** that shares its name with the Query (e.g., `fetchFilteredTasks`). -This function takes a single optional argument - an object containing any serializable data you wish to use inside the Query. -Wasp will pass this object to the Query's implementation as its first positional argument (i.e., `args` from the previous step). -Such an abstraction works thanks to an HTTP API route handler Wasp generates on the server, which calls the Query's NodeJS implementation under the hood. -- Wasp **generates a server-side NodeJS function** that shares its name with the Query. This function's interface is identical to the client-side function from the previous point. - -Generating two such functions ensures a uniform calling interface across the entire app (both client and server). - - -#### Using the Query -To use the Query, you can import it from `@wasp` and call it directly. As mentioned, the usage is the same regardless of whether you're on the server or the client: -```javascript -import fetchAllTasks from '@wasp/queries/fetchAllTasks.js' -import fetchFilteredTasks from '@wasp/queries/fetchFilteredTasks.js' - -// ... - -const allTasks = await fetchAllTasks(); -const doneTasks = await fetchFilteredTasks({isDone: true}) -``` - -**NOTE**: Wasp will not stop you from importing a Query's NodeJS implementation from `./queries.js` and calling it directly. However, we advise against this, as you'll lose all the useful features a Wasp Query provides (e.g., entity injection). - -#### The `useQuery` hook -When using Queries on the client, you can make them reactive with the help of the `useQuery` hook. -This hook comes bundled with Wasp and is a thin wrapper around the `useQuery` hook from [_react-query_](https://github.com/tannerlinsley/react-query). - -Wasp's `useQuery` hook accepts three arguments: -- `queryFn` (required): A Wasp query declared in the previous step or, in other words, the client-side query function generated by Wasp based on a `query` declaration. -- `queryFnArgs` (optional): The arguments object (payload) you wish to pass into the Query. The Query's NodeJS implementation will receive this object as its first positional argument. -- `options` (optional): A _react-query_ `options` object. Use this to change - [the default - behaviour](https://react-query.tanstack.com/guides/important-defaults) for - this particular query. If you want to change the global defaults, you can do - so in the [client setup function](#overriding-default-behaviour-for-queries). - -Wasp's `useQuery` hook behaves mostly the same as [_react-query_'s `useQuery` hook](https://react-query.tanstack.com/docs/api#usequery), the only difference being in not having to supply the key (Wasp does this automatically under the hood). - -Here's an example of calling the Queries using the `useQuery` hook: -```jsx -import React from 'react' -import { useQuery } from '@wasp/queries' - -import fetchAllTasks from '@wasp/queries/fetchAllTasks' -import fetchFilteredTasks from '@wasp/queries/fetchFilteredTasks' - -const MainPage = () => { - const { - data: allTasks, - error: error1 - } = useQuery(fetchAllTasks) - - const { - data: doneTasks, - error: error2 - } = useQuery(fetchFilteredTasks, { isDone: true }) - - return ( -
-

All Tasks

- {allTasks ? allTasks.map(task => ) : error1} - -

Finished Tasks

- {doneTasks ? doneTasks.map(task => ) : error2} -
- ) -} - -const Task = ({ description, isDone }) => { - return ( -
-

Description: { description }

-

Is done: { isDone ? 'Yes' : 'No' }

-
- ) -} - - -export default MainPage -``` - -#### Error Handling -For security reasons, all exceptions thrown in the Query's NodeJS implementation are sent to the client as responses with the HTTP status code `500`, with all other details removed. -Hiding error details by default helps against accidentally leaking possibly sensitive information over the network. - -If you do want to pass additional error information to the client, you can construct and throw an appropriate `HttpError` in your NodeJS Query function: -```js title=src/server/queries.js -import HttpError from '@wasp/core/HttpError.js' - -export const getTasks = async (args, context) => { - const statusCode = 403 - const message = 'You can\'t do this!' - const data = { foo: 'bar' } - throw new HttpError(statusCode, message, data) -} -``` - -If the status code is `4xx`, the client will receive a response object with the corresponding `.message` and `.data` fields and rethrow the error (with these fields included). -To prevent information leakage, the server won't forward these fields for any other HTTP status codes. - -#### Using Entities in Queries -In most cases, resources used in Queries will be [Entities](#entity). -To use an Entity in your Query, add it to the query declaration in Wasp: - -```wasp {4,9} title="main.wasp" - -query fetchAllTasks { - fn: import { getAllTasks } from "@server/queries.js", - entities: [Task] -} - -query fetchFilteredTasks { - fn: import { getFilteredTasks } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp will inject the specified Entity into the Query's `context` argument, giving you access to the Entity's Prisma API: -```js title="src/server/queries.js" -export const getAllTasks = async (args, context) => { - return context.entities.Task.findMany({}) -} - -export const getFilteredTasks = async (args, context) => { - return context.entities.Task.findMany({ - where: { isDone: args.isDone } - }) -} -``` - -The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/crud). - - -### Action - -Actions are very similar to Queries. So similar, in fact, we will only list the differences: -1. They can (and most often should) modify the server's state, while Queries are only allowed to read it. -2. Actions don't need to be reactive so you can call them directly. Still, Wasp does provide a `useAction` React hook for adding extra behavior to the Action (e.g., optimistic updates). -Read more about the [`useAction` hook](#the-useaction-hook) below. -3. `action` declarations in Wasp are mostly identical to `query` declarations. The only difference is in the declaration's name. - -Here's an implementation of a simple Action: - -```js title=src/server/actions.js -export const sayHi = async () => { - console.log('The client said Hi!') -} -``` -Its corresponding declaration in Wasp: - -```wasp title="main.wasp" -// ... - -action sayHi { - fn: import { sayHi } from "@server/actions.js" -} -``` -And an example of how to import and call the declared Action: - -```js -import sayHi from '@wasp/actions/sayHi' - -// ... - -sayHi() -``` - -Here's an example on how you might define a less contrived Action. -```js title=src/server/actions.js -// ... -export const updateTaskIsDone = ({ id, isDone }, context) => { - return context.entities.Task.update({ - where: { id }, - data: { isDone } - }) -} -``` -```wasp title=main.wasp -action updateTaskIsDone { - fn: import { updateTaskIsDone } from "@server/actions.js", - entities: [Task] -} -``` - -And here is how you might use it: -```jsx {4,18} title=src/client/pages/Task.js -import React from 'react' -import { useQuery } from '@wasp/queries' -import fetchTask from '@wasp/queries/fetchTask' -import updateTaskIsDone from '@wasp/actions/updateTaskIsDone' - -const TaskPage = ({ id }) => { - const { data: task } = useQuery(fetchTask, { id }) - - if (!task) { - return

"Loading"

- } - - const { description, isDone } = task - return ( -
-

Description: {description}

-

Is done: {isDone ? 'Yes' : 'No'}

- -
- ) -} -``` - -#### The `useAction` hook -When using Actions in components, you can enhance them with the help of the `useAction` hook. This hook comes bundled with Wasp and decorates Wasp Actions. -In other words, the hook returns a function whose API matches the original Action while also doing something extra under the hood (depending on how you configure it). - -The `useAction` hook accepts two arguments: -- `actionFn` (required) - The Wasp Action (i.e., the client-side query function generated by Wasp based on a query declaration) you wish to enhance. -- `actionOptions` (optional) - An object configuring the extra features you want to add to the given Action. While this argument is technically optional, there is no point in using the `useAction` hook without providing it (it would be the same as using the Action directly). The Action options object supports the following fields: - - `optimisticUpdates` (optional) - An array of objects where each object defines an [optimistic update](https://stackoverflow.com/a/33009713) to perform on the query cache. To define an optimistic update, you must specify the following properties: - - `getQuerySpecifier` (required) - A function returning the query specifier (i.e., a value used to address the query you want to update). A query specifier is an array specifying the query function and arguments. For example, to optimistically update the query used with `useQuery(fetchFilteredTasks, {isDone: true }]`, your `getQuerySpecifier` function would have to return the array `[fetchFilteredTasks, { isDone: true}]`. Wasp will forward the argument you pass into the decorated Action to this function (i.e., you can use the properties of the added/change item to address the query). - - `updateQuery` (required) - The function used to perform the optimistic update. It should return the desired state of the cache. Wasp will call it with the following arguments: - - `item` - The argument you pass into the decorated Action. - - `oldData` - The currently cached value for the query identified by the specifier. - -**NOTE:** The `updateQuery` function must be a pure function. It must return the desired cache value identified by the `getQuerySpecifier` function and _must not_ perform any side effects. Also, make sure you only update the query caches affected by your action causing the optimistic update (Wasp cannot yet verify this). Finally, your implementation of the `updateQuery` function should work correctly regardless of the state of `oldData` (e.g., don't rely on array positioning). If you need to do something else during your optimistic update, you can directly use _react-query_'s lower-level API (read more about it [here](#advanced-usage)). - -Here's an example showing how to configure the Action from the previous example to perform an optimistic update: -```jsx {3,9,10,11,12,13,14,15,16,27} title=src/client/pages/Task.js -import React from 'react' -import { useQuery } from '@wasp/queries' -import { useAction } from '@wasp/actions' -import fetchTask from '@wasp/queries/fetchTask' -import updateTaskIsDone from '@wasp/actions/updateTaskIsDone' - -const TaskPage = ({ id }) => { - const { data: task } = useQuery(fetchTask, { id }) - const updateTaskIsDoneOptimistically = useAction(updateTaskIsDone, { - optimisticUpdates: [ - { - getQuerySpecifier: ({ id }) => [fetchTask, { id }], - updateQuery: ({ isDone }, oldData) => ({ ...oldData, isDone }) - } - ] - }) - - if (!task) { - return

"Loading"

- } - - const { description, isDone } = task - return ( -
-

Description: {description}

-

Is done: {isDone ? 'Yes' : 'No'}

- -
- Back to main page -
-
- ) -} - -export default TaskPage -``` -#### Advanced usage -The `useAction` hook currently only supports specifying optimistic updates. You can expect more features in future versions of Wasp. - -Wasp's optimistic update API is deliberately small and focuses exclusively on updating Query caches (as that's the most common use case). You might need an API that offers more options or a higher level of control. If that's the case, instead of using Wasp's `useAction` hook, you can use _react-query_'s `useMutation` hook and directly work with [their low-level API](https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates). - -If you decide to use _react-query_'s API directly, you will need access to the Query's cache key. Wasp internally uses this key but abstracts it from the programmer. Still, you can easily obtain it by accessing the `queryCacheKey` property on a Query: -```js -import { fetchTasks } from '@wasp/queries' - -const queryKey = fetchTasks.queryCacheKey -``` - -### Cache Invalidation -One of the trickiest parts of managing a web app's state is making sure the data returned by the queries is up to date. -Since Wasp uses _react-query_ for Query management, we must make sure to invalidate Queries (more specifically, their cached results managed by _react-query_) whenever they become stale. - -It's possible to invalidate the caches manually through several mechanisms _react-query_ provides (e.g., refetch, direct invalidation). -However, since manual cache invalidation quickly becomes complex and error-prone, Wasp offers a quicker and a more effective solution to get you started: **automatic Entity-based Query cache invalidation**. -Because Actions can (and most often do) modify the state while Queries read it, Wasp invalidates a Query's cache whenever an Action that uses the same Entity is executed. - -For example, let's assume that Action `createTask` and Query `getTasks` both use Entity `Task`. If `createTask` is executed, `getTasks`'s cached result may no longer be up-to-date. -Wasp will therefore invalidate it, making `getTasks` refetch data from the server, bringing it up to date again. - -In practice, this means that Wasp keeps the queries "fresh" without requiring you to think about cache invalidation. - -On the other hand, this kind of automatic cache invalidation can become wasteful (some updates might not be necessary) and will only work for Entities. If that's an issue, you can use the mechanisms provided by _react-query_ for now, and expect more direct support in Wasp for handling those use cases in a nice, elegant way. - -If you wish to optimistically set cache values after performing an action, you can do so using [optimistic updates](https://stackoverflow.com/a/33009713). Configure them using Wasp's [useAction hook](#the-useaction-hook). This is currently the only manual cache invalidation mechanism Wasps supports natively. For everything else, you can always rely on _react-query_. - -### Prisma Error Helpers -In your Operations, you may wish to handle general Prisma errors with HTTP-friendly responses. We have exposed two helper functions, `isPrismaError`, and `prismaErrorToHttpError`, for this purpose. As of now, we convert two specific Prisma errors (which we will continue to expand), with the rest being `500`. See the [source here](https://github.com/wasp-lang/wasp/blob/main/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/utils.js). - -#### `import statement`: -```js -import { isPrismaError, prismaErrorToHttpError } from '@wasp/utils.js' -``` - -##### Example of usage: -```js -try { - await context.entities.Task.create({...}) -} catch (e) { - if (isPrismaError(e)) { - throw prismaErrorToHttpError(e) - } else { - throw e - } -} -``` - -### CRUD operations on top of entities - -:::caution Early preview -This feature is currently in early preview. It doesn't contain all the planned features. - -In the future iterations of Wasp we plan on supporting: -- **authorization** that will allow you to specify which users can perform which operations -- **validation** of input data (e.g. using Zod schema validation) -::: - -For a specific [Entity](/docs/language/features#entity), you can tell Wasp to automatically instantiate server-side logic ([Queries](/docs/language/features#query) and [Actions](/docs/language/features#action)) for creating, reading, updating and deleting such entities. - -#### Which operations are supported? - -If we create CRUD operations for an entity named `Task`, - -```wasp title="main.wasp" -crud Tasks { // crud name here is "Tasks" - entity: Task, - operations: { - getAll: { - isPublic: true, // optional, defaults to false - }, - get: {}, - create: { - overrideFn: import { createTask } from "@server/tasks.js", // optional - }, - update: {}, - }, -} -``` - -Wasp will give you the following default implementations: - -**getAll** - returns all entities - -```js -// ... - -// If the operation is not public, Wasp checks if an authenticated user -// is making the request. - -return Task.findMany() -``` - -**get** - returns one entity by id field - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.findUnique({ where: { id: args.id } }) -``` - -**create** - creates a new entity - -```js -// ... -return Task.create({ data: args.data }) -``` - -**update** - updates an existing entity - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.update({ where: { id: args.id }, data: args.data }) -``` - -**delete** - deletes an existing entity - -```js -// ... -// Wasp uses the field marked with `@id` in Prisma schema as the id field. -return Task.delete({ where: { id: args.id } }) -``` - -:::info Current Limitations -In the default `create` and `update` implementations, we are saving all of the data that the client sends to the server. This is not always desirable, i.e. in the case when the client should not be able to modify all of the data in the entity. - -[In the future](#/docs/guides/crud#future-of-crud-operations-in-wasp), we are planning to add validation of action input, where only the data that the user is allowed to change will be saved. - -For now, the solution is to provide an override function. You can override the default implementation by using the `overrideFn` option and implementing the validation logic yourself. - -::: - -#### CRUD declaration - -The CRUD declaration works on top of an existing entity declaration. It is declared as follows: - -```wasp title="main.wasp" -crud Tasks { // crud name here is "Tasks" - entity: Task, - operations: { - getAll: { - isPublic: true, // optional, defaults to false - }, - get: {}, - create: { - overrideFn: import { createTask } from "@server/tasks.js", // optional - }, - update: {}, - }, -} -``` - -It has the following fields: -- `entity: Entity` - the entity to which the CRUD operations will be applied. -- `operations: { [operationName]: CrudOperationOptions }` - the operations to be generated. The key is the name of the operation, and the value is the operation configuration. - - The possible values for `operationName` are: - - `getAll` - - `get` - - `create` - - `update` - - `delete` - - `CrudOperationOptions` can have the following fields: - - `isPublic: bool` - Whether the operation is public or not. If it is public, no auth is required to access it. If it is not public, it will be available only to authenticated users. Defaults to `false`. - - `overrideFn: ServerImport` - The import statement of the optional override implementation in Node.js. - -#### Defining the overrides - -Like with actions and queries, you can define the implementation in a Javascript/Typescript file. The overrides are functions that take the following arguments: -- `args` - The arguments of the operation i.e. the data that's sent from the client. -- `context` - Context contains the `user` making the request and the `entities` object containing the entity that's being operated on. - -You can also import types for each of the functions you want to override from `@wasp/crud/{crud name}`. The available types are: -- `GetAllQuery` -- `GetQuery` -- `CreateAction` -- `UpdateAction` -- `DeleteAction` - -If you have a CRUD named `Tasks`, you would import the types like this: -```ts -import type { GetAllQuery, GetQuery, CreateAction, UpdateAction, DeleteAction } from '@wasp/crud/Tasks' - -// Each of the types is a generic type, so you can use it like this: -export const getAllOverride: GetAllQuery = async (args, context) => { - // ... -} -``` - -We are showing an example of an override in the [CRUD guide](/docs/guides/crud). - -#### Using the CRUD operations in client code - -On the client, you import the CRUD operations from `@wasp/crud/{crud name}`. The names of the imports are the same as the names of the operations. For example, if you have a CRUD called `Tasks`, you would import the operations like this: - -```jsx title="SomePage.jsx" -import { Tasks } from '@wasp/crud/Tasks' -``` - -You can then access the operations like this: -```jsx title="SomePage.jsx" -const { data } = Tasks.getAll.useQuery() -const { data } = Tasks.get.useQuery({ id: 1 }) -const createAction = Tasks.create.useAction() -const updateAction = Tasks.update.useAction() -const deleteAction = Tasks.delete.useAction() - -// The CRUD operations are using the existing actions and queries -// under the hood, so all the options are available as before. -``` - -Check out the [CRUD guide](/docs/guides/crud) to see how to use the CRUD operations in client code. - -## APIs - -In Wasp, the default client-server interaction mechanism is through [Operations](#queries-and-actions-aka-operations). However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an `api`! Best of all, they should look and feel very familiar. - -### API - -APIs are used to tie a JS function to an HTTP (method, path) pair. They are distinct from Operations and have no client-side helpers (like `useQuery`). - -To create a Wasp API, you must: -1. Define the APIs NodeJS implementation -2. Declare the API in Wasp using the `api` declaration - -After completing these two steps, you'll be able to call the API from client code (via our Axios wrapper), or from the outside world. - -:::note -In order to leverage the benefits of TypeScript and use types in your NodeJS implementation (step 1), you must add your `api` declarations to your `.wasp` file (step 2) _and_ compile the Wasp project. This will enable the Wasp compiler to generate any new types based on your `.wasp`file definitions for use in your implementation files. -::: - -#### Defining the APIs NodeJS implementation -An API should be implemented as a NodeJS function that takes three arguments. -1. `req`: Express Request object -2. `res`: Express Response object -3. `context`: An additional context object **injected into the API by Wasp**. This object contains user session information, as well as information about entities. The examples here won't use the context for simplicity purposes. You can read more about it in the [section about using entities in APIs](#using-entities-in-apis). - -##### Simple API example -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar : FooBar = (req, res, context) => { - res.set('Access-Control-Allow-Origin', '*') // Example of modifying headers to override Wasp default CORS middleware. - res.json({ msg: `Hello, ${context.user?.username || "stranger"}!` }) -} -``` - -##### More complicated TypeScript example -Let's say you wanted to create some `GET` route that would take an email address as a param, and provide them the answer to "Life, the Universe and Everything." :) What would this look like in TypeScript? - -```wasp title="main.wasp" -api fooBar { - fn: import { fooBar } from "@server/apis.js", - entities: [Task], - httpRoute: (GET, "/foo/bar/:email") -} -``` - -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar: FooBar< -{ email: string }, // params -{ answer: number } // response -> = (req, res, _context) => { - console.log(req.params.email) - res.json({ answer: 42 }) -} -``` - -#### Declaring an API in Wasp -After implementing your APIs in NodeJS, all that's left to do before using them is tell Wasp about it! -You can easily do this with the `api` declaration, which supports the following fields: -- `fn: ServerImport` (required) - The import statement of the APIs NodeJs implementation. -- `httpRoute: (HttpMethod, string)` (required) - The HTTP (method, path) pair, where the method can be one of: - - `ALL`, `GET`, `POST`, `PUT` or `DELETE` - - and path is an Express path `string`. -- `entities: [Entity]` (optional) - A list of entities you wish to use inside your API. -We'll leave this option aside for now. You can read more about it [here](#using-entities-in-apis). -- `auth: bool` (optional) - If auth is enabled, this will default to `true` and provide a `context.user` object. If you do not wish to attempt to parse the JWT in the Authorization Header, you may set this to `false`. -- `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/guides/middleware-customization#2-customize-api-specific-middleware). - -Wasp APIs and their implementations don't need to (but can) have the same name. With that in mind, this is how you might declare the API that uses the implementations from the previous step: -```wasp title="pages/main.wasp" -// ... - -api fooBar { - fn: import { fooBar } from "@server/apis.js", - httpRoute: (GET, "/foo/bar") -} -``` - -#### Using the API -To use the API externally, you simply call the endpoint using the method and path you used. For example, if your app is running at `https://example.com` then from the above you could issue a `GET` to `https://example/com/foo/callback` (in your browser, Postman, `curl`, another web service, etc.). - -To use the API from your client, including with auth support, you can import the Axios wrapper from `@wasp/api` and invoke a call. For example: -```ts -import React, { useEffect } from 'react' -import api from '@wasp/api' - -async function fetchCustomRoute() { - const res = await api.get('/foo/bar') - console.log(res.data) -} - -export const Foo = () => { - useEffect(() => { - fetchCustomRoute() - }, []); - - return ( - <> - // ... - - ) -} -``` - -#### Using Entities in APIs -In many cases, resources used in APIs will be [Entities](#entity). -To use an Entity in your API, add it to the `api` declaration in Wasp: - -```wasp {3} title="main.wasp" -api fooBar { - fn: import { fooBar } from "@server/apis.js", - entities: [Task], - httpRoute: (GET, "/foo/bar") -} -``` - -Wasp will inject the specified Entity into the APIs `context` argument, giving you access to the Entity's Prisma API: -```ts title="src/server/apis.ts" -import { FooBar } from '@wasp/apis/types' - -export const fooBar : FooBar = (req, res, context) => { - res.json({ count: await context.entities.Task.count() }) -} - -``` - -The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/crud). - -### apiNamespace - -An `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path. For example: - -```wasp title="main.wasp" -apiNamespace fooBar { - middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js", - path: "/foo/bar" -} -``` - -For more information about middleware configuration, please see: [Middleware Configuration](/docs/guides/middleware-customization) - -## Jobs - -If you have server tasks that you do not want to handle as part of the normal request-response cycle, Wasp allows you to make that function a `job` and it will gain some "superpowers." Jobs will: - * persist between server restarts - * can be retried if they fail - * can be delayed until the future - * can have a recurring schedule! - -Some examples where you may want to use a `job` on the server include sending an email, making an HTTP request to some external API, or doing some nightly calculations. - -### Job Executors - -Job executors handle the scheduling, monitoring, and execution of our jobs. - -Wasp allows you to choose which job executor will be used to execute a specific job that you define, which affects some of the finer details of how jobs will behave and how they can be further configured. Each job executor has its pros and cons, which we will explain in more detail below, so you can pick the one that best suits your needs. - -Currently, Wasp supports only one type of job executor, which is `PgBoss`, but in the future, it will likely support more. - -#### pg-boss - -We have selected [pg-boss](https://github.com/timgit/pg-boss/) as our first job executor to handle the low-volume, basic job queue workloads many web applications have. By using PostgreSQL (and [SKIP LOCKED](https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/)) as its storage and synchronization mechanism, it allows us to provide many job queue pros without any additional infrastructure or complex management. - -:::info -Keep in mind that pg-boss jobs run alongside your other server-side code, so they are not appropriate for CPU-heavy workloads. Additionally, some care is required if you modify scheduled jobs. Please see pg-boss details below for more information. - -
- pg-boss details - - pg-boss provides many useful features, which can be found [here](https://github.com/timgit/pg-boss/blob/8.4.2/README.md). - - When you add pg-boss to a Wasp project, it will automatically add a new schema to your database called `pgboss` with some internal tracking tables, including `job` and `schedule`. pg-boss tables have a `name` column in most tables that will correspond to your `job` identifier. Additionally, these tables maintain arguments, states, return values, retry information, start and expiration times, and other metadata required by pg-boss. - - If you need to customize the creation of the pg-boss instance, you can set an environment variable called `PG_BOSS_NEW_OPTIONS` to a stringified JSON object containing [these initialization parameters](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#newoptions). **NOTE**: Setting this overwrites all Wasp defaults, so you must include database connection information as well. - - ##### pg-boss considerations - - Wasp starts pg-boss alongside your web server's application, where both are simultaneously operational. This means that jobs running via pg-boss and the rest of the server logic (like Operations) share the CPU, therefore you should avoid running CPU-intensive tasks via jobs. - - Wasp does not (yet) support independent, horizontal scaling of pg-boss-only applications, nor starting them as separate workers/processes/threads. - - The job name/identifier in your `.wasp` file is the same name that will be used in the `name` column of pg-boss tables. If you change a name that had a `schedule` associated with it, pg-boss will continue scheduling those jobs but they will have no handlers associated, and will thus become stale and expire. To resolve this, you can remove the applicable row from the `schedule` table in the `pgboss` schema of your database. - - If you remove a `schedule` from a job, you will need to do the above as well. - - If you wish to deploy to Heroku, you need to set an additional environment variable called `PG_BOSS_NEW_OPTIONS` to `{"connectionString":"","ssl":{"rejectUnauthorized":false}}`. This is because pg-boss uses the `pg` extension, which does not seem to connect to Heroku over SSL by default, which Heroku requires. Additionally, Heroku uses a self-signed cert, so we must handle that as well. -- https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js - -
-::: - -### Basic job definition and usage - -To declare a `job` in Wasp, simply add a declaration with a reference to an `async` function, like the following: - -```wasp title="main.wasp" -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js" - } -} -``` - -Then, in your [Operations](/docs/language/features#queries-and-actions-aka-operations) or [setupFn](/docs/language/features#setupfn-serverimport-optional) (or any other NodeJS code), you can submit work to be done: -```js -import { mySpecialJob } from '@wasp/jobs/mySpecialJob.js' - -const submittedJob = await mySpecialJob.submit({ job: "args" }) -console.log(await submittedJob.pgBoss.details()) - -// Or, if you'd prefer it to execute in the future, just add a .delay(). -// It takes a number of seconds, Date, or ISO date string. -await mySpecialJob.delay(10).submit({ job: "args" }) -``` - -And that is it! Your job will be executed by the job executor (pg-boss, in this case) as if you called `foo({ job: "args" })`. - -Note that in our example, `foo` takes an argument, but this does not always have to be the case. It all depends on how you've implemented your worker function. - -### Recurring jobs - -If you have work that needs to be done on some recurring basis, you can add a `schedule` to your job declaration: - -```wasp {6-9} title="main.wasp" -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js" - }, - schedule: { - cron: "0 * * * *", - args: {=json { "job": "args" } json=} // optional - } -} -``` - -In this example, you do _not_ need to invoke anything in JavaScript. You can imagine `foo({ job: "args" })` getting automatically scheduled and invoked for you every hour. - -### Fully specified example -Both `perform` and `schedule` accept `executorOptions`, which we pass directly to the named job executor when you submit jobs. In this example, the scheduled job will have a `retryLimit` set to 0, as `schedule` overrides any similar property from `perform`. Lastly, we add an entity to pass in via the context argument to `perform.fn`. - -```wasp -job mySpecialJob { - executor: PgBoss, - perform: { - fn: import { foo } from "@server/workers/bar.js", - executorOptions: { - pgBoss: {=json { "retryLimit": 1 } json=} - } - }, - schedule: { - cron: "*/5 * * * *", - args: {=json { "foo": "bar" } json=}, - executorOptions: { - pgBoss: {=json { "retryLimit": 0 } json=} - } - }, - entities: [Task], -} -``` - -### Fields - -#### `executor: JobExecutor` (required) -`PgBoss` is currently our only job executor, and is recommended for low-volume production use cases. It requires your `app.db.system` to be `PostgreSQL`. - -#### `perform: dict` (required) - - - ##### `fn: ServerImport` (required) - An `async` JavaScript function of work to be performed. Since Wasp executes jobs on the server, you must import it from `@server`. The function receives a first argument which may be passed when the job is called, as well as the context containing any declared entities as the second (this is passed automatically by Wasp). Here is a sample signature: - - ```js - export async function foo(args, context) { - // Can reference context.entities.Task, for example. - } - ``` - - - ##### `executorOptions: dict` (optional) - Executor-specific default options to use when submitting jobs. These are passed directly through and you should consult the documentation for the job executor. These can be overridden during invocation with `submit()` or in a `schedule`. - - - ##### `pgBoss: JSON` (optional) - See the docs for [pg-boss](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#sendname-data-options). - -#### `schedule: dict` (optional) - - - ##### `cron: string` (required) - A 5-placeholder format cron expression string. See rationale for minute-level precision [here](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#scheduling). - - _If you need help building cron expressions, Check out_ [Crontab guru](https://crontab.guru/#0_*_*_*_*). - - - ##### `args: JSON` (optional) - The arguments to pass to the `perform.fn` function when invoked. - - - ##### `executorOptions: dict` (optional) - Executor-specific options to use when submitting jobs. These are passed directly through and you should consult the documentation for the job executor. The `perform.executorOptions` are the default options, and `schedule.executorOptions` can override/extend those. - - - ##### `pgBoss: JSON` (optional) - See the docs for [pg-boss](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#sendname-data-options). - -#### `entities: [Entity]` (optional) -A list of entities you wish to use inside your Job (similar to Queries and Actions). - -### JavaScript API - -#### Invocation -##### `import` - -```js -import { mySpecialJob } from '@wasp/jobs/mySpecialJob.js' -``` - -##### `submit(jobArgs, executorOptions)` -- ###### `jobArgs: JSON` (optional) -- ###### `executorOptions: JSON` (optional) - -Submits a `job` to be executed by an executor, optionally passing in a JSON job argument your job handler function will receive, and executor-specific submit options. - -```js -const submittedJob = await mySpecialJob.submit({ job: "args" }) -``` - -##### `delay(startAfter)` (optional) -- ###### `startAfter: int | string | Date` (required) - -Delaying the invocation of the job handler. The delay can be one of: -- Integer: number of seconds to delay. [Default 0] -- String: ISO date string to run at. -- Date: Date to run at. - -```js -const submittedJob = await mySpecialJob.delay(10).submit({ job: "args" }, { "retryLimit": 2 }) -``` - -#### Tracking -The return value of `submit()` is an instance of `SubmittedJob`, which minimally contains: -- `jobId`: A getter returning the UUID String ID for the job in that executor. -- `jobName`: A getter returning the name of the job you used in your `.wasp` file. -- `executorName`: A getter returning a Symbol of the name of the job executor. - - For pg-boss, you can import a Symbol from: `import { PG_BOSS_EXECUTOR_NAME } from '@wasp/jobs/core/pgBoss/pgBossJob.js'` if you wish to compare against `executorName`. - -There will also be namespaced, job executor-specific objects. - -- For pg-boss, you may access: `pgBoss` - - **NOTE**: no arguments are necessary, as we already applied the `jobId` in the available functions. - - `details()`: pg-boss specific job detail information. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#getjobbyidid) - - `cancel()`: attempts to cancel a job. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#cancelid) - - `resume()`: attempts to resume a canceled job. [Reference](https://github.com/timgit/pg-boss/blob/8.4.2/docs/readme.md#resumeid) - -## Dependencies - -You can specify additional npm dependencies via `dependencies` field in `app` declaration, in following way: - -```wasp -app MyApp { - title: "My app", - // ... - dependencies: [ - ("redux", "^4.0.5"), - ("react-redux", "^7.1.3") - ] -} -``` - -You will need to re-run `wasp start` after adding a dependency for Wasp to pick it up. - -**NOTE**: In current implementation of Wasp, if Wasp is already internally using certain npm dependency with certain version specified, you are not allowed to define that same npm dependency yourself while specifying different version. -If you do that, you will get an error message telling you which exact version you have to use for that dependency. -This means Wasp dictates exact versions of certain packages, so for example you can't choose version of React you want to use. -In the future, we will add support for picking any version you like, but we have not implemented that yet. Check [issue #59](https://github.com/wasp-lang/wasp/issues/59) to check out the progress or contribute. - - -## Authentication & Authorization - -Wasp provides authentication and authorization support out-of-the-box. Enabling it for your app is optional and can be done by configuring the `auth` field of the `app` declaration: - -```wasp -app MyApp { - title: "My app", - //... - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - usernameAndPassword: {}, // use this or email, not both - email: {}, // use this or usernameAndPassword, not both - google: {}, - gitHub: {}, - }, - onAuthFailedRedirectTo: "/someRoute" - } -} - -//... -``` - -`app.auth` is a dictionary with following fields: - -#### `userEntity: entity` (required) -Entity which represents the user. - -#### `externalAuthEntity: entity` (optional) -Entity which associates a user with some external authentication provider. We currently offer support for Google and GitHub. See the sections on [Social Login Providers](#social-login-providers-oauth-20) for more info. - -#### `methods: dict` (required) -List of authentication methods that Wasp app supports. Currently supported methods are: -* `usernameAndPassword`: authentication with a username and password. See [here](#username-and-password) for more. -* `email`: authentication with a email and password. See [here](#email-authentication) for more. -* `google`: authentication via Google accounts. See [here](#social-login-providers-oauth-20) for more. -* `gitHub`: authentication via GitHub accounts. See [here](#social-login-providers-oauth-20) for more. - -#### `onAuthFailedRedirectTo: String` (required) -Path where an unauthenticated user will be redirected to if they try to access a private page (which is declared by setting `authRequired: true` for a specific page). -Check out this [section of our Todo app tutorial](/docs/tutorial/auth#update-the-main-page-to-require-auth) to see an example of usage. - -#### `onAuthSucceededRedirectTo: String` (optional) -Path where a successfully authenticated user will be sent upon successful login/signup. -Default value is "/". - -:::note -Automatic redirect on successful login only works when using the Wasp provided [`Signup` and `Login` forms](#high-level-api) -::: - -### Username and Password - -`usernameAndPassword` authentication method makes it possible to signup/login into the app by using a username and password. -This method requires that `userEntity` specified in `auth` contains `username: string` and `password: string` fields: - -```wasp -app MyApp { - title: "My app", - //... - - auth: { - userEntity: User, - methods: { - usernameAndPassword: {}, - }, - onAuthFailedRedirectTo: "/someRoute" - } -} - -// Wasp requires the userEntity to have at least the following fields -entity User {=psl - id Int @id @default(autoincrement()) - username String @unique - password String -psl=} -``` - -We provide basic validations out of the box, which you can customize as shown below. Default validations are: -- `username`: non-empty -- `password`: non-empty, at least 8 characters, and contains a number - -Note that `username`s are stored in a case-sensitive manner. - -#### High-level API - -The quickest way to get started is by using the following API generated by Wasp: -- Signup and Login forms at `@wasp/auth/forms/Signup` and `@wasp/auth/forms/Login` routes - - For styling, these default authentication components have form classes associated for both login (`login-form`) and signup (`signup-form`). Additionally, they both share a common class (`auth-form`). -- `logout` function -- `useAuth()` React hook -**NOTE:** If the signup is successful, the Signup form will automatically log in the user. - -Check our [Todo app tutorial](/docs/tutorial/auth) to see how it works. See below for detailed specification of each of these methods. - -#### Lower-level API - -If you require more control in your authentication flow, you can achieve that in the following ways: -- If you don't want to use already generated Signup and Login forms and want to create your own, you can use `signup` and `login` function by invoking them from the client. -- If you want to execute custom code on the server during sign up, create your own sign up action which invokes Prisma client as `context.entities.[USER_ENTITY].create()` function, along with your custom code. - -The code of your custom sign-up action would look like this (your user entity being `User` in this instance): -```js title="src/server/auth/signup.js" -export const signUp = async (args, context) => { - // Your custom code before sign-up. - // ... - - const newUser = context.entities.User.create({ - data: { - username: args.username, - password: args.password // password hashed automatically by Wasp! 🐝 - } - }) - - // Your custom code after sign-up. - // ... - return newUser -} -``` - -:::info -You don't need to worry about hashing the password yourself! Even when you are using Prisma's client directly and calling `create()` with a plain-text password, Wasp's middleware takes care of hashing it before storing it in the database. An additional middleware also performs field validation. -::: - -##### Customizing user entity validations - -To disable/enable default validations, or add your own, you can modify your custom signUp function like so: -```js -const newUser = context.entities.User.create({ - data: { - username: args.username, - password: args.password // password hashed automatically by Wasp! 🐝 - }, - _waspSkipDefaultValidations: false, // can be omitted if false (default), or explicitly set to true - _waspCustomValidations: [ - { - validates: 'password', - message: 'password must contain an uppercase letter', - validator: password => /[A-Z]/.test(password) - }, - ] -}) -``` - -:::info -Validations always run on `create()`, but only when the field mentioned in `validates` is present for `update()`. The validation process stops on the first `validator` to return false. If enabled, default validations run first and validate basic properties of both the `'username'` or `'password'` fields. -::: - -#### Specification - -#### `login()` -An action for logging in the user. -```js -login(username, password) -``` -:::info -When using the exposed `login()` function, make sure to implement your own redirect on successful login logic -::: - -#### `username: string` -Username of the user logging in. - -#### `password: string` -Password of the user logging in. - -#### `import statement`: -```js -import login from '@wasp/auth/login' -``` -Login is a regular action and can be used directly from the frontend. - - -#### `signup()` -An action for signing up the user. This action does not log in the user, you still need to call `login()`. - -```js -signup(userFields) -``` -#### `userFields: object` -Auth-related fields (either `username` or `email` and `password`) of the user entity which was declared in `auth`. - -:::info -Wasp only stores the auth-related fields of the user entity. Adding extra fields to `userFields` will not have any effect. - -If you need to add extra fields to the user entity, we suggest doing it in a separate step after the user logs in for the first time. -::: - -#### `import statement`: -```js -import signup from '@wasp/auth/signup' -``` -Signup is a regular action and can be used directly from the frontend. - -#### `logout()` -An action for logging out the user. -```js -logout() -``` - -#### `import statement`: -```js -import logout from '@wasp/auth/logout' -``` - -##### Example of usage: -```jsx -import logout from '@wasp/auth/logout' - -const SignOut = () => { - return ( - - ) -} -``` - -#### Updating a user's password -If you need to update user's password, you can do it safely via Prisma client, e.g. within an action: -```js -export const updatePassword = async (args, context) => { - return context.entities.User.update({ - where: { id: args.userId }, - data: { - password: 'New pwd which will be hashed automatically!' - } - }) -} -``` -You don't need to worry about hashing the password yourself - if you have an `auth` declaration -in your `.wasp` file, Wasp already set a middleware on Prisma that makes sure whenever password -is created or updated on the user entity, it is also hashed before it is stored to the database. - -### Email authentication - -:::info -We have written a step-by-step guide on how to set up the e-mail authentication with Wasp's included Auth UI. - -Read more in the [email authentication guide](/docs/guides/email-auth). -::: - -:::warning -If a user signs up with Google or Github (and you set it up to save their social provider e-mail info on the `User` entity), they'll be able to reset their password and login with e-mail and password. - -If a user signs up with the e-mail and password and then tries to login with a social provider (Google or Github), they won't be able to do that. - -In the future, we will lift this limitation and enable smarter merging of accounts. -::: - -`email` authentication method makes it possible to signup/login into the app by using an e-mail and a password. - -```wasp title="main.wasp" -app MyApp { - title: "My app", - // ... - - auth: { - userEntity: User, - methods: { - email: { - // we'll deal with `email` below - }, - }, - onAuthFailedRedirectTo: "/someRoute" - }, - // ... -} - -// Wasp requires the userEntity to have at least the following fields -entity User {=psl - id Int @id @default(autoincrement()) - email String? @unique - password String? - isEmailVerified Boolean @default(false) - emailVerificationSentAt DateTime? - passwordResetSentAt DateTime? -psl=} -``` - -This method requires that `userEntity` specified in `auth` contains: - -- optional `email` field of type `String` -- optional `password` field of type `String` -- `isEmailVerified` field of type `Boolean` with a default value of `false` -- optional `emailVerificationSentAt` field of type `DateTime` -- optional `passwordResetSentAt` field of type `DateTime` - -#### Fields in the `email` dict - -```wasp title="main.wasp" -app MyApp { - title: "My app", - // ... - - auth: { - userEntity: User, - methods: { - email: { - fromField: { - name: "My App", - email: "hello@itsme.com" - }, - emailVerification: { - clientRoute: EmailVerificationRoute, - getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js", - }, - passwordReset: { - clientRoute: PasswordResetRoute, - getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js", - }, - allowUnverifiedLogin: false, - }, - }, - onAuthFailedRedirectTo: "/someRoute" - }, - // ... -} -``` - -##### `fromField: EmailFromField` (required) -`fromField` is a dict that specifies the name and e-mail address of the sender of the e-mails sent by Wasp. It is required to be defined. The object has the following fields: -- `name`: name of the sender (optional) -- `email`: e-mail address of the sender - -##### `emailVerification: EmailVerificationConfig` (required) -`emailVerification` is a dict that specifies the e-mail verification process. It is required to be defined. - -The object 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. - -```js title="src/pages/EmailVerificationPage.jsx" -import { verifyEmail } from '@wasp/auth/email/actions'; -... -await verifyEmail({ token }); -``` - -Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth). - -- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional) - -Defining `getEmailContentFn` can be done by defining a Javscript or Typescript file in the `server` directory. - -```ts title="server/email.ts" -import { GetVerificationEmailContentFn } from '@wasp/types' - -export const getVerificationEmailContent: GetVerificationEmailContentFn = ({ - verificationLink, -}) => ({ - 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 - `, -}) -``` - -##### `passwordReset: PasswordResetConfig` (required) -`passwordReset` is a dict that specifies the password reset process. It is required to be defined. The object 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. - -```js title="src/pages/ForgotPasswordPage.jsx" -import { requestPasswordReset } from '@wasp/auth/email/actions'; -... -await requestPasswordReset({ email }); -``` - -```js title="src/pages/PasswordResetPage.jsx" -import { resetPassword } from '@wasp/auth/email/actions'; -... -await resetPassword({ password, token }) -``` - -##### `allowUnverifiedLogin: bool`: a boolean that specifies whether the user can login without verifying their e-mail address. (optional) - -It defaults to `false`. If `allowUnverifiedLogin` is set to `true`, the user can login without verifying their e-mail address, otherwise users will receive a `401` error when trying to login without verifying their e-mail address. - - -Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth). - -- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional) - -Defining `getEmailContentFn` is done by defining a function that looks like this: - -```ts title="server/email.ts" -import { GetPasswordResetEmailContentFn } from '@wasp/types' - -export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({ - passwordResetLink, -}) => ({ - subject: 'Password reset', - text: `Click the link below to reset your password: ${passwordResetLink}`, - html: ` -

Click the link below to reset your password

- Reset password - `, -}) -``` - -#### Email sender for email authentication - -We require that you define an `emailSender`, so that Wasp knows how to send e-mails. Read more about that [here](#email-sender). - -#### Validations - -We provide basic validations out of the box. The validations are: -- `email`: non-empty, valid e-mail address -- `password`: non-empty, at least 8 characters, and contains a number - -Note that `email`s are stored in a case-insensitive manner. - -:::info -You don't need to worry about hashing the password yourself! Even when you are using Prisma's client directly and calling `create()` with a plain-text password, Wasp's middleware takes care of hashing it before storing it in the database. An additional middleware also performs field validation. -::: - - - -### Social Login Providers (OAuth 2.0) -Wasp allows you to easily add social login providers to your app. - -The following is a list of links to guides that will help you get started with the currently supported providers: -- [GitHub](/docs/integrations/github) -- [Google](/docs/integrations/google) - -When using Social Login Providers, Wasp gives you the following options: -- Default settings to get you started quickly -- UI Helpers to make it easy to add social login buttons and actions -- Override settings to customize the behavior of the providers - -#### Default Settings - - - - - -```wasp - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - google: {}, - }, - } -``` - -

Add google: {} to your auth.methods dictionary to use it with default settings

-

By default, Wasp expects you to set two environment variables in order to use Google authentication:

-
    -
  • GOOGLE_CLIENT_ID
  • -
  • GOOGLE_CLIENT_SECRET
  • -
-

These can be obtained in your Google Cloud Console project dashboard. See here for a detailed guide.

- -
- - -```wasp - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - gitHub: {}, - }, - } -``` - -

Add gitHub: {} to your auth.methods dictionary to use it with default settings

-

By default, Wasp expects you to set two environment variables in order to use GitHub authentication:

-
    -
  • GITHUB_CLIENT_ID
  • -
  • GITHUB_CLIENT_SECRET
  • -
-

These can be obtained in your GitHub project dashboard. See here for a detailed guide.

- -
-
- -When a user signs in for the first time, if the `userEntity` has `username` and/or `password` fields Wasp assigns generated values to those fields by default (e.g. `username: nice-blue-horse-14357` and a strong random `password`). This is a historical coupling between auth methods that will be removed over time. If you'd like to change this behavior, these values can be overridden as described below. - -:::tip Overriding Defaults -It is also possible to [override the default](features#overrides-for-social-login-providers) login behaviors that Wasp provides for you. This allows you to create custom setups, such as allowing Users to define a username rather than the default random username assigned by Wasp on initial Login. -::: - -#### `externalAuthEntity` -Anytime an authentication method is used that relies on an external authorization provider, for example, Google, we require an `externalAuthEntity` specified in `auth`, in addition to the `userEntity`, that contains the following configuration: - -```wasp {4,14} -//... - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, -//... - -entity User {=psl - id Int @id @default(autoincrement()) - //... - externalAuthAssociations SocialLogin[] -psl=} - -entity SocialLogin {=psl - id Int @id @default(autoincrement()) - provider String - providerId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId Int - createdAt DateTime @default(now()) - @@unique([provider, providerId, userId]) -psl=} -``` -:::note -the same `externalAuthEntity` can be used across different social login providers (e.g., both GitHub and Google can use the same entity). -::: -#### UI helpers - -Wasp provides sign-in buttons, logos and URLs for your login page: - -```jsx -... -import { SignInButton as GoogleSignInButton, signInUrl as googleSignInUrl, logoUrl as googleLogoUrl } from '@wasp/auth/helpers/Google' -import { SignInButton as GitHubSignInButton, signInUrl as gitHubSignInUrl, logoUrl as gitHubLogoUrl } from '@wasp/auth/helpers/GitHub' - -const Login = () => { - return ( - <> - ... - - - - {/* or */} - Sign in with Google - Sign in with GitHub - - ) -} - -export default Login -``` - -If you need more customization than what the buttons provide, you can create your own custom components using the `signInUrl`s. - -#### Overrides - -When a user signs in for the first time, Wasp will create a new User account and link it to the chosen Auth Provider account for future logins. If the `userEntity` contains a `username` field it will default to a random dictionary phrase that does not exist in the database, such as `nice-blue-horse-27160`. This is a historical coupling between auth methods that will be removed over time. - -If you would like to allow the user to select their own username, or some other sign up flow, you could add a boolean property to your `User` entity indicating the account setup is incomplete. You can then check this user's property on the client with the [`useAuth()`](#useauth) hook and redirect them when appropriate - - e.g. check on homepage if `user.isAuthSetup === false`, redirect them to `EditUserDetailsPage` where they can edit the `username` property. - -Alternatively, you could add a `displayName` property to your User entity and assign it using the details of their provider account. Below is an example of how to do this by using: - - the `getUserFieldsFn` function to configure the user's `username` or `displayName` from their provider account - -We also show you how to customize the configuration of the Provider's settings using: - - the `configFn` function - -```wasp title=main.wasp {9,10,13,14,26} -app Example { - //... - - auth: { - userEntity: User, - externalAuthEntity: SocialLogin, - methods: { - google: { - configFn: import { config } from "@server/auth/google.js", - getUserFieldsFn: import { getUserFields } from "@server/auth/google.js" - }, - gitHub: { - configFn: import { config } from "@server/auth/github.js", - getUserFieldsFn: import { getUserFields } from "@server/auth/github.js" - } - }, - - //... - } -} - -entity User {=psl - id Int @id @default(autoincrement()) - username String @unique - password String - displayName String? - externalAuthAssociations SocialLogin[] -psl=} - -//... - -``` - - -#### `configFn` - -This function should return an object with the following shape: - - - -```js title=src/server/auth/google.js -export function config() { - // ... - return { - clientID, // look up from env or elsewhere, - clientSecret, // look up from env or elsewhere, - scope: ['profile'] // must include at least 'profile' for Google - } -} - -// ... -``` - -

Here is a link to the default implementations as a reference

-
- - -```js title=src/server/auth/github.js -export function config() { - // ... - return { - clientID, // look up from env or elsewhere, - clientSecret, // look up from env or elsewhere, - scope: [] // default is an empty array for GitHub - } -} - -// ... -``` - -

Here is a link to the default implementations as a reference

-
-
- -#### `getUserFieldsFn` - -This function should return the user fields to use when creating a new user upon their first time logging in with a Social Auth Provider. The context contains a User entity for DB access, and the args are what the OAuth provider responds with. Here is how you could generate a username based on the Google display name. In your model, you could choose to add more attributes and set additional information. - ```js title=src/server/auth/google.js - import { generateAvailableUsername } from '@wasp/core/auth.js' - - // ... - - export async function getUserFields(_context, args) { - const username = await generateAvailableUsername(args.profile.displayName.split(' '), { separator: '.' }) - return { username } - } - ``` - - Or you could set the optional `displayName` property on the `User` entity instead: - ```js title=src/server/auth/google.js - import { generateAvailableDictionaryUsername, generateAvailableUsername } from '@wasp/core/auth.js' - - // ... - - export async function getUserFields(_context, args) { - const username = await generateAvailableDictionaryUsername() - const displayName = await generateAvailableUsername(args.profile.displayName.split(' '), { separator: '.' }) - return { username, displayName } - } - ``` - - `generateAvailableUsername` takes an array of Strings and an optional separator and generates a string ending with a random number that is not yet in the database. For example, the above could produce something like "Jim.Smith.3984" for a Google user Jim Smith. - - `generateAvailableDictionaryUsername` generates a random dictionary phrase that is not yet in the database. For example, `nice-blue-horse-27160`. - - -### Validation Error Handling -When creating, updating, or deleting entities, you may wish to handle validation errors. We have exposed a class called `AuthError` for this purpose. This could also be combined with [Prisma Error Helpers](/docs/language/features#prisma-error-helpers). - -#### `import statement`: -```js -import AuthError from '@wasp/core/AuthError.js' -``` - -##### Example of usage: -```js -try { - await context.entities.User.update(...) -} catch (e) { - if (e instanceof AuthError) { - throw new HttpError(422, 'Validation failed', { message: e.message }) - } else { - throw e - } -} -``` - -## Accessing the currently logged in user -When authentication is enabled in a Wasp app, we need a way to tell whether a user is logged in and access its data. -With that, we can further implement access control and decide which content is private and which public. - -#### On the client -On the client, Wasp provides a React hook you can use in functional components - `useAuth`. -This hook is actually a thin wrapper over Wasp's [`useQuery` hook](http://localhost:3002/docs/language/features#the-usequery-hook) and returns data in the same format. - -### `useAuth()` -#### `import statement`: -```js -import useAuth from '@wasp/auth/useAuth' -``` - -##### Example of usage: -```js title="src/client/pages/MainPage.js" -import React from 'react' - -import { Link } from 'react-router-dom' -import useAuth from '@wasp/auth/useAuth' -import logout from '@wasp/auth/logout' -import Todo from '../Todo' -import '../Main.css' - -const Main = () => { - const { data: user } = useAuth() - - if (!user) { - return ( - - Please login or sign up. - - ) - } else { - return ( - <> - - - < /> - ) - } -} - -export default Main -``` - -#### On the server - -### `context.user` - -When authentication is enabled, all operations (actions and queries) will have access to the `user` through the `context` argument. `context.user` will contain all the fields from the user entity except for the password. - -##### Example of usage: -```js title="src/server/actions.js" -import HttpError from '@wasp/core/HttpError.js' - -export const createTask = async (task, context) => { - if (!context.user) { - throw new HttpError(403) - } - - const Task = context.entities.Task - return Task.create({ - data: { - description: task.description, - user: { - connect: { id: context.user.id } - } - } - }) -} -``` -In order to implement access control, each operation is responsible for checking `context.user` and -acting accordingly - e.g. if `context.user` is `undefined` and the operation is private then user -should be denied access to it. - -## Client configuration - -You can configure the client using the `client` field inside the `app` -declaration, - -```wasp -app MyApp { - title: "My app", - // ... - client: { - rootComponent: import Root from "@client/Root.jsx", - setupFn: import mySetupFunction from "@client/myClientSetupCode.js" - } -} -``` - -`app.client` is a dictionary with the following fields: - -#### `rootComponent: ClientImport` (optional) - -`rootComponent` defines the root component of your client application. It is -expected to be a React component, and Wasp will use it to wrap your entire app. -It must render its children, which are the actual pages of your application. - -You can use it to define a common layout for your application: - -```jsx title="src/client/Root.jsx" -export default async function Root({ children }) { - return ( -
-
-

My App

-
- {children} -
-

My App footer

-
-
- ) -} -``` - -You can use it to set up various providers that your application needs: - -```jsx title="src/client/Root.jsx" -import store from './store' -import { Provider } from 'react-redux' - -export default async function Root({ children }) { - return ( - - {children} - - ) -} -``` - -As long as you render the children, you can do whatever you want in your root -component. Here's an example of a root component both sets up a provider and -renders a custom layout: - -```jsx title="src/client/Root.jsx" -import store from './store' -import { Provider } from 'react-redux' - -export default function Root({ children }) { - return ( - - - {children} - - - ) -} - -function Layout({ children }) { - return ( -
-
-

My App

-
- {children} -
-

My App footer

-
-
- ) -} -``` - - -#### `setupFn: ClientImport` (optional) - -`setupFn` declares a JavaScript function that Wasp executes on the client -before everything else. It is expected to be asynchronous, and -Wasp will await its completion before rendering the page. The function takes no -arguments, and its return value is ignored. - -You can use this function to perform any custom setup (e.g., setting up -client-side periodic jobs). - -Here's a dummy example of such a function: - -```js title="src/client/myClientSetupCode.js" -export default async function mySetupFunction() { - let count = 1; - setInterval( - () => console.log(`You have been online for ${count++} hours.`), - 1000 * 60 * 60, - ) -} -``` - -##### Overriding default behaviour for Queries -As mentioned, our `useQuery` hook uses _react-query_'s hook of the same name. -Since _react-query_ comes configured with aggressive but sane default options, -you most likely won't have to change those defaults for all Queries (you can -change them for a single Query using the `options` object, as described -[here](#the-usequery-hook)). - -Still, if you do need the global defaults, you can do so inside client setup -function. Wasp exposes a `configureQueryClient` hook that lets you configure -_react-query_'s `QueryClient` object: - - -```js title="src/client/myClientSetupCode.js" -import { configureQueryClient } from '@wasp/queryClient' - -export default async function mySetupFunction() { - // ... some setup - configureQueryClient({ - defaultOptions: { - queries: { - staleTime: Infinity, - } - } - }) - // ... some more setup -} -``` - -Make sure to pass in an object expected by the `QueryClient`'s constructor, as -explained in -[_react-query_'s docs](https://tanstack.com/query/v4/docs/react/reference/QueryClient). - -## Public static files on the client - -If you wish to override the default `favicon.ico` file or expose any other static files to the client, you can do so by placing them in the `public` directory in the `src/client` folder. - -The contents of this directory will be copied to the `dist/public` directory during the build process. This makes these files available at the root of the domain. For example, if you have a file `favicon.ico` in the `public` directory, it will be available at `https://example.com/favicon.ico`. - -For example, doing this: -``` -src -└── client - β”œβ”€β”€ public - β”‚ └── favicon.ico - └── ... -``` -will result in the following directory structure in the `build` folder: -``` -build -└── public - └── favicon.ico -``` - -:::warning Usage in client code -You **can't import these files** from your client code. They are only exposed at the root of the domain, e.g. `https://example.com/favicon.ico`. -::: - -## Server configuration - -Via `server` field of `app` declaration, you can configure the behavior of the Node.js server (one that is executing wasp operations). - -```wasp -app MyApp { - title: "My app", - // ... - server: { - setupFn: import mySetupFunction from "@server/myServerSetupCode.js" - } -} -``` - -`app.server` is a dictionary with the following fields: - -#### `middlewareConfigFn: ServerImport` (optional) - -The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See [the guide here](/docs/guides/middleware-customization#1-customize-global-middleware). - -#### `setupFn: ServerImport` (optional) - -`setupFn` declares a JS function that will be executed on server start. This function is expected to be async and will be awaited before the server starts accepting any requests. - -It allows you to do any custom setup, e.g. setting up additional database/websockets or starting cron/scheduled jobs. - -The `setupFn` function receives the `express.Application` and the `http.Server` instances as part of its context. They can be useful for setting up any custom server routes or for example, setting up `socket.io`. -```ts -export type ServerSetupFn = (context: ServerSetupFnContext) => Promise - -export type ServerSetupFnContext = { - app: Application, // === express.Application - server: Server, // === http.Server -} -``` - -As an example, adding a custom route would look something like: -```ts title="src/server/myServerSetupCode.ts" -import { ServerSetupFn, Application } from '@wasp/types' - -const mySetupFunction: ServerSetupFn = async ({ app }) => { - addCustomRoute(app) -} - -function addCustomRoute(app: Application) { - app.get('/customRoute', (_req, res) => { - res.send('I am a custom route') - }) -} -``` - -In case you want to store some values for later use, or to be accessed by the Operations, recommended way is to store those in variables in the same module/file where you defined the javascript setup function and then expose additional functions for reading those values, which you can then import directly from Operations and use. This effectively turns your module into a singleton whose construction is performed on server start. - -Dummy example of such function and its usage: - -```js title="src/server/myServerSetupCode.js" -let someResource = undefined - -const mySetupFunction = async () => { - // Let's pretend functions setUpSomeResource and startSomeCronJob - // are implemented below or imported from another file. - someResource = await setUpSomeResource() - startSomeCronJob() -} - -export const getSomeResource = () => someResource - -export default mySetupFunction -``` - -```js title="src/server/queries.js" -import { getSomeResource } from './myServerSetupCode.js' - -... - -export const someQuery = async (args, context) => { - const someResource = getSomeResource() - return queryDataFromSomeResource(args, someResource) -} -``` - - -## .env - -Your project will likely be using environment variables for configuration, typically to define connection to the database, API keys for external services and similar. - -When in production, you will typically define environment variables through mechanisms provided by your hosting provider. - -However, when in development, you might also need to supply certain environment variables, and to avoid doing it "manually", Wasp supports `.env` (dotenv) files where you can define environment variables that will be used during development (they will not be used during production). - -Since environmental variables are usually different for server-side and client apps, in Wasp root directly you can create two files, `.env.server` for server-side of your Wasp project, and `.env.client` for client side (or web app) of Wasp project. - - -`.env.server` and `.env.client` files have to be defined in the root of your Wasp project. - -`.env.server` and `.env.client` files should not be committed to the version control - we already ignore it by default in the .gitignore file we generate when you create a new Wasp project via `wasp new` cli command. - -Variables are defined in `.env.server` or `.env.client` files in the form of `NAME=VALUE`, for example: -``` -DATABASE_URL=postgresql://localhost:5432 -MY_VAR=somevalue -``` - -Any env vars defined in the `.env.server` / `.env.client` files will be forwarded to the server-side / web app of your Wasp project and therefore accessible in your javascript code via `process.env`, for example: -```js -console.log(process.env.DATABASE_URL) -``` - -## Database configuration - -Via `db` field of `app` declaration, you can configure the database used by Wasp. - -```wasp -app MyApp { - title: "My app", - // ... - db: { - system: PostgreSQL, - seeds: [ - import devSeed from "@server/dbSeeds.js" - ], - prisma: { - clientPreviewFeatures: ["extendedWhereUnique"] - } - } -} -``` - -`app.db` is a dictionary with following fields: - -#### - `system: DbSystem` (Optional) -The database system Wasp will use. It can be either `PostgreSQL` or `SQLite`. -If not defined, or even if whole `db` field is not present, default value is `SQLite`. -If you add/remove/modify `db` field, run `wasp db migrate-dev` to apply the changes. - -#### - `seeds: [ServerImport]` (Optional) -Defines seed functions that you can use via `wasp db seed` to seed your database with initial data. -Check out [Seeding](#seeding) section for more details. - -#### - `prisma: [PrismaOptions]` (Optional) -Additional configuration for Prisma. -It currently only supports a single field: - - `clientPreviewFeatures : string` - allows you to define [Prisma client preview features](https://www.prisma.io/docs/concepts/components/preview-features/client-preview-features). - - -### SQLite -Default database is `SQLite`, since it is great for getting started with a new project (needs no configuring), but it can be used only in development - once you want to deploy Wasp to production you will need to switch to `PostgreSQL` and stick with it. -Check below for more details on how to migrate from SQLite to PostgreSQL. - -### PostgreSQL -When using `PostgreSQL` as your database (`app: { db: { system: PostgreSQL } }`), you will need to make sure you have a postgres database running during development (when running `wasp start` or doing `wasp db ...` commands). - -### Using Wasp provided dev database - -Wasp provides `wasp start db` command that starts the default dev db for you. - -Your Wasp app will automatically connect to it once you have it running via `wasp start db`, no additional configuration is needed. This command relies on Docker being installed on your machine. - -### Connecting to existing database - -If instead of using `wasp start db` you would rather spin up your own dev database or connect to some external database, you will need to provide Wasp with `DATABASE_URL` environment variable that Wasp will use to connect to it. - -The easiest way to provide the needed `DATABASE_URL` environment variable is by adding it to the [.env.server](https://wasp-lang.dev/docs/language/features#env) file in the root dir of your Wasp project (if that file doesn't yet exist, create it). - -You can also set it per command by doing `DATABASE_URL= wasp ...` -> this can be useful if you want to run specific `wasp` command on a specific database. -Example: you could do `DATABASE_URL= wasp db seed myProdSeed` to seed data for a fresh staging or production database. - -### Migrating from SQLite to PostgreSQL -To run Wasp app in production, you will need to switch from `SQLite` to `PostgreSQL`. - -1. Set `app.db.system` to `PostgreSQL`. -3. Delete old migrations, since they are SQLite migrations and can't be used with PostgreSQL: `rm -r migrations/`. -3. Run `wasp start db` to start your new db running (or check instructions above if you prefer using your own db). Leave it running, since we need it for the next step. -4. In a different terminal, run `wasp db migrate-dev` to apply new changes and create new, initial migration. -5. That is it, you are all done! - -### Seeding - -**Database seeding** is a term for populating database with some initial data. - -Seeding is most commonly used for two following scenarios: - 1. To put development database into a state convenient for testing / playing with it. - 2. To initialize dev/staging/prod database with some essential data needed for it to be useful, - for example default currencies in a Currency table. - -#### Writing a seed function - -Wasp enables you to define multiple **seed functions** via `app.db.seeds`: - -```wasp -app MyApp { - // ... - db: { - // ... - seeds: [ - import { devSeedSimple } from "@server/dbSeeds.js", - import { prodSeed } from "@server/dbSeeds.js" - ] - } -} -``` - -Each seed function is expected to be an async function that takes one argument, `prismaClient`, which is a [Prisma Client](https://www.prisma.io/docs/concepts/components/prisma-client/crud) instance that you can use to interact with the database. -This is the same instance of Prisma Client that Wasp uses internally, so you e.g. get password hashing for free. - -Since a seed function is part of the server-side code, it can also import other server-side code, so you can and will normally want to import and use Actions to perform the seeding. - -Example of a seed function that imports an Action (+ a helper function to create a user): - -```js -import { createTask } from './actions.js' - -export const devSeedSimple = async (prismaClient) => { - const user = await createUser(prismaClient, { - username: "RiuTheDog", - password: "bark1234" - }) - - await createTask( - { description: "Chase the cat" }, - { user, entities: { Task: prismaClient.task } } - ) -} - -async function createUser (prismaClient, data) { - const { password, ...newUser } = await prismaClient.user.create({ data }) - return newUser -} -``` - -#### Running seed functions - - - `wasp db seed`: If you have just one seed function, it will run it. If you have multiple, it will interactively ask you to choose one to run. - - - `wasp db seed `: It will run the seed function with the specified name, where the name is the identifier you used in its `import` expression in the `app.db.seeds` list. Example: `wasp db seed devSeedSimple`. - -:::tip - Often you will want to call `wasp db seed` right after you ran `wasp db reset`: first you empty your database, then you fill it with some initial data. -::: - - -## Email sender - -#### `provider: EmailProvider` (required) - -We support multiple different providers for sending e-mails: `SMTP`, `SendGrid` and `Mailgun`. - -### SMTP - -SMTP e-mail sender uses your SMTP server to send e-mails. - -Read [our guide](/docs/advanced/email#using-the-smtp-provider) for setting up SMTP for more details. - - -### SendGrid - -SendGrid is a popular service for sending e-mails that provides both API and SMTP methods of sending e-mails. We use their official SDK for sending e-mails. - -Check out [our guide](/docs/advanced/email#using-the-sendgrid-provider) for setting up Sendgrid for more details. - -### Mailgun - -Mailgun is a popular service for sending e-mails that provides both API and SMTP methods of sending e-mails. We use their official SDK for sending e-mails. - -Check out [our guide](/docs/advanced/email#using-the-mailgun-provider) for setting up Mailgun for more details. - -#### `defaultSender: EmailFromField` (optional) - -You can optionally provide a default sender info that will be used when you don't provide it explicitly when sending an e-mail. - -```wasp -app MyApp { - title: "My app", - // ... - emailSender: { - provider: SMTP, - defaultFrom: { - name: "Hello", - email: "hello@itsme.com" - }, - }, -} -``` - -After you set up the email sender, you can use it in your code to send e-mails. For example, you can send an e-mail when a user signs up, or when a user resets their password. - -### Sending e-mails - - - -To send an e-mail, you can use the `emailSender` that is provided by the `@wasp/email` module. - -```ts title="src/actions/sendEmail.js" -import { emailSender } from '@wasp/email/index.js' - -// In some action handler... -const info = await emailSender.send({ - to: 'user@domain.com', - subject: 'Saying hello', - text: 'Hello world', - html: 'Hello world' -}) -``` diff --git a/web/versioned_docs/version-0.11.8/project/client-config.md b/web/versioned_docs/version-0.11.8/project/client-config.md index e7ca198e0..9b9070ef9 100644 --- a/web/versioned_docs/version-0.11.8/project/client-config.md +++ b/web/versioned_docs/version-0.11.8/project/client-config.md @@ -211,7 +211,7 @@ export default async function mySetupFunction(): Promise { ### Overriding Default Behaviour for Queries :::info -You can change the options for a **single** Query using the `options` object, as described [here](/docs/data-model/operations/queries#the-usequery-hook-1). +You can change the options for a **single** Query using the `options` object, as described [here](../data-model/operations/queries#the-usequery-hook-1). ::: Wasp's `useQuery` hook uses `react-query`'s `useQuery` hook under the hood. Since `react-query` comes configured with aggressive but sane default options, you most likely won't have to change those defaults for all Queries. diff --git a/web/versioned_docs/version-0.11.8/project/css-frameworks.md b/web/versioned_docs/version-0.11.8/project/css-frameworks.md index f5ca4ecdf..5fa751aee 100644 --- a/web/versioned_docs/version-0.11.8/project/css-frameworks.md +++ b/web/versioned_docs/version-0.11.8/project/css-frameworks.md @@ -85,7 +85,7 @@ Make sure to use the `.cjs` extension for these config files, if you name them w ### Adding Tailwind Plugins -To add Tailwind plugins, add it to [dependencies](/docs/project/dependencies) in your `main.wasp` file and to the plugins list in your `tailwind.config.cjs` file: +To add Tailwind plugins, add it to [dependencies](../project/dependencies) in your `main.wasp` file and to the plugins list in your `tailwind.config.cjs` file: ```wasp title="./main.wasp" {4-5} app todoApp { diff --git a/web/versioned_docs/version-0.11.8/project/customizing-app.md b/web/versioned_docs/version-0.11.8/project/customizing-app.md index fe3bffea4..08fc73b4b 100644 --- a/web/versioned_docs/version-0.11.8/project/customizing-app.md +++ b/web/versioned_docs/version-0.11.8/project/customizing-app.md @@ -113,28 +113,28 @@ The rest of the fields are covered in dedicated sections of the docs: - `auth: dict` - Authentication configuration. Read more in the [authentication section](/docs/auth/overview) of the docs. + Authentication configuration. Read more in the [authentication section](../auth/overview) of the docs. - `client: dict` - Configuration for the client side of your app. Read more in the [client configuration section](/docs/project/client-config) of the docs. + Configuration for the client side of your app. Read more in the [client configuration section](../project/client-config) of the docs. - `server: dict` - Configuration for the server side of your app. Read more in the [server configuration section](/docs/project/server-config) of the docs. + Configuration for the server side of your app. Read more in the [server configuration section](../project/server-config) of the docs. - `db: dict` - Database configuration. Read more in the [database configuration section](/docs/data-model/backends) of the docs. + Database configuration. Read more in the [database configuration section](../data-model/backends) of the docs. - `dependencies: [(string, string)]` - List of npm dependencies for your app. Read more in the [dependencies section](/docs/project/dependencies) of the docs. + List of npm dependencies for your app. Read more in the [dependencies section](../project/dependencies) of the docs. - `emailSender: dict` - Email sender configuration. Read more in the [email sending section](/docs/advanced/email) of the docs. + Email sender configuration. Read more in the [email sending section](../advanced/email) of the docs. - `webSocket: dict` - WebSocket configuration. Read more in the [WebSocket section](/docs/advanced/web-sockets) of the docs. + WebSocket configuration. Read more in the [WebSocket section](../advanced/web-sockets) of the docs. diff --git a/web/versioned_docs/version-0.11.8/project/env-vars.md b/web/versioned_docs/version-0.11.8/project/env-vars.md index d4157b239..775c75cc3 100644 --- a/web/versioned_docs/version-0.11.8/project/env-vars.md +++ b/web/versioned_docs/version-0.11.8/project/env-vars.md @@ -131,4 +131,4 @@ The way you provide env vars to your Wasp project in production depends on where flyctl secrets set SOME_VAR_NAME=somevalue ``` -You can read a lot more details in the [deployment section](/docs/advanced/deployment/manually) of the docs. We go into detail on how to define env vars for each deployment option. +You can read a lot more details in the [deployment section](../advanced/deployment/manually) of the docs. We go into detail on how to define env vars for each deployment option. diff --git a/web/versioned_docs/version-0.11.8/project/server-config.md b/web/versioned_docs/version-0.11.8/project/server-config.md index 5fe5e60f2..10800d1f5 100644 --- a/web/versioned_docs/version-0.11.8/project/server-config.md +++ b/web/versioned_docs/version-0.11.8/project/server-config.md @@ -90,7 +90,7 @@ function addCustomRoute(app: Application) { ### Storing Some Values for Later Use -In case you want to store some values for later use, or to be accessed by the [Operations](/docs/data-model/operations/overview) you do that in the `setupFn` function. +In case you want to store some values for later use, or to be accessed by the [Operations](../data-model/operations/overview) you do that in the `setupFn` function. Dummy example of such function and its usage: @@ -247,4 +247,4 @@ app MyApp { - #### `middlewareConfigFn: ServerImport` - The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See more in the [configuring middleware section](/docs/advanced/middleware-config#1-customize-global-middleware). + The import statement to an Express middleware config function. This is a global modification affecting all operations and APIs. See more in the [configuring middleware section](../advanced/middleware-config#1-customize-global-middleware). diff --git a/web/versioned_docs/version-0.11.8/project/testing.md b/web/versioned_docs/version-0.11.8/project/testing.md index b64f2ed0b..5816cd1c2 100644 --- a/web/versioned_docs/version-0.11.8/project/testing.md +++ b/web/versioned_docs/version-0.11.8/project/testing.md @@ -69,7 +69,7 @@ Wasp provides several functions to help you write React tests: const { mockQuery, mockApi } = mockServer(); ``` - - `mockQuery`: Takes a Wasp [query](/docs/data-model/operations/queries) to mock and the JSON data it should return. + - `mockQuery`: Takes a Wasp [query](../data-model/operations/queries) to mock and the JSON data it should return. ```js import getTasks from "@wasp/queries/getTasks"; @@ -81,7 +81,7 @@ Wasp provides several functions to help you write React tests: - Behind the scenes, Wasp uses [`msw`](https://npmjs.com/package/msw) to create a server request handle that responds with the specified data. - Mock are cleared between each test. - - `mockApi`: Similar to `mockQuery`, but for [APIs](/docs/advanced/apis). Instead of a Wasp query, it takes a route containing an HTTP method and a path. + - `mockApi`: Similar to `mockQuery`, but for [APIs](../advanced/apis). Instead of a Wasp query, it takes a route containing an HTTP method and a path. ```js import { HttpMethod } from "@wasp/types"; diff --git a/web/versioned_docs/version-0.11.8/tutorial/01-create.md b/web/versioned_docs/version-0.11.8/tutorial/01-create.md index a735e1667..96d31de7e 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/01-create.md +++ b/web/versioned_docs/version-0.11.8/tutorial/01-create.md @@ -5,7 +5,7 @@ title: 1. Creating a New Project import useBaseUrl from '@docusaurus/useBaseUrl'; :::info -You'll need to have the latest version of Wasp installed locally to follow this tutorial. If you haven't installed it yet, check out the [QuickStart](/docs/quick-start) guide! +You'll need to have the latest version of Wasp installed locally to follow this tutorial. If you haven't installed it yet, check out the [QuickStart](../quick-start) guide! ::: In this section, we'll guide you through the process of creating a simple Todo app with Wasp. In the process, we'll take you through the most important and useful features of Wasp. diff --git a/web/versioned_docs/version-0.11.8/tutorial/02-project-structure.md b/web/versioned_docs/version-0.11.8/tutorial/02-project-structure.md index 0160d5ceb..168e6ac85 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/02-project-structure.md +++ b/web/versioned_docs/version-0.11.8/tutorial/02-project-structure.md @@ -35,7 +35,7 @@ By _your code_, we mean the _"the code you write"_, as opposed to the code gener Many of the other files (`tsconfig.json`, `vite-env.d.ts`, etc.) are used by your IDE to improve your development experience with tools like autocompletion, intellisense, and error reporting. The file `vite.config.ts` is used to configure [Vite](https://vitejs.dev/guide/), Wasp's build tool of choice. -We won't be configuring Vite in this tutorial, so you can safely ignore the file. Still, if you ever end up wanting more control over Vite, you'll find everything you need to know in [custom Vite config docs](/docs/project/custom-vite-config.md). +We won't be configuring Vite in this tutorial, so you can safely ignore the file. Still, if you ever end up wanting more control over Vite, you'll find everything you need to know in [custom Vite config docs](../project/custom-vite-config.md). :::note TypeScript Support Wasp supports TypeScript out of the box, but you are free to choose between or mix JavaScript and TypeScript as you see fit. diff --git a/web/versioned_docs/version-0.11.8/tutorial/03-pages.md b/web/versioned_docs/version-0.11.8/tutorial/03-pages.md index ca0f9944a..7c8052f10 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/03-pages.md +++ b/web/versioned_docs/version-0.11.8/tutorial/03-pages.md @@ -142,7 +142,7 @@ Now you can visit `/hello/johnny` and see "Here's johnny!" :::tip Type-safe links -Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](/docs/advanced/links) for more details. +Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](../advanced/links) for more details. ::: diff --git a/web/versioned_docs/version-0.11.8/tutorial/04-entities.md b/web/versioned_docs/version-0.11.8/tutorial/04-entities.md index 42ee9d68c..2565c9d3d 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/04-entities.md +++ b/web/versioned_docs/version-0.11.8/tutorial/04-entities.md @@ -21,7 +21,7 @@ psl=} :::note Wasp uses [Prisma](https://www.prisma.io) as a way to talk to the database. You define entities by defining [Prisma models](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/data-model/) using the Prisma Schema Language (PSL) between the `{=psl psl=}` tags. -Read more in the [Entities](/docs/data-model/entities) section of the docs. +Read more in the [Entities](../data-model/entities) section of the docs. ::: To update the database schema to include this entity, stop the `wasp start` process, if its running, and run: diff --git a/web/versioned_docs/version-0.11.8/tutorial/05-queries.md b/web/versioned_docs/version-0.11.8/tutorial/05-queries.md index 748de193f..2056a71e8 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/05-queries.md +++ b/web/versioned_docs/version-0.11.8/tutorial/05-queries.md @@ -5,7 +5,7 @@ title: 5. Querying the Database import useBaseUrl from '@docusaurus/useBaseUrl'; import { ShowForTs, ShowForJs } from '@site/src/components/TsJsHelpers'; -We want to know which tasks we need to do, so let's list them! The primary way of interacting with entities in Wasp is by using [queries and actions](/docs/data-model/operations/overview), collectively known as _operations_. +We want to know which tasks we need to do, so let's list them! The primary way of interacting with entities in Wasp is by using [queries and actions](../data-model/operations/overview), collectively known as _operations_. Queries are used to read an entity, while actions are used to create, modify, and delete entities. Since we want to list the tasks, we'll want to use a query. @@ -225,14 +225,14 @@ Most of this code is regular React, the only exception being the two< - `import getTasks from '@wasp/queries/getTasks'` - Imports the client-side query function. -- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](/docs/data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. +- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](../data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. - `import getTasks from '@wasp/queries/getTasks'` - Imports the client-side query function. -- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](/docs/data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. +- `import { useQuery } from '@wasp/queries'` - Imports Wasp's [useQuery](../data-model/operations/queries#the-usequery-hook-1) React hook, which is based on [react-query](https://github.com/tannerlinsley/react-query)'s hook with the same name. - `import { Task } from '@wasp/entities'` - The type for the task entity we defined in `main.wasp`. Notice how you don't need to annotate the type of the query's return value: Wasp uses the types you defined while implementing the query for the generated client-side function. This is **full-stack type safety**: the types on the client always match the types on the server. diff --git a/web/versioned_docs/version-0.11.8/tutorial/07-auth.md b/web/versioned_docs/version-0.11.8/tutorial/07-auth.md index 8e7ebde46..2efeb6d3d 100644 --- a/web/versioned_docs/version-0.11.8/tutorial/07-auth.md +++ b/web/versioned_docs/version-0.11.8/tutorial/07-auth.md @@ -39,7 +39,7 @@ wasp db migrate-dev ## Adding Auth to the Project -Next, we want to tell Wasp that we want to use full-stack [authentication](/docs/auth/overview) in our app: +Next, we want to tell Wasp that we want to use full-stack [authentication](../auth/overview) in our app: ```wasp {7-16} title="main.wasp" app TodoApp { @@ -65,13 +65,13 @@ app TodoApp { By doing this, Wasp will create: -- [Auth UI](/docs/auth/ui) with login and signup forms. +- [Auth UI](../auth/ui) with login and signup forms. - A `logout()` action. - A React hook `useAuth()`. - `context.user` for use in Queries and Actions. :::info -Wasp also supports authentication using [Google](/docs/auth/social-auth/google), [GitHub](/docs/auth/social-auth/github), and [email](/docs/auth/email), with more on the way! +Wasp also supports authentication using [Google](../auth/social-auth/google), [GitHub](../auth/social-auth/github), and [email](../auth/email), with more on the way! ::: ## Adding Login and Signup Pages @@ -216,7 +216,7 @@ export default SignupPage :::tip Type-safe links -Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](/docs/advanced/links) for more details. +Since you are using Typescript, you can benefit from using Wasp's type-safe `Link` component and the `routes` object. Check out the [type-safe links docs](../advanced/links) for more details. ::: @@ -515,8 +515,8 @@ You should be ready to learn about more complicated features and go more in-dept Looking for inspiration? -- Get a jump start on your next project with [Starter Templates](/docs/project/starter-templates) -- Make a real-time app with [Web Sockets](/docs/advanced/web-sockets) +- Get a jump start on your next project with [Starter Templates](../project/starter-templates) +- Make a real-time app with [Web Sockets](../advanced/web-sockets) :::note If you notice that some of the features you'd like to have are missing, or have any other kind of feedback, please write to us on [Discord](https://discord.gg/rzdnErX) or create an issue on [Github](https://github.com/wasp-lang/wasp), so we can learn which features to add/improve next πŸ™ diff --git a/web/versioned_docs/version-0.11.8/typescript.md b/web/versioned_docs/version-0.11.8/typescript.md deleted file mode 100644 index 6f04d7277..000000000 --- a/web/versioned_docs/version-0.11.8/typescript.md +++ /dev/null @@ -1,521 +0,0 @@ ---- -title: TypeScript Support ---- - -import OldDocsNote from '@site/docs/OldDocsNote' - -# Using Wasp with TypeScript - - - -TypeScript is a programming language that brings static type analysis to JavaScript. It is a superset of JavaScript (i.e., all valid JavaScript programs are valid TypeScript programs) and compiles to JavaScript before running. TypeScript's type system detects common errors at build time (reducing the chance of runtime errors in production) and enables type-based auto-completion in IDEs. - -This document assumes you are familiar with TypeScript and primarily focuses on how to use it with Wasp. To learn more about TypeScript itself, we recommend reading [the official docs](https://www.typescriptlang.org/docs/). - -The document also assumes a basic understanding of core Wasp features (e.g., Queries, Actions, Entities). You can read more about these features in [our feature docs](/docs/language/features). - -Besides allowing you to write your code in TypeScript, Wasp also supports: - -- Importing and using Wasp Entity types (on both the server and the client). -- Automatic full-stack type support for Queries and Actions - frontend types are automatically inferred from backend definitions. -- Type-safe generic hooks (`useQuery` and `useAction`) with the accompanying type inference. -- Type-safe optimistic update definitions. - -We'll dig into the details of each feature in the following sections. But first, let's see how you can introduce TypeScript to an existing Wasp project. - -:::info -To get the best IDE experience, make sure to leave `wasp start` running in the background. Wasp will track the working directory and ensure the generated code/types are up to date with your changes. - -Your editor may sometimes report type and import errors even while `wasp start` is running. This happens when the TypeScript Language Server gets out of sync with the current code. If you're using VS Code, you can manually restart the language server by opening the command palette and selecting _"TypeScript: Restart TS Server."_ -::: - -## Migrating your project to TypeScript - -Wasp supports TypeScript out of the box! - -Our scaffolding already includes TypeScript, so migrating your project to TypeScript is as simple as changing file extensions and using the language. This approach allows you to gradually migrate your project on a file-by-file basis. - -### Example - -Let's first assume your Wasp file contains the following definitions: - -```wasp title=main.wasp -entity Task {=psl - id Int @id @default(autoincrement()) - description String - isDone Boolean @default(false) -psl=} - -query getTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Let's now assume that your `queries.js` file looks something like this: - -```javascript title="src/server/queries.js" -import HttpError from "@wasp/core/HttpError.js" - -function getInfoMessage(task) { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` -To migrate this file to TypeScript, all you have to do is: - -1. Change the filename from `queries.js` to `queries.ts`. -2. Write some types. - -Let's start by only providing a basic `getInfoMessage` function. We'll see how to properly type the rest of the file in the following sections. - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" - -// highlight-next-line -function getInfoMessage(task: { - isDone: boolean - description: string -}): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` - -You don't need to change anything inside the `.wasp` file. -:::caution - - - -Even when you use TypeScript, and your file is called `queries.ts`, you still need to import it using the `.js` extension: - -```wasp -query getTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp internally uses `esnext` module resolution, which always requires specifying the extension as `.js` (i.e., the extension used in the emitted JS file). This applies to all `@server` imports (and files on the server in general). This quirk does not apply to client files (the transpiler takes care of it). - -Read more about ES modules in TypeScript [here](https://www.typescriptlang.org/docs/handbook/esm-node.html). If you're interested in the discussion and the reasoning behind this, read about it [in this GitHub issue](https://github.com/microsoft/TypeScript/issues/33588). -::: - -## Entity Types - -Instead of manually specifying the types for `isDone` and `description`, we can get them from the `Task` entity type. Wasp will generate types for all entities and let you import them from `"@wasp/entities"`: - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" -// highlight-next-line -import { Task } from "@wasp/entities" - -// highlight-next-line -function getInfoMessage(task: Pick): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -export const getTaskInfo = async ({ id }, context) => { - const Task = context.entities.Task - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - return getInfoMessage(task) -} -``` - -By doing this, we've connected the argument type of the `getInfoMessage` function with the `Task` entity. This coupling removes duplication and ensures the function keeps the correct signature even if we change the entity. Of course, the function might throw type errors depending on how we change the entity, but that's precisely what we want! - -Don't worry about typing the query function for now. We'll see how to handle this in the next section. - -Entity types are also available on the client under the same import: - -```tsx title=src/client/Main.jsx -import { Task } from "@wasp/entities" - -export function ExamplePage() {} - const task: Task = { - id: 123, - description: "Some random task", - isDone: false, - } - return
{task.description}
-} - -``` - -The mentioned type safety mechanisms also apply here: changing the task entity in our `.wasp` file changes the imported type, which might throw a type error and warn us that our task definition is outdated. - -## Backend type support for Queries and Actions - -Wasp automatically generates the appropriate types for all Operations (i.e., Actions and Queries) you define inside your `.wasp` file. Assuming your `.wasp` file contains the following definition: - -```wasp title=main.wasp -// ... - -query GetTaskInfo { - fn: import { getTaskInfo } from "@server/queries.js", - entities: [Task] -} -``` - -Wasp will generate a type called `GetTaskInfo`, which you can use to type the Query's implementation. By assigning the `GetTaskInfo` type to your function, you get the type information for its context. In this case, TypeScript will know the `context.entities` object must include the `Task` entity. If the Query had auth enabled, it would also know that `context` includes user information. - -`GetTaskInfo` can is a generic type that takes two (optional) type arguments: - -1. `Input` - The argument (i.e., payload) received by the query function. -2. `Output` - The query function's return type. - -Suppose you don't care about typing the Query's inputs and outputs. In that case, you can omit both type arguments, and TypeScript will infer the most general types (i.e., `never` for the input, `unknown` for the output.). - -```typescript title=src/server/queries.ts -import HttpError from "@wasp/core/HttpError.js" -import { Task } from "@wasp/entities" -// highlight-next-line -import { GetTaskInfo } from "@wasp/queries/types" - -function getInfoMessage(task: Pick): string { - const isDoneText = task.isDone ? "is done" : "is not done" - return `Task '${task.description}' is ${isDoneText}.` -} - -// Use the type parameters to specify the Query's argument and return types. -// highlight-next-line -export const getTaskInfo: GetTaskInfo, string> = async ({ id }, context) => { - // Thanks to the definition in your .wasp file, the compiler knows the type of - // `context` (and that it contains the `Task` entity). - const Task = context.entities.Task - - // Thanks to the first type argument in `GetTaskInfo`, the compiler knows `args` - // is of type `Pick`. - const task = await Task.findUnique({ where: { id } }) - if (!task) { - throw new HttpError(404) - } - - // Thanks to the second type argument in `GetTaskInfo`, the compiler knows the - // function must return a value of type `string`. - return getInfoMessage(task) -} -``` -Everything described above applies to Actions as well. -:::tip - -If don't want to define a new type for the Query's return value, the new `satisfies` keyword will allow TypeScript to infer it automatically: -```typescript -const getFoo = (async (_args, context) => { - const foos = await context.entities.Foo.findMany() - return { - foos, - message: "Here are some foos!", - queriedAt: new Date(), - } -}) satisfies GetFoo -``` -From the snippet above, TypeScript knows: -1. The correct type for `context`. -2. The Query's return type is `{ foos: Foo[], message: string, queriedAt: Date }`. - -If you don't need the context, you can skip specifying the Query's type (and arguments): -```typescript -const getFoo = () => {{ name: 'Foo', date: new Date() }} -``` - -::: - -## Frontend type support for Queries and Actions - -Wasp supports automatic full-stack type safety Γ  la tRPC. You only need to define the Operation's type on the backend, and the frontend will automatically know how to call it. - -### Frontend type support for Queries -The examples assume you've defined the Query `getTaskInfo` from the previous sections: - -```typescript title="src/server/queries.ts" -export const getTaskInfo: GetTaskInfo, string> = - async ({ id }, context) => { - // ... - } -``` - -Wasp will use the type of `getTaskInfo` to infer the Query's types on the frontend: - -```tsx title="src/client/TaskInfo.tsx" -import { useQuery } from "@wasp/queries" -// Wasp knows the type of `getTaskInfo` thanks to your backend definition. -// highlight-next-line -import getTaskInfo from "@wasp/queries/getTaskInfo" - -export const TaskInfo = () => { - const { - // TypeScript knows `taskInfo` is a `string | undefined` thanks to the - // backend definition. - data: taskInfo, - // TypeScript also knows `isError` is a `boolean`. - isError, - // TypeScript knows `error` is of type `Error`. - error, - // TypeScript knows `id` must be a `Task["id"]` (i.e., a number) thanks to - // your backend definition. - // highlight-next-line - } = useQuery(getTaskInfo, { id: 1 }) - - if (isError) { - return
Error during fetching tasks: {error.message || "unknown"}
- } - - // TypeScript forces you to perform this check. - return taskInfo === undefined ? ( -
Waiting for info...
- ) : ( -
{taskInfo}
- ) -} -``` - -### Frontend type support for Actions - -Assuming the following action definition in your `.wasp` file - -```wasp title=main.wasp -action addTask { - fn: import { addTask } from "@server/actions.js" - entities: [Task] -} -``` - -And its corresponding implementation in `src/server/actions.ts`: - -```typescript title=src/server/actions.ts -import { AddTask } from "@wasp/actions/types" - -type TaskPayload = Pick - -const addTask: AddTask = async (args, context) => { - // ... -} -``` - -Here's how to use it on the frontend: -```tsx title=src/client/AddTask.tsx -import { useAction } from "@wasp/actions" -// TypeScript knows `addTask` is a function that expects a value of type -// `TaskPayload` and returns a value of type `Promise`. -import addTask from "@wasp/queries/addTask" - -const AddTask = ({ description }: Pick) => { - return ( -
- - -
- ) -} - -``` -#### Type support for the `useAction` hook -Type inference also works if you decide to use the action via the `useAction` hook: -```typescript -// addTaskFn is of type (args: TaskPayload) => Task -const addTaskFn = useAction(addTask) -``` - -The `useAction` hook also includes support for optimistic updates. Read [the feature docs](/docs/language/features#the-useaction-hook) to understand more about optimistic updates and how to define them in Wasp. - -Here's an example that shows how you can use static type checking in their definitions (the example assumes an appropriate action defined in the `.wasp` file and implemented on the server): - -```tsx title=Task.tsx -import { useQuery } from "@wasp/queries" -import { OptimisticUpdateDefinition, useAction } from "@wasp/actions" -import updateTaskIsDone from "@wasp/actions/updateTaskIsDone" - -type TaskPayload = Pick - -const Task = ({ taskId }: Pick) => { - const updateTaskIsDoneOptimistically = useAction( - updateTaskIsDone, - { - optimisticUpdates: [ - { - getQuerySpecifier: () => [getTask, { id: taskId }], - // This query's cache should should never be empty - updateQuery: ({ isDone }, oldTask) => ({ ...oldTask!, isDone }), - // highlight-next-line - } as OptimisticUpdateDefinition, - { - getQuerySpecifier: () => [getTasks], - updateQuery: (updatedTask, oldTasks) => - oldTasks && - oldTasks.map((task) => - task.id === updatedTask.id ? { ...task, ...updatedTask } : task - ), - // highlight-next-line - } as OptimisticUpdateDefinition, - ], - } - ) - // ... -} -``` - -## Database seeding - -When implementing a seed function in TypeScript, you can import a `DbSeedFn` type via - -```ts -import type { DbSeedFn } from "@wasp/dbSeed/types.js" -``` - -and use it to type your seed function like this: - -```ts -export const devSeedSimple: DbSeedFn = async (prismaClient) => { ... } -``` - -## CRUD operations on entities - -For a specific [Entity](/docs/language/features#entity), you can tell Wasp to automatically instantiate server-side logic ([Queries](/docs/language/features#query) and [Actions](/docs/language/features#action)) for creating, reading, updating and deleting such entities. - -Read more about CRUD operations in Wasp [here](/docs/language/features#crud-operations). - -### Using types for CRUD operations overrides - -If you writing the override implementation in Typescript, you'll have access to generated types. The overrides are functions that take the following arguments: -- `args` - The arguments of the operation i.e. the data that's sent from the client. -- `context` - Context containing the `user` making the request and the `entities` object containing the entity that's being operated on. - -You can types for each of the functions you want to override from `@wasp/crud/{crud name}`. The types that are available are: -- `GetAllQuery` -- `GetQuery` -- `CreateAction` -- `UpdateAction` -- `DeleteAction` - -If you have a CRUD named `Tasks`, you would import the types like this: -```ts -import type { GetAllQuery, GetQuery, CreateAction, UpdateAction, DeleteAction } from '@wasp/crud/Tasks' - -// Each of the types is a generic type, so you can use it like this: -export const getAllOverride: GetAllQuery = async (args, context) => { - // ... -} -``` - -## WebSocket full-stack type support - - -Defining event names with the matching payload types on the server makes those types exposed automatically on the client. This helps you avoid mistakes when emitting events or handling them. - -### Defining the events handler -On the server, you will get Socket.IO `io: Server` argument and `context` for your WebSocket function, which contains all entities you defined in your Wasp app. You can type the `webSocketFn` function like this: - -```ts title=src/server/webSocket.ts -import type { WebSocketDefinition, WaspSocketData } from '@wasp/webSocket' - -// Using the generic WebSocketDefinition type to define the WebSocket function. -type WebSocketFn = WebSocketDefinition< - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData -> - -interface ServerToClientEvents { - // The type for the payload of the "chatMessage" event. - chatMessage: (msg: { id: string, username: string, text: string }) => void; -} - -interface ClientToServerEvents { - // The type for the payload of the "chatMessage" event. - chatMessage: (msg: string) => void; -} - -interface InterServerEvents {} - -interface SocketData extends WaspSocketData {} - -// Use the WebSocketFn to type the webSocketFn function. -export const webSocketFn: WebSocketFn = (io, context) => { - io.on('connection', (socket) => { - socket.on('chatMessage', async (msg) => { - io.emit('chatMessage', { ... }) - }) - }) -} -``` - -### Using the WebSocket on the client - -After you have defined the WebSocket function on the server, you can use it on the client. The `useSocket` hook will give you the `socket` instance and the `isConnected` boolean. The `socket` instance is typed with the types you defined on the server. - -The `useSocketListener` hook will give you a type-safe event handler. The event name and its payload type are defined on the server. - -You can additonally use the `ClientToServerPayload` and `ServerToClientPayload` helper types to get the payload type for a specific event. - -```tsx title=src/client/ChatPage.tsx -import React, { useState } from 'react' -import { - useSocket, - useSocketListener, - ServerToClientPayload, - ClientToServerPayload, -} from '@wasp/webSocket' - -export const ChatPage = () => { - const [messageText, setMessageText] = useState< - // We are using a helper type to get the payload type for the "chatMessage" event. - ClientToServerPayload<'chatMessage'> - >('') - - const [messages, setMessages] = useState< - // We are using a helper type to get the payload type for the "chatMessage" event. - ServerToClientPayload<'chatMessage'>[] - >([]) - - // The "socket" instance is typed with the types you defined on the server. - const { socket, isConnected } = useSocket() - - // This is a type-safe event handler: "chatMessage" event and its payload type - // are defined on the server. - useSocketListener('chatMessage', logMessage) - - function logMessage(msg: ServerToClientPayload<'chatMessage'>) { - // ... - } - - function handleSubmit(e: React.FormEvent) { - e.preventDefault() - // This is a type-safe event emitter: "chatMessage" event and its payload type - // are defined on the server. - socket.emit('chatMessage', messageText) - setMessageText('') - } - - return ( - ... - ) -} -``` \ No newline at end of file diff --git a/web/versioned_sidebars/version-0.11.8-sidebars.json b/web/versioned_sidebars/version-0.11.8-sidebars.json index 53a9bfba3..a6dc4cf75 100644 --- a/web/versioned_sidebars/version-0.11.8-sidebars.json +++ b/web/versioned_sidebars/version-0.11.8-sidebars.json @@ -6,8 +6,8 @@ "collapsed": false, "collapsible": false, "items": [ - "introduction/what-is-wasp", - "introduction/getting-started", + "introduction/introduction", + "introduction/quick-start", "introduction/editor-setup" ] },