this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Refactor `expectApiResponse` into `createApiError` with explicit response checks (#16)

Replace `expectApiResponse` with `createApiError` for more explicit error handling across all API hooks.

Previously, `expectApiResponse` wrapped the entire fetch call in a try/catch, obscuring network errors under a generic fallback message and hiding the response from the caller. The new `createApiError` function accepts an already-resolved `Response` and returns a constructed `ApiError`, leaving the fetch call and `response.ok` check inline at each call site.

The error message construction has also been improved: if the response body contains an error message that differs from the fallback, both are combined as `"<fallback>: <response message>"` rather than discarding one in favor of the other.

authored by

James Blair and committed by
GitHub
ccab25fc ba082183

+112 -105
+6 -5
apps/web/src/hooks/use-characters.ts
··· 1 1 import { useQuery } from "@tanstack/react-query"; 2 2 import { client } from "#/lib/api"; 3 - import { expectApiResponse } from "#/lib/api-error"; 3 + import { createApiError } from "#/lib/api-error"; 4 4 5 5 export function useCharacters() { 6 6 return useQuery({ 7 7 queryKey: ["characters"], 8 8 queryFn: async () => { 9 - const response = await expectApiResponse( 10 - client.api.characters.$get(), 11 - "Failed to load characters", 12 - ); 9 + const response = await client.api.characters.$get(); 10 + 11 + if (!response.ok) { 12 + throw await createApiError(response, "Failed to load characters"); 13 + } 13 14 14 15 return response.json(); 15 16 },
+8 -7
apps/web/src/hooks/use-chat.ts
··· 1 1 import { useAuth } from "@clerk/clerk-react"; 2 2 import { useQuery } from "@tanstack/react-query"; 3 3 import { client } from "#/lib/api"; 4 - import { expectApiResponse } from "#/lib/api-error"; 4 + import { createApiError } from "#/lib/api-error"; 5 5 import { chatQueryKey } from "#/lib/chat-query"; 6 6 7 7 interface UseChatOptions { ··· 15 15 enabled: isLoaded && isSignedIn, 16 16 queryKey: chatQueryKey(userId, id), 17 17 queryFn: async () => { 18 - const response = await expectApiResponse( 19 - client.api.chats[":id"].$get({ 20 - param: { id }, 21 - }), 22 - "Failed to load chat", 23 - ); 18 + const response = await client.api.chats[":id"].$get({ 19 + param: { id }, 20 + }); 21 + 22 + if (!response.ok) { 23 + throw await createApiError(response, "Failed to load chat"); 24 + } 24 25 25 26 return response.json(); 26 27 },
+6 -5
apps/web/src/hooks/use-chats.ts
··· 1 1 import { useAuth } from "@clerk/clerk-react"; 2 2 import { useQuery } from "@tanstack/react-query"; 3 3 import { client } from "#/lib/api"; 4 - import { expectApiResponse } from "#/lib/api-error"; 4 + import { createApiError } from "#/lib/api-error"; 5 5 import { chatsQueryKey } from "#/lib/chat-query"; 6 6 7 7 export function useChats() { ··· 11 11 enabled: isLoaded && isSignedIn, 12 12 queryKey: chatsQueryKey(userId), 13 13 queryFn: async () => { 14 - const response = await expectApiResponse( 15 - client.api.chats.$get(), 16 - "Failed to load chats", 17 - ); 14 + const response = await client.api.chats.$get(); 15 + 16 + if (!response.ok) { 17 + throw await createApiError(response, "Failed to load chats"); 18 + } 18 19 19 20 return response.json(); 20 21 },
+8 -7
apps/web/src/hooks/use-delete-chat.ts
··· 2 2 import { useMutation, useQueryClient } from "@tanstack/react-query"; 3 3 import { useNavigate } from "@tanstack/react-router"; 4 4 import { client } from "#/lib/api"; 5 - import { expectApiResponse } from "#/lib/api-error"; 5 + import { createApiError } from "#/lib/api-error"; 6 6 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 7 7 import type { ChatSummary } from "#/lib/types"; 8 8 ··· 13 13 14 14 return useMutation({ 15 15 mutationFn: async (id: string) => { 16 - await expectApiResponse( 17 - client.api.chats[":id"].$delete({ 18 - param: { id }, 19 - }), 20 - "Failed to delete chat", 21 - ); 16 + const response = await client.api.chats[":id"].$delete({ 17 + param: { id }, 18 + }); 19 + 20 + if (!response.ok) { 21 + throw await createApiError(response, "Failed to delete chat"); 22 + } 22 23 23 24 return id; 24 25 },
+11 -8
apps/web/src/hooks/use-edit-last-message.ts
··· 5 5 useQueryClient, 6 6 } from "@tanstack/react-query"; 7 7 import { client } from "#/lib/api"; 8 - import { expectApiResponse } from "#/lib/api-error"; 8 + import { createApiError } from "#/lib/api-error"; 9 9 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 10 10 import type { Chat, ChatMessage } from "#/lib/types"; 11 11 ··· 43 43 } 44 44 45 45 async function editLastMessage(chatId: string, text: string) { 46 - return expectApiResponse( 47 - client.api.chats[":id"].messages["edit-last"].$post({ 48 - param: { id: chatId }, 49 - json: { text }, 50 - }), 51 - "Failed to edit message", 52 - ); 46 + const response = await client.api.chats[":id"].messages["edit-last"].$post({ 47 + param: { id: chatId }, 48 + json: { text }, 49 + }); 50 + 51 + if (!response.ok) { 52 + throw await createApiError(response, "Failed to edit message"); 53 + } 54 + 55 + return response; 53 56 } 54 57 55 58 async function readTextStream(
+6 -5
apps/web/src/hooks/use-models.ts
··· 1 1 import { useQuery } from "@tanstack/react-query"; 2 2 import { client } from "#/lib/api"; 3 - import { expectApiResponse } from "#/lib/api-error"; 3 + import { createApiError } from "#/lib/api-error"; 4 4 5 5 export function useModels() { 6 6 return useQuery({ 7 7 queryKey: ["models"], 8 8 queryFn: async () => { 9 - const response = await expectApiResponse( 10 - client.api.models.$get(), 11 - "Failed to load models", 12 - ); 9 + const response = await client.api.models.$get(); 10 + 11 + if (!response.ok) { 12 + throw await createApiError(response, "Failed to load models"); 13 + } 13 14 14 15 return response.json(); 15 16 },
+10 -7
apps/web/src/hooks/use-regenerate-last-message.ts
··· 5 5 useQueryClient, 6 6 } from "@tanstack/react-query"; 7 7 import { client } from "#/lib/api"; 8 - import { expectApiResponse } from "#/lib/api-error"; 8 + import { createApiError } from "#/lib/api-error"; 9 9 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 10 10 import type { Chat, ChatMessage } from "#/lib/types"; 11 11 ··· 43 43 } 44 44 45 45 async function regenerateLastMessage(chatId: string) { 46 - return expectApiResponse( 47 - client.api.chats[":id"].messages.regenerate.$post({ 48 - param: { id: chatId }, 49 - }), 50 - "Failed to regenerate message", 51 - ); 46 + const response = await client.api.chats[":id"].messages.regenerate.$post({ 47 + param: { id: chatId }, 48 + }); 49 + 50 + if (!response.ok) { 51 + throw await createApiError(response, "Failed to regenerate message"); 52 + } 53 + 54 + return response; 52 55 } 53 56 54 57 async function readTextStream(
+9 -8
apps/web/src/hooks/use-rename-chat.ts
··· 1 1 import { useAuth } from "@clerk/clerk-react"; 2 2 import { useMutation, useQueryClient } from "@tanstack/react-query"; 3 3 import { client } from "#/lib/api"; 4 - import { expectApiResponse } from "#/lib/api-error"; 4 + import { createApiError } from "#/lib/api-error"; 5 5 import { chatsQueryKey } from "#/lib/chat-query"; 6 6 import type { ChatSummary } from "#/lib/types"; 7 7 ··· 11 11 12 12 return useMutation({ 13 13 mutationFn: async ({ id, title }: { id: string; title: string }) => { 14 - const response = await expectApiResponse( 15 - client.api.chats[":id"].$patch({ 16 - param: { id }, 17 - json: { title }, 18 - }), 19 - "Failed to rename chat", 20 - ); 14 + const response = await client.api.chats[":id"].$patch({ 15 + param: { id }, 16 + json: { title }, 17 + }); 18 + 19 + if (!response.ok) { 20 + throw await createApiError(response, "Failed to rename chat"); 21 + } 21 22 22 23 return response.json(); 23 24 },
+22 -18
apps/web/src/hooks/use-send-message.ts
··· 6 6 } from "@tanstack/react-query"; 7 7 import { useNavigate } from "@tanstack/react-router"; 8 8 import { client } from "#/lib/api"; 9 - import { expectApiResponse } from "#/lib/api-error"; 9 + import { createApiError } from "#/lib/api-error"; 10 10 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 11 11 import type { 12 12 CharacterId, ··· 90 90 } 91 91 92 92 async function sendMessageToChat(chatId: string, text: string) { 93 - return expectApiResponse( 94 - client.api.chats[":id"].messages.$post({ 95 - param: { id: chatId }, 96 - json: { text }, 97 - }), 98 - "Failed to send message", 99 - ); 93 + const response = await client.api.chats[":id"].messages.$post({ 94 + param: { id: chatId }, 95 + json: { text }, 96 + }); 97 + 98 + if (!response.ok) { 99 + throw await createApiError(response, "Failed to send message"); 100 + } 101 + 102 + return response; 100 103 } 101 104 102 105 async function readTextStream( ··· 142 145 throw new Error("Character is required"); 143 146 } 144 147 145 - const response = await expectApiResponse( 146 - client.api.chats.$post({ 147 - json: { 148 - title: "New chat", 149 - characterId, 150 - modelId, 151 - }, 152 - }), 153 - "Failed to create chat", 154 - ); 148 + const response = await client.api.chats.$post({ 149 + json: { 150 + title: "New chat", 151 + characterId, 152 + modelId, 153 + }, 154 + }); 155 + 156 + if (!response.ok) { 157 + throw await createApiError(response, "Failed to create chat"); 158 + } 155 159 156 160 const chat = await response.json(); 157 161
+9 -8
apps/web/src/hooks/use-update-chat-character.ts
··· 1 1 import { useAuth } from "@clerk/clerk-react"; 2 2 import { useMutation, useQueryClient } from "@tanstack/react-query"; 3 3 import { client } from "#/lib/api"; 4 - import { expectApiResponse } from "#/lib/api-error"; 4 + import { createApiError } from "#/lib/api-error"; 5 5 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 6 6 import type { 7 7 Chat, ··· 19 19 20 20 return useMutation({ 21 21 mutationFn: async ({ id, characterId }: UpdateChatCharacterInput) => { 22 - const response = await expectApiResponse( 23 - client.api.chats[":id"].$patch({ 24 - param: { id }, 25 - json: { characterId }, 26 - }), 27 - "Failed to update chat character", 28 - ); 22 + const response = await client.api.chats[":id"].$patch({ 23 + param: { id }, 24 + json: { characterId }, 25 + }); 26 + 27 + if (!response.ok) { 28 + throw await createApiError(response, "Failed to update chat character"); 29 + } 29 30 30 31 return response.json(); 31 32 },
+9 -8
apps/web/src/hooks/use-update-chat-model.ts
··· 1 1 import { useAuth } from "@clerk/clerk-react"; 2 2 import { useMutation, useQueryClient } from "@tanstack/react-query"; 3 3 import { client } from "#/lib/api"; 4 - import { expectApiResponse } from "#/lib/api-error"; 4 + import { createApiError } from "#/lib/api-error"; 5 5 import { chatQueryKey, chatsQueryKey } from "#/lib/chat-query"; 6 6 import type { 7 7 Chat, ··· 18 18 19 19 return useMutation({ 20 20 mutationFn: async ({ id, modelId }: UpdateChatModelInput) => { 21 - const response = await expectApiResponse( 22 - client.api.chats[":id"].$patch({ 23 - param: { id }, 24 - json: { modelId }, 25 - }), 26 - "Failed to update chat model", 27 - ); 21 + const response = await client.api.chats[":id"].$patch({ 22 + param: { id }, 23 + json: { modelId }, 24 + }); 25 + 26 + if (!response.ok) { 27 + throw await createApiError(response, "Failed to update chat model"); 28 + } 28 29 29 30 return response.json(); 30 31 },
+8 -19
apps/web/src/lib/api-error.ts
··· 28 28 return undefined; 29 29 } 30 30 31 - export async function expectApiResponse( 32 - responsePromise: Promise<Response>, 31 + export async function createApiError( 32 + response: Response, 33 33 fallbackMessage: string, 34 34 ) { 35 - try { 36 - const response = await responsePromise; 35 + const responseMessage = await getResponseErrorMessage(response); 36 + const message = 37 + responseMessage && responseMessage !== fallbackMessage 38 + ? `${fallbackMessage}: ${responseMessage}` 39 + : fallbackMessage; 37 40 38 - if (!response.ok) { 39 - const message = 40 - (await getResponseErrorMessage(response)) ?? fallbackMessage; 41 - 42 - throw new ApiError(message, { status: response.status }); 43 - } 44 - 45 - return response; 46 - } catch (error) { 47 - if (error instanceof ApiError) { 48 - throw error; 49 - } 50 - 51 - throw new ApiError(fallbackMessage); 52 - } 41 + return new ApiError(message, { status: response.status }); 53 42 } 54 43 55 44 export function getApiErrorMessage(error: unknown) {