From fbf12d99474c35a933d272fdfeebbfd9d0998a20 Mon Sep 17 00:00:00 2001 From: Sonny Date: Sun, 12 Nov 2023 03:30:58 +0100 Subject: [PATCH] refactor: remove axios --- package-lock.json | 91 ----------------------------- package.json | 1 - src/components/Links/LinkItem.tsx | 29 +++++---- src/lib/api/handler.ts | 7 +-- src/lib/request.ts | 19 ++++++ src/pages/category/create.tsx | 35 +++++------ src/pages/category/edit/[cid].tsx | 37 +++++------- src/pages/category/remove/[cid].tsx | 36 +++++------- src/pages/link/create.tsx | 41 ++++++------- src/pages/link/edit/[lid].tsx | 43 ++++++-------- src/pages/link/remove/[lid].tsx | 34 +++++------ src/utils/client.ts | 8 --- src/utils/front.ts | 24 +------- 13 files changed, 133 insertions(+), 272 deletions(-) create mode 100644 src/lib/request.ts delete mode 100644 src/utils/client.ts diff --git a/package-lock.json b/package-lock.json index 93c1cab..303fcae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "@prisma/client": "^5.5.2", "@svgr/webpack": "^8.1.0", "accept-language": "^3.0.18", - "axios": "^1.6.1", "framer-motion": "^10.16.4", "i18next": "^23.7.1", "next": "^14.0.2", @@ -3125,11 +3124,6 @@ "has-symbols": "^1.0.3" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3151,16 +3145,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", - "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -3525,17 +3509,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3788,14 +3761,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4755,25 +4720,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4783,19 +4729,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/framer-motion": { "version": "10.16.4", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", @@ -5856,25 +5789,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -6560,11 +6474,6 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/package.json b/package.json index 3588a5a..20d94e9 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "@prisma/client": "^5.5.2", "@svgr/webpack": "^8.1.0", "accept-language": "^3.0.18", - "axios": "^1.6.1", "framer-motion": "^10.16.4", "i18next": "^23.7.1", "next": "^14.0.2", diff --git a/src/components/Links/LinkItem.tsx b/src/components/Links/LinkItem.tsx index 761cc54..cfb6dd0 100644 --- a/src/components/Links/LinkItem.tsx +++ b/src/components/Links/LinkItem.tsx @@ -1,22 +1,19 @@ -import axios from "axios"; import { motion } from "framer-motion"; import LinkTag from "next/link"; import { AiFillStar } from "react-icons/ai"; - import PATHS from "constants/paths"; import { Link } from "types"; - import EditItem from "components/QuickActions/EditItem"; import FavoriteItem from "components/QuickActions/FavoriteItem"; import RemoveItem from "components/QuickActions/RemoveItem"; import LinkFavicon from "./LinkFavicon"; - import styles from "./links.module.scss"; +import { makeRequest } from "lib/request"; export default function LinkItem({ link, toggleFavorite, - index, + index }: { link: Link; toggleFavorite: (linkId: Link["id"]) => void; @@ -24,14 +21,16 @@ export default function LinkItem({ }) { const { id, name, url, favorite } = link; const onFavorite = () => { - const payload = { - name, - url, - favorite: !favorite, - categoryId: link.category.id, - }; - axios - .put(`${PATHS.API.LINK}/${link.id}`, payload) + makeRequest({ + url: `${PATHS.API.LINK}/${link.id}`, + method: "PUT", + body: { + name, + url, + favorite: !favorite, + categoryId: link.category.id + } + }) .then(() => toggleFavorite(link.id)) .catch(console.error); }; @@ -46,11 +45,11 @@ export default function LinkItem({ type: "spring", stiffness: 260, damping: 20, - delay: index * 0.05, + delay: index * 0.05 }} > - + {name} {favorite && } diff --git a/src/lib/api/handler.ts b/src/lib/api/handler.ts index 09eb9e7..d551a39 100644 --- a/src/lib/api/handler.ts +++ b/src/lib/api/handler.ts @@ -9,7 +9,7 @@ type ApiHandlerMethod = ({ req, res, session, - user, + user }: { req: NextApiRequest; res: NextApiResponse; @@ -66,14 +66,13 @@ function errorHandler(error: any, response: NextApiResponse) { ? handlePrismaError(error) // Handle Prisma specific errors : error.message; - console.error(error); - return response.status(500).json({ error: errorMessage }); + return response.status(400).json({ error: errorMessage }); } function handlePrismaError({ meta, code, - message, + message }: PrismaClientKnownRequestError) { switch (code) { case "P2002": diff --git a/src/lib/request.ts b/src/lib/request.ts new file mode 100644 index 0000000..11a86fc --- /dev/null +++ b/src/lib/request.ts @@ -0,0 +1,19 @@ +import nProgress from "nprogress"; +import { i18n } from "next-i18next"; + +export async function makeRequest({ + method = "GET", + url, + body +}: { method?: RequestInit["method"], url: string, body?: object | any[] }): Promise { + nProgress.start(); + const request = await fetch(url, { + method, body: body ? JSON.stringify(body) : undefined, headers: { + "Content-Type": "application/json" + } + }); + nProgress.done(); + + const data = await request.json(); + return request.ok ? data : Promise.reject(data?.error || i18n.t("common:generic-error")); +} \ No newline at end of file diff --git a/src/pages/category/create.tsx b/src/pages/category/create.tsx index 4e2ab29..dc15231 100644 --- a/src/pages/category/create.tsx +++ b/src/pages/category/create.tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import FormLayout from "components/FormLayout"; import PageTransition from "components/PageTransition"; import TextBox from "components/TextBox"; @@ -8,15 +7,13 @@ import { getServerSideTranslation } from "i18n"; import getUserCategoriesCount from "lib/category/getUserCategoriesCount"; import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; -import nProgress from "nprogress"; -import { useMemo, useState } from "react"; +import { FormEvent, useMemo, useState } from "react"; import styles from "styles/form.module.scss"; -import { redirectWithoutClientCache } from "utils/client"; -import { HandleAxiosError } from "utils/front"; import { withAuthentication } from "utils/session"; +import { makeRequest } from "lib/request"; export default function PageCreateCategory({ - categoriesCount, + categoriesCount }: { categoriesCount: number; }) { @@ -35,23 +32,19 @@ export default function PageCreateCategory({ [name.length, submitted] ); - const handleSubmit = async (event) => { + const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); setSubmitted(true); - nProgress.start(); - try { - const { data } = await axios.post(PATHS.API.CATEGORY, { name }); - redirectWithoutClientCache(router, ""); - router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - setSubmitted(false); - } finally { - nProgress.done(); - } + makeRequest({ + url: PATHS.API.CATEGORY, + method: "POST", + body: { name } + }) + .then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`)) + .catch(setError) + .finally(() => setSubmitted(false)); }; return ( @@ -86,8 +79,8 @@ export const getServerSideProps = withAuthentication( props: { session, categoriesCount, - ...(await getServerSideTranslation(locale)), - }, + ...(await getServerSideTranslation(locale)) + } }; } ); diff --git a/src/pages/category/edit/[cid].tsx b/src/pages/category/edit/[cid].tsx index 6e8b19e..4146268 100644 --- a/src/pages/category/edit/[cid].tsx +++ b/src/pages/category/edit/[cid].tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import FormLayout from "components/FormLayout"; import PageTransition from "components/PageTransition"; import TextBox from "components/TextBox"; @@ -8,12 +7,11 @@ import { getServerSideTranslation } from "i18n"; import getUserCategory from "lib/category/getUserCategory"; import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; -import nProgress from "nprogress"; -import { useMemo, useState } from "react"; +import { FormEvent, useMemo, useState } from "react"; import styles from "styles/form.module.scss"; import { Category } from "types"; -import { HandleAxiosError } from "utils/front"; import { withAuthentication } from "utils/session"; +import { makeRequest } from "lib/request"; export default function PageEditCategory({ category }: { category: Category }) { const { t } = useTranslation(); @@ -30,24 +28,19 @@ export default function PageEditCategory({ category }: { category: Category }) { [category.name, name, submitted] ); - const handleSubmit = async (event) => { + const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); setSubmitted(true); - nProgress.start(); - try { - const { data } = await axios.put(`${PATHS.API.CATEGORY}/${category.id}`, { - name, - }); - router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - setSubmitted(false); - } finally { - nProgress.done(); - } + makeRequest({ + url: `${PATHS.API.CATEGORY}/${category.id}`, + method: "PUT", + body: { name } + }) + .then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`)) + .catch(setError) + .finally(() => setSubmitted(false)); }; return ( @@ -80,8 +73,8 @@ export const getServerSideProps = withAuthentication( if (!category) { return { redirect: { - destination: PATHS.HOME, - }, + destination: PATHS.HOME + } }; } @@ -89,8 +82,8 @@ export const getServerSideProps = withAuthentication( props: { session, category: JSON.parse(JSON.stringify(category)), - ...(await getServerSideTranslation(locale)), - }, + ...(await getServerSideTranslation(locale)) + } }; } ); diff --git a/src/pages/category/remove/[cid].tsx b/src/pages/category/remove/[cid].tsx index 6e07bb6..1298d50 100644 --- a/src/pages/category/remove/[cid].tsx +++ b/src/pages/category/remove/[cid].tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import Checkbox from "components/Checkbox"; import FormLayout from "components/FormLayout"; import PageTransition from "components/PageTransition"; @@ -8,15 +7,14 @@ import { getServerSideTranslation } from "i18n"; import getUserCategory from "lib/category/getUserCategory"; import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; -import nProgress from "nprogress"; -import { useEffect, useMemo, useState } from "react"; +import { FormEvent, useEffect, useMemo, useState } from "react"; import styles from "styles/form.module.scss"; import { Category } from "types"; -import { HandleAxiosError } from "utils/front"; import { withAuthentication } from "utils/session"; +import { makeRequest } from "lib/request"; export default function PageRemoveCategory({ - category, + category }: { category: Category; }) { @@ -32,22 +30,18 @@ export default function PageRemoveCategory({ [category.links.length, confirmDelete, submitted] ); - const handleSubmit = async (event) => { + const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); setSubmitted(true); - nProgress.start(); - try { - await axios.delete(`${PATHS.API.CATEGORY}/${category.id}`); - router.push(PATHS.HOME); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - setSubmitted(false); - } finally { - nProgress.done(); - } + makeRequest({ + url: `${PATHS.API.CATEGORY}/${category.id}`, + method: "DELETE" + }) + .then((data) => router.push(PATHS.HOME)) + .catch(setError) + .finally(() => setSubmitted(false)); }; useEffect(() => { @@ -94,8 +88,8 @@ export const getServerSideProps = withAuthentication( if (!category) { return { redirect: { - destination: PATHS.HOME, - }, + destination: PATHS.HOME + } }; } @@ -103,8 +97,8 @@ export const getServerSideProps = withAuthentication( props: { session, category: JSON.parse(JSON.stringify(category)), - ...(await getServerSideTranslation(locale)), - }, + ...(await getServerSideTranslation(locale)) + } }; } ); diff --git a/src/pages/link/create.tsx b/src/pages/link/create.tsx index d9764ba..dc991c3 100644 --- a/src/pages/link/create.tsx +++ b/src/pages/link/create.tsx @@ -1,4 +1,3 @@ -import axios from "axios"; import Checkbox from "components/Checkbox"; import FormLayout from "components/FormLayout"; import PageTransition from "components/PageTransition"; @@ -10,15 +9,15 @@ import { getServerSideTranslation } from "i18n"; import getUserCategories from "lib/category/getUserCategories"; import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; -import nProgress from "nprogress"; -import { useMemo, useState } from "react"; +import { FormEvent, useMemo, useState } from "react"; import styles from "styles/form.module.scss"; import { Category, Link } from "types"; -import { HandleAxiosError, IsValidURL } from "utils/front"; +import { IsValidURL } from "utils/front"; import { withAuthentication } from "utils/session"; +import { makeRequest } from "lib/request"; export default function PageCreateLink({ - categories, + categories }: { categories: Category[]; }) { @@ -47,23 +46,19 @@ export default function PageCreateLink({ [name, url, favorite, categoryId, submitted] ); - const handleSubmit = async (event) => { + const handleSubmit = (event: FormEvent) => { event.preventDefault(); setError(null); setSubmitted(true); - nProgress.start(); - try { - const payload = { name, url, favorite, categoryId }; - const { data } = await axios.post(PATHS.API.LINK, payload); - router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - } finally { - setSubmitted(true); - nProgress.done(); - } + makeRequest({ + url: PATHS.API.LINK, + method: "POST", + body: { name, url, favorite, categoryId } + }) + .then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`)) + .catch(setError) + .finally(() => setSubmitted(false)); }; return ( @@ -99,7 +94,7 @@ export default function PageCreateLink({ onChangeCallback={(value: number) => setCategoryId(value)} options={categories.map(({ id, name }) => ({ label: name, - value: id, + value: id }))} /> { + const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); setSubmitted(true); - nProgress.start(); - try { - const payload = { name, url, favorite, categoryId }; - const { data } = await axios.put(`${PATHS.API.LINK}/${link.id}`, payload); - router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - setSubmitted(false); - } finally { - nProgress.done(); - } + makeRequest({ + url: `${PATHS.API.LINK}/${link.id}`, + method: "PUT", + body: { name, url, favorite, categoryId } + }) + .then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`)) + .catch(setError) + .finally(() => setSubmitted(false)); }; return ( @@ -114,7 +109,7 @@ export default function PageEditLink({ onChangeCallback={(value: number) => setCategoryId(value)} options={categories.map(({ id, name }) => ({ label: name, - value: id, + value: id }))} /> { + const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); - nProgress.start(); + setSubmitted(true); - try { - const { data } = await axios.delete(`${PATHS.API.LINK}/${link.id}`); - router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`); - setSubmitted(true); - } catch (error) { - setError(HandleAxiosError(error)); - } finally { - nProgress.done(); - } + makeRequest({ + url: `${PATHS.API.LINK}/${link.id}`, + method: "DELETE" + }) + .then((data) => router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`)) + .catch(setError) + .finally(() => setSubmitted(false)); }; return ( @@ -101,8 +97,8 @@ export const getServerSideProps = withAuthentication( if (!link) { return { redirect: { - destination: PATHS.HOME, - }, + destination: PATHS.HOME + } }; } @@ -110,8 +106,8 @@ export const getServerSideProps = withAuthentication( props: { session, link: JSON.parse(JSON.stringify(link)), - ...(await getServerSideTranslation(locale)), - }, + ...(await getServerSideTranslation(locale)) + } }; } ); diff --git a/src/utils/client.ts b/src/utils/client.ts deleted file mode 100644 index e338c84..0000000 --- a/src/utils/client.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NextRouter } from "next/router"; - -export function redirectWithoutClientCache(router: NextRouter, url: string) { - router.push(url, undefined, { - unstable_skipClientCache: true, - }); - // FIXME: invalidate catch instead of weird hack -} diff --git a/src/utils/front.ts b/src/utils/front.ts index 56f430f..9daeb7b 100644 --- a/src/utils/front.ts +++ b/src/utils/front.ts @@ -1,28 +1,6 @@ -import axios from "axios"; import { VALID_URL_REGEX } from "constants/url"; export function IsValidURL(url: string): boolean { const regex = new RegExp(VALID_URL_REGEX); - return url.match(regex) ? true : false; -} - -export function HandleAxiosError(error): string { - let errorText: string; - - if (axios.isAxiosError(error)) { - if (error.response) { - const responseError = - error.response.data?.["error"] || error.response.data; - errorText = responseError || "An error has occured"; - } else if (error.request) { - errorText = "No data returned from the server"; - } else { - errorText = "Something went wrong"; - } - } else { - errorText = "An error has occured"; - } - - console.error(error); - return errorText; + return !!url.match(regex); }