# How I use PrismaClient with Qwik on Cloudflare pages

I was playing around with Qwik. The most insanely fast web framework out there. Yes, the most.

And this is mainly because of the philosophy behind Qwik and its creator. Which you can read about [here](https://qwik.builder.io/docs/think-qwik/). An excellent read. If you ever liked Angular like me. **Same creator.**

Qwik apps are instant because there is nothing to do on application startup if the framework is resumable. No hydration, no islands. Just code splitting and lazy-loading components, functions, server functions, and even lazy-loading styles.

So of course we want an instant web app on the edge, right?

So I wanted to build the entire stack for a saas idea my girlfriend and I want to execute, and obviously, I want to put it at the edge!

In the next 8 min, I will show you the small details that could be really challenging if this is your first time with Cloudflare, Vite, Qwik, Prisma or even TypeScript. 📚

And I will tell you how to solve them, too. Enjoy.

## Let's analyze the potential problems you'll face.

When working with Prisma at the edge, in this case Cloudflare which, by the way, published some new products and beta releases this month, and even alpha previews of their new database called D1, for example. **Or even their new TCP adapter which now allows you to connect to your traditional database just like the old days**. [Learn more about it here](https://blog.cloudflare.com/workers-tcp-socket-api-connect-databases/).

But if you don't want to lose the value that the workers offer to your product and most important: your customers.

> Then you want to be at the edge, and not just that! You want to use a CloudDB (HTTP) and you want to be resumable. #NeverHydrateAgain.

That's why we want to use Qwik, which is a very different framework in comparison with the most popular Next, Remix, or the smart SvelteKit.

Qwik allows you to not do hydration, they argue that hydration is a hack to recover the `APP_STATE` and `FRAMEWORK_STATE` by eagerly executing the app code in the browser. This involves:

1. downloading component code
    
2. executing component code
    
3. recovering the `WHAT`(`APP_STATE` and `FRAMEWORK_STATE`) and `WHERE` to get event handler closure
    
4. attaching `WHAT` (the event handler closure) to `WHERE` (a DOM element)
    

All the explanations about `WHAT`, `APP_STATE` etc. [can be found here](https://www.builder.io/blog/hydration-is-pure-overhead).

![enter image description here](https://cdn.builder.io/api/v1/image/assets/YJIGb4i01jvw0SRdL5Bt/4f48568655904e4586ba34b413beb790?format=webp&width=1200 align="left")

## Which DB should we use?

Last but not least: The Database. What is the best database? Wait, be careful, we can make a better question: Which database is the best, for the edge, specifically for Cloudflare?

Not an easy answer, right? That's why they are building a brand-new native [SQL DB (D1).](https://developers.cloudflare.com/d1/)

But we need a production-ready db for our saas apps today, with the ability to use HTTPS communication instead of TCP. There are not so many HTTPS DBs out there yet, but one of the most interesting is **Vitess**, the open-source database technology that was invented at YouTube in 2010 to solve the scaling issues they faced with their massive **MySQL database**. The same DB that [PlanetScale](https://planetscale.com/) uses underneath.

I just randomly chose (So many options 🥶) I know I can do some SQL on `mysql2.js` in a very frictionless way. But hey!, don't forget about type safety and Schema management. Nevertheless, the pick is:

`MongoDBAtlas` + `PrismaProxy` + `PrismaClient`

Cool ha? I also like to work with this group of nice APIs.

> **If all you have is a hammer, everything looks like a nail**.

Well, we already know this won't be easy, but if we achieve the goal, this could be blazingly fast and amazingly powerful. So the trade-off seems to be pretty good.

> Prepare the weekend! or a couple of nights 👊🏼 🍕🎧📚

Finally, We are using Cloudflare because of their amazing Edge network, which may be one of the game changers on cloud computing and don't forget that they arrived at first. We want all the benefits of a CDN, with the power of computing code if necessary in a beastly fast way. ⚡️ Yes, we want it all. 😳

So in summary. I'm very sure we are about to deal with:

* Because Qwik is a server framework, we need to wrap our functions and components inside $
    
* PrismaClient and Vite SSR builder are not best friends.
    
* MongoDB Atlas does not offer an HTTPS connection with the free plan.
    
* A Free Prisma cloud account would be necessary to proxy the connection to your DB via HTTPS.
    
* Since Prisma recognizes Cloudflare runtime as a Browser (they are very similar) will need to instantiate PrismaClient isolated.
    
* Cloudflare Env variables could not be visible for the PrismaClient
    
* And any other random bug that can possibly happen is about to happen today. 🪲
    

But that's ok. We were made for this. 🐝

## Let's install

[Here is the link](https://github.com/FixterGeek/form-saas) to the repo, so you can go directly to the source and read (which is much faster than reading this post). But if you stay, I'd like to show you the details and explain'em.

You can start an empty project with the tool I used: **Vite**. Vite is super easy to use and config and is semantically beautiful and maybe Vite poses one of the most active, inclusive, and smart communities. And the tools are just amazingly powerful, it is also a good idea to standardize a bit, so if you have plans to build your own framework, please, do it on [Vite](https://vitejs.dev/guide/why.html).

`npm create qwik@latest`

This will run a CLI and will generate a brand-new Qwik+ViteSSR project.

We're also going to install some dev tools: `npm i prisma --save-dev`

And some others for the project: `npm i @prisma/client`

Also used in this project:

```json
  "devDependencies": {
    "@builder.io/qwik": "^1.1.4",
    "@builder.io/qwik-city": "^1.1.4",
    "@types/eslint": "8.37.0",
    "@types/node": "^20.1.4",
    "@typescript-eslint/eslint-plugin": "5.59.5",
    "@typescript-eslint/parser": "5.59.5",
    "autoprefixer": "^10.4.14",
    "daisyui": "^2.51.6",
    "eslint": "8.40.0",
    "eslint-plugin-qwik": "^1.1.4",
    "postcss": "^8.4.23",
    "prettier": "2.8.8",
    "prisma": "^4.14.1",
    "tailwindcss": "^3.3.1",
    "typescript": "5.0.4",
    "undici": "5.22.1",
    "vite": "4.3.5",
    "vite-tsconfig-paths": "4.2.0",
    "wrangler": "^2.20.0",
    "tiny-invariant": "^1.3.1",
  },
  "dependencies": {
    "@prisma/client": "^4.14.1",
    "zod": "^3.21.4"
  }
```

How much code can we avoid building or avoid delivering to the client, just by working full-stack? Isn't this nice?

## Now the Prisma.Schema

We can pull our database info with Prisma, so we do not need to write a single character of the Schemas (one of the main reasons why I want to use Prisma as a dev-tool):

`npx prisma init` This will create our schema file at `prisma/shcema.prisma` let's add the following:

```apache
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider  = "mongodb"
  url       = env("DATABASE_URL")
  directUrl = env("MONGO_ATLAS")
}
model User {
  name          String?
  email         String  @unique
  id            String  @id @default(auto()) @map("_id") @db.ObjectId
  access_token  String?
  picture       String?
  provider      String?
  refresh_token String?
}
```

Not much happening here, but for the two URLs: `url` and `directURL`.

You don't really need `directURL` but since Prisma's `--data-proxy` command doesn't support `db push` or `db pull` (more about [Prisma Data Proxy here](https://www.prisma.io/docs/data-platform/data-proxy)) we need to add it, so we can pull/push from the regular connection while we are developing.

Not bad ha?

So, let's get that two env variables.

## Prisma Data Proxy

Yep, a Prisma service. Am I putting so much faith in Prisma?

[cloud.prisma.io](http://cloud.prisma.io)

![Prisma cloud](https://i.imgur.com/KVQpcXc.png align="left")

Once you've related your database link to a proxy and chosen the region, it's time to create a .env file.

```apache
PRISMA_PROXY=prisma://aws-us-east-psdfsdflknjsadf
MONGO_ATLAS=mongodb+srv://blissmoasdpojdfsñihasdf
```

Remember, the Prisma proxy is about to deal as an intermediary between your web app and a TCP connection to your Atlas DB. Not exactly a CloudDB, but we are on the way. 🔥

## Prisma Client at the edge

We also need to use a special Prisma Client that knows how to deal with the edge: `npx prisma generate --data-proxy`

So, we've pulled our database and **updated some Models**. It's time to generate the client:

`npx prisma db push`

And then:

`npx prisma generate --data-proxy`

With this, we are now able to use the `import { PrismaClient } from "@prisma/client/edge"` inside of our code.

> 👀 Do not forget to add `"postinstall": "prisma generate && prisma generate --data-proxy"` into yor scripts inside `package.json`. This way Prisma generates at build time on deploy.

## Let's connect and use the database.

I've created one function to pass the Cloudflare Env variables to the PrismaClient in order to avoid any potential Env problems on Cloudflare, which are really hard to address. Kinda hacky, but useful.

`src/db/getDB.ts`

```typescript
import { PrismaClient } from "@prisma/client/edge";

export const getDB = (
  env: QwikCityPlatform["env"] & { get: (arg0: string) => string | undefined }
) => {
  return new PrismaClient({
    datasources: {
      db: {
        url: env.get("DATABASE_URL"),
      },
    },
  });
};
```

It is important to mention that the import of the regular client is different from the proxy one, this will come from: `@prisma/client/edge`

> This is very important. Please verify if you don't have incorrect imports in your files. 🕵🏽‍♂️

We don't have too much worry about resources with HTTP in comparison to TCP. Not our responsibility anymore… Thanks Prisma? (Is a Prisma proxy something interesting to build? 🤓)

Well, the use of these functions looks like this:

```typescript
// this's a server function on Qwik:
const createPrismaUser = server$(
  async (data: UserType, prisma: PrismaClient): Promise<UserType> => {
    return (await prisma.user.upsert({
      where: { email: data.email },
      create: data,
      update: data,
    })) as UserType;
  }
);
```

Disclaimer: I'm very sure this is not the best approach. But I'm very new to Qwik as a lot of my generation of developers, I want to be up-to-date and follow the most exciting open source projects. JS is such a gorgeous and fecund community. Please be patient. And to yourself. 🤓

So, check how I pass the Prisma client to this function: `const user = await createPrismaUser(data, getDB(request.env));`

```typescript
export const onGet: RequestHandler = async (request) => {
  const url = new URL(request.url);
  const code = url.searchParams.get("code");
  if (!code) return;
  const { access_token, token_type } = await getToken(code, request);
  const data = await getNewUserData({ access_token, token_type });
  const user = await createPrismaUser(data, getDB(request.env));

  request.cookie.set("userId", String(user.id), {
    maxAge: [7, "days"],
    path: "/",
    httpOnly: true,
    sameSite: "strict",
  });
};
```

`createPrismaUser` will get, as a second parameter, the PrismaClient. It's ok to instantiate on every connection. We are dealing with HTTP connections, remember? `that's what make any DB a CloudDB`.

This `Qwik endpoints` are just lovely 💜

## Vite - Prisma build issue

We've got it all in place, now we need to try to build and see what happens.

One of the main problems, at the building step, is Vite saying:

`./prisma/client/index-browser` or `.prisma/client/edge`.

![.prisma/client](https://i.imgur.com/FqEumGG.png align="left")

Not a very fortunate decision, to save the generated client in a hidden folder inside `node_modules` 😔. So we need to alias it on Vite's config file:

`vite.config.ts`

```jsx
export default defineConfig(() => {
  return {
    resolve:{
      alias: {
        ".prisma/client/edge":"./node_modules/.prisma/client/edge.js"
      }
    },
      plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
        preview: {
          headers: {
            'Cache-Control': 'public, max-age=600',
          },
        }
  };})
```

Every time you find `.prisma/client/edge` you'll use `./node_modules/.prisma/client/edge.js` instead. That's what it does. Yeah, that's a good one, Vite! 🥳😎 **Maybe subscribe as a way to say thank you? 😜**

## Finally, let's deploy

Don't forget to add your Env variables to the [Cloudflare console](https://dash.cloudflare.com/) at the **settings tab &gt; Environment variables** of your project.

I created the project on Cloudflare via their wizard, where I connected my GitHub account and selected my repo.

![Cloudflare pages](https://i.imgur.com/SxeIKc1.png align="left")

That's it, you now have it all, I've nothing left to give you today. Come back tomorrow. Maybe one more thing: #NeverHydrateAgain

Abrazo. Bliss.

## Related links

[PlanetScale](https://planetscale.com/)

[Why Qwik?](https://qwik.builder.io/docs/think-qwik/)

[Check my courses in Spanish](fixtergeek.com/cursos)

[Official Prisma guide for Cloudflare](https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-cloudflare-workers#8-generate-a-prisma-client)

[Prisma Data Proxy](https://www.prisma.io/docs/data-platform/data-proxy)

[Repo of this project](https://github.com/FixterGeek/form-saas)

[Hydration is pure overhead](https://www.builder.io/blog/hydration-is-pure-overhead)
