api: add full blown mood endpoint
Holy shit, this was a wild ride. I've refactored to address a couple of bunch of lying issues: - No auth middleware being used - Weird ass models that weren't models at all - Basically ignoring types everywhere - Weird shape of parameters being tossed around - Controllers doing validations + models doing validations These need to be ported now to the auth and user endpoints as well. Then it'll probably be easier to just keep it tidy. Now, Prisma can keep working as our repository layer, our services actually behave as services instead of "models", validation happens with Zod at the controller level, and schemas are there to keep track of what needs to be validated. A problem is that now it lacks good i18n translation, so we gotta patch that sometime
Parents:
bc4e3ca6 file(s) changed
- api/src/controllers/moods.ts +42 -0
- api/src/createApp.ts +2 -0
- api/src/routes/moods.ts +10 -0
- api/src/schemas/index.ts +8 -0
- api/src/schemas/mood.schema.ts +23 -0
- api/src/services/mood.service.ts +27 -0
api/src/controllers/moods.ts
@@ -0,0 +1,42 @@
1 + import { Request, Response, NextFunction } from 'express';
2 + import { AuthenticatedRequest } from '@app/middleware/auth';
3 + import {
4 + createMood,
5 + getMoodsByUserId
6 + } from '@app/services/mood.service';
7 +
8 + import {
9 + CreateMoodSchema
10 + } from '@app/schemas'
11 +
12 + export const MoodsController = {
13 + create: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
14 + try {
15 + const parsed = CreateMoodSchema.safeParse(req.body.mood);
16 +
17 + if (!parsed.success) {
18 + return res.status(400).json({
19 + errors: parsed.error!.issues
20 + });
21 + }
22 +
23 + const mood = await createMood(req.userId!, parsed.data);
24 +
25 + return res.status(201).json({ mood });
26 + } catch (err) {
27 + next(err);
28 + }
29 + },
30 +
31 + index: async (req: Request, res: Response, next: NextFunction) => {
32 + try {
33 + const moods = await getMoodsByUserId(Number(req.params.id));
34 +
35 + return res.status(200).json({
36 + moods
37 + })
38 + } catch (err) {
39 + next(err);
40 + }
41 + },
42 + };
api/src/createApp.ts
@@ -5,6 +5,7 @@ import morgan from 'morgan';
5 5 import mainRouter from '@app/routes/main';
6 6 import userRouter from '@app/routes/users';
7 7 import authRouter from '@app/routes/auth';
8 + import moodRouter from '@app/routes/moods';
8 9 import { errorHandler } from '@app/middleware/errorHandler';
9 10 import { PrismaClient } from '@prisma/client';
10 11 import { bootWorkers, closeAllWorkers, closeAllQueues } from '@app/lib/queue';
@@ -29,6 +30,7 @@
29 30 app.get('/', mainRouter);
30 31 app.use("/users", userRouter);
31 32 app.use("/auth", authRouter);
33 + app.use("/moods", moodRouter);
32 34
33 35 app.use(errorHandler); // always last
34 36
api/src/routes/moods.ts
@@ -0,0 +1,10 @@
1 + import { Router } from "express";
2 + import { MoodsController } from '@app/controllers/moods'
3 + import { requireAuth } from '@app/middleware/auth';
4 +
5 + const router = Router();
6 +
7 + router.get('/', MoodsController.index);
8 + router.post('/', requireAuth, MoodsController.create);
9 +
10 + export default router;
api/src/schemas/index.ts
@@ -0,0 +1,8 @@
1 + export {
2 + CreateMoodSchema,
3 + MoodComponentSchema
4 + } from '@app/schemas/mood.schema';
5 +
6 + export type {
7 + CreateMoodInput
8 + } from '@app/schemas/mood.schema';
api/src/schemas/mood.schema.ts
@@ -0,0 +1,23 @@
1 + import { z } from 'zod';
2 + import {
3 + BaseMoodOption,
4 + MoodComponentOption,
5 + IntensityLevel
6 + } from '@prisma/client';
7 +
8 + export const MoodComponentSchema = z.object({
9 + component: z.nativeEnum(MoodComponentOption),
10 + intensity: z.nativeEnum(IntensityLevel)
11 + });
12 +
13 + export const CreateMoodSchema = z.object({
14 + annotation: z.string().optional(),
15 + moment: z.coerce.date(),
16 + selectedMood: z.nativeEnum(BaseMoodOption),
17 + anxietyLevel: z.number().int().min(0).max(10),
18 + stressLevel: z.number().int().min(0).max(10),
19 + energyLevel: z.number().int().min(0).max(10),
20 + moodComponents: z.array(MoodComponentSchema).min(1)
21 + });
22 +
23 + export type CreateMoodInput = z.infer<typeof CreateMoodSchema>;
api/src/services/mood.service.ts
@@ -0,0 +1,27 @@
1 + import { prisma } from '@app/lib/prisma';
2 + import { Mood } from '@prisma/client';
3 +
4 + import { CreateMoodInput } from '@app/schemas';
5 +
6 + export const createMood = async (userId: number, input: CreateMoodInput): Promise<Mood> => {
7 + const { moodComponents, ...moodData } = input;
8 +
9 + return prisma.mood.create({
10 + data: {
11 + ...moodData,
12 + userId,
13 + moodComponents: {
14 + create: moodComponents
15 + }
16 + },
17 + include: { moodComponents: true }
18 + });
19 + };
20 +
21 + export const getMoodsByUserId = async (userId: number): Promise<Mood[]> => {
22 + return prisma.mood.findMany({
23 + where: { userId },
24 + include: { moodComponents: true },
25 + orderBy: { moment: 'desc' }
26 + });
27 + };