Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
final files + docs for integration
  • Loading branch information
Natalie Abrams authored and Natalie Abrams committed Oct 10, 2025
commit dd53daca03fa646a56958c7a4816c9bbfde1f709
156 changes: 128 additions & 28 deletions packages/docs/src/routes/docs/integrations/gel/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Gel | Integrations
keywords: 'Gel, EdgeDB, database, data, postgres'
contributors:
- nabrams
updated_at: '2025-09-25T18:53:23Z'
updated_at: '2025-10-10T18:53:23Z'
created_at: '2025-08-01T23:00:50Z'
---

Expand All @@ -17,7 +17,7 @@ Gel gives the relational model a fresh facelift, solves n+1, simplifies migratio

Gel can be used in Qwik with `routeLoader$`, `routeAction$` and `server$` functions. These are Qwik APIs to allow code to execute only on the server-side.

The simplest way to add Gel to your Qwik project is by running the Qwik CLI command. This will automatically install all necessary dependencies, set up a basic schema in the `dbschema` folder, and generate a `queries` directory to help you start building your own API queries. Additionally, it will scaffold `actions` and `hooks` folders with example routes and files, giving you a ready-to-use foundation for working with Gel in your app.
The simplest way to add Gel to your Qwik project is by running the Qwik CLI command. This will automatically install all necessary dependencies, set up a basic schema in the `dbschema` folder, and generate a `queries` directory to help you start building your own API queries. Additionally, it will scaffold an `actions` folder with example routes and files, giving you a ready-to-use foundation for working with Gel in your app.

It will also create a `gel.toml` file to configure the gel instance. Everything you need to start building your app with Gel is there.

Expand Down Expand Up @@ -54,7 +54,8 @@ It is already installed for you and you can use the following scripts to utilize

*** Before you start, make sure you have run the following command:
```shell
pnpm run gel:generate:all
pnpm gel:generate:queries &&
pnpm gel:generate:types
```

The following `<generator>`s are currently supported:
Expand All @@ -64,9 +65,6 @@ The following `<generator>`s are currently supported:
- Command: `gel:generate:types` >
- `interfaces`: Generate interfaces for your schema types
- `edgeql-js`: Generate the query builder
- Command: `gel:generate:all` >
- `queries` + `interfaces` + `edgeql-js`




Expand All @@ -78,63 +76,165 @@ To do so, run the following command:
<span q:slot="pnpm">

```shell
pnpm run gel:ui
pnpm gel:ui
```
</span>
<span q:slot="npm">
```shell
npm run gel:ui
npm gel:ui
```
</span>
<span q:slot="yarn">
```shell
yarn run gel:ui
yarn gel:ui
```
</span>
<span q:slot="bun">
```shell
bun run gel:ui
bun gel:ui
```
</span>
</PackageManagerTabs>

## Client user actions

We will use these client actions to query and insert users in the DB below.

```tsx {7} /Gel/ title="src/actions/client.tsx"
import { server$ } from "@builder.io/qwik-city";
import { executeQuery } from "../../actions/client";
import * as queries from "../../../dbschema/queries";

export const getAllUsers = server$(async () => {
return await executeQuery(queries.getAllUsers);
});

export const insertUser = server$(
async (name: string, email: string, has_profile: boolean) => {
return await executeQuery(queries.insertUser, {
name: name,
email: email,
has_profile: has_profile,
});
},
);
```

## Listing users

We will use `routeLoader$` to query all users in the DB, using `db.query.users.findMany()`, and returning the result.
We will use `routeLoader$` to query all users in the DB, and return the result.

```tsx {7} /Gel/ title="src/routes/users/index.tsx"

import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import { schema } from "../../../drizzle/schema";

export const useGetUsers = routeLoader$(async () => {
const sqlite = new Database("./drizzle/db/db.sqlite");
const db = drizzle(sqlite, { schema });
const users = await db.query.users.findMany();
import { getAllUsers } from "../../actions/user";

export const useGetAllUsers = routeLoader$(async () => {
const users = await getAllUsers();
return users;
});

export default component$(() => {
const users = useGetUsers();
const users = useGetAllUsers().value;

return (
<section>
<h1>User's directory</h1>
<h1>Users Directory</h1>
<ul>
{users.value.map((user) => (
<li key={user.id}>
<a href={`/users/${user.id}`}>
{user.name} ({user.email})
</a>
</li>
))}
{Array.isArray(users) && users.length > 0 ? (
users.map((user: any) => (
<li key={user.id ?? user.name}>
<b>Name:</b> {user.name}
{user.email && (
<div style={{ marginLeft: "1rem" }}>
<span>
<b>Email:</b> {user.email}
</span>
</div>
)}
</li>
))
) : (
<li>No users found.</li>
)}
</ul>
</section>
);
});


```

## Adding users

We will use `routeAction$` and `zod$` form to create user.

```tsx {7} /Gel/ title="src/routes/users/index.tsx"

import { component$ } from "@builder.io/qwik";
import { routeAction$, zod$, z, Form } from "@builder.io/qwik-city";
import { insertUser } from "../../actions/user";

export const useCreateUser = routeAction$(
async ({ name, email }) => {
const user = await insertUser(name, email, true);
if (!user)
return { error: "A user already exists with that email.", user: null };
return { error: null, user };
},
zod$({
name: z.string().min(1, { message: "Name is required" }),
email: z.string().email({ message: "Invalid email address" }),
}),
);

export default component$(() => {
const action = useCreateUser();
const errors = action.value?.fieldErrors;
const customError = action.value?.error;

return (
<section>
<h1>Create User</h1>
<Form action={action}>
<label>
Name
<input name="name" value={action.formData?.get("name") ?? ""} />
</label>
<label>
Email
<input name="email" value={action.formData?.get("email") ?? ""} />
</label>
<button type="submit">Create</button>
</Form>
{action.value?.user && <h2>User created successfully!</h2>}
{(errors || customError) && (
<div style={{ color: "red" }}>
{errors?.name && (
<div>
<h2>{errors.name}</h2>
</div>
)}
{errors?.email && (
<div>
<h2>{errors.email}</h2>
</div>
)}
{customError && (
<div>
<h2>{customError}</h2>
</div>
)}
</div>
)}
</section>
);
});

```





12 changes: 3 additions & 9 deletions starters/features/gel/dbschema/default.gel
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ module default {
}

type User extending Timestamped {
required name: str {
constraint exclusive {
errmessage := 'A user with that name already exists!';
};
};
required name: str;
email: str {
constraint exclusive {
errmessage := 'A user with that email already exists!';
};
constraint exclusive;
};
has_profile: bool;

index on (.name);
index on (.email);
}
}
Loading