From 42a5fbec6b2e0c1e0801bb2160f1a8d15cc4fbfd Mon Sep 17 00:00:00 2001 From: Le Tuan Kiet Date: Sun, 2 Oct 2022 14:14:57 +0700 Subject: [PATCH] [RTT-03] Implement axios --- .env | 2 ++ package.json | 2 ++ src/App.tsx | 4 +++- src/api/index.ts | 8 +++++++ src/react-app-env.d.ts | 7 ++++++ src/utils/constants/api.ts | 9 +++++++ src/utils/reducer/api-reducer.ts | 41 ++++++++++++++++++++++++++++++++ src/utils/service/api.ts | 36 ++++++++++++++++++++++++++++ src/utils/types/api.d.ts | 12 ++++++++++ yarn.lock | 24 ++++++++++++++++++- 10 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 .env create mode 100644 src/api/index.ts create mode 100644 src/utils/constants/api.ts create mode 100644 src/utils/reducer/api-reducer.ts create mode 100644 src/utils/service/api.ts create mode 100644 src/utils/types/api.d.ts diff --git a/.env b/.env new file mode 100644 index 0000000..c8eafc4 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +REACT_APP_API_ENDPOINT=https://api.openweathermap.org/data/2.5/ +REACT_APP_KEY = 623949ba087cb249c44a770ad1a293fe \ No newline at end of file diff --git a/package.json b/package.json index 6917922..667a8f2 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,12 @@ "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@types/uuid": "^8.3.4", + "axios": "^0.27.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "typescript": "^4.4.2", + "url-join": "^5.0.0", "uuid": "^9.0.0", "web-vitals": "^2.1.0" }, diff --git a/src/App.tsx b/src/App.tsx index 1e363c0..e030407 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,8 @@ import './App.css'; -function App() { +function App() { + + return (
diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..502a1ea --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,8 @@ +/* eslint-disable import/no-anonymous-default-export */ +import API from '../utils/constants/api'; +import RestService from '../utils/service/api'; + +export default { + // EXAMPLE: Your need delete it + // weather: new RestService(API.WEATHER), +}; diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 6431bc5..9534e83 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1 +1,8 @@ /// +declare namespace NodeJS { + interface ProcessEnv { + //types of envs + NODE_ENV: "development" | "production" | "test"; + PUBLIC_URL: string; + } +} diff --git a/src/utils/constants/api.ts b/src/utils/constants/api.ts new file mode 100644 index 0000000..63e6341 --- /dev/null +++ b/src/utils/constants/api.ts @@ -0,0 +1,9 @@ +export enum API_STATUS { + HTTP_404_NOT_FOUND = 400, + HTTP_500_SERVER = 500, +} + +//TODO: you need delete when integrate API +// export default { +// WEATHER: 'weather', +// }; diff --git a/src/utils/reducer/api-reducer.ts b/src/utils/reducer/api-reducer.ts new file mode 100644 index 0000000..e472c97 --- /dev/null +++ b/src/utils/reducer/api-reducer.ts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export const INITIAL_STATE: InfoApi = { + isLoading: false, + isError: false, + data: null, + errorMsg: '', +}; + +export enum ACTION_TYPE { + FETCH_START = 'START', + FETCH_SUCCESS = 'SUCCESS', + FETCH_FAILED = 'FAILED', +} + +export const apiReducer = (state: InfoApi, action: ActionReducer) => { + switch (action.type) { + case ACTION_TYPE.FETCH_START: + return { + ...state, + isLoading: true, + isError: false, + }; + case ACTION_TYPE.FETCH_SUCCESS: + return { + isLoading: false, + isError: false, + data: action.payload, + }; + + case ACTION_TYPE.FETCH_FAILED: + return { + ...state, + isLoading: false, + isError: true, + errorMsg: action.errorMsg, + }; + + default: + return INITIAL_STATE; + } +}; diff --git a/src/utils/service/api.ts b/src/utils/service/api.ts new file mode 100644 index 0000000..7d4ddc7 --- /dev/null +++ b/src/utils/service/api.ts @@ -0,0 +1,36 @@ +import axios, { AxiosError, AxiosResponse } from 'axios'; +import urlJoin from 'url-join'; +import { API_STATUS } from '../constants/api'; + +const baseURL = process.env.REACT_APP_API_ENDPOINT || ''; + +const linkApi = (resource: string) => { + return urlJoin(baseURL, resource); +}; + +const handleError = (error: AxiosError) => { + if (!error.response?.status || [API_STATUS.HTTP_500_SERVER].includes(error.response?.status)) { + console.log(error.response); + } else { + return error.response.data; + } +}; + +class RestService { + constructor(protected resource: string) {} + + index(params?: R) { + return new Promise>((resolve, rejects) => { + axios + .get(linkApi(this.resource), { + params, + }) + .then(resolve) + .catch((error: AxiosError) => { + rejects(handleError(error)); + }); + }); + } +} + +export default RestService; diff --git a/src/utils/types/api.d.ts b/src/utils/types/api.d.ts new file mode 100644 index 0000000..8356150 --- /dev/null +++ b/src/utils/types/api.d.ts @@ -0,0 +1,12 @@ +interface InfoApi { + isLoading: boolean; + data?: any; + isError: boolean; + errorMsg?: string; +} + +interface ActionReducer { + type: string; + payload?: ApiResponse; + errorMsg?: string; +} diff --git a/yarn.lock b/yarn.lock index 39f37fc..97d0cc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2862,6 +2862,14 @@ axe-core@^4.4.3: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -4637,7 +4645,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.9: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -4670,6 +4678,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -8765,6 +8782,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-join@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-5.0.0.tgz#c2f1e5cbd95fa91082a93b58a1f42fecb4bdbcf1" + integrity sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA== + url-parse@^1.5.3: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"