Gel TypeScript/JS Client​
Installation​
You can install the published database driver and optional (but recommended!) generators from npm using your package manager of choice.
$
npm install --save-prod gel # database driver
$
npm install --save-dev @gel/generate # generators
$
yarn add gel # database driver
$
yarn add --dev @gel/generate # generators
$
pnpm add --save-prod gel # database driver
$
pnpm add --save-dev @gel/generate # generators
import * as gel from "http://deno.land/x/gel/mod.ts";
$
bun add gel # database driver
$
bun add --dev @gel/generate # generators
Deno users
Create these two files in your project root:
{
"imports": {
"gel": "https://deno.land/x/gel/mod.ts",
"gel/": "https://deno.land/x/gel/"
}
}
{
"importMap": "./importMap.json"
}
Quickstart​
Setup​
This section assumes you have gone through the Quickstart Guide and understand how to update schemas, run migrations, and have
a working Gel project. Let's update the schema to make the title
property
of the Movie
type exclusive. This will help with filtering by
Movie.title
in our queries.
} type Movie { required title: str; required title: str { constraint exclusive; }; multi actors: Person; } }
Generate the new migration and apply them:
$
gel migration create
$
gel migrate
We'll be using TypeScript and Node for this example, so let's setup a simple app:
$
npm init -y # initialize a new npm project
$
npm i gel
$
npm i -D typescript @types/node @gel/generate tsx
$
npx tsc --init # initialize a basic TypeScript project
Client​
The Client
class implements the core functionality required to establish a
connection to your database and execute queries. If you prefer writing queries
as strings, the Client API is all you need.
Let's create a simple Node.js script that seeds the database by running an insert query directly with the driver:
import * as gel from "gel";
const client = gel.createClient();
async function main() {
await client.execute(`
insert Person { name := "Robert Downey Jr." };
insert Person { name := "Scarlett Johansson" };
insert Movie {
title := <str>$title,
actors := (
select Person filter .name in {
"Robert Downey Jr.",
"Scarlett Johansson"
}
)
}
`, { title: "Iron Man 2" });
}
main();
We can now seed the database by running this script with tsx
$
npx tsx seed.ts
Feel free to explore the database in the Gel UI, where you will find the new data you inserted through this script, as well as any data you inserted when running the Quickstart.
A word on module systems
Different build tools and runtimes have different specifications for how modules are imported, and we support a wide-range of those styles. For clarity, we will be sticking to standard TypeScript-style ESM module importing without a file extension throughout this documentation. Please see your build or environment tooling's guidance on how to adapt this style.
Querying with plain strings​
Now, let's write a Node.js script that queries the database for details about Iron Man 2:
import * as gel from "gel";
const client = gel.createClient();
async function main() {
const result = await client.querySingle(`
select Movie {
id,
title,
actors: {
id,
name,
}
} filter .title = "Iron Man 2"
`);
console.log(JSON.stringify(result, null, 2));
}
main();
Interfaces​
Since we're using TypeScript, it would be nice to be able to type the return value of this query, so let's use our first generator, the interfaces generator to tell TypeScript what the type of our result is.
First we run the generator:
$
npx @gel/generate interfaces
This generator introspects your database schema and generates a set of equivalent TypeScript interfaces.
Now we can annotate our query since we are selecting the whole Movie
type:
async function main() { // result will be inferred as Movie | null const result = await client.querySingle(` const result = await client.querySingle<Movie>(` select Movie { id, title,
You can now run the script with tsx
:
$
npx tsx query.ts
Queries generator​
Wouldn't it be great if we could write any arbitrary query and get a type-safe
function that we could call? Good news, that's exactly what the next generator
does! The queries generator scans your project for
*.edgeql
files and generates a file containing a strongly-typed function.
First, move the query into a separate file called getMovie.edgeql
.
select Movie {
id,
title,
actors: {
id,
name,
}
};
Next, we'll run the queries
generator, specifying the --file
option
which will compile all the queries it finds into a single TypeScript module:
$
npx @gel/generate queries --file
Now, let's update our query script to call the generated function, which will provide us with type-safe querying.
import * as gel from "gel"; import { Movie } from "./dbschema/interfaces" import { getMovie } from "./dbschema/queries" const client = gel.createClient(); async function main() { // result will be inferred as Movie | null const result = await client.querySingle<Movie>(` select Movie { id, title, actors: { id, name, } } filter .title = "Iron Man 2" `); const result = await getMovie(client); console.log(JSON.stringify(result, null, 2)); }
Now, if you change the query to return different data, or take parameters, and run the queries generator again, the type of the newly generated function will change. It'll be completely type safe!
Query builder​
At last we've arrived at the most powerful API for querying your Gel instance: the query builder. The Gel query builder provides a code-first way to write fully-typed EdgeQL queries with TypeScript. We recommend it for TypeScript users, or anyone who prefers writing queries with code.
First, we'll run the query builder generator:
$
npx @gel/generate edgeql-js
Version control
The first time you run the generator, you'll be prompted to add the generated
files to your .gitignore
. Confirm this prompt to automatically add a line
to your .gitignore
that excludes the generated files.
For consistency, we recommend omitting the generated files from version control and re-generating them as part of your deployment process. However, there may be circumstances where checking the generated files into version control is desirable, e.g. if you are building Docker images that must contain the full source code of your application.
Now, we can import the generated query builder and express our query completely in TypeScript, getting editor completion, type checking, and type inferrence:
import * as gel from "gel"; import { getMovie } from "./dbschema/queries"; import e from "./dbschema/edgeql-js"; const client = gel.createClient(); async function main() { // result will be inferred as Movie | null // result will be inferred based on the query const result = await getMovie(client); const result = await e .select(e.Movie, () => ({ id: true, title: true, actors: () => ({ id: true, name: true }), filter_single: { title: "Iron Man 2" }, })) .run(client); console.log(JSON.stringify(result, null, 2)); }
What's next​
We recommend reading the client docs first and getting
familiar with configuring the client. You'll find important APIs like
withGlobals
and connection details there. After that, depending on your
preferences, look through the query builder documentation
and use the other pages as a reference for writing code-first Gel queries.