diff --git a/.github/tests/orm/graphql-gqloom/run.sh b/.github/tests/orm/graphql-gqloom/run.sh
new file mode 100755
index 000000000000..a6a2f7270fe1
--- /dev/null
+++ b/.github/tests/orm/graphql-gqloom/run.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+set -eu
+
+echo "๐ Starting test setup for graphql-gqloom..."
+
+echo "๐ Current working directory before REPO_ROOT: $(pwd)"
+echo "๐ Listing contents:"
+ls -la
+
+REPO_ROOT="$(git rev-parse --show-toplevel)"
+echo "๐ Detected repo root: $REPO_ROOT"
+
+cd "$REPO_ROOT/orm/graphql-gqloom"
+echo "๐ Changed directory to: $(pwd)"
+
+echo "๐ฆ Installing test deps..."
+npm install
+
+# Go to Node script dir and install its deps
+NODE_SCRIPT_DIR="../../.github/get-ppg-dev"
+pushd "$NODE_SCRIPT_DIR" > /dev/null
+npm install
+
+# Start Prisma Dev server
+LOG_FILE="./ppg-dev-url.log"
+rm -f "$LOG_FILE"
+touch "$LOG_FILE"
+
+echo "๐ Starting Prisma Dev in background..."
+node index.js >"$LOG_FILE" &
+NODE_PID=$!
+
+# Wait for DATABASE_URL
+echo "๐ Waiting for Prisma Dev to emit DATABASE_URL..."
+MAX_WAIT=60
+WAITED=0
+until grep -q '^prisma+postgres://' "$LOG_FILE"; do
+ sleep 1
+ WAITED=$((WAITED + 1))
+ if [ "$WAITED" -ge "$MAX_WAIT" ]; then
+ echo "โ Timeout waiting for DATABASE_URL"
+ cat "$LOG_FILE"
+ kill "$NODE_PID" || true
+ exit 1
+ fi
+done
+
+DB_URL=$(grep '^prisma+postgres://' "$LOG_FILE" | tail -1)
+export DATABASE_URL="$DB_URL"
+echo "โ
DATABASE_URL: $DATABASE_URL"
+
+popd > /dev/null # Back to orm/graphql-gqloom
+
+# Run migrations and seed
+npx prisma migrate reset --force --skip-seed
+npx prisma migrate dev --name init
+npx prisma db seed
+
+# Start the app
+npm run dev &
+pid=$!
+
+# Wait for app to be ready
+echo "๐ Waiting for app to be ready..."
+MAX_WAIT=60
+WAITED=0
+until curl -s http://localhost:4000/ > /dev/null 2>&1; do
+ sleep 1
+ WAITED=$((WAITED + 1))
+ if [ "$WAITED" -ge "$MAX_WAIT" ]; then
+ echo "โ Timeout waiting for app to start"
+ kill "$pid" 2>/dev/null || true
+ kill "$NODE_PID" 2>/dev/null || true
+ exit 1
+ fi
+done
+echo "โ
App is ready"
+
+# Run GraphQL Postman collection
+echo "๐งช Running Postman tests..."
+npx newman run "$REPO_ROOT/.github/tests/postman_collections/graphql.json" --bail
+
+# Cleanup
+echo "๐ Shutting down app (PID $pid) and Prisma Dev (PID $NODE_PID)..."
+kill "$pid" 2>/dev/null || true
+kill "$NODE_PID"
+wait "$NODE_PID" 2>/dev/null || true
+wait "$pid" 2>/dev/null || true
diff --git a/orm/graphql-gqloom/.gitignore b/orm/graphql-gqloom/.gitignore
new file mode 100644
index 000000000000..da5b597e73cc
--- /dev/null
+++ b/orm/graphql-gqloom/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+dist/
+*.env*
+src/generated
\ No newline at end of file
diff --git a/orm/graphql-gqloom/.nvmrc b/orm/graphql-gqloom/.nvmrc
new file mode 100644
index 000000000000..deed13c0169b
--- /dev/null
+++ b/orm/graphql-gqloom/.nvmrc
@@ -0,0 +1 @@
+lts/jod
diff --git a/orm/graphql-gqloom/README.md b/orm/graphql-gqloom/README.md
new file mode 100644
index 000000000000..7170185a4917
--- /dev/null
+++ b/orm/graphql-gqloom/README.md
@@ -0,0 +1,653 @@
+# GraphQL Server Example with GraphQL Yoga & Prisma Postgres & GQLoom
+
+This example shows how to implement a **GraphQL server with TypeScript** with the following stack:
+
+- [**GraphQL Yoga**](https://the-guild.dev/graphql/yoga-server): GraphQL server
+- [**GQLoom**](https://gqloom.dev/): Code-First GraphQL Schema library that weaves TypeScript runtime types into GraphQL schemas
+- [**Zod**](https://zod.dev/): Schema validation library for input types
+- [**Prisma Client**](https://www.prisma.io/docs/concepts/components/prisma-client): Databases access (ORM)
+- [**Prisma Migrate**](https://www.prisma.io/docs/concepts/components/prisma-migrate): Database migrations
+- [**Prisma Postgres**](https://www.prisma.io/postgres): A serverless PostgreSQL database built on unikernels.
+
+## Getting started
+
+### 1. Download example and navigate into the project directory
+
+Download this example:
+
+```
+npx try-prisma@latest --template orm/graphql-gqloom --install npm --name graphql-gqloom
+```
+
+Then, navigate into the project directory:
+
+```
+cd graphql-gqloom
+```
+
+Alternative: Clone the entire repo
+
+Clone this repository:
+
+```terminal
+git clone git@github.com:prisma/prisma-examples.git --depth=1
+```
+
+Install npm dependencies:
+
+```terminal
+cd prisma-examples/orm/graphql-gqloom
+npm install
+```
+
+
+
+### 2. Create and seed the database
+
+Create a new [Prisma Postgres](https://www.prisma.io/docs/postgres/overview) database by executing:
+
+```terminal
+npx prisma init --db
+```
+
+If you don't have a [Prisma Data Platform](https://console.prisma.io/) account yet, or if you are not logged in, the command will prompt you to log in using one of the available authentication providers. A browser window will open so you can log in or create an account. Return to the CLI after you have completed this step.
+
+Once logged in (or if you were already logged in), the CLI will prompt you to:
+1. Select a **region** (e.g. `us-east-1`)
+1. Enter a **project name**
+
+After successful creation, you will see output similar to the following:
+
+
+
+CLI output
+
+```terminal
+Let's set up your Prisma Postgres database!
+? Select your region: ap-northeast-1 - Asia Pacific (Tokyo)
+? Enter a project name: testing-migration
+โ Success! Your Prisma Postgres database is ready โ
+
+We found an existing schema.prisma file in your current project directory.
+
+--- Database URL ---
+
+Connect Prisma ORM to your Prisma Postgres database with this URL:
+
+prisma+postgres://accelerate.prisma-data.net/?api_key=...
+
+--- Next steps ---
+
+Go to https://pris.ly/ppg-init for detailed instructions.
+
+1. Install and use the Prisma Accelerate extension
+Prisma Postgres requires the Prisma Accelerate extension for querying. If you haven't already installed it, install it in your project:
+npm install @prisma/extension-accelerate
+
+...and add it to your Prisma Client instance:
+import { withAccelerate } from "@prisma/extension-accelerate"
+
+const prisma = new PrismaClient().$extends(withAccelerate())
+
+2. Apply migrations
+Run the following command to create and apply a migration:
+npx prisma migrate dev
+
+3. Manage your data
+View and edit your data locally by running this command:
+npx prisma studio
+
+...or online in Console:
+https://console.prisma.io/{workspaceId}/{projectId}/studio
+
+4. Send queries from your app
+If you already have an existing app with Prisma ORM, you can now run it and it will send queries against your newly created Prisma Postgres instance.
+
+5. Learn more
+For more info, visit the Prisma Postgres docs: https://pris.ly/ppg-docs
+```
+
+
+
+Locate and copy the database URL provided in the CLI output. Then, create a `.env` file in the project root:
+
+```bash
+touch .env
+```
+
+Now, paste the URL into it as a value for the `DATABASE_URL` environment variable. For example:
+
+```bash
+# .env
+DATABASE_URL=prisma+postgres://accelerate.prisma-data.net/?api_key=ey...
+```
+
+Run the following command to create tables in your database. This creates the `User` and `Post` tables that are defined in [`prisma/schema.prisma`](./prisma/schema.prisma):
+
+```terminal
+npx prisma migrate dev --name init
+```
+
+Execute the seed file in [`prisma/seed.ts`](./prisma/seed.ts) to populate your database with some sample data, by running:
+
+```terminal
+npx prisma db seed
+```
+
+### 3. Start the GraphQL server
+
+Launch your GraphQL server with this command:
+
+```
+npm run dev
+```
+
+Navigate to [http://localhost:4000](http://localhost:4000) in your browser to explore the API of your GraphQL server in a [GraphQL Playground](https://github.com/prisma/graphql-playground).
+
+
+## Using the GraphQL API
+
+The schema that specifies the API operations of your GraphQL server is defined in the resolvers located in [`./src/resolvers/`](./src/resolvers/). Below are a number of operations that you can send to the API using the GraphQL Playground.
+
+Feel free to adjust any operation by adding or removing fields. The GraphQL Playground helps you with its auto-completion and query validation features.
+
+### Retrieve all published posts and their authors
+
+```graphql
+query {
+ feed {
+ id
+ title
+ content
+ published
+ author {
+ id
+ name
+ email
+ }
+ }
+}
+```
+
+See more API operations
+
+### Retrieve the drafts of a user
+
+```graphql
+{
+ draftsByUser(
+ userUniqueInput: {
+ email: "mahmoud@prisma.io"
+ }
+ ) {
+ id
+ title
+ content
+ published
+ author {
+ id
+ name
+ email
+ }
+ }
+}
+```
+
+
+### Create a new user
+
+```graphql
+mutation {
+ signupUser(data: { name: "Sarah", email: "sarah@prisma.io" }) {
+ id
+ }
+}
+```
+
+### Create a new draft
+
+```graphql
+mutation {
+ createDraft(
+ data: { title: "Join the Prisma Discord", content: "https://pris.ly/discord" }
+ authorEmail: "alice@prisma.io"
+ ) {
+ id
+ viewCount
+ published
+ author {
+ id
+ name
+ }
+ }
+}
+```
+
+### Publish/unpublish an existing post
+
+```graphql
+mutation {
+ togglePublishPost(id: __POST_ID__) {
+ id
+ published
+ }
+}
+```
+
+Note that you need to replace the `__POST_ID__` placeholder with an actual `id` from a `Post` record in the database, e.g.`5`:
+
+```graphql
+mutation {
+ togglePublishPost(id: 5) {
+ id
+ published
+ }
+}
+```
+
+### Increment the view count of a post
+
+```graphql
+mutation {
+ incrementPostViewCount(id: __POST_ID__) {
+ id
+ viewCount
+ }
+}
+```
+
+Note that you need to replace the `__POST_ID__` placeholder with an actual `id` from a `Post` record in the database, e.g.`5`:
+
+```graphql
+mutation {
+ incrementPostViewCount(id: 5) {
+ id
+ viewCount
+ }
+}
+```
+
+### Search for posts that contain a specific string in their title or content
+
+```graphql
+{
+ feed(
+ searchString: "prisma"
+ ) {
+ id
+ title
+ content
+ published
+ }
+}
+```
+
+### Paginate and order the returned posts
+
+```graphql
+{
+ feed(
+ skip: 2
+ take: 2
+ orderBy: { updatedAt: desc }
+ ) {
+ id
+ updatedAt
+ title
+ content
+ published
+ }
+}
+```
+
+### Retrieve a single post
+
+```graphql
+{
+ postById(id: __POST_ID__ ) {
+ id
+ title
+ content
+ published
+ }
+}
+```
+
+Note that you need to replace the `__POST_ID__` placeholder with an actual `id` from a `Post` record in the database, e.g.`5`:
+
+```graphql
+{
+ postById(id: 5 ) {
+ id
+ title
+ content
+ published
+ }
+}
+```
+
+### Delete a post
+
+```graphql
+mutation {
+ deletePost(id: __POST_ID__) {
+ id
+ }
+}
+```
+
+Note that you need to replace the `__POST_ID__` placeholder with an actual `id` from a `Post` record in the database, e.g.`5`:
+
+```graphql
+mutation {
+ deletePost(id: 5) {
+ id
+ }
+}
+```
+
+
+
+
+## Evolving the app
+
+Evolving the application typically requires two steps:
+
+1. Migrate your database using Prisma Migrate
+1. Update your application code
+
+For the following example scenario, assume you want to add a "profile" feature to the app where users can create a profile and write a short bio about themselves.
+
+### 1. Migrate your database using Prisma Migrate
+
+The first step is to add a new table, e.g. called `Profile`, to the database. You can do this by adding a new model to your [Prisma schema file](./prisma/schema.prisma) file and then running a migration afterwards:
+
+```diff
+// ./prisma/schema.prisma
+
+model User {
+ id Int @id @default(autoincrement())
+ email String @unique
+ name String?
+ posts Post[]
++ profile Profile?
+}
+
+model Post {
+ id Int @id @default(autoincrement())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ title String
+ content String?
+ published Boolean @default(false)
+ viewCount Int @default(0)
+ author User? @relation(fields: [authorId], references: [id])
+ authorId Int?
+}
+
++model Profile {
++ id Int @id @default(autoincrement())
++ bio String?
++ user User @relation(fields: [userId], references: [id])
++ userId Int @unique
++}
+```
+
+Once you've updated your data model, you can execute the changes against your database with the following command:
+
+```
+npx prisma migrate dev --name add-profile
+```
+
+This adds another migration to the `prisma/migrations` directory and creates the new `Profile` table in the database.
+
+### 2. Update your application code
+
+You can now use your `PrismaClient` instance to perform operations against the new `Profile` table. Those operations can be used to implement queries and mutations in the GraphQL API.
+
+#### 2.1. Add the `Profile` type to your GLoom schema
+
+First, create a new `profile.ts` file in `src/resolvers/` and define the Profile resolver using GLoom's declarative syntax:
+
+```diff
+// ./src/resolvers/profile.ts
+import { field, mutation, query, resolver } from '@gqloom/core'
+import * as z from 'zod'
+import { Profile } from '../generated/gqloom'
+import { PrismaResolverFactory } from '@gqloom/prisma'
+import { prisma } from '../db'
+
+const profileFactory = new PrismaResolverFactory(Profile, prisma)
+
+export const profileResolver = resolver.of(Profile, {
+ userId: field.hidden,
+ user: profileFactory.relationField('user'),
+
+ createProfile: mutation(Profile)
+ .input({
+ userId: z.number().int(),
+ bio: z.string().nullish(),
+ })
+ .resolve(({ userId, bio }) => {
+ return prisma.profile.create({
+ data: {
+ bio: bio ?? undefined,
+ user: {
+ connect: { id: userId },
+ },
+ },
+ })
+ }),
+})
+```
+
+Then update your `server.ts` to include the new resolver:
+
+```diff
+// ./src/server.ts
+import { weave } from '@gqloom/core'
+import { PrismaWeaver } from '@gqloom/prisma'
+import { postResolver } from './resolvers/post'
+import { userResolver } from './resolvers/user'
+import { profileResolver } from './resolvers/profile'
+
+const schema = weave(
+ ZodWeaver,
+ PrismaWeaver.config({ ... }),
+ userResolver,
+ postResolver,
+ profileResolver,
+)
+```
+
+Also, update the `User` resolver to include the profile relation:
+
+```diff
+// ./src/resolvers/user.ts
+
+export const userResolver = resolver.of(User, {
+ // ... existing fields ...
+ profile: userFactory.relationField('profile'),
+
+ // ... existing queries and mutations ...
+})
+```
+
+#### 2.2. Add a `createProfile` GraphQL mutation
+
+The `createProfile` mutation was already added in the `profileResolver` above, but you can also update the User resolver to expose it directly:
+
+```graphql
+mutation {
+ createProfile(
+ userId: 1
+ bio: "I like turtles"
+ ) {
+ id
+ bio
+ user {
+ id
+ name
+ }
+ }
+}
+```
+
+Expand to view more sample Prisma Client queries on Profile
+
+Here are some more sample Prisma Client queries on the new Profile model:
+
+##### Create a new profile for an existing user
+
+```ts
+const profile = await prisma.profile.create({
+ data: {
+ bio: 'Hello World',
+ user: {
+ connect: { id: 1 },
+ },
+ },
+})
+```
+
+##### Create a new user with a new profile
+
+```ts
+const user = await prisma.user.create({
+ data: {
+ email: 'john@prisma.io',
+ name: 'John',
+ profile: {
+ create: {
+ bio: 'Hello World',
+ },
+ },
+ },
+})
+```
+
+##### Update the profile of an existing user
+
+```ts
+const userWithUpdatedProfile = await prisma.user.update({
+ where: { id: 1 },
+ data: {
+ profile: {
+ update: {
+ bio: 'Hello Friends',
+ },
+ },
+ },
+})
+```
+
+
+
+## Switch to another database (e.g. SQLite, MySQL, SQL Server, MongoDB)
+
+If you want to try this example with another database than Postgres, you can adjust the the database connection in [`prisma/schema.prisma`](./prisma/schema.prisma) by reconfiguring the `datasource` block.
+
+Learn more about the different connection configurations in the [docs](https://www.prisma.io/docs/reference/database-reference/connection-urls).
+
+Expand for an overview of example configurations with different databases
+
+### Remove the Prisma Client extension
+
+Before you proceed to use your own database, you should remove the Prisma client extension required for Prisma Postgres:
+
+```terminal
+npm uninstall @prisma/extension-accelerate
+```
+
+Remove the client extension from your `PrismaClient`:
+
+```diff
+- const prisma = new PrismaClient().$extends(withAccelerate())
++ const prisma = new PrismaClient()
+```
+
+### Your own PostgreSQL database
+
+To use your own PostgreSQL database remove the `@prisma/extension-accelerate` package and remove the Prisma client extension.
+
+### SQLite
+
+Modify the `provider` value in the `datasource` block in the [`prisma.schema`](./prisma/schema.prisma) file:
+
+```prisma
+datasource db {
+ provider = "sqlite"
+ url = env("DATABASE_URL")
+}
+```
+
+Create an `.env` file and add the SQLite database connection string in it. For example:
+
+```terminal
+DATABASE_URL="file:./dev.db"
+```
+
+### MySQL
+
+Modify the `provider` value in the `datasource` block in the [`prisma.schema`](./prisma/schema.prisma) file:
+
+```prisma
+datasource db {
+ provider = "mysql"
+ url = env("DATABASE_URL")
+}
+```
+
+Create an `.env` file and add a MySQL database connection string in it. For example:
+
+```terminal
+## This is a placeholder url
+DATABASE_URL="mysql://janedoe:mypassword@localhost:3306/notesapi"
+```
+
+### Microsoft SQL Server
+
+Modify the `provider` value in the `datasource` block in the [`prisma.schema`](./prisma/schema.prisma) file:
+
+```prisma
+datasource db {
+ provider = "sqlserver"
+ url = env("DATABASE_URL")
+}
+```
+
+Create an `.env` file and add a Microsoft SQL Server database connection string in it. For example:
+
+```terminal
+## This is a placeholder url
+DATABASE_URL="sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;"
+```
+
+### MongoDB
+
+Modify the `provider` value in the `datasource` block in the [`prisma.schema`](./prisma/schema.prisma) file:
+
+```prisma
+datasource db {
+ provider = "mongodb"
+ url = env("DATABASE_URL")
+}
+```
+
+Create an `.env` file and add a local MongoDB database connection string in it. For example:
+
+```terminal
+## This is a placeholder url
+DATABASE_URL="mongodb://USERNAME:PASSWORD@HOST/DATABASE?authSource=admin&retryWrites=true&w=majority"
+```
+
+
+
+## Next steps
+
+- Check out the [Prisma docs](https://www.prisma.io/docs)
+- Learn more about [GLoom](https://gqloom.dev/)
+- [Join our community on Discord](https://pris.ly/discord?utm_source=github&utm_medium=prisma_examples&utm_content=next_steps_section) to share feedback and interact with other users.
+- [Subscribe to our YouTube channel](https://pris.ly/youtube?utm_source=github&utm_medium=prisma_examples&utm_content=next_steps_section) for live demos and video tutorials.
+- [Follow us on X](https://pris.ly/x?utm_source=github&utm_medium=prisma_examples&utm_content=next_steps_section) for the latest updates.
+- Report issues or ask [questions on GitHub](https://pris.ly/github?utm_source=github&utm_medium=prisma_examples&utm_content=next_steps_section).
diff --git a/orm/graphql-gqloom/package.json b/orm/graphql-gqloom/package.json
new file mode 100644
index 000000000000..f25ec249a85e
--- /dev/null
+++ b/orm/graphql-gqloom/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "typescript-graphql-gqloom",
+ "private": true,
+ "type": "commonjs",
+ "scripts": {
+ "dev": "tsx watch --env-file=.env src/server.ts",
+ "start": "tsx --env-file=.env src/server.ts",
+ "generate": "prisma generate"
+ },
+ "devDependencies": {
+ "@types/node": "^24.8.1",
+ "prisma": "^6.17.1",
+ "tsx": "^4.20.6",
+ "typescript": "^5.9.3"
+ },
+ "dependencies": {
+ "@gqloom/core": "^0.12.1",
+ "@gqloom/prisma": "^0.12.2",
+ "@gqloom/zod": "^0.12.2",
+ "@prisma/adapter-pg": "^6.17.1",
+ "@prisma/client": "^6.17.1",
+ "@prisma/extension-accelerate": "^2.0.2",
+ "graphql": "^16.11.0",
+ "graphql-scalars": "^1.25.0",
+ "graphql-yoga": "^5.16.0",
+ "zod": "^4.1.12"
+ },
+ "prisma": {
+ "seed": "tsx prisma/seed.ts"
+ }
+}
diff --git a/orm/graphql-gqloom/prisma/schema.prisma b/orm/graphql-gqloom/prisma/schema.prisma
new file mode 100644
index 000000000000..90127c2b1eb1
--- /dev/null
+++ b/orm/graphql-gqloom/prisma/schema.prisma
@@ -0,0 +1,34 @@
+datasource db {
+ provider = "postgresql"
+ url = env("DATABASE_URL")
+}
+
+generator client {
+ provider = "prisma-client"
+ engineType = "client"
+ output = "../src/generated/prisma"
+}
+
+generator gqloom {
+ provider = "prisma-gqloom"
+ output = "../src/generated/gqloom"
+}
+
+model User {
+ id Int @id @default(autoincrement())
+ email String @unique
+ name String?
+ posts Post[]
+}
+
+model Post {
+ id Int @id @default(autoincrement())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ title String
+ content String?
+ published Boolean @default(false)
+ viewCount Int @default(0)
+ author User? @relation(fields: [authorId], references: [id])
+ authorId Int?
+}
diff --git a/orm/graphql-gqloom/prisma/seed.ts b/orm/graphql-gqloom/prisma/seed.ts
new file mode 100644
index 000000000000..a9240c2ac3b3
--- /dev/null
+++ b/orm/graphql-gqloom/prisma/seed.ts
@@ -0,0 +1,73 @@
+import { PrismaPg } from '@prisma/adapter-pg'
+import { type Prisma, PrismaClient } from '../src/generated/prisma/client'
+import { withAccelerate } from '@prisma/extension-accelerate'
+import { prisma } from '../src/db'
+
+const userData: Prisma.UserCreateInput[] = [
+ {
+ name: 'Alice',
+ email: 'alice@prisma.io',
+ posts: {
+ create: [
+ {
+ title: 'Join the Prisma Discord',
+ content: 'https://pris.ly/discord',
+ published: true,
+ },
+ ],
+ },
+ },
+ {
+ name: 'Nilu',
+ email: 'nilu@prisma.io',
+ posts: {
+ create: [
+ {
+ title: 'Follow Prisma on Twitter',
+ content: 'https://www.twitter.com/prisma',
+ published: true,
+ viewCount: 42,
+ },
+ ],
+ },
+ },
+ {
+ name: 'Mahmoud',
+ email: 'mahmoud@prisma.io',
+ posts: {
+ create: [
+ {
+ title: 'Ask a question about Prisma on GitHub',
+ content: 'https://www.github.com/prisma/prisma/discussions',
+ published: true,
+ viewCount: 128,
+ },
+ {
+ title: 'Prisma on YouTube',
+ content: 'https://pris.ly/youtube',
+ },
+ ],
+ },
+ },
+]
+
+async function main() {
+ console.log(`Start seeding ...`)
+ for (const u of userData) {
+ const user = await prisma.user.create({
+ data: u,
+ })
+ console.log(`Created user with id: ${user.id}`)
+ }
+ console.log(`Seeding finished.`)
+}
+
+main()
+ .then(async () => {
+ await prisma.$disconnect()
+ })
+ .catch(async (e) => {
+ console.error(e)
+ await prisma.$disconnect()
+ process.exit(1)
+ })
diff --git a/orm/graphql-gqloom/schema.graphql b/orm/graphql-gqloom/schema.graphql
new file mode 100644
index 000000000000..811dae81a539
--- /dev/null
+++ b/orm/graphql-gqloom/schema.graphql
@@ -0,0 +1,279 @@
+input BoolFilter {
+ equals: Boolean
+ not: NestedBoolFilter
+}
+
+"""
+A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
+"""
+scalar DateTime
+
+input DateTimeFilter {
+ equals: DateTime
+ gt: DateTime
+ gte: DateTime
+ in: [DateTime!]
+ lt: DateTime
+ lte: DateTime
+ not: NestedDateTimeFilter
+ notIn: [DateTime!]
+}
+
+input FeedOrderByInput {
+ updatedAt: FeedOrderByUpdatedAtInput
+}
+
+enum FeedOrderByUpdatedAtInput {
+ asc
+ desc
+}
+
+input IntFilter {
+ equals: Int
+ gt: Int
+ gte: Int
+ in: [Int!]
+ lt: Int
+ lte: Int
+ not: NestedIntFilter
+ notIn: [Int!]
+}
+
+input IntNullableFilter {
+ equals: Int
+ gt: Int
+ gte: Int
+ in: [Int!]
+ lt: Int
+ lte: Int
+ not: NestedIntNullableFilter
+ notIn: [Int!]
+}
+
+type Mutation {
+ createDraft(authorEmail: String!, data: PostCreateInput!): Post!
+ deletePost(id: Int!): Post!
+ incrementPostViewCount(id: Int!): Post!
+ signupUser(data: SignupUserDataInput!): User!
+ togglePublishPost(id: Int!): Post!
+}
+
+input NestedBoolFilter {
+ equals: Boolean
+ not: NestedBoolFilter
+}
+
+input NestedDateTimeFilter {
+ equals: DateTime
+ gt: DateTime
+ gte: DateTime
+ in: [DateTime!]
+ lt: DateTime
+ lte: DateTime
+ not: NestedDateTimeFilter
+ notIn: [DateTime!]
+}
+
+input NestedIntFilter {
+ equals: Int
+ gt: Int
+ gte: Int
+ in: [Int!]
+ lt: Int
+ lte: Int
+ not: NestedIntFilter
+ notIn: [Int!]
+}
+
+input NestedIntNullableFilter {
+ equals: Int
+ gt: Int
+ gte: Int
+ in: [Int!]
+ lt: Int
+ lte: Int
+ not: NestedIntNullableFilter
+ notIn: [Int!]
+}
+
+input NestedStringFilter {
+ contains: String
+ endsWith: String
+ equals: String
+ gt: String
+ gte: String
+ in: [String!]
+ lt: String
+ lte: String
+ not: NestedStringFilter
+ notIn: [String!]
+ startsWith: String
+}
+
+input NestedStringNullableFilter {
+ contains: String
+ endsWith: String
+ equals: String
+ gt: String
+ gte: String
+ in: [String!]
+ lt: String
+ lte: String
+ not: NestedStringNullableFilter
+ notIn: [String!]
+ startsWith: String
+}
+
+enum NullsOrder {
+ first
+ last
+}
+
+type Post {
+ author: User
+ content: String
+ createdAt: DateTime!
+ id: ID!
+ published: Boolean!
+ title: String!
+ updatedAt: DateTime!
+ viewCount: Int!
+}
+
+input PostCreateInput {
+ content: String
+ title: String!
+}
+
+input PostListRelationFilter {
+ every: PostWhereInput
+ none: PostWhereInput
+ some: PostWhereInput
+}
+
+input PostOrderByRelationAggregateInput {
+ _count: SortOrder
+}
+
+input PostWhereInput {
+ AND: [PostWhereInput!]
+ NOT: [PostWhereInput!]
+ OR: [PostWhereInput!]
+ author: UserNullableScalarRelationFilter
+ authorId: IntNullableFilter
+ content: StringNullableFilter
+ createdAt: DateTimeFilter
+ id: IntFilter
+ published: BoolFilter
+ title: StringFilter
+ updatedAt: DateTimeFilter
+ viewCount: IntFilter
+}
+
+type Query {
+ draftsByUser(userUniqueInput: UserUniqueInput!): [Post!]!
+ feed(orderBy: FeedOrderByInput, searchString: String, skip: Float, take: Float): [Post!]!
+ postById(id: Int!): Post
+ users(cursor: UserWhereUniqueInput, distinct: [UserScalarFieldEnum!], orderBy: [UserOrderByWithRelationInput!], skip: Int, take: Int, where: UserWhereInput): [User!]!
+}
+
+enum QueryMode {
+ default
+ insensitive
+}
+
+input SignupUserDataInput {
+ email: String!
+ name: String
+ posts: [PostCreateInput!]!
+}
+
+enum SortOrder {
+ asc
+ desc
+}
+
+input SortOrderInput {
+ nulls: NullsOrder
+ sort: SortOrder!
+}
+
+input StringFilter {
+ contains: String
+ endsWith: String
+ equals: String
+ gt: String
+ gte: String
+ in: [String!]
+ lt: String
+ lte: String
+ mode: QueryMode
+ not: NestedStringFilter
+ notIn: [String!]
+ startsWith: String
+}
+
+input StringNullableFilter {
+ contains: String
+ endsWith: String
+ equals: String
+ gt: String
+ gte: String
+ in: [String!]
+ lt: String
+ lte: String
+ mode: QueryMode
+ not: NestedStringNullableFilter
+ notIn: [String!]
+ startsWith: String
+}
+
+type User {
+ email: String!
+ id: ID!
+ name: String
+ posts: [Post!]!
+}
+
+input UserNullableScalarRelationFilter {
+ is: UserWhereInput
+ isNot: UserWhereInput
+}
+
+input UserOrderByWithRelationInput {
+ email: SortOrder
+ id: SortOrder
+ name: SortOrderInput
+ posts: PostOrderByRelationAggregateInput
+}
+
+enum UserScalarFieldEnum {
+ email
+ id
+ name
+}
+
+input UserUniqueInput {
+ email: String
+ id: Int
+}
+
+input UserWhereInput {
+ AND: [UserWhereInput!]
+ NOT: [UserWhereInput!]
+ OR: [UserWhereInput!]
+ email: StringFilter
+ id: IntFilter
+ name: StringNullableFilter
+ posts: PostListRelationFilter
+}
+
+input UserWhereUniqueInput {
+ AND: [UserWhereInput!]
+ NOT: [UserWhereInput!]
+ OR: [UserWhereInput!]
+ email: String
+ id: Int
+ name: StringNullableFilter
+ posts: PostListRelationFilter
+}
\ No newline at end of file
diff --git a/orm/graphql-gqloom/src/db.ts b/orm/graphql-gqloom/src/db.ts
new file mode 100644
index 000000000000..c9f23d5a7d89
--- /dev/null
+++ b/orm/graphql-gqloom/src/db.ts
@@ -0,0 +1,12 @@
+import { PrismaPg } from '@prisma/adapter-pg'
+import { PrismaClient } from './generated/prisma/client'
+import { withAccelerate } from '@prisma/extension-accelerate'
+
+if (!process.env.DATABASE_URL) {
+ throw new Error('DATABASE_URL environment variable is not set')
+}
+
+const adapter = new PrismaPg({
+ connectionString: process.env.DATABASE_URL,
+})
+export const prisma = new PrismaClient({ adapter }).$extends(withAccelerate())
diff --git a/orm/graphql-gqloom/src/resolvers/post.ts b/orm/graphql-gqloom/src/resolvers/post.ts
new file mode 100644
index 000000000000..7d9c7e4ad6c9
--- /dev/null
+++ b/orm/graphql-gqloom/src/resolvers/post.ts
@@ -0,0 +1,165 @@
+import { field, mutation, query, resolver } from '@gqloom/core'
+import * as z from 'zod'
+import { Post } from '../generated/gqloom'
+import { PrismaResolverFactory } from '@gqloom/prisma'
+import { useSelectedFields } from '@gqloom/prisma/context'
+import { prisma } from '../db'
+
+export const PostCreateInput = z.object({
+ __typename: z.literal('PostCreateInput').nullish(),
+ title: z.string(),
+ content: z
+ .string()
+ .nullish()
+ .transform((x) => x ?? undefined),
+})
+
+const postFactory = new PrismaResolverFactory(Post, prisma)
+
+export const postResolver = resolver.of(Post, {
+ authorId: field.hidden,
+ author: postFactory.relationField('author'),
+
+ postById: query(Post.nullable())
+ .input({
+ id: z.int(),
+ })
+ .resolve(({ id }) =>
+ prisma.post.findUnique({
+ select: useSelectedFields(Post),
+ where: { id },
+ }),
+ ),
+
+ feed: query(Post.list())
+ .input({
+ searchString: z.string().nullish(),
+ skip: z
+ .number()
+ .int()
+ .nullish()
+ .transform((x) => x ?? undefined),
+ take: z
+ .number()
+ .int()
+ .nullish()
+ .transform((x) => x ?? undefined),
+ orderBy: z
+ .object({
+ updatedAt: z
+ .enum(['asc', 'desc'])
+ .nullish()
+ .transform((x) => x ?? undefined),
+ })
+ .nullish()
+ .transform((x) => x ?? undefined),
+ })
+ .resolve(({ searchString, skip, take, orderBy }) => {
+ const or = searchString
+ ? {
+ OR: [
+ { title: { contains: searchString } },
+ { content: { contains: searchString } },
+ ],
+ }
+ : {}
+
+ return prisma.post.findMany({
+ select: useSelectedFields(Post),
+ where: { ...or, published: true },
+ take,
+ skip,
+ orderBy,
+ })
+ }),
+ draftsByUser: query(Post.list())
+ .input({
+ userUniqueInput: z.object({
+ __typename: z.literal('UserUniqueInput').nullish(),
+ id: z
+ .number()
+ .int()
+ .nullish()
+ .transform((x) => x ?? undefined),
+ email: z
+ .email()
+ .nullish()
+ .transform((x) => x ?? undefined),
+ }),
+ })
+ .resolve(({ userUniqueInput }) => {
+ return prisma.post.findMany({
+ select: useSelectedFields(Post),
+ where: {
+ published: false,
+ author: {
+ is: {
+ id: userUniqueInput.id,
+ email: userUniqueInput.email,
+ },
+ },
+ },
+ })
+ }),
+ createDraft: mutation(Post)
+ .input({
+ data: PostCreateInput,
+ authorEmail: z.email(),
+ })
+ .resolve(({ data, authorEmail }) => {
+ return prisma.post.create({
+ select: useSelectedFields(Post),
+ data: {
+ title: data.title,
+ content: data.content,
+ published: false,
+ author: {
+ connect: { email: authorEmail },
+ },
+ },
+ })
+ }),
+
+ togglePublishPost: mutation(Post)
+ .input({
+ id: z.number().int(),
+ })
+ .resolve(async ({ id }) => {
+ // TODO: Simplify once https://github.com/prisma/prisma/issues/16715 is fixed
+ const postPublished = await prisma.post.findUnique({
+ where: { id },
+ select: { published: true },
+ })
+ if (!postPublished) {
+ throw new Error('Post not found')
+ }
+ return prisma.post.update({
+ select: useSelectedFields(Post),
+ where: { id },
+ data: { published: !postPublished?.published },
+ })
+ }),
+
+ incrementPostViewCount: mutation(Post)
+ .input({
+ id: z.int(),
+ })
+ .resolve(({ id }) => {
+ return prisma.post.update({
+ select: useSelectedFields(Post),
+ where: { id },
+ data: { viewCount: { increment: 1 } },
+ })
+ }),
+
+ deletePost: mutation(Post)
+ .input({
+ id: z.int(),
+ })
+ .resolve(({ id }) => {
+ return prisma.post.delete({
+ select: useSelectedFields(Post),
+ where: { id },
+ })
+ }),
+})
diff --git a/orm/graphql-gqloom/src/resolvers/user.ts b/orm/graphql-gqloom/src/resolvers/user.ts
new file mode 100644
index 000000000000..611816174abc
--- /dev/null
+++ b/orm/graphql-gqloom/src/resolvers/user.ts
@@ -0,0 +1,36 @@
+import { mutation, resolver } from '@gqloom/core'
+import { PrismaResolverFactory } from '@gqloom/prisma'
+import { useSelectedFields } from '@gqloom/prisma/context'
+import * as z from 'zod'
+import { User } from '../generated/gqloom'
+import { prisma } from '../db'
+import { PostCreateInput } from './post'
+
+const userFactory = new PrismaResolverFactory(User, prisma)
+
+export const userResolver = resolver.of(User, {
+ users: userFactory.findManyQuery(),
+
+ posts: userFactory.relationField('posts'),
+
+ signupUser: mutation(User)
+ .input({
+ data: z.object({
+ email: z.email(),
+ name: z.string().optional(),
+ posts: z.array(PostCreateInput),
+ }),
+ })
+ .resolve(({ data }) => {
+ return prisma.user.create({
+ select: useSelectedFields(User),
+ data: {
+ email: data.email,
+ name: data.name,
+ posts: {
+ create: data.posts.map(({ __typename, ...post }) => post),
+ },
+ },
+ })
+ }),
+})
diff --git a/orm/graphql-gqloom/src/server.ts b/orm/graphql-gqloom/src/server.ts
new file mode 100644
index 000000000000..993c341341bf
--- /dev/null
+++ b/orm/graphql-gqloom/src/server.ts
@@ -0,0 +1,46 @@
+import * as fs from 'node:fs'
+import { createServer } from 'node:http'
+import path from 'node:path'
+import { weave } from '@gqloom/core'
+import { asyncContextProvider } from '@gqloom/core/context'
+import { PrismaWeaver } from '@gqloom/prisma'
+import { ZodWeaver } from '@gqloom/zod'
+import { lexicographicSortSchema, printSchema } from 'graphql'
+import { GraphQLDateTime } from 'graphql-scalars'
+import { createYoga } from 'graphql-yoga'
+import { postResolver } from './resolvers/post'
+import { userResolver } from './resolvers/user'
+
+const schema = weave(
+ ZodWeaver,
+ PrismaWeaver.config({
+ presetGraphQLType: (type) => {
+ switch (type) {
+ case 'DateTime':
+ return GraphQLDateTime
+ }
+ },
+ }),
+ asyncContextProvider,
+ userResolver,
+ postResolver,
+)
+try {
+ fs.writeFileSync(
+ path.join(__dirname, '../schema.graphql'),
+ printSchema(lexicographicSortSchema(schema)),
+ )
+ console.info('โ
GraphQL schema written to schema.graphql')
+} catch (error) {
+ console.error('โ ๏ธ Failed to write schema file:', error)
+ // Non-fatal: server can still run without the file
+}
+
+const yoga = createYoga({
+ graphqlEndpoint: '/',
+ schema,
+})
+const server = createServer(yoga)
+server.listen(4000, () => {
+ console.info('Server is running on http://localhost:4000')
+})
diff --git a/orm/graphql-gqloom/tsconfig.json b/orm/graphql-gqloom/tsconfig.json
new file mode 100644
index 000000000000..de052a6133c0
--- /dev/null
+++ b/orm/graphql-gqloom/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true
+ },
+ "include": ["src/**/*", "prisma/**/*"]
+}