diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0035f4e --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +NUXT_API_URL=https://www.themealdb.com/api/json/v1/1/ diff --git a/bun.lockb b/bun.lockb index 819d8b9..d7cc001 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7c2c0aa..b1b2966 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "nuxt": "^3.11.2", "nuxt-icon": "^0.6.10", "vue": "^3.4.21", - "vue-router": "^4.3.0" + "vue-router": "^4.3.0", + "zod": "^3.23.8" }, "devDependencies": { "@tailwindcss/typography": "^0.5.13", diff --git a/types/recipe.ts b/types/recipe.ts index a0c6498..5683d31 100644 --- a/types/recipe.ts +++ b/types/recipe.ts @@ -1,3 +1,5 @@ +import { z } from "zod"; + export type Recipe = { title: string; pictureUrl: string; @@ -7,3 +9,66 @@ export type Recipe = { ingredients: { name: string; quantity: string }[]; instructions: string; }; + +const mealSchema = z.object({ + idMeal: z.string(), + strMeal: z.string(), + strDrinkAlternate: z.string().nullable(), + strCategory: z.string(), + strArea: z.string(), + strInstructions: z.string(), + strMealThumb: z.string().url(), + strTags: z.string().nullable(), + strYoutube: z.string(), + strIngredient1: z.string().optional(), + strIngredient2: z.string().optional(), + strIngredient3: z.string().optional(), + strIngredient4: z.string().optional(), + strIngredient5: z.string().optional(), + strIngredient6: z.string().optional(), + strIngredient7: z.string().optional(), + strIngredient8: z.string().optional(), + strIngredient9: z.string().optional(), + strIngredient10: z.string().optional(), + strIngredient11: z.string().optional(), + strIngredient12: z.string().optional(), + strIngredient13: z.string().optional(), + strIngredient14: z.string().optional(), + strIngredient15: z.string().optional(), + strIngredient16: z.string().optional(), + strIngredient17: z.string().optional(), + strIngredient18: z.string().optional(), + strIngredient19: z.string().optional(), + strIngredient20: z.string().optional(), + strMeasure1: z.string().optional(), + strMeasure2: z.string().optional(), + strMeasure3: z.string().optional(), + strMeasure4: z.string().optional(), + strMeasure5: z.string().optional(), + strMeasure6: z.string().optional(), + strMeasure7: z.string().optional(), + strMeasure8: z.string().optional(), + strMeasure9: z.string().optional(), + strMeasure10: z.string().optional(), + strMeasure11: z.string().optional(), + strMeasure12: z.string().optional(), + strMeasure13: z.string().optional(), + strMeasure14: z.string().optional(), + strMeasure15: z.string().optional(), + strMeasure16: z.string().optional(), + strMeasure17: z.string().optional(), + strMeasure18: z.string().optional(), + strMeasure19: z.string().optional(), + strMeasure20: z.string().optional(), + strSource: z.string().optional(), + strImageSource: z.string().nullable(), + strCreativeCommonsConfirmed: z.string().nullable(), + dateModified: z.string().optional().nullable(), +}); + +export const apiResponseSchema = z.object({ + meals: z.array(mealSchema), +}); + +export type Meal = z.infer; +export type ApiResponse = z.infer; diff --git a/utils/recipes.ts b/utils/recipes.ts index 38392b9..76ff094 100644 --- a/utils/recipes.ts +++ b/utils/recipes.ts @@ -1,18 +1,14 @@ -import type { Recipe } from "~/types/recipe"; +import type { ApiResponse, Meal, Recipe } from "~/types/recipe"; +import { apiResponseSchema } from "~/types/recipe"; -export function parseRecipeData(data: { meals: unknown }): Recipe[] { - return data.meals.map((meal: unknown) => { - // Extract ingredients and measurements +export function parseRecipeData(data: ApiResponse): Recipe[] { + return apiResponseSchema.parse(data).meals.map((meal: Meal) => { const ingredients: { name: string; quantity: string }[] = []; for (let i = 1; i <= 20; i++) { const ingredientName = meal[`strIngredient${i}`]; const ingredientQuantity = meal[`strMeasure${i}`]; - if ( - ingredientName && - ingredientName.trim() && - ingredientQuantity && - ingredientQuantity.trim() - ) { + + if (ingredientName?.trim() && ingredientQuantity?.trim()) { ingredients.push({ name: ingredientName.trim(), quantity: ingredientQuantity.trim(), @@ -26,7 +22,7 @@ export function parseRecipeData(data: { meals: unknown }): Recipe[] { videoUrl: meal.strYoutube, category: meal.strCategory, origin: meal.strArea, - ingredients: ingredients, + ingredients, instructions: meal.strInstructions, }; });