encore icon indicating copy to clipboard operation
encore copied to clipboard

TS : Generate Client, Error with input params.

Open portedison opened this issue 10 months ago • 9 comments

I'm getting the following error -

error: 
Internal compiler error [E0001]

An unhandled panic occurred in the Encore compiler: runtime error: invalid memory address or nil
pointer dereference

This is a bug in Encore and should not have occurred. Please report this issue to the Encore team
either on Github at https://github.com/encoredev/encore/issues/new and include this error.

╭─[Stack Trace]
├─▶ clientgen.Client.func1                encr.dev/internal/clientgen/client.go:65
├─▶ clientgen.(*typescript).handleBailout encr.dev/internal/clientgen/typescript.go:1534
├─▶ clientgen.(*typescript).isRecursive   encr.dev/internal/clientgen/typescript.go:1624
├─▶ clientgen.(*typescript).writeTyp      encr.dev/internal/clientgen/typescript.go:1492
├─▶ clientgen.(*typescript).writeService  encr.dev/internal/clientgen/typescript.go:256
├─▶ clientgen.(*typescript).Generate      encr.dev/internal/clientgen/typescript.go:96
╰─[... remaining frames omitted ...]


Error executing adminApiCommand: Command failed: encore gen client --services=adminApi --output=../../packages/client-encore-admin/generated/client.ts --env=local

This occurs from the example project code (https://github.com/encoredev/examples/blob/main/ts/prisma/users/user.controller.ts#L84) -

export const update = api(
  { expose: true, method: "PATCH", path: "/users/:id" },
  async ({
    id,
    data,
  }: {
    id: number;
    data: UpdateUserDto;
  }): Promise<UserResponse> => {
    try {
      const result = await UserService.update(id, data);
      return result;
    } catch (error) {
      throw APIError.aborted(error?.toString() || "Error updating user");
    }
  },
);

All other endpoints in that file generate without an issue.

portedison avatar Jan 16 '25 01:01 portedison

Possible that this is a typescript/node issue -

Swapping out with -

id: number;
data: {
  name?: string;
  surname?: string;
};

rather than

id: number;
data: UpdateUserDto;

Seems to "fix" the issue.

portedison avatar Jan 16 '25 02:01 portedison

I've changed this to -

async ({ id, ...data }: { id: number } & UpdateUserDto): Promise<UserResponse> => {

And now it compiles correctly.

This is potentially a change in versions? I'm using "encore.dev": "^1.45.6" while the example is using "encore.dev": "^1.39.5"

🫤 I'm still a little confused why it doesn't like the data: UpdateUserDto;

portedison avatar Jan 16 '25 03:01 portedison

Yeah seems to not recognise the type/interface from children, this test also fails -

type Other = {
  id: number;
}

async ({
    id,
    ...data
  }: { id: number; other: Other } & UpdateUserDto): Promise<UserResponse> => {...

portedison avatar Jan 16 '25 03:01 portedison

I tried it with the latest version of encore (1.45.7), checked out the prisma example, and it generated without problems. Have you made any changes to the example code?

What is the exact command that you run when you encounter this problem in the example?

If you cant reproduce it with the example, could you share some code so that I can try to reproduce it. Specifically the type definitions would be interesting to see

fredr avatar Jan 16 '25 12:01 fredr

This issues persists, I'm on the latest version 1.46.4. It seem to be that the issue appears when using a defined type. Some examples below.

The following example fails -

// in controller.ts (requires the workaround with {} & DefinedType), without that it fails too.

async ({ id, ...input }: { id: string} & ProjectCreationInput): Promise<ProjectResponse> => {
...


// in interface.ts

export type ProjectIcon = "ICON_01" | "ICON_02" | "ICON_03" | "ICON_04" | "ICON_05";

export interface ProjectCreationInput {
  name: string;

  // option 1. no matter what this fails. And this isn't really an option icon: { other?: string } & ProjectIcon;
  icon: ProjectIcon;

  // option 2. this works if I do the above workaround (with {} & DefinedType).
  icon: "ICON_01" | "ICON_02" | "ICON_03" | "ICON_04" | "ICON_05";
}

I can confirm there are no issues with response types eg. ProjectResponse also has icon: ProjectIcon; and generates the client, with types wonderfully!

portedison avatar Feb 12 '25 23:02 portedison

When I visit http://localhost:9400/... it seems to still recognise the types values.

Image

portedison avatar Feb 13 '25 00:02 portedison

This may relate to the environment (turborepo). I just tested the prisma example project, and changed the following,

export interface Other {
  other: string;
}

export interface UpdateUserDto {
  /** Name of the user */
  name?: string;
  /** Surname of the user */
  surname?: string;
  other: Other;
}

There was no issue in the example project but as soon as I shifted it into my project it failed.

portedison avatar Feb 13 '25 00:02 portedison

We managed to find a work around, seems that when we define the whole type there is no issue -

type ProjectCreationUpdate = { id: string } & ProjectCreationInput;

export const projectCreationUpdate = api(
  { expose: true, auth: true, method: "PATCH", path: "/projects/:id/creation" },
  async ({ id, ...input }: ProjectCreationUpdate): Promise<ProjectResponse> => {
        ....
  }
);

Generates to -

export interface ProjectCreationUpdate {
    name: string;
    icon: ProjectIcon;
  }

public async projectCreationUpdate(
      id: string,
      params: project.ProjectCreationUpdate,
    ): Promise<project.ProjectResponse> { ...

portedison avatar Feb 13 '25 03:02 portedison

Here is a minimal reproduction https://github.com/gladeye/encore-clientgen-error It only happens when you use an inline type in an api definition, that type references a named type AND the name of the service does not match the name of the folder the service is in.

Kethatril avatar Feb 17 '25 23:02 Kethatril