import { fetchKijijiItems } from "@marketplace-scrapers/core"; import { logger } from "../logger"; import { emptySearchResponse, getRequiredSearchQuery, parseDollarPriceParam, parseNonNegativeIntegerParam, } from "./helpers"; /** * GET /api/kijiji?q={query} * Search Kijiji marketplace for listings */ export async function kijijiRoute(req: Request): Promise { const reqUrl = new URL(req.url); const SEARCH_QUERY = getRequiredSearchQuery(req); if (SEARCH_QUERY instanceof Response) { return SEARCH_QUERY; } const maxPages = parseNonNegativeIntegerParam( reqUrl.searchParams, "maxPages", 5, ); if (maxPages instanceof Response) { return maxPages; } const priceMin = parseDollarPriceParam(reqUrl.searchParams, "priceMin"); if (priceMin instanceof Response) { return priceMin; } const priceMax = parseDollarPriceParam(reqUrl.searchParams, "priceMax"); if (priceMax instanceof Response) { return priceMax; } const hideUnstableResults = reqUrl.searchParams.get("unstableFilter") === "true"; const searchOptions = { location: reqUrl.searchParams.get("location") || undefined, category: reqUrl.searchParams.get("category") || undefined, keywords: reqUrl.searchParams.get("keywords") || undefined, sortBy: (reqUrl.searchParams.get("sortBy") as | "relevancy" | "date" | "price" | "distance" | undefined) || undefined, sortOrder: (reqUrl.searchParams.get("sortOrder") as "desc" | "asc" | undefined) || undefined, maxPages, priceMin, priceMax, }; try { if (hideUnstableResults) { const items = await fetchKijijiItems( SEARCH_QUERY, 4, // 4 requests per second for faster scraping "https://www.kijiji.ca", searchOptions, {}, { hideUnstableResults: true }, ); if (items.results.length === 0 && items.unstableResults.length === 0) { return emptySearchResponse( `Kijiji matches ALL words in the query against listing titles. ` + `Try a shorter or more common query (e.g. "macbook air m1" instead of "macbook air m1 apple silicon").`, ); } return Response.json(items, { status: 200 }); } const items = await fetchKijijiItems( SEARCH_QUERY, 4, // 4 requests per second for faster scraping "https://www.kijiji.ca", searchOptions, {}, ); if (!items || items.length === 0) { return emptySearchResponse( `Kijiji matches ALL words in the query against listing titles. ` + `Try a shorter or more common query (e.g. "macbook air m1" instead of "macbook air m1 apple silicon").`, ); } return Response.json(items, { status: 200 }); } catch (error) { logger.error("Kijiji scraping error:", error); const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return Response.json({ message: errorMessage }, { status: 400 }); } }