Skip to content

GET (query) requests don't wrap input in {json: ...}, breaking every parameterized read against tRPC servers using the superjson transformer #46

Description

@afifsagent

Bug

apiPost in dist/client.js correctly wraps mutation payloads as { json: data } before sending, matching tRPC's superjson transformer convention:

export async function apiPost(endpoint, data) {
    const client = createClient();
    const response = await client.post(`/trpc/${endpoint}`, data ? { json: data } : undefined);
    ...
}

apiGet does not — it sends the raw params object as the query string, unwrapped:

export async function apiGet(endpoint, params) {
    const client = createClient();
    const query = params
        ? `?input=${encodeURIComponent(JSON.stringify(params))}`
        : "";
    ...
}

Against a Dokploy server using the superjson transformer, any query procedure that requires input parameters fails with a 400:

{
  "error": {
    "json": {
      "message": "[\n  {\n    \"expected\": \"object\",\n    \"code\": \"invalid_type\",\n    \"path\": [],\n    \"message\": \"Invalid input: expected object, received undefined\"\n  }\n]",
      "code": -32600,
      "data": { "code": "BAD_REQUEST", "httpStatus": 400, "path": "application.one", ... }
    }
  }
}

Repro

dokploy application one --applicationId <id>
# Request failed with status code 400

No-argument queries (e.g. project all) work fine, since an empty/undefined input happens to satisfy those procedures' schemas either way — which is why this doesn't show up immediately.

Expected

apiGet should wrap params the same way apiPost wraps data:

const query = params
    ? `?input=${encodeURIComponent(JSON.stringify({ json: params }))}`
    : "";

Environment

  • @dokploy/cli 0.29.4 (reports dokploy --version → 0.3.0)
  • Dokploy server v0.29.11
  • Affects any query command that takes parameters: application one, deployment all, mounts list-by-service-id, log reads, etc.

Activity

  1. ambymon commented on Jul 28, 2026

    @ambymon

    Confirming this on a live Dokploy v0.29.13 panel with CLI 0.29.4 — the diagnosis holds exactly:

    GET /api/trpc/application.one?input={"applicationId":"<id>"}          -> 400
    GET /api/trpc/application.one?input={"json":{"applicationId":"<id>"}} -> 200
    

    It hits every parameterized query I tried (application one, deployment all, project one, environment one). project all is the only survivor, because it takes no input at all.

    Note for whoever picks this up: #45 already fixes the symptom, but by moving the client to the REST routes rather than wrapping the input. If a minimal tRPC-side fix is preferred instead, the unwrap has to stay — the tRPC route returns {"result":{"data":{...}}} while REST returns the bare object:

    GET /api/application.one       -> {appName, applicationId, applicationStatus, ...}
    GET /api/trpc/application.one  -> {"result":{"data":{...}}}
    

    So wrapping the request without keeping ?.result?.data?.json would break the same reads a second time, just one layer further in.

  2. thomasorgeval commented on Aug 28, 2026

    @thomasorgeval

    Confirmed on @dokploy/cli 0.30.2.

    Additional reproduction:

    • dokploy docker get-server-health --serverId REDACTED --sinceHours 1 --json returns Request failed with status code 400.
    • dokploy server one --serverId REDACTED --json returns the same 400.

    For docker.getServerHealth, sending the same values to the tRPC endpoint with input encoded as {"json":{"serverId":"REDACTED","sinceHours":1}} returns 200. This confirms that the healthcheck failure in 0.30.2 is another instance of the generic apiGet wrapping issue described here.

  3. cavalinho-xdd commented on Oct 9, 2026

    @cavalinho-xdd

    Still reproducible with @dokploy/cli 0.30.7 (latest on npm) against a Dokploy v0.30.8 server: application one, project one and deployment all all return Request failed with status code 400, while parameterless commands like project all work.

    Wrapping the GET input as ?input={"json":{...}} fixes it. The approach in #52 works for me. Could #52 be merged and released? Until then, every read command that takes an ID is unusable.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions