The query builder is auto-generated by introspecting the schema of your database.

When developing locally, we recommend initializing an EdgeDB project for your application. Follow the Quickstart for detailed instructions on installing the CLI, initializing a project, writing a basic schema, and executing your first migration.

Install the edgedb package from NPM.

npm install edgedb       # npm users
yarn add edgedb          # yarn users

Generate the query builder with the following command.

npx edgeql-js           # npm users
yarn edgeql-js          # yarn users

You’ll see something similar to this. (The first line will differ depending on whether you are using TypeScript or plain JavaScript.)

npx edgeql-js
Detected tsconfig.json, generating TypeScript files.
  To override this, use the --target flag.
  Run `npx edgeql-js --help` for details.
Generating query builder into ./dbschema/edgeql-js
Connecting to EdgeDB instance...
Introspecting database schema...
Generation successful!

Important. The npx edgeql-js establishes a connection to your database, introspects the current schema, and generates a bunch of files. It does not simply read your local .esdl files. You must create and apply migrations to your development database before running npx edgeql-js.

Generating the query builder requires establishing a connection to an active EdgeDB instance. Remember that object types can contain computed fields that correspond to arbitrary EdgeQL queries. It isn’t possible to determine the type and cardinality of these queries without implementing a full EdgeQL parser and static analyzer in JavaScript, which is not on our roadmap (to put it lightly!). As such, we rely on the existence of an active EdgeDB instance containing the target schema.

By default, npx edgeql-js generated files into the ./dbschema/edgeql-js directory, as defined relative to your project root. The project root is identified by scanning up the file system for a package.json.

Connection issue?

This command must be able to connect to a running EdgeDB instance. If you’re using edgedb project init, this is automatically handled for you. Otherwise, you’ll need to explicitly pass connection information, just like any other CLI command. See Client Libraries > Connection for guidance.

The generation command looks at your environment and guesses what kind of files to generate (.ts vs .js + .d.ts) and what module system to use (CommonJS vs ES modules). You can override this with the --target flag.

--target ts

Generate TypeScript files (.ts)

--target mts

Generate TypeScript files (.mts) with extensioned ESM imports

--target esm

Generate .js with ESM syntax and .d.ts declaration files

--target cjs

Generate JavaScript with CommonJS syntax and and .d.ts declaration files

The first time you run the command, you’ll be prompted to add the generated files to your .gitignore. Confirm this prompt, and a line will be automatically added to your .gitignore to exclude the generated files from Git.

$ npx edgeql-js
Checking the generated query builder into version control
is NOT RECOMMENDED. Would you like to update .gitignore to ignore
the query builder directory? The following line will be added:


[y/n] (leave blank for "y")

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.

Once the query builder is generated, it’s ready to use! We recommend importing the query builder as a single default import called e.

// TypeScript
import e from "./dbschema/edgeql-js";

// TypeScript with ESM

// JavaScript (CommonJS)
const e = require("./dbschema/edgeql-js");

// JavaScript (ES modules)
import e from "./dbschema/edgeql-js/index.mjs";

If you’re using ES modules, remember that imports require a file extension. The rest of the documentation assumes you are using TypeScript-style (extensionless) import syntax.

Here’s a full “Hello world” example.

import * as edgedb from "edgedb";
import e from "./dbschema/edgeql-js";

const client = edgedb.createClient();

async function run(){
  // declare a simple query
  const myQuery = e.str("Hello world!");

  // execute the expression
  const result = await;

  // print the result
  console.log(result); // "Hello world!"

The generation command is configurable in a number of ways.

--output-dir <path>

Sets the output directory for the generated files.

--target <ts|cjs|esm|mts>

What type of files to generate. Documented above.


To avoid accidental changes, you’ll be prompted to confirm whenever the --target has changed from the previous run. To avoid this prompt, pass --force-overwrite.


Prints full documentation.

The generator also supports all the connection flags supported by the EdgeDB CLI. These aren’t necessary when using a project or environment variables to configure a connection.

Certain link/property names will create conflicts with parts of the query builder API. Avoid using the following names in your schema.

  • filter

  • order_by

  • limit

  • offset

  • run

  • is

  • index

  • slice

  • destructure

While the e object is all that’s required to build queries, npx edgeql-js also generates TypeScript interfaces representing your current schema. These are not needed to construct queries, but are generated as a convenience.

import e, {Person, Movie} from "./dbschema/edgeql-js";

Given this EdgeDB schema:

module default {
  scalar type Genre extending enum<Horror, Comedy, Drama>;
  type Person {
    required property name -> str;
  type Movie {
    required property title -> str;
    property genre -> Genre;
    multi link actors -> Person;

The following interfaces will be generated (simplified for clarify):

enum Genre {
  Horror = "Horror",
  Comedy = "Comedy",
  Drama = "Drama"

interface Person {
  id: string;
  name: string;

interface Movie {
  id: string;
  title: string;
  genre?: Genre | null;
  actors: Person[];

Any types declared in a non-default module will be generated into an accordingly named namespace.