The fastest way to develop full-stack web apps with React & Node.js.
MIT License
Bot releases are visible (Hide)
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Check below for details on each of them!
import signup from '@wasp/auth/signup
now accepts only the user entity fields relevant to the auth process (e.g. username
and password
).age
or address
).useEffect
hooks run twice on component mount.Wasp now supports a public
directory in the client
directory!
main.wasp
src/
├── client/
| ├── public/ # <-- NEW!
| | ├── favicon.ico
| | └── robots.txt
| └── ...
└── ...
All the files in this directory will be copied as they are to the public
directory in the build folder.
This is useful for adding static assets to your project, like favicons, robots.txt, etc.
Wasp now supports WebSockets! This will allow you to have a persistent, realtime connection between your client and server, which is great for chat apps, games, and more.
What's more, Wasp's WebSockets support full-stack type safety, so you can be sure that your client and server are communicating with strongly typed events.
Enable WebSockets in your project by adding the following to your main.wasp
file:
app todoApp {
// ...
webSocket: {
fn: import { webSocketFn } from "@server/webSocket.js",
autoConnect: true, // optional, default: true
},
}
Then implement it on the server with optional types:
import type { WebSocketDefinition } from '@wasp/webSocket'
export const webSocketFn: WebSocketFn = (io, context) => {
io.on('connection', (socket) => {
// ...
})
}
type WebSocketFn = WebSocketDefinition<
ClientToServerEvents,
ServerToClientEvents
>
interface ServerToClientEvents {
chatMessage: (msg: { id: string, username: string, text: string }) => void;
}
interface ClientToServerEvents {
chatMessage: (msg: string) => void;
}
And use it on the client with automatic type inference:
import React, { useState } from 'react'
import {
useSocket,
useSocketListener,
ServerToClientPayload,
} 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<
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'>) {
setMessages((priorMessages) => [msg, ...priorMessages])
}
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
// This is a type-safe event emitter: "chatMessage" event and its payload type
// are defined on the server.
socket.emit('chatMessage', messageText)
// ...
}
// ...
}
You can tell Wasp to automatically generate server-side logic (Queries and Actions) for creating, reading, updating, and deleting a specific entity. As you change that entity, Wasp automatically regenerates the backend logic.
Example of a Task
entity with automatic CRUD:
crud Tasks {
entity: Task,
operations: {
getAll: {
isPublic: true, // by default only logged in users can perform operations
},
get: {},
create: {
overrideFn: import { createTask } from "@server/tasks.js",
},
update: {},
delete: {},
},
}
This gives us the following operations: getAll
, get
, create
, update
and delete
, which we can use in our client like this:
import { Tasks } from '@wasp/crud/Tasks'
import { useState } from 'react'
export const MainPage = () => {
const { data: tasks, isLoading, error } = Tasks.getAll.useQuery()
const createTask = Tasks.create.useAction()
// ...
function handleCreateTask() {
createTask({ description: taskDescription, isDone: false })
setTaskDescription('')
}
// ...
}
query getRecipes {
fn: import { getRecipes } from "@server/recipe.js", // <- You can now click on this import!
entities: [Recipe],
}
Wasp language server just got smarter regarding imports in wasp file!
We have more ideas in this direction on the way though!
A bit of a sneak peek of what is coming soon: if Wasp recognizes file / symbol is missing, it will offer to scaffold the code for you!
app RecipeApp {
title: "My Recipes",
wasp: { version: "^0.10.0" },
auth: {
methods: { usernameAndPassword: {} },
█ // <- your cursor
}
}
As per popular request, Wasp language server now recognizes when you are in dictionary and will offer possible key values for autocompletion!
For instance, in the code example above, it will offer completions such as onAuthFailedRedirectTo
, userEntity
, ... .
It will also show their types.
unique
attribute.Full Changelog: https://github.com/wasp-lang/wasp/compare/v0.10.6...v0.11.0
Published by github-actions[bot] over 1 year ago
useAuth
hook when user entity isn't named User
by @craigmc08 in https://github.com/wasp-lang/wasp/pull/1231
Wasp/Generator/Node/Version
to Wasp/Node/Version
by @craigmc08 in https://github.com/wasp-lang/wasp/pull/1226
Full Changelog: https://github.com/wasp-lang/wasp/compare/v0.10.6...v0.11.0-rc4
Published by github-actions[bot] over 1 year ago
Full Changelog: https://github.com/wasp-lang/wasp/compare/v0.11.0-rc2...v0.11.0-rc3
Published by github-actions[bot] over 1 year ago
Full Changelog: https://github.com/wasp-lang/wasp/compare/v0.11.0-rc1...v0.11.0-rc2
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Same like v0.10.6, but with React upgraded from 17 to 18. For testing.
Published by github-actions[bot] over 1 year ago
wasp deploy fly launch
now supports the latest flyctl launch
toml file for the web client (which changed their default structure and port).wasp deploy fly
optionswasp deploy fly
now supports a --org
option, as well as setting secrets during launch
.
Published by github-actions[bot] over 1 year ago
We now offer the ability to customize Express middleware:
We now offer an interactive way to create a new project. You can run wasp new
and follow the prompts to create a new project. This is the recommended way to create a new project. It will ask you for the project name and to choose one of the starter templates.
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
LoginForm
and SignupForm
to use a named export instead of a default export, this means you must use them like this:
import { LoginForm } from '@wasp/auth/forms/Login'
import { SignupForm } from '@wasp/auth/Signup'
useAuth.js
to useAuth.ts
and you should import it like this: import useAuth from '@wasp/auth/useAuth'
(without the .js
extension)useQuery
and useAction
hooks. They now take two arguments (the Error
type argument was removed):
Input
- This type argument specifies the type for the request's payload.Output
- This type argument specifies the type for the resposne's payload.Frontend code can now infer correct payload/response types for Queries and Actions from their definitions on the server.
Define a Query on the server:
export const getTask: GetTaskInfo<Pick<Task, "id">, Task> =
async ({ id }, context) => {
// ...
}
Get properly typed functions and data on the frontend:
import { useQuery } from "@wasp/queries"
// Wasp knows the type of `getTask` thanks to your backend definition.
import getTask from "@wasp/queries/getTask"
export const TaskInfo = () => {
const {
// TypeScript knows `task` is a `Task | undefined` thanks to the
// backend definition.
data: task,
// TypeScript knows `isError` is a `boolean`.
isError,
// TypeScript knows `error` is of type `Error`.
error,
// TypeScript knows the second argument must be a `Pick<Task, "id">` thanks
// to the backend definition.
} = useQuery(getTask, { id: 1 })
if (isError) {
return <div> Error during fetching tasks: {error.message || "unknown"}</div>
}
// TypeScript forces you to perform this check.
return taskInfo === undefined ? (
<div>Waiting for info...</div>
) : (
<div>{taskInfo}</div>
)
}
The same feature is available for Actions.
Client and the server can now communicate with richer payloads.
Return a Superjson-compatible object from your Operation:
type FooInfo = { foos: Foo[], message: string, queriedAt: Date }
const getFoos: GetFoo<void, FooInfo> = (_args, context) => {
const foos = context.entities.Foo.findMany()
return {
foos,
message: "Here are some foos!",
queriedAt: new Date(),
}
}
And seamlessly use it on the frontend:
import getfoos from "@wasp/queries/getTask"
const { data } = useQuery(getfoos)
const { foos, message, queriedAt } = data
// foos: Foo[]
// message: string
// queriedAt: Date
You can now use e-mail authentication in your Wasp app! This means that users can sign up and log in using their e-mail address. You get e-mail verification and password reset out of the box.
app MyApp {
// ...
auth: {
// ...
email: {
fromField: {
name: "ToDO App",
email: "[email protected]"
},
emailVerification: {
allowUnverifiedLogin: false,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
clientRoute: EmailVerificationRoute,
},
passwordReset: {
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
clientRoute: PasswordResetRoute
},
},
}
}
You can only use one of e-mail or username & password authentication in your app. You can't use both at the same time.
Wasp now provides a set of UI components for authentication. You can use them to quickly build a login and signup page for your app. The UI changes dynamically based on your Wasp config.
We provide LoginForm
, SignupForm
, ForgotPassworForm
, ResetPasswordForm
andVerifyEmailForm
components. You can import them from @wasp/auth/forms
like:
import { LoginForm } from '@wasp/auth/forms/Login'
import { SignupForm } from '@wasp/auth/forms/Signup'
import { ForgotPasswordForm } from '@wasp/auth/forms/ForgotPassword'
import { ResetPasswordForm } from '@wasp/auth/forms/ResetPassword'
import { VerifyEmailForm } from '@wasp/auth/forms/VerifyEmail'
You can now define JS/TS functions for seeding the database!
app MyApp {
// ...
db: {
seeds: [
import { devSeedSimple } from "@server/dbSeeds.js",
import { prodSeed } from "@server/dbSeeds.js",
]
}
}
import { createTask } from './actions.js'
export const devSeedSimple = async (prismaClient) => {
const { password, ...newUser } = await prismaClient.user.create({
username: "RiuTheDog", password: "bark1234"
})
await createTask(
{ description: "Chase the cat" },
{ user: newUser, entities: { Task: prismaClient.task } }
)
}
//...
Run wasp db seed
to run database seeding. If there is only one seed, it will run that one, or it will interactively ask you to pick one.
You can also do wasp db seed <name>
to run a seed with specific name: for example, for the case above, you could do wasp db seed prodSeed
.
api
keyword for defining an arbitrary endpoint and URLNeed a specific endpoint, like /healthcheck
or /foo/callback
? Or need complete control of the response? Use an api
to define one by tying a JS function to any HTTP method and path! For example:
// main.wasp
api fooBar {
fn: import { foo } from "@server/apis.js",
entities: [Task],
httpRoute: (GET, "/foo/callback")
}
// server/api.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"}!` })
}
Wasp now supports sending e-mails! You can use the emailSender
app property to configure the e-mail provider and optionally the defaultFrom
address. Then, you can use the send
function in your backend code to send e-mails.
// main.wasp
app MyApp {
emailSender: {
provider: SendGrid,
defaultFrom: {
name: "My App",
email: "[email protected]"
},
},
}
// server/actions.ts
import { emailSender } from '@wasp/email/index.js'
// In some action handler...
const info = await emailSender.send({
to: '[email protected]',
subject: 'Saying hello',
text: 'Hello world',
html: 'Hello <strong>world</strong>'
})
wasp start db
-> Wasp can now run your dev database for you with a single commandMoving from SQLite to PostgreSQL with Wasp can feel like increase in complexity, because suddenly you have to care about running your PostgreSQL database, providing connection URL for it via env var, and if you checkout somebody's else Wasp project, or your old Wasp project that you have no memory of any more, you also have to figure all that out.
To help with that, we now added wasp start db
, which runs a development database for you!
That it, all you need to do is run wasp start db
and you are good to go. No env var setting, no remembering how to run the db.
NOTE: Requires docker
to be installed and in PATH
, and docker daemon to be running.
wasp test client
-> Wasp can now test your web app codeBy leveraging Vitest and some supporting libraries, Wasp now makes it super easy to add unit tests and React component tests to your frontend codebase.
pg-boss
upgraded to latest version (8.4.2)This pg-boss
release fixes an issue where the node server would exit due to an unhandled exception when the DB connection was lost.
usernameAndPassword
and social logins. Now, your userEntity
no longer requires a username
or password
field if you only want to use Google/GitHub for auth.Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago
Published by github-actions[bot] over 1 year ago