mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Refactor: move service logic out of api routes and into separate files
This commit is contained in:
@@ -49,6 +49,7 @@ const useBeerPosts = ({ pageSize }: { pageSize: number }) => {
|
|||||||
const { data, error, isLoading, setSize, size } = useSWRInfinite(
|
const { data, error, isLoading, setSize, size } = useSWRInfinite(
|
||||||
(index) => `/api/beers?page_num=${index + 1}&page_size=${pageSize}`,
|
(index) => `/api/beers?page_num=${index + 1}&page_size=${pageSize}`,
|
||||||
fetcher,
|
fetcher,
|
||||||
|
{ parallel: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const beerPosts = data?.flatMap((d) => d.beerPosts) ?? [];
|
const beerPosts = data?.flatMap((d) => d.beerPosts) ?? [];
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const useBreweryPosts = ({ pageSize }: { pageSize: number }) => {
|
|||||||
const { data, error, isLoading, setSize, size } = useSWRInfinite(
|
const { data, error, isLoading, setSize, size } = useSWRInfinite(
|
||||||
(index) => `/api/breweries?page_num=${index + 1}&page_size=${pageSize}`,
|
(index) => `/api/breweries?page_num=${index + 1}&page_size=${pageSize}`,
|
||||||
fetcher,
|
fetcher,
|
||||||
|
{ parallel: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const breweryPosts = data?.flatMap((d) => d.breweryPosts) ?? [];
|
const breweryPosts = data?.flatMap((d) => d.breweryPosts) ?? [];
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
|||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import ServerError from '@/config/util/ServerError';
|
import ServerError from '@/config/util/ServerError';
|
||||||
import DBClient from '@/prisma/DBClient';
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
import findBeerCommentById from '@/services/BeerComment/findBeerCommentById';
|
||||||
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema';
|
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema';
|
||||||
|
import editBeerCommentById from '@/services/BeerComment/editBeerCommentById';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter, NextHandler } from 'next-connect';
|
import { createRouter, NextHandler } from 'next-connect';
|
||||||
@@ -27,9 +28,7 @@ const checkIfCommentOwner = async (
|
|||||||
) => {
|
) => {
|
||||||
const { id } = req.query;
|
const { id } = req.query;
|
||||||
const user = req.user!;
|
const user = req.user!;
|
||||||
const comment = await DBClient.instance.beerComment.findUnique({
|
const comment = await findBeerCommentById(id);
|
||||||
where: { id },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!comment) {
|
if (!comment) {
|
||||||
throw new ServerError('Comment not found', 404);
|
throw new ServerError('Comment not found', 404);
|
||||||
@@ -48,13 +47,10 @@ const editComment = async (
|
|||||||
) => {
|
) => {
|
||||||
const { id } = req.query;
|
const { id } = req.query;
|
||||||
|
|
||||||
const updated = await DBClient.instance.beerComment.update({
|
const updated = await editBeerCommentById({
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
content: req.body.content,
|
content: req.body.content,
|
||||||
rating: req.body.rating,
|
rating: req.body.rating,
|
||||||
updatedAt: new Date(),
|
id,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
import { BeerImage } from '@prisma/client';
|
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
@@ -14,6 +12,8 @@ import { NextApiResponse } from 'next';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import ServerError from '@/config/util/ServerError';
|
import ServerError from '@/config/util/ServerError';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
|
import processImageDataIntoDB from '@/services/BeerImage/processImageDataIntoDB';
|
||||||
|
import ImageMetadataValidationSchema from '@/services/types/ImageSchema/ImageMetadataValidationSchema';
|
||||||
|
|
||||||
const { storage } = cloudinaryConfig;
|
const { storage } = cloudinaryConfig;
|
||||||
|
|
||||||
@@ -34,15 +34,10 @@ const uploadMiddleware = expressWrapper(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const BeerPostImageValidationSchema = z.object({
|
|
||||||
caption: z.string(),
|
|
||||||
alt: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface UploadBeerPostImagesRequest extends UserExtendedNextApiRequest {
|
interface UploadBeerPostImagesRequest extends UserExtendedNextApiRequest {
|
||||||
files?: Express.Multer.File[];
|
files?: Express.Multer.File[];
|
||||||
query: { id: string };
|
query: { id: string };
|
||||||
body: z.infer<typeof BeerPostImageValidationSchema>;
|
body: z.infer<typeof ImageMetadataValidationSchema>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const processImageData = async (
|
const processImageData = async (
|
||||||
@@ -54,24 +49,15 @@ const processImageData = async (
|
|||||||
if (!files || !files.length) {
|
if (!files || !files.length) {
|
||||||
throw new ServerError('No images uploaded', 400);
|
throw new ServerError('No images uploaded', 400);
|
||||||
}
|
}
|
||||||
const beerImagePromises: Promise<BeerImage>[] = [];
|
|
||||||
|
|
||||||
files.forEach((file) => {
|
const beerImages = await processImageDataIntoDB({
|
||||||
beerImagePromises.push(
|
|
||||||
DBClient.instance.beerImage.create({
|
|
||||||
data: {
|
|
||||||
alt: body.alt,
|
alt: body.alt,
|
||||||
postedBy: { connect: { id: user!.id } },
|
|
||||||
beerPost: { connect: { id: req.query.id } },
|
|
||||||
path: file.path,
|
|
||||||
caption: body.caption,
|
caption: body.caption,
|
||||||
},
|
beerPostId: req.query.id,
|
||||||
}),
|
userId: user!.id,
|
||||||
);
|
files,
|
||||||
});
|
});
|
||||||
|
|
||||||
const beerImages = await Promise.all(beerImagePromises);
|
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
message: `Successfully uploaded ${beerImages.length} image${
|
message: `Successfully uploaded ${beerImages.length} image${
|
||||||
@@ -90,7 +76,7 @@ router.post(
|
|||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
uploadMiddleware,
|
uploadMiddleware,
|
||||||
validateRequest({ bodySchema: BeerPostImageValidationSchema }),
|
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
|
||||||
processImageData,
|
processImageData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import removeBeerPostLikeById from '@/services/BeerPostLike/removeBeerPostLikeBy
|
|||||||
import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
|
import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
|
||||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import DBClient from '@/prisma/DBClient';
|
import getBeerPostLikeCount from '@/services/BeerPostLike/getBeerPostLikeCount';
|
||||||
|
|
||||||
const sendLikeRequest = async (
|
const sendLikeRequest = async (
|
||||||
req: UserExtendedNextApiRequest,
|
req: UserExtendedNextApiRequest,
|
||||||
@@ -25,7 +25,10 @@ const sendLikeRequest = async (
|
|||||||
throw new ServerError('Could not find a beer post with that id', 404);
|
throw new ServerError('Could not find a beer post with that id', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alreadyLiked = await findBeerPostLikeById(beer.id, user.id);
|
const alreadyLiked = await findBeerPostLikeById({
|
||||||
|
beerPostId: beer.id,
|
||||||
|
likedById: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
const jsonResponse = {
|
const jsonResponse = {
|
||||||
success: true as const,
|
success: true as const,
|
||||||
@@ -50,9 +53,7 @@ const getLikeCount = async (
|
|||||||
) => {
|
) => {
|
||||||
const id = req.query.id as string;
|
const id = req.query.id as string;
|
||||||
|
|
||||||
const likeCount = await DBClient.instance.beerPostLike.count({
|
const likeCount = await getBeerPostLikeCount(id);
|
||||||
where: { beerPostId: id },
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -2,25 +2,20 @@ import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
|||||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||||
import DBClient from '@/prisma/DBClient';
|
|
||||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import { createRouter } from 'next-connect';
|
import { createRouter } from 'next-connect';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import findBeerPostLikeById from '@/services/BeerPostLike/findBeerPostLikeById';
|
||||||
|
|
||||||
const checkIfLiked = async (
|
const checkIfLiked = async (
|
||||||
req: UserExtendedNextApiRequest,
|
req: UserExtendedNextApiRequest,
|
||||||
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
|
||||||
) => {
|
) => {
|
||||||
const user = req.user!;
|
const user = req.user!;
|
||||||
const id = req.query.id as string;
|
const beerPostId = req.query.id as string;
|
||||||
|
|
||||||
const alreadyLiked = await DBClient.instance.beerPostLike.findFirst({
|
const alreadyLiked = await findBeerPostLikeById({ beerPostId, likedById: user.id });
|
||||||
where: {
|
|
||||||
beerPostId: id,
|
|
||||||
likedById: user.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -37,11 +32,7 @@ const router = createRouter<
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
validateRequest({
|
validateRequest({ querySchema: z.object({ id: z.string().uuid() }) }),
|
||||||
querySchema: z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
checkIfLiked,
|
checkIfLiked,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
22
src/services/BeerComment/editBeerCommentById.ts
Normal file
22
src/services/BeerComment/editBeerCommentById.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
|
||||||
|
interface EditBeerCommentByIdArgs {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
rating: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const editBeerCommentById = async ({ id, content, rating }: EditBeerCommentByIdArgs) => {
|
||||||
|
const updated = await DBClient.instance.beerComment.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
content,
|
||||||
|
rating,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default editBeerCommentById;
|
||||||
11
src/services/BeerComment/findBeerCommentById.ts
Normal file
11
src/services/BeerComment/findBeerCommentById.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
|
||||||
|
const findBeerCommentById = async (id: string) => {
|
||||||
|
const comment = await DBClient.instance.beerComment.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return comment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default findBeerCommentById;
|
||||||
39
src/services/BeerImage/processImageDataIntoDB.ts
Normal file
39
src/services/BeerImage/processImageDataIntoDB.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
import { BeerImage } from '@prisma/client';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import ImageMetadataValidationSchema from '../types/ImageSchema/ImageMetadataValidationSchema';
|
||||||
|
|
||||||
|
interface ProcessImageDataArgs {
|
||||||
|
files: Express.Multer.File[];
|
||||||
|
alt: z.infer<typeof ImageMetadataValidationSchema>['alt'];
|
||||||
|
caption: z.infer<typeof ImageMetadataValidationSchema>['caption'];
|
||||||
|
beerPostId: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processImageDataIntoDB = ({
|
||||||
|
alt,
|
||||||
|
caption,
|
||||||
|
files,
|
||||||
|
beerPostId,
|
||||||
|
userId,
|
||||||
|
}: ProcessImageDataArgs) => {
|
||||||
|
const beerImagePromises: Promise<BeerImage>[] = [];
|
||||||
|
files.forEach((file) => {
|
||||||
|
beerImagePromises.push(
|
||||||
|
DBClient.instance.beerImage.create({
|
||||||
|
data: {
|
||||||
|
alt,
|
||||||
|
caption,
|
||||||
|
postedBy: { connect: { id: userId } },
|
||||||
|
beerPost: { connect: { id: beerPostId } },
|
||||||
|
path: file.path,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(beerImagePromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default processImageDataIntoDB;
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
import DBClient from '@/prisma/DBClient';
|
import DBClient from '@/prisma/DBClient';
|
||||||
|
|
||||||
const findBeerPostLikeById = async (beerPostId: string, likedById: string) =>
|
interface FindBeerPostLikeByIdArgs {
|
||||||
|
beerPostId: string;
|
||||||
|
likedById: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const findBeerPostLikeById = async ({
|
||||||
|
beerPostId,
|
||||||
|
likedById,
|
||||||
|
}: FindBeerPostLikeByIdArgs) =>
|
||||||
DBClient.instance.beerPostLike.findFirst({ where: { beerPostId, likedById } });
|
DBClient.instance.beerPostLike.findFirst({ where: { beerPostId, likedById } });
|
||||||
|
|
||||||
export default findBeerPostLikeById;
|
export default findBeerPostLikeById;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const ImageMetadataValidationSchema = z.object({
|
||||||
|
caption: z.string().min(1, { message: 'Caption is required.' }),
|
||||||
|
alt: z.string().min(1, { message: 'Alt text is required.' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ImageMetadataValidationSchema;
|
||||||
Reference in New Issue
Block a user