fix(api): use notInArray + inArray in unread-count subqueries
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

the sql.join() form of NOT IN crashed the route handler before
it could respond — vercel surfaced the crash as a plaintext 404
instead of going through hono's exception handler. switching to
drizzle's notInArray() / inArray() emits stable parameter
bindings and resolves both /v1/me/topics (fresh endpoint) and
/v1/topics (older endpoint with the same ANY() pattern bug).

also cleans up debug instrumentation that was added while
chasing the 404.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-05-03 01:05:42 +01:00
parent c795df4fd4
commit 3964de4962
2 changed files with 6 additions and 8 deletions

View File

@@ -41,8 +41,9 @@ export async function request<T = unknown>(opts: RequestOpts): Promise<T> {
}); });
if (!res.ok) { if (!res.ok) {
let body: unknown; const text = await res.text();
try { body = await res.json(); } catch { body = await res.text(); } let body: unknown = text;
try { body = JSON.parse(text); } catch { /* leave as text */ }
throw new ApiError(res.status, res.statusText, body); throw new ApiError(res.status, res.statusText, body);
} }

View File

@@ -39,7 +39,7 @@ import {
messageQueue, messageQueue,
presence, presence,
} from "@turbostarter/db/schema/mesh"; } from "@turbostarter/db/schema/mesh";
import { and, asc, count, desc, eq, gt, inArray, isNull, lt, sql } from "drizzle-orm"; import { and, asc, count, desc, eq, gt, inArray, isNull, lt, notInArray, sql } from "drizzle-orm";
import { validate } from "../../middleware"; import { validate } from "../../middleware";
import { import {
@@ -596,10 +596,7 @@ export const v1Router = new Hono<Env>()
and( and(
inArray(meshTopicMessage.topicId, topicIds), inArray(meshTopicMessage.topicId, topicIds),
sql`${meshTopicMessage.createdAt} > COALESCE(${meshTopicMember.lastReadAt}, '1970-01-01'::timestamp)`, sql`${meshTopicMessage.createdAt} > COALESCE(${meshTopicMember.lastReadAt}, '1970-01-01'::timestamp)`,
sql`${meshTopicMessage.senderMemberId} NOT IN (${sql.join( notInArray(meshTopicMessage.senderMemberId, myMemberIds),
myMemberIds.map((id) => sql`${id}`),
sql`, `,
)})`,
), ),
) )
.groupBy(meshTopicMessage.topicId); .groupBy(meshTopicMessage.topicId);
@@ -688,7 +685,7 @@ export const v1Router = new Hono<Env>()
) )
.where( .where(
and( and(
sql`${meshTopicMessage.topicId} = ANY(${topicIds})`, inArray(meshTopicMessage.topicId, topicIds),
sql`${meshTopicMessage.createdAt} > COALESCE(${meshTopicMember.lastReadAt}, '1970-01-01'::timestamp)`, sql`${meshTopicMessage.createdAt} > COALESCE(${meshTopicMember.lastReadAt}, '1970-01-01'::timestamp)`,
sql`${meshTopicMessage.senderMemberId} <> ${key.issuedByMemberId}`, sql`${meshTopicMessage.senderMemberId} <> ${key.issuedByMemberId}`,
), ),