use trpc for recipe router

This commit is contained in:
Ruidy 2024-12-13 13:01:48 +01:00
parent 767471f2bb
commit a9f4df0071
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
11 changed files with 63 additions and 64 deletions

View file

@ -1,4 +0,0 @@
export default function useGreeting(text: string) {
const { $client } = useNuxtApp();
return $client.hello.useQuery({ text });
}

View file

@ -0,0 +1,4 @@
export default function useRecipeById(id: number) {
const { $client } = useNuxtApp();
return $client.recipeGet.useQuery(id);
}

View file

@ -26,7 +26,7 @@
{{ error?.statusCode || "Error" }} {{ error?.statusCode || "Error" }}
</h1> </h1>
<p class="text-xl mb-6"> <p class="text-xl mb-6">
{{ error?.message || "Something went wrong" }} {{ error?.statusMessage || "Something went wrong" }}
</p> </p>
<!-- Action Buttons --> <!-- Action Buttons -->

View file

@ -1,31 +1,32 @@
<script setup lang="ts"> <script setup lang="ts">
import { idSchema } from "~/types/id";
const { params } = useRoute(); const { params } = useRoute();
const routeParam = params.id; const routeParam = params.id;
const parsed = idSchema.safeParse(routeParam); const id =
typeof routeParam === "string" ? Number(routeParam) : Number(routeParam[0]);
const { data: recipe, pending, error } = await useRecipeById(id);
if (error.value) {
let statusCode = 400;
if (error.value.message === "Recipe not found") {
throw createError({
statusCode: 404,
statusMessage: "Recipe not found",
});
}
if (!parsed.success) {
throw createError({ throw createError({
statusCode: 400, statusCode,
statusMessage: "Invalid recipe id", statusMessage: "Invalid recipe id",
message: error.value.message,
}); });
} }
const {
data: recipe,
pending,
error,
} = await useFetch(`/api/recipes/${id}`, {
lazy: true,
});
</script> </script>
<template> <template>
<div v-if="pending">Loading</div> <div v-if="pending">Loading</div>
<div v-else-if="error">Failed: {{ error.statusMessage }}</div>
<section v-else> <section v-else>
<Recipe :recipe="recipe" /> <Recipe :recipe="recipe!" />
</section> </section>
</template> </template>

View file

@ -1,7 +1,3 @@
<script setup lang="ts">
const { data } = useGreeting("chef");
</script>
<template> <template>
<div class="hero min-h-full bg-base-200"> <div class="hero min-h-full bg-base-200">
<div class="hero-content flex-col lg:flex-row-reverse"> <div class="hero-content flex-col lg:flex-row-reverse">
@ -9,7 +5,6 @@ const { data } = useGreeting("chef");
<div> <div>
<h1 class="text-5xl font-bold prose">Eat Something New</h1> <h1 class="text-5xl font-bold prose">Eat Something New</h1>
<p class="py-6 prose">{{ data?.greeting }}</p>
<p class="py-6 prose">Generate a random recipe.</p> <p class="py-6 prose">Generate a random recipe.</p>
<NuxtLink to="/random" class="btn btn-primary"> <NuxtLink to="/random" class="btn btn-primary">
Random Recipe Now Random Recipe Now

View file

@ -1,30 +0,0 @@
import { parseRecipeData } from "~/utils/recipes";
import type { Meal } from "~/types/recipe";
import { idSchema } from "~/types/id";
export default defineEventHandler(async (event) => {
const { apiUrl } = useRuntimeConfig();
const routeParam = getRouterParam(event, "id");
const parsed = idSchema.safeParse(routeParam);
if (!parsed.success) {
throw createError({
statusCode: 400,
statusMessage: "Invalid recipe id",
message: parsed.error.message,
});
}
const data = await $fetch<{ meals: Meal[] }>(
new URL(`lookup.php?i=${parsed.data}`, apiUrl).toString(),
);
if (!data?.meals) {
throw createError({
statusCode: 404,
statusMessage: "Recipe not found",
});
}
const recipes = parseRecipeData(data);
return recipes[0];
});

View file

@ -8,7 +8,6 @@ export async function createContext(event: H3Event) {
const authorization = getRequestHeader(event, "authorization"); const authorization = getRequestHeader(event, "authorization");
async function getUserFromHeader() { async function getUserFromHeader() {
if (authorization) { if (authorization) {
console.log("authorization:", authorization);
return { isAdmin: true }; return { isAdmin: true };
} }
return null; return null;

View file

@ -1,8 +1,9 @@
import type { inferRouterOutputs } from "@trpc/server"; import type { inferRouterOutputs } from "@trpc/server";
import { z } from "zod"; import { z } from "zod";
import { publicProcedure, privateProcedure, router } from "../trpc"; import { privateProcedure, router, mergeRouters } from "../trpc";
import { recipeRouter } from "./recipes";
export const appRouter = router({ export const helloRouter = router({
// hello: publicProcedure // hello: publicProcedure
hello: privateProcedure hello: privateProcedure
.input( .input(
@ -17,6 +18,7 @@ export const appRouter = router({
}), }),
}); });
export const appRouter = mergeRouters(helloRouter, recipeRouter);
// export type definition of API // export type definition of API
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;
export type RouterOutput = inferRouterOutputs<AppRouter>; export type RouterOutput = inferRouterOutputs<AppRouter>;

View file

@ -0,0 +1,32 @@
import { z } from "zod";
import { publicProcedure, router } from "../trpc";
import type { Meal } from "~/types/recipe";
import { parseRecipeData } from "~/utils/recipes";
const { apiUrl } = useRuntimeConfig();
export const recipeRouter = router({
recipeGet: publicProcedure
.input(
z.coerce
.number({
required_error: "recipe id is required",
invalid_type_error: "recipe id must be a number",
})
.positive("recipe id must be positive"),
)
.query(async ({ input }) => {
const data = await $fetch<{ meals: Meal[] }>(
new URL(`lookup.php?i=${input}`, apiUrl).href,
);
if (!data?.meals) {
throw createError({
statusCode: 404,
statusMessage: "Recipe not found",
});
}
const recipes = parseRecipeData(data);
return recipes[0];
}),
});

View file

@ -11,6 +11,7 @@ const t = initTRPC.context<Context>().create();
export const publicProcedure = t.procedure; export const publicProcedure = t.procedure;
export const router = t.router; export const router = t.router;
export const middleware = t.middleware; export const middleware = t.middleware;
export const mergeRouters = t.mergeRouters;
export const authMiddleware = middleware(({ next, ctx }) => { export const authMiddleware = middleware(({ next, ctx }) => {
if (!ctx.user?.isAdmin) { if (!ctx.user?.isAdmin) {
@ -22,4 +23,5 @@ export const authMiddleware = middleware(({ next, ctx }) => {
}, },
}); });
}); });
export const privateProcedure = t.procedure.use(authMiddleware); export const privateProcedure = t.procedure.use(authMiddleware);

View file

@ -1,8 +1,6 @@
import { z } from "zod"; import { z } from "zod";
export const idSchema = z export const idSchema = z.string({
.number({ required_error: "recipe id is required",
required_error: "recipe id is required", invalid_type_error: "recipe id must be a number",
invalid_type_error: "recipe id must be a number", });
})
.positive("recipe id must be positive");