---
# System prepended metadata

title: "\U0001F3C5  Day 16 - TodoList API & Promise"
tags: [TypeScript 直播班-2025]

---

# 🏅 Day 16 - TodoList API & Promise

![截圖 2024-01-23 下午2.01.58](https://hackmd.io/_uploads/BktP8C3Fp.png)

學習一定的階段之後，相信大家也已經吸收到許多關於 TypeScript 的知識了，那麼接下來就是將這些知識給運用在開發上的練習！透過這些練習也能讓大家對於主線任務的進行更加掌握和熟悉！

這邊使用的是六角的 TodoList API 幫助大家練習~

1. API 網址：https://todolist-api.hexschool.io/
2. API 文件：https://todolist-api.hexschool.io/doc/

通通做好做滿，新增、編輯、刪除應有盡有，讓同學們可從中思考，如果有相同程式邏輯的程式碼，該如何進行優化~

## 建立 Vue with TypeScript 專案

首先我們要來建立一個 Vue 的專案，並且支援 TypeScript 的語法，同學可以自己選用習慣的方式來建立專案，或是也可以參考[第一天](https://hackmd.io/@hex-course/SyGd4x8Aee)的任務內容，使用 Vite 來將專案給初始化起來~

```shell
$ npm create vite@latest
... (輸入專案名稱)
Select a framework >> Vue
Select a variant >> TypeScript
... (按照預設選項即可)
```

建立完成後進入到專案內，可以透過 `npm run dev` 讓專案執行起來，也可以運用先前課程學到的內容，針對 `tsconfig` 的部分自行調整~


## 實作時間

請使用 TodoList API 進行開發並嘗試導入 TypeScript 進行優化，本日可以聚焦在如何設計關於 API 的模組化、泛型。
參考 API 文件當中各個 API 的請求、回傳格式，設計一個通用的函式 `apiFetch` 讓後續的 API 呼叫可以透過這個 `apiFetch` 來統一處理，並且支援請求和回傳的型別格式。
另外關於呼叫 API 的工具，除了利用原生的 `fetch` 之外，也可以嘗試使用外部套件如 `axios` 等來整合~

```ts
// src/api/api.ts

export const apiFetch = async () => {
  // ...
};

// 範例 API 設計
// src/api/user.ts

export const register = async () => {
  const response = await apiFetch("users/sign_up", ...);
  return response;
}; // 此處帶入的參數、形式會根據 apiFetch 的設計而有所不同

export const login = async () => {
  const response = await apiFetch("users/sign_in", ...);
  return response;
};
```

<!-- 參考範例：
 type ApiArgs<T> = {
  endpoint: string;
  method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
  requestBody?: T;
};

const apiFetch = async <T, U>(args: ApiArgs<T>): Promise<U> => {
  // 一般會將 baseUrl 使用環境變數做管理，這邊為了簡化範例故直接寫死在函式當中
  const baseUrl = "https://todolist-api.hexschool.io";
  const url = `${baseUrl}/${args.endpoint}`;

  try {
    const response = await fetch(url, {
      method: args.method,
      body: args.requestBody ? JSON.stringify(args.requestBody) : null,
      headers: {
        "Content-Type": "application/json",
        Authorization: localStorage.getItem("token") || "",
      },
    });
    const responseData = (await response.json()) as U;
    return responseData;
  } catch (error) {
    throw new Error("Error occurred fetching: " + String(error));
  }
};

type Status = {
  status: boolean;
};

type signupData = {
  email: string;
  password: string;
  nickname: string;
};

type signinData = Omit<signupData, "nickname">;

type signupResponse = {
  uid: string;
} & Status;

type signinResponse = {
  exp: number;
  nickname: string;
  token: string;
} & Status;

const register = async (signupData: signupData) => {
  const response = await apiFetch<signupData, signupResponse>({
    endpoint: "users/sign_up",
    method: "POST",
    requestBody: signupData,
  });
  return response;
};

const login = async (signinData: signinData) => {
  const response = await apiFetch<signinData, signinResponse>({
    endpoint: "users/sign_in",
    method: "POST",
    requestBody: signinData,
  });
  return response;
};
-->