Skip to content

Conversation

@freeatnet
Copy link
Contributor

@freeatnet freeatnet commented Sep 30, 2025

Why?

This PR adds the ability for users of the queries generator to override the TypeScript types of custom scalar variables via OverrideCodecType. The default scalar types are non-overrideable.

Example:

  1. Set up a schema and a query:
    default.gel:
module default {
  scalar type EvmAddress extending str {
    constraint expression on ( re_test(r'^0x[a-f0-9]{40}$', __subject__) );
  }

  scalar type HumanAge extending int16 {
    constraint max_value(120);
  }

  type User {
    required property primaryAddress -> EvmAddress {
      constraint exclusive;
    };
    required property firstName -> str;
    property username -> str {
      constraint exclusive;
    };
    property age -> HumanAge;
  }
}

findUser.edgeql:

select User {
  id,
  primaryAddress,
  username,
  age
} filter .id = <uuid>$id
  1. Run generate queries --target ts --use-resolved-codec-type

  2. Verify findUser.query.ts includes the overridable types:

export type FindUserReturns = {
  "primaryAddress": ResolvedCodecType<"EvmAddress", string>;
  "age": ResolvedCodecType<"HumanAge", number>;
  "id": string;
  "username": string | null;
} | null;

// Checking types -- should be types for base scalars
let result: NonNullable<FindUserReturns>;
result.username
    // ^? (property) "username": string | null

result.primaryAddress
    // ^? (property) "primaryAddress": string

result.age
    // ^? (property) "age": number
  1. Add an override:
declare module "gel" {
  export interface OverrideCodecType {
    "EvmAddress": `0x${string}`;
  }
}
  1. Check types with overrides:
let result: NonNullable<FindUserReturns>;
result.username
    // ^? (property) "username": string | null

result.primaryAddress
    // ^? (property) "primaryAddress": `0x${string}`

result.age
    // ^? (property) "age": number

How?

This PR attempts to implement this functionality in a backwards-compatible way, since analyzeQuery is technically a public API.1

Footnotes

  1. Although it's only used in the queries generator within the repo, it's mentioned in the README as the primary API to build a custom generator, and it looks like other projects are using it.

},
);

async function analyzeQuery(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to expand the existing public function to take options (let's use the existing CommandOptions type instead of this anonymous type) and make it default to {} like you're doing here. The main first-party consumer of this function is just this queries generator, so it'd be weird if the public one was not the one that the queries generator used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scotttrinh updated. Didn't use CommandOptions in analyzeQuery because the command options are defined in generate, while analyzeQuery is in the main package. Let me know if that doesn't work for you!

@freeatnet freeatnet force-pushed the feat/queries-with-custom-scalar-registry branch from 130ab1c to 8fa0759 Compare October 1, 2025 18:03
Copy link
Collaborator

@scotttrinh scotttrinh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 Thanks for bearing with me on the back and forth!

@scotttrinh scotttrinh merged commit 80f2c89 into geldata:master Oct 2, 2025
8 of 9 checks passed
@freeatnet
Copy link
Contributor Author

@scotttrinh no worries at all — I appreciate your responsiveness!

@MartinCura
Copy link
Contributor

Neat!

i'm trying this and because of TS magic i'm not familiar with claude had to add

import type {} from "gel"

to the overrides.d.ts so the gel type import in my autogenerated.ts didn't fail

@scotttrinh
Copy link
Collaborator

Oh, thanks for the report! What is autogenerated.ts?

@MartinCura
Copy link
Contributor

Oh, thanks for the report! What is autogenerated.ts?

Should've been explicit, i use

pnpm exec generate queries --target ts --use-resolved-codec-type --file ./queries/autogenerated && prettier --experimental-cli -w ./queries

autogenerated.ts is the one file where all my generated queries go, now starts with

// GENERATED by @gel/generate v0.6.4
// This file is automatically generated from .edgeql query files.
// To make changes, edit the source .edgeql file and regenerate.

import type {Executor, ResolvedCodecType} from "gel";

// ...

Also

// overrides.d.ts
// This import makes this file a module, enabling proper module augmentation
import type {} from "gel"

declare module "gel" {
  export interface OverrideCodecType {
    "default::ShopHandle": `@${string}`
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants