diff --git a/src/api/apiUtils.js b/src/api/apiUtils.js new file mode 100644 index 0000000..a97e14f --- /dev/null +++ b/src/api/apiUtils.js @@ -0,0 +1,17 @@ +export async function handleResponse(response) { + if (response.ok) return response.json(); + if (response.status === 400) { + // So, a server-side validation error occurred. + // Server side validation returns a string error message, so parse as text instead of json. + const error = await response.text(); + throw new Error(error); + } + throw new Error("Network response was not ok."); +} + +// In a real app, would likely call an error logging service. +export function handleError(error) { + // eslint-disable-next-line no-console + console.error("API call failed. " + error); + throw error; +} diff --git a/src/api/authorApi.js b/src/api/authorApi.js new file mode 100644 index 0000000..6c8ba73 --- /dev/null +++ b/src/api/authorApi.js @@ -0,0 +1,22 @@ +import { handleResponse, handleError } from "./apiUtils"; +const baseUrl = process.env.REACT_APP_API_URL + "/authors/"; + +export function getAuthors() { + return fetch(baseUrl).then(handleResponse).catch(handleError); +} + +export function saveAuthor(author) { + return fetch(baseUrl + (author.id || ""), { + method: author.id ? "PUT" : "POST", // POST for create, PUT to update when id already exists. + headers: { "content-type": "application/json" }, + body: JSON.stringify(author), + }) + .then(handleResponse) + .catch(handleError); +} + +export function deleteAuthor(authorId) { + return fetch(baseUrl + authorId, { method: "DELETE" }) + .then(handleResponse) + .catch(handleError); +} diff --git a/src/api/courseApi.js b/src/api/courseApi.js new file mode 100644 index 0000000..eabadcc --- /dev/null +++ b/src/api/courseApi.js @@ -0,0 +1,39 @@ +import { handleResponse, handleError } from "./apiUtils"; + +const baseUrl = process.env.REACT_APP_API_URL + "/courses/"; + +export function getCourses() { + return fetch(baseUrl).then(handleResponse).catch(handleError); +} + +export function getCourseBySlug(slug) { + return fetch(baseUrl + "?slug=" + slug) + .then((response) => { + if (!response.ok) throw new Error("Network response was not ok."); + return response.json().then((courses) => { + if (courses.length !== 1) throw new Error("Course not found: " + slug); + return courses[0]; // should only find one course for a given slug, so return it. + }); + }) + .catch(handleError); +} + +export function saveCourse(course) { + return fetch(baseUrl + (course.id || ""), { + method: course.id ? "PUT" : "POST", // POST for create, PUT to update when id already exists. + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + ...course, + // Parse authorId to a number (in case it was sent as a string). + authorId: parseInt(course.authorId, 10), + }), + }) + .then(handleResponse) + .catch(handleError); +} + +export function deleteCourse(courseId) { + return fetch(baseUrl + courseId, { method: "DELETE" }) + .then(handleResponse) + .catch(handleError); +} diff --git a/src/api/userApi.js b/src/api/userApi.js new file mode 100644 index 0000000..cd25ac6 --- /dev/null +++ b/src/api/userApi.js @@ -0,0 +1,39 @@ +import { handleResponse, handleError } from "./apiUtils"; + +const baseUrl = process.env.REACT_APP_API_URL + "/users/"; + +export function getUsers() { + return fetch(baseUrl).then(handleResponse).catch(handleError); +} + +export function getUserBySlug(slug) { + return fetch(baseUrl + "?slug=" + slug) + .then((response) => { + if (!response.ok) throw new Error("Network response was not ok."); + return response.json().then((users) => { + if (users.length !== 1) throw new Error("User not found: " + slug); + return users[0]; // should only find one user for a given slug, so return it. + }); + }) + .catch(handleError); +} + +export function saveUser(user) { + return fetch(baseUrl + (user.id || ""), { + method: user.id ? "PUT" : "POST", // POST for create, PUT to update when id already exists. + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + ...user, + // Parse authorId to a number (in case it was sent as a string). + id: parseInt(user.id, 10), + }), + }) + .then(handleResponse) + .catch(handleError); +} + +export function deleteUser(userId) { + return fetch(baseUrl + userId, { method: "DELETE" }) + .then(handleResponse) + .catch(handleError); +}