Migration from 0.13.X to 0.14.X
This guide only covers the migration from 0.13.X to 0.14.X. If you are migrating from 0.11.X or earlier, please read the migration guide from 0.11.X to 0.12.X first.
What's new in 0.14.0?
Using Prisma Schema file directly
Before 0.14.0, users defined their entities in the .wasp file, and Wasp generated the schema.prisma file based on that. This approach had some limitations, and users couldn't use some advanced Prisma features.
Wasp now exposes the schema.prisma file directly to the user. You now define your entities in the schema.prisma file and Wasp uses that to generate the database schema and Prisma client. You can use all the Prisma features directly in the schema.prisma file. Simply put, the schema.prisma file is now the source of truth for your database schema.
- Before
- After
app myApp {
wasp: {
version: "^0.13.0"
},
title: "MyApp",
db: {
system: PostgreSQL
},
}
entity User {=psl
id Int @id @default(autoincrement())
tasks Task[]
psl=}
entity Task {=psl
id Int @id @default(autoincrement())
description String
isDone Boolean
userId Int
user User @relation(fields: [userId], references: [id])
psl=}
app myApp {
wasp: {
version: "^0.14.0"
},
title: "MyApp",
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
tasks Task[]
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean
userId Int
user User @relation(fields: [userId], references: [id])
}
Better auth user API
Wasp introduced a much simpler API for accessing user auth fields like username, email or isEmailVerified on the user object. You don't need to use helper functions every time you want to access the user's username or do extra steps to get proper typing.
How to migrate?
To migrate your app to Wasp 0.14.x, you must:
- Bump the version in
main.waspand update yourtsconfig.json. - Migrate your entities into the new
schema.prismafile. - Update code that accesses user fields.
Bump the version and update tsconfig.json
Let's start with something simple. Update the version field in your Wasp file to ^0.14.0:
app MyApp {
wasp: {
version: "^0.14.0"
},
}
To ensure your project works correctly with Wasp 0.14.0, you must also update your
tsconfig.json file.
If you haven't changed anything in your project's tsconfig.json file (this is
the case for most users), just replace its contents with the new version shown
below.
If you have made changes to your tsconfig.json file, we recommend taking the
new version of the file and reapplying them.
Here's the new version of the tsconfig.json file:
// =============================== IMPORTANT =================================
//
// This file is only used for Wasp IDE support. You can change it to configure
// your IDE checks, but none of these options will affect the TypeScript
// compiler. Proper TS compiler configuration in Wasp is coming soon :)
{
"compilerOptions": {
"module": "esnext",
"target": "esnext",
// We're bundling all code in the end so this is the most appropriate option,
// it's also important for autocomplete to work properly.
"moduleResolution": "bundler",
// JSX support
"jsx": "preserve",
"strict": true,
// Allow default imports.
"esModuleInterop": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"typeRoots": [
// This is needed to properly support Vitest testing with jest-dom matchers.
// Types for jest-dom are not recognized automatically and Typescript complains
// about missing types e.g. when using `toBeInTheDocument` and other matchers.
"node_modules/@testing-library",
// Specifying type roots overrides the default behavior of looking at the
// node_modules/@types folder so we had to list it explicitly.
// Source 1: https://www.typescriptlang.org/tsconfig#typeRoots
// Source 2: https://github.com/testing-library/jest-dom/issues/546#issuecomment-1889884843
"node_modules/@types"
],
// Since this TS config is used only for IDE support and not for
// compilation, the following directory doesn't exist. We need to specify
// it to prevent this error:
// https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file
"outDir": ".wasp/phantom"
}
}
Migrate to the new schema.prisma file
To use the new schema.prisma file, you need to move your entities from the .wasp file to the schema.prisma file.
1. Create a new schema.prisma file
Create a new file named schema.prisma in the root of your project:
.
├── main.wasp
...
├── schema.prisma
├── src
├── tsconfig.json
└── vite.config.ts
2. Add the datasource block to the schema.prisma file
This block specifies the database type and connection URL:
- Sqlite
- PostgreSQL
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
-
The
providershould be either"postgresql"or"sqlite". -
The
urlmust be set toenv("DATABASE_URL")so that Wasp can inject the database URL from the environment variables.
3. Add the generator block to the schema.prisma file
This block specifies the Prisma Client generator Wasp uses:
- Sqlite
- PostgreSQL
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
- The
providershould be set to"prisma-client-js".
4. Move your entities to the schema.prisma file
Move the entities from the .wasp file to the schema.prisma file:
- Sqlite
- PostgreSQL
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
// There are some example entities, you should move your entities here
model User {
id Int @id @default(autoincrement())
tasks Task[]
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean
userId Int
user User @relation(fields: [userId], references: [id])
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
// There are some example entities, you should move your entities here
model User {
id Int @id @default(autoincrement())
tasks Task[]
}
model Task {
id Int @id @default(autoincrement())
description String
isDone Boolean
userId Int
user User @relation(fields: [userId], references: [id])
}
When moving the entities over, you'll need to change entity to model and remove the =psl and psl= tags.
If you had the following in the .wasp file:
entity Task {=psl
// Stays the same
psl=}
... it would look like this in the schema.prisma file:
model Task {
// Stays the same
}
5. Remove app.db.system field from the Wasp file
We now configure the DB system in the schema.prisma file, so there is no need for that field in the Wasp file.
app MyApp {
// ...
db: {
system: PostgreSQL,
}
}
6. Migrate Prisma preview features config to the schema.prisma file
If you didn't use any Prisma preview features, you can skip this step.
If you had the following in the .wasp file:
app MyApp {
// ...
db: {
prisma: {
clientPreviewFeatures: ["postgresqlExtensions"]
dbExtensions: [
{ name: "hstore", schema: "myHstoreSchema" },
{ name: "pg_trgm" },
{ name: "postgis", version: "2.1" },
]
}
}
}
... it will become this:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
extensions = [hstore(schema: "myHstoreSchema"), pg_trgm, postgis(version: "2.1")]
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
All that's left to do is migrate the database.
To avoid type errors, it's best to take care of database migrations after you've migrated the rest of the code. So, just keep reading, and we will remind you to migrate the database as the last step of the migration guide.
Read more about the Prisma Schema File and how Wasp uses it to generate the database schema and Prisma client.
Migrate how you access user auth fields
We had to make a couple of breaking changes to reach the new simpler API.
Follow the steps below to migrate:
-
Replace the
getUsernamehelper withuser.identities.username.idIf you didn't use the
getUsernamehelper in your code, you can skip this step.This helper changed and it no longer works with the
useryou receive as a prop on a page or through thecontext. You'll need to replace it withuser.identities.username.id.- Before
- After
src/MainPage.tsximport { getUsername, AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const username = getUsername(user)
// ...
}src/tasks.tsimport { getUsername } from 'wasp/auth'
export const createTask: CreateTask<...> = async (args, context) => {
const username = getUsername(context.user)
// ...
}src/MainPage.tsximport { AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const username = user.identities.username?.id
// ...
}src/tasks.tsexport const createTask: CreateTask<...> = async (args, context) => {
const username = context.user.identities.username?.id
// ...
} -
Replace the
getEmailhelper withuser.identities.email.idIf you didn't use the
getEmailhelper in your code, you can skip this step.This helper changed and it no longer works with the
useryou receive as a prop on a page or through thecontext. You'll need to replace it withuser.identities.email.id.- Before
- After
src/MainPage.tsximport { getEmail, AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const email = getEmail(user)
// ...
}src/tasks.tsimport { getEmail } from 'wasp/auth'
export const createTask: CreateTask<...> = async (args, context) => {
const email = getEmail(context.user)
// ...
}src/MainPage.tsximport { AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const email = user.identities.email?.id
// ...
}src/tasks.tsexport const createTask: CreateTask<...> = async (args, context) => {
const email = context.user.identities.email?.id
// ...
} -
Replace accessing
providerDatawithuser.identities.<provider>.<value>If you didn't use any data from the
providerDataobject, you can skip this step.Replace
<provider>with the provider name (for exampleusername,email,google,github, etc.) and<value>with the field you want to access (for exampleisEmailVerified).- Before
- After
src/MainPage.tsximport { findUserIdentity, AuthUser } from 'wasp/auth'
function getProviderData(user: AuthUser) {
const emailIdentity = findUserIdentity(user, 'email')
// We needed this before check for proper type support
return emailIdentity && 'isEmailVerified' in emailIdentity.providerData
? emailIdentity.providerData
: null
}
const MainPage = ({ user }: { user: AuthUser }) => {
const providerData = getProviderData(user)
const isEmailVerified = providerData ? providerData.isEmailVerified : null
// ...
}src/MainPage.tsximport { AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
// The email object is properly typed, so we can access `isEmailVerified` directly
const isEmailVerified = user.identities.email?.isEmailVerified
// ...
} -
Use
getFirstProviderUserIddirectly on the user objectIf you didn't use
getFirstProviderUserIdin your code, you can skip this step.You should replace
getFirstProviderUserId(user)withuser.getFirstProviderUserId().- Before
- After
src/MainPage.tsximport { getFirstProviderUserId, AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const userId = getFirstProviderUserId(user)
// ...
}src/tasks.tsimport { getFirstProviderUserId } from 'wasp/auth'
export const createTask: CreateTask<...> = async (args, context) => {
const userId = getFirstProviderUserId(context.user)
// ...
}src/MainPage.tsximport { AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const userId = user.getFirstProviderUserId()
// ...
}src/tasks.tsexport const createTask: CreateTask<...> = async (args, context) => {
const userId = user.getFirstProviderUserId()
// ...
} -
Replace
findUserIdentitywith checks onuser.identities.<provider>If you didn't use
findUserIdentityin your code, you can skip this step.Instead of using
findUserIdentityto get the identity object, you can directly check if the identity exists on theidentitiesobject.- Before
- After
src/MainPage.tsximport { findUserIdentity, AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
const usernameIdentity = findUserIdentity(user, 'username')
if (usernameIdentity) {
// ...
}
}src/tasks.tsimport { findUserIdentity } from 'wasp/auth'
export const createTask: CreateTask<...> = async (args, context) => {
const usernameIdentity = findUserIdentity(context.user, 'username')
if (usernameIdentity) {
// ...
}
}src/MainPage.tsximport { AuthUser } from 'wasp/auth'
const MainPage = ({ user }: { user: AuthUser }) => {
if (user.identities.username) {
// ...
}
}src/tasks.tsexport const createTask: CreateTask<...> = async (args, context) => {
if (context.user.identities.username) {
// ...
}
}
Migrate the database
Finally, you can Run the Wasp CLI to regenerate the new Prisma client:
wasp db migrate-dev
This command generates the Prisma client based on the schema.prisma file.
Read more about the Prisma Schema File and how Wasp uses it to generate the database schema and Prisma client.
That's it!
You should now be able to run your app with the new Wasp 0.14.0. We recommend reading through the updated Accessing User Data section to get a better understanding of the new API.