Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kenny/8 Service util setup #16

Merged
merged 7 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions src/services/authService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import fetchService from "./fetchService";

export interface LoginRequestBody {
email: string;
password: string;
}

export interface RegistrationRequestBody {
name: string;
email: string;
password: string;
confirmPassword: string;
}

export interface ForgotPasswordRequestBody {
email: string;
}

export interface VerificationRequestBody {
userId: string;
code: string;
}

export interface ChangePasswordRequestBody {
password: string;
confirmPassword: string;
}

export interface AuthResponseBody {
token: string;
}

export const authService = {
login: async (email: string, password: string): Promise<AuthResponseBody> => {
const loginReqquestBody: LoginRequestBody = { email, password };
return fetchService<AuthResponseBody>(`/auth/login`, {
method: "POST",
body: JSON.stringify(loginReqquestBody),
});
},

register: async (
name: string,
email: string,
password: string,
confirmPassword: string,
): Promise<AuthResponseBody> => {
const registrationRequestBody: RegistrationRequestBody = {
name,
email,
password,
confirmPassword,
};
return fetchService<AuthResponseBody>(`/auth/register`, {
method: "POST",
body: JSON.stringify(registrationRequestBody),
});
},

forgotPassword: async (email: string): Promise<void> => {
const forgotPasswordRequestBody: ForgotPasswordRequestBody = { email };
return fetchService<void>(`/auth/forget-password`, {
method: "POST",
body: JSON.stringify(forgotPasswordRequestBody),
});
},

verifyForgotPassword: async (userId: string, code: string): Promise<void> => {
const verificationRequestBody: VerificationRequestBody = { userId, code };
return fetchService<void>(`/auth/verify`, {
method: "POST",
body: JSON.stringify(verificationRequestBody),
});
},

changePassword: async (
password: string,
confirmPassword: string,
): Promise<void> => {
const changePasswordRequestBody: ChangePasswordRequestBody = {
password,
confirmPassword,
};
return fetchService<void>(`/auth/change-password`, {
method: "POST",
body: JSON.stringify(changePasswordRequestBody),
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
},
};
75 changes: 75 additions & 0 deletions src/services/fetchService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

const BASE_URL = "/api/v1";

/**
* Verifies the fetchService parameters based on the method type, the standard is based on MDN standards
* @param method HTTP method of request
* @param request Request information
*/
function verifyFetchRequest(method: HttpMethod, request: RequestInit): void {
switch (method) {
// GET, DELETE doesn't have request body
case "GET":
case "DELETE":
if (request.body) {
throw new Error(`${method} can't have a body.`);
}
break;
// POST, PUT, PATCH requires a request body
case "POST":
case "PUT":
case "PATCH":
if (!request.body) {
throw new Error(`${method} expects a request body.`);
}
// Check if body is a string
if (typeof request.body !== "string") {
throw new Error(`Request body for ${method} must be a JSON string`);
}
// Check if body is a JSON string
try {
JSON.parse(request.body);
} catch {
throw new Error(
`Request body for ${method} must be a valid JSON string`,
);
}
break;
// Invalid HTTP method
default:
throw new Error(`Invalid HTTP method: ${method}.`);
}
}

/**
* Service layer function that calls fetch on the backend API
* @param endpoint Url endpoint for the fetch
* @param request RequestInit object, body is expected to be stringified beforehand
* @returns <T> Promise type
*/
export default async function fetchService<T>(
endpoint: string,
request: RequestInit = {},
): Promise<T> {
const httpMethod = (request.method as HttpMethod) || "NO METHOD PROVIDED";
verifyFetchRequest(httpMethod, request);

const response = await fetch(`${BASE_URL}${endpoint}`, {
...request,
headers: {
"Content-Type": "application/json",
...request.headers,
},
});

if (!response.ok) {
throw new Error(`${response.status}`);
}

if (response.status == 204) {
return null as T;
}

return response.json();
}
41 changes: 41 additions & 0 deletions src/services/petService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import fetchService from "@/services/fetchService";
import { Pet } from "@/types/pet";

interface CreatePetBodyRequestBody {
name: string;
userId: string;
}

interface UpdatePetRequestBody {
name: string;
}

export const petService = {
createPet: async (name: string, userId: string): Promise<Pet> => {
const CreatePetBodyRequestBody: CreatePetBodyRequestBody = { name, userId };
return fetchService<Pet>("/pets", {
method: "POST",
body: JSON.stringify(CreatePetBodyRequestBody),
});
},

getPet: async (userId: string): Promise<Pet> => {
return fetchService<Pet>(`/pets/${userId}`, {
method: "GET",
});
},

updatePet: async (name: string, userId: string): Promise<void> => {
const updatePetRequestBody: UpdatePetRequestBody = { name };
return fetchService<void>(`/pets/${userId}`, {
method: "PATCH",
body: JSON.stringify(updatePetRequestBody),
});
},

deletePet: async (userId: string): Promise<void> => {
return fetchService<void>(`/pets/${userId}`, {
method: "DELETE",
});
},
};
8 changes: 8 additions & 0 deletions src/types/pet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface Pet {
KennyKeni marked this conversation as resolved.
Show resolved Hide resolved
_id?: string;
name: string;
xpGained: number;
xpLevel: number;
coins: number;
userId: string;
}