feat(api): refactor project API functions to use $fetch for improved error handling
Some checks failed
ci / ci (22) (pull_request) Failing after 6m4s
Some checks failed
ci / ci (22) (pull_request) Failing after 6m4s
This commit is contained in:
parent
79e0efc71e
commit
a4f909289c
|
|
@ -1,35 +1,8 @@
|
||||||
import type { Project, ProjectInsertDTO, ProjectUpdateDTO } from "@workspace/schema";
|
import type { Project, ProjectInsertDTO, ProjectUpdateDTO } from "@workspace/schema";
|
||||||
|
|
||||||
interface ApiResponse<T> {
|
|
||||||
data: T | null;
|
|
||||||
error: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBaseUrl(): string {
|
|
||||||
const config = useRuntimeConfig();
|
|
||||||
return config.public.apiBaseUrl as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function request<T>(path: string, options?: RequestInit): Promise<ApiResponse<T>> {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${getBaseUrl()}${path}`, {
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const body = await res.json().catch(() => null);
|
|
||||||
return { data: null, error: body?.message ?? `HTTP ${res.status}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = (await res.json()) as T;
|
|
||||||
return { data, error: null };
|
|
||||||
} catch (err) {
|
|
||||||
return { data: null, error: err instanceof Error ? err.message : "Unknown error" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useProjectApi() {
|
export function useProjectApi() {
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
const apiBase = config.public.apiBaseUrl as string;
|
||||||
const NOT_FOUND = -1;
|
const NOT_FOUND = -1;
|
||||||
|
|
||||||
const projects = ref<Project[]>([]);
|
const projects = ref<Project[]>([]);
|
||||||
|
|
@ -39,67 +12,65 @@ export function useProjectApi() {
|
||||||
async function fetchProjects(): Promise<void> {
|
async function fetchProjects(): Promise<void> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
try {
|
||||||
const { data, error: err } = await request<Project[]>("/projects");
|
projects.value = await $fetch<Project[]>(`${apiBase}/projects`);
|
||||||
if (data) projects.value = data;
|
} catch (err) {
|
||||||
else error.value = err;
|
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||||
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createProject(body: Partial<ProjectInsertDTO>): Promise<Project | null> {
|
async function createProject(body: Partial<ProjectInsertDTO>): Promise<Project | null> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
try {
|
||||||
const { data, error: err } = await request<Project>("/projects", {
|
const created = await $fetch<Project>(`${apiBase}/projects`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(body),
|
body,
|
||||||
});
|
});
|
||||||
|
projects.value.push(created);
|
||||||
if (data) {
|
return created;
|
||||||
projects.value.push(data);
|
} catch (err) {
|
||||||
} else {
|
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||||
error.value = err;
|
return null;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = false;
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateProject(id: string, body: Partial<ProjectUpdateDTO>): Promise<Project | null> {
|
async function updateProject(id: string, body: Partial<ProjectUpdateDTO>): Promise<Project | null> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
try {
|
||||||
const { data, error: err } = await request<Project>(`/projects/${id}`, {
|
const updated = await $fetch<Project>(`${apiBase}/projects/${id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify(body),
|
body,
|
||||||
});
|
});
|
||||||
|
const idx = projects.value.findIndex((p) => p.id === id);
|
||||||
if (data) {
|
if (idx !== NOT_FOUND) projects.value[idx] = updated;
|
||||||
const idx = projects.value.findIndex(p => p.id === id);
|
return updated;
|
||||||
if (idx !== NOT_FOUND) projects.value[idx] = data;
|
} catch (err) {
|
||||||
} else {
|
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||||
error.value = err;
|
return null;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = false;
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteProject(id: string): Promise<boolean> {
|
async function deleteProject(id: string): Promise<boolean> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
try {
|
||||||
const { error: err } = await request(`/projects/${id}`, { method: "DELETE" });
|
await $fetch(`${apiBase}/projects/${id}`, { method: "DELETE" });
|
||||||
|
projects.value = projects.value.filter((p) => p.id !== id);
|
||||||
if (err) {
|
return true;
|
||||||
error.value = err;
|
} catch (err) {
|
||||||
} else {
|
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||||
projects.value = projects.value.filter(p => p.id !== id);
|
return false;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading.value = false;
|
|
||||||
return !err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { projects, loading, error, fetchProjects, createProject, updateProject, deleteProject };
|
return { projects, loading, error, fetchProjects, createProject, updateProject, deleteProject };
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,14 @@ const project = ref<Project | null>(null);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const notFound = ref(false);
|
const notFound = ref(false);
|
||||||
|
|
||||||
const config = useRuntimeConfig();
|
|
||||||
const apiBase = config.public.apiBaseUrl as string;
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const res = await fetch(`${apiBase}/projects/${route.params.id}`);
|
try {
|
||||||
if (res.ok) {
|
project.value = await $fetch<Project>(`${useRuntimeConfig().public.apiBaseUrl as string}/projects/${route.params.id}`);
|
||||||
project.value = await res.json();
|
} catch {
|
||||||
} else {
|
|
||||||
notFound.value = true;
|
notFound.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
loading.value = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleSubmit(body: { name: string; description?: string; status: string }) {
|
async function handleSubmit(body: { name: string; description?: string; status: string }) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
import withNuxt from "./.nuxt/eslint.config.mjs"
|
import withNuxt from "./.nuxt/eslint.config.mjs";
|
||||||
import { baseRules, } from "@workspace/tooling/eslint/base"
|
import { baseRules, } from "@workspace/tooling/eslint/base";
|
||||||
|
|
||||||
export default withNuxt({
|
export default withNuxt({
|
||||||
rules: {
|
rules: {
|
||||||
...baseRules,
|
...baseRules,
|
||||||
"@stylistic/member-delimiter-style": "off",
|
"@stylistic/member-delimiter-style": "off",
|
||||||
|
"@stylistic/arrow-parens": "off",
|
||||||
"vue/max-attributes-per-line": "off",
|
"vue/max-attributes-per-line": "off",
|
||||||
"vue/singleline-html-element-content-newline": "off",
|
"vue/singleline-html-element-content-newline": "off",
|
||||||
"vue/html-closing-bracket-newline": "off",
|
"vue/html-closing-bracket-newline": "off",
|
||||||
"vue/no-multiple-template-root": "off",
|
"vue/no-multiple-template-root": "off",
|
||||||
|
"max-lines-per-function": "off",
|
||||||
|
"max-statements": "off",
|
||||||
},
|
},
|
||||||
},)
|
},);
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,4 @@ export {
|
||||||
type ProjectUpdateDTO,
|
type ProjectUpdateDTO,
|
||||||
} from "./drizzle/projects";
|
} from "./drizzle/projects";
|
||||||
export { projectsRelations, tasksRelations } from "./drizzle/relations";
|
export { projectsRelations, tasksRelations } from "./drizzle/relations";
|
||||||
export {
|
export { tasks, taskSelect, taskInsert, taskUpdate, type Task } from "./drizzle/tasks";
|
||||||
tasks,
|
|
||||||
taskSelect,
|
|
||||||
taskInsert,
|
|
||||||
taskUpdate,
|
|
||||||
type Task,
|
|
||||||
} from "./drizzle/tasks";
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue