Repository: openfoodfacts/openfoodfacts-explorer
Pull request #469: feat: nodejs SDK Integration for Open Prices APIs
Author: login: heymitali
Created: 2025-04-13T14:49:41Z | Merged: 2025-04-25T14:48:47Z

## Description

_Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context._
- Removed direct API calls to Open Prices in explorer project.
- Replaced it with respective Nodejs SDK.
- Fixed issue with showing Prices for a product. Initially prices list was not showing any data.

Screenshot showing Price List for a product.
http://localhost:5173/products/3254382016921

<img width="1335" alt="Screenshot 2025-04-13 at 8 23 36 PM" src="https://github.com/user-attachments/assets/965614ac-b04a-463f-8d57-9a2acdff3bbb" />


Fixes # (issue)
#270 

---

## Checklist:

**Author Self-Review:**

- [x] I have performed a self-review of my own code.
- [x] I understand the changes I'm proposing and why they are needed.
- [x] My changes generate no new warnings or errors (linting, console).
- [x] I have made corresponding changes to the documentation (if applicable).

**LLM Usage Disclosure:**
_Please be transparent about the use of AI assistance._

- [x] If I **did** use an AI Large Language Model, I have reviewed the generated code/text to ensure its accuracy, security, and relevance to the project's context and licensing.

**Triggering Code Review:**

- You can request an AI-powered code review by commenting `/gemini review` on this PR after it's been created.

<!-- You can erase any parts of this template not applicable to your Pull Request. -->



Diff:
diff --git a/package.json b/package.json
index 1987c2362..39f81974f 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
 		"@iconify-json/simple-icons": "^1.2.32",
 		"@iconify/tailwind": "^1.2.0",
 		"@iconify/tailwind4": "^1.0.6",
-		"@openfoodfacts/openfoodfacts-nodejs": "2.0.0-alpha.4",
+		"@openfoodfacts/openfoodfacts-nodejs": "2.0.0-alpha.5",
 		"@sveltejs/adapter-vercel": "^5.7.0",
 		"@sveltejs/kit": "^2.20.7",
 		"@sveltejs/vite-plugin-svelte": "^5.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 34c9bcb01..132a82b20 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -33,8 +33,8 @@ importers:
         specifier: ^1.0.6
         version: 1.0.6(tailwindcss@4.1.4)
       '@openfoodfacts/openfoodfacts-nodejs':
-        specifier: 2.0.0-alpha.4
-        version: 2.0.0-alpha.4
+        specifier: 2.0.0-alpha.5
+        version: 2.0.0-alpha.5
       '@sveltejs/adapter-vercel':
         specifier: ^5.7.0
         version: 5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.1)(vite@6.3.2(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.28.1)(vite@6.3.2(jiti@2.4.2)(lightningcss@1.29.2)))(rollup@4.40.0)
@@ -767,8 +767,8 @@ packages:
     resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
     engines: {node: '>= 8'}
 
-  '@openfoodfacts/openfoodfacts-nodejs@2.0.0-alpha.4':
-    resolution: {integrity: sha512-LUbbE3ab9S3+H4A75llaL1rxjMf7JYcWyekv/GG8+GzFYENZsoMEuht9AzlF1WNkrHcnUdodoSKrrDuw+yKFWg==}
+  '@openfoodfacts/openfoodfacts-nodejs@2.0.0-alpha.5':
+    resolution: {integrity: sha512-yxOSf27NNJYWKAuFMh8qZD23+qoYDNbwudQrt/4W/fVTrTBjVabWsGjksKKPgBZKcwRV++aUw3QCwYkKDDy5+w==}
 
   '@pkgjs/parseargs@0.11.0':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
@@ -2832,7 +2832,7 @@ snapshots:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.19.1
 
-  '@openfoodfacts/openfoodfacts-nodejs@2.0.0-alpha.4':
+  '@openfoodfacts/openfoodfacts-nodejs@2.0.0-alpha.5':
     dependencies:
       openapi-fetch: 0.13.5
 
diff --git a/src/lib/api/prices.ts b/src/lib/api/prices.ts
index 928a9026b..369ed8d96 100644
--- a/src/lib/api/prices.ts
+++ b/src/lib/api/prices.ts
@@ -1,13 +1,6 @@
-import createClient from 'openapi-fetch';
-import type { paths } from './prices.d';
-
-type PricesQuery = paths['/api/v1/prices']['get']['parameters']['query'];
-type PriceStats = paths['/api/v1/prices/stats']['get']['parameters']['query'];
-
-export type PricesCreate =
-	paths['/api/v1/prices']['post']['requestBody']['content']['application/json'];
-export type Prices =
-	paths['/api/v1/prices']['get']['responses']['200']['content']['application/json'];
+import { get } from 'svelte/store';
+import { preferences } from '$lib/settings';
+import { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
 
 const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
 
@@ -15,68 +8,17 @@ export function isConfigured() {
 	return BASE_URL != null;
 }
 
-export class PricesApi {
-	private readonly client: ReturnType<typeof createClient<paths>>;
-	private readonly fetch: typeof window.fetch;
-
-	constructor(fetch: typeof window.fetch) {
-		this.client = createClient({ fetch, baseUrl: BASE_URL, credentials: 'include' });
-		this.fetch = fetch;
-	}
-
-	getPrices(query: PricesQuery) {
-		return this.client.GET('/api/v1/prices', { params: { query } });
-	}
-	createPrice(body: PricesCreate) {
-		return this.client.POST('/api/v1/prices', { body });
-	}
-	getPriceById(id: number) {
-		return this.client.GET('/api/v1/prices/{id}', {
-			params: { path: { id } }
-		});
-	}
-	updatePrice(id: number, body: Partial<PricesCreate>) {
-		return this.client.PATCH('/api/v1/prices/{id}', { params: { path: { id } }, body });
-	}
-
-	deletePrice(id: number) {
-		return this.client.DELETE('/api/v1/prices/{id}', { params: { path: { id } } });
-	}
-
-	getPriceStats(query: PriceStats) {
-		return this.client.GET('/api/v1/prices/stats', { params: { query } });
-	}
-	login(body: { username: string; password: string }) {
-		return this.client.POST('/api/v1/auth', {
-			// @ts-expect-error - The type definition doesn't include set_cookie parameter but the API requires it
-			params: { query: { set_cookie: 1 } },
-			body,
-			headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-			bodySerializer: (body) => new URLSearchParams(body as Record<string, string>)
-		});
-	}
-
-	uploadProof(body: { file: Blob }) {
-		return this.client.POST('/api/v1/proofs/upload', {
-			// @ts-expect-error - FormData is not supported by openapi-fetch
-			body: body,
-			headers: {
-				'Content-Type': 'multipart/form-data'
-			}
-		});
-	}
-
-	getProofs() {
-		return this.client.GET('/api/v1/proofs');
-	}
-
-	async isAuthenticated() {
-		const res = await this.client.GET('/api/v1/session');
-		return res.response.ok;
-	}
-
-	async getStatus() {
-		const res = await this.client.GET('/api/v1/status');
-		return res.data;
-	}
-}
+export const createPricesApi = (fetch: typeof window.fetch): PricesApi => {
+	const pricesApi = new PricesApi(fetch, {
+		baseUrl: BASE_URL,
+		authToken: `${get(preferences)?.prices?.authToken}`
+	});
+	return pricesApi;
+};
+
+export const updatePricesAuthToken = (token: string | null) => {
+	preferences.update((p) => ({
+		...p,
+		prices: { ...p.prices, authToken: token }
+	}));
+};
diff --git a/src/lib/settings.ts b/src/lib/settings.ts
index 7e149dd19..4b9974f7a 100644
--- a/src/lib/settings.ts
+++ b/src/lib/settings.ts
@@ -9,6 +9,9 @@ export const preferences = persisted('preferences', {
 	folksonomy: {
 		authToken: null as string | null
 	},
+	prices: {
+		authToken: null as string | null
+	},
 	username: null as string | null,
 	password: null as string | null
 });
diff --git a/src/routes/products/[barcode]/+page.ts b/src/routes/products/[barcode]/+page.ts
index 6eb213a4f..37cc73b07 100644
--- a/src/routes/products/[barcode]/+page.ts
+++ b/src/routes/products/[barcode]/+page.ts
@@ -11,7 +11,7 @@ import {
 } from '$lib/api';
 import { error } from '@sveltejs/kit';
 import { FolksonomyApi } from '$lib/api/folksonomy';
-import { PricesApi, isConfigured as isPricesConfigured } from '$lib/api/prices';
+import { createPricesApi, isConfigured as isPriceConfigured } from '$lib/api/prices';
 
 export const ssr = false;
 
@@ -37,9 +37,9 @@ export const load: PageLoad = async ({ params, fetch }) => {
 	const folksonomyTags = folkApi.getProduct(params.barcode);
 	const folksonomyKeys = folkApi.getKeys();
 
-	const pricesApi = new PricesApi(fetch);
+	const pricesApi = createPricesApi(fetch);
 	let pricesResponse = null;
-	if (isPricesConfigured()) {
+	if (isPriceConfigured()) {
 		pricesResponse = pricesApi.getPrices({ product_code: params.barcode });
 	}
 
diff --git a/src/routes/products/[barcode]/Prices.svelte b/src/routes/products/[barcode]/Prices.svelte
index 815856cf1..5e5259cc9 100644
--- a/src/routes/products/[barcode]/Prices.svelte
+++ b/src/routes/products/[barcode]/Prices.svelte
@@ -1,12 +1,12 @@
 <script lang="ts">
 	import { preventDefault } from 'svelte/legacy';
-
-	import { PricesApi } from '$lib/api/prices';
 	import { onMount } from 'svelte';
 
 	import { getNearStores, idToName, type OverpassAPIResult } from '$lib/location';
 	import { invalidateAll } from '$app/navigation';
 	import type { components } from '$lib/api/prices.d';
+	import { createPricesApi, updatePricesAuthToken } from '$lib/api/prices';
+	import type { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
 
 	type CurrencyEnum = components['schemas']['CurrencyEnum'];
 	type ApiResponse<T> = { data?: T; error?: object };
@@ -46,10 +46,11 @@
 
 	interface Props {
 		prices: {
-			count: number;
-			next?: string | null;
-			previous?: string | null;
-			results: PriceResult[];
+			items: PriceResult[];
+			page: number;
+			pages: number;
+			size: number;
+			total: number;
 		};
 		barcode: string;
 	}
@@ -63,9 +64,8 @@
 	let nearStores: OverpassAPIResult | undefined = $state();
 
 	onMount(async () => {
-		pricesApi = new PricesApi(fetch);
+		pricesApi = createPricesApi(fetch);
 		authenticated = await pricesApi.isAuthenticated();
-
 		nearStores = await getNearStores();
 	});
 
@@ -100,6 +100,10 @@
 		} else {
 			console.debug('Logged in', res.data);
 			authStatus = true;
+
+			// set the auth token in the preferences store
+			updatePricesAuthToken(res?.data?.access_token ?? null);
+
 			setTimeout(() => {
 				authenticated = true;
 			}, 1000);
@@ -130,12 +134,10 @@
 		});
 
 		if (res.error != null) {
-			// @ts-expect-error - TODO: Types should be specified in a better way
 			console.error('Error while submitting price', res.error);
 		} else {
 			console.debug('Submitted price', res.data);
-			// @ts-expect-error - TODO: Types should be specified in a better way
-			prices.results.push(res.data);
+			prices.items.push(res.data);
 			invalidateAll();
 		}
 	}
@@ -144,7 +146,7 @@
 <div>
 	<div id="prices">
 		<span class="font-bold">
-			Prices: ({Math.min(prices?.results?.length ?? 0, prices?.count ?? 0)}/{prices?.count ?? 0})
+			Prices: ({Math.min(prices?.items?.length ?? 0, prices?.total ?? 0)}/{prices?.total ?? 0})
 		</span>
 		<table class="table-zebra table">
 			<thead>
@@ -155,7 +157,7 @@
 				</tr>
 			</thead>
 			<tbody>
-				{#each prices.results as price (price.id)}
+				{#each prices.items as price (price.id)}
 					<tr>
 						<td
 							>{price.price != null ? price.price + ' ' + (price.currency ?? 'Unknown') : 'N/A'}</td


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @heymitali, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request integrates the Node.js SDK for Open Prices APIs into the existing SvelteKit application. The primary goal is to fetch and display price data for products based on their barcode. The changes involve removing the locally defined `PricesApi` and related configurations, and instead utilizing the `@openfoodfacts/openfoodfacts-nodejs` package for interacting with the Open Prices API. This simplifies the code and leverages a dedicated SDK for price-related operations.

### Highlights
* **SDK Integration**: Integrates the `@openfoodfacts/openfoodfacts-nodejs` package for handling Open Prices API interactions, replacing the previous custom implementation.
* **Code Simplification**: Removes the local `src/lib/api/prices.ts` file and associated configurations, streamlining the codebase.
* **Data Display**: Modifies the `Prices.svelte` component to correctly display price data fetched using the new SDK, adapting to the SDK's data structure.
* **Configuration Removal**: Removes the unnecessary check for price API configuration (`isPriceConfigured()`) in `+page.svelte`, as the SDK handles configuration internally.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **src/lib/api/prices.ts**
  * This file has been removed as the functionality is now provided by the `@openfoodfacts/openfoodfacts-nodejs` package.
* **src/routes/products/[barcode]/+page.svelte**
  * Removed import and usage of `isPriceConfigured` from `$lib/api/prices` (line 3).
  * Removed conditional rendering based on `isPriceConfigured()` (line 240), now directly rendering if price data is available.
* **src/routes/products/[barcode]/+page.ts**
  * Replaced the local `PricesApi` import with the one from `@openfoodfacts/openfoodfacts-nodejs` (line 14).
  * Removed the conditional check `isPricesConfigured()` before fetching prices (lines 42-44), now directly fetching price data.
* **src/routes/products/[barcode]/Prices.svelte**
  * Replaced the local `PricesApi` import with the one from `@openfoodfacts/openfoodfacts-nodejs` (line 4).
  * Updated the `Props` interface to reflect the data structure returned by the new SDK, changing `results` to `items` and adding `page`, `pages`, `size`, and `total` (lines 49-53).
  * Updated the price submission logic to push new prices to `prices.items` instead of `prices.results` (line 139).
  * Updated the price display logic to use `prices.items` and `prices.total` instead of `prices.results` and `prices.count` (line 148).
  * Updated the `{#each}` block to iterate over `prices.items` instead of `prices.results` (line 159).

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> A price sought,
> SDK whispers, data brought,
> Barcodes now sing.


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-13T14:49:52Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request integrates the Node.js SDK for Open Prices APIs into the existing Svelte application. This involves removing the old prices API implementation and replacing it with the new SDK. The changes look good overall, but there are a few areas that could be improved.

### Summary of Findings
* **Removal of `isPriceConfigured` check**: The `isPriceConfigured` check has been removed, which might lead to unexpected behavior if the prices API is not configured correctly. It's important to ensure that the application handles cases where the API URL is not set.
* **Type definition updates**: The type definitions for prices have been updated to reflect the new API response structure. Ensure that all components using these types are updated accordingly to prevent runtime errors.
* **Dependency update**: The pull request introduces a new dependency `@openfoodfacts/openfoodfacts-nodejs`. Ensure that this dependency is properly managed and that its license is compatible with the project.

### Merge Readiness
The pull request introduces significant changes to the way prices are handled in the application. While the changes seem to be well-structured, it's crucial to ensure that the new dependency is properly managed and that the application handles cases where the API URL is not set. I recommend addressing the `isPriceConfigured` check before merging. I am unable to approve this pull request, and recommend that others review and approve this code before merging.
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-04-13T14:50:04Z
user: login: VaiTon
body: Thank you for your PR!
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-13T16:30:04Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T16:42:51Z
user: login: VaiTon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T16:53:43Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-13T16:58:53Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T04:05:38Z
user: login: VaiTon
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-24T14:27:52Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-24T18:01:56Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-24T18:02:15Z
user: login: VaiTon
body: Just some nitpicks
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-24T18:22:15Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T06:27:12Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T06:27:29Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T06:28:19Z
user: login: heymitali
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T06:28:25Z
user: login: VaiTon
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-25T14:48:38Z

Review comments:
diff_hunk: @@ -38,10 +38,7 @@ export const load: PageLoad = async ({ params, fetch }) => {
 	const folksonomyKeys = folkApi.getKeys();
 
 	const pricesApi = new PricesApi(fetch);
-	let pricesResponse = null;
-	if (isPricesConfigured()) {
-		pricesResponse = pricesApi.getPrices({ product_code: params.barcode });
-	}
+	let pricesResponse = pricesApi.getPrices({ product_code: params.barcode });
 
 	// TODO: parseInt should be removed. Barcodes are strings
 	const questions = off.robotoff.questionsByProductCode(parseInt(params.barcode)).then(
path: src/routes/products/[barcode]/+page.ts
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The `isPricesConfigured()` check has been removed. It's important to ensure that the application handles cases where the API URL is not set. Consider adding a try-catch block around the `pricesApi.getPrices` call to handle potential errors if the API is not configured correctly.

```typescript
let pricesResponse = null;
try {
	pricesResponse = pricesApi.getPrices({ product_code: params.barcode });
} catch (error) {
	console.error("Error fetching prices", error);
	pricesResponse = { items: [], page: 0, pages: 0, size: 0, total: 0 }; // Provide a default value
}
```
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -1,6 +1,5 @@
 <script lang="ts">
 	import { getOrDefault } from '$lib/api';
-	import { isConfigured as isPriceConfigured } from '$lib/api/prices';
 	import { isConfigured as isFolksonomyConfigured } from '$lib/api/folksonomy';
path: src/routes/products/[barcode]/+page.svelte
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Since the `PricesApi` is now coming from `@openfoodfacts/openfoodfacts-nodejs`, the `isPriceConfigured` check is no longer relevant here. Consider removing this import to avoid confusion.
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -46,10 +46,11 @@
 
 	interface Props {
 		prices: {
-			count: number;
-			next?: string | null;
-			previous?: string | null;
-			results: PriceResult[];
+			items: PriceResult[];
+			page: number;
+			pages: number;
+			size: number;
+			total : number;
path: src/routes/products/[barcode]/Prices.svelte
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The type definitions for `prices` have been updated. Ensure that all components using these types are updated accordingly to prevent runtime errors. Double check that the new fields are being used correctly throughout the component.
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -135,7 +136,7 @@
 		} else {
 			console.debug('Submitted price', res.data);
 			// @ts-expect-error - TODO: Types should be specified in a better way
-			prices.results.push(res.data);
+			prices.items.push(res.data);
path: src/routes/products/[barcode]/Prices.svelte
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The type definitions for `prices` have been updated. Ensure that all components using these types are updated accordingly to prevent runtime errors. Double check that the new fields are being used correctly throughout the component.
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -144,7 +145,7 @@
 <div>
 	<div id="prices">
 		<span class="font-bold">
-			Prices: ({Math.min(prices?.results?.length ?? 0, prices?.count ?? 0)}/{prices?.count ?? 0})
+			Prices: ({Math.min(prices?.items?.length ?? 0, prices?.total ?? 0)}/{prices?.total ?? 0})
path: src/routes/products/[barcode]/Prices.svelte
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The type definitions for `prices` have been updated. Ensure that all components using these types are updated accordingly to prevent runtime errors. Double check that the new fields are being used correctly throughout the component.
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -155,7 +156,7 @@
 				</tr>
 			</thead>
 			<tbody>
-				{#each prices.results as price (price.id)}
+				{#each prices.items as price (price.id)}
path: src/routes/products/[barcode]/Prices.svelte
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The type definitions for `prices` have been updated. Ensure that all components using these types are updated accordingly to prevent runtime errors. Double check that the new fields are being used correctly throughout the component.
created_at: 2025-04-13T14:50:04Z
author_association: NONE
diff_hunk: @@ -1,82 +0,0 @@
-import createClient from 'openapi-fetch';
-import type { paths } from './prices.d';
-
-type PricesQuery = paths['/api/v1/prices']['get']['parameters']['query'];
-type PriceStats = paths['/api/v1/prices/stats']['get']['parameters']['query'];
-
-export type PricesCreate =
-	paths['/api/v1/prices']['post']['requestBody']['content']['application/json'];
-export type Prices =
-	paths['/api/v1/prices']['get']['responses']['200']['content']['application/json'];
-
-const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
-
-export function isConfigured() {
-	return BASE_URL != null;
-}
path: src/lib/api/prices.ts
user: login: VaiTon
body: We still need this section, as right now we're losing the ability to specify a different base url for the OpenPrices deployments (which in turns means no staging / production A/B testing)
created_at: 2025-04-13T16:29:41Z
author_association: MEMBER
diff_hunk: @@ -1,82 +0,0 @@
-import createClient from 'openapi-fetch';
-import type { paths } from './prices.d';
-
-type PricesQuery = paths['/api/v1/prices']['get']['parameters']['query'];
-type PriceStats = paths['/api/v1/prices/stats']['get']['parameters']['query'];
-
-export type PricesCreate =
-	paths['/api/v1/prices']['post']['requestBody']['content']['application/json'];
-export type Prices =
-	paths['/api/v1/prices']['get']['responses']['200']['content']['application/json'];
-
-const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
-
-export function isConfigured() {
-	return BASE_URL != null;
-}
path: src/lib/api/prices.ts
user: login: heymitali
body: @VaiTon Thanks for the review.

FYI, Nodejs SDK already support passing of custom base URL, So Instead of keeping things in explorer in prices.ts file, we can also pass the base URL in the constructor of PricesApi. Sharing screenshot for your reference.

<img width="582" alt="Screenshot 2025-04-13 at 10 10 12 PM" src="https://github.com/user-attachments/assets/2367e313-af2b-4bec-84ba-31d9e36948b5" />

created_at: 2025-04-13T16:42:51Z
author_association: MEMBER
diff_hunk: @@ -1,82 +0,0 @@
-import createClient from 'openapi-fetch';
-import type { paths } from './prices.d';
-
-type PricesQuery = paths['/api/v1/prices']['get']['parameters']['query'];
-type PriceStats = paths['/api/v1/prices/stats']['get']['parameters']['query'];
-
-export type PricesCreate =
-	paths['/api/v1/prices']['post']['requestBody']['content']['application/json'];
-export type Prices =
-	paths['/api/v1/prices']['get']['responses']['200']['content']['application/json'];
-
-const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
-
-export function isConfigured() {
-	return BASE_URL != null;
-}
path: src/lib/api/prices.ts
user: login: VaiTon
body: @heymitali sure, but what if I want to disable a certain feature? I can do that right now by setting the relevant env var to a blank string. Also, it's not really good to always specify `import.meta.env.VITE_PRICES_API_URL`
created_at: 2025-04-13T16:53:43Z
author_association: MEMBER
diff_hunk: @@ -1,82 +0,0 @@
-import createClient from 'openapi-fetch';
-import type { paths } from './prices.d';
-
-type PricesQuery = paths['/api/v1/prices']['get']['parameters']['query'];
-type PriceStats = paths['/api/v1/prices/stats']['get']['parameters']['query'];
-
-export type PricesCreate =
-	paths['/api/v1/prices']['post']['requestBody']['content']['application/json'];
-export type Prices =
-	paths['/api/v1/prices']['get']['responses']['200']['content']['application/json'];
-
-const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
-
-export function isConfigured() {
-	return BASE_URL != null;
-}
path: src/lib/api/prices.ts
user: login: heymitali
body: got it, I will revert this `isConfigured` related change. And will also pass the BASE Url in sdk constructor for supporting custom urls.
created_at: 2025-04-13T16:58:53Z
author_association: MEMBER
diff_hunk: @@ -20,7 +22,15 @@ export class PricesApi {
 	private readonly fetch: typeof window.fetch;
 
 	constructor(fetch: typeof window.fetch) {
-		this.client = createClient({ fetch, baseUrl: BASE_URL, credentials: 'include' });
+		this.client = createClient({
+			fetch,
+			baseUrl: BASE_URL,
+			credentials: 'include',
+			headers: {
+				'Content-Type': 'application/json',
+				Authorization: 'Bearer ' + get(preferences)?.prices?.authToken
path: src/lib/api/prices.ts
user: login: heymitali
body: Raised a PR to do this in nodejs SDK: https://github.com/openfoodfacts/openfoodfacts-nodejs/pull/627
created_at: 2025-04-23T04:05:37Z
author_association: MEMBER
diff_hunk: @@ -37,7 +40,12 @@ export const load: PageLoad = async ({ params, fetch }) => {
 	const folksonomyTags = folkApi.getProduct(params.barcode);
 	const folksonomyKeys = folkApi.getKeys();
 
-	const pricesApi = new PricesApi(fetch);
+	const baseUrl = import.meta.env.VITE_PRICES_API_URL;
+
+	const pricesApi = new PricesApi(fetch, {
+		baseUrl,
+		authToken: `${get(preferences)?.prices?.authToken}`
+	});
path: src/routes/products/[barcode]/+page.ts
user: login: VaiTon
body: This can be extracted to a function (in the deleted `prices.ts`) that accepts only the `fetch` as the options are always the same
created_at: 2025-04-24T14:27:01Z
author_association: MEMBER
diff_hunk: @@ -100,6 +107,13 @@
 		} else {
 			console.debug('Logged in', res.data);
 			authStatus = true;
+
+			// set the auth token in the preferences store
+			preferences.update((p) => ({
+				...p,
+				prices: { ...p.prices, authToken: res?.data?.access_token ?? null }
+			}));
+
path: src/routes/products/[barcode]/Prices.svelte
user: login: VaiTon
body: Create a function that updates the token in the `prices.ts` file.
created_at: 2025-04-24T14:27:37Z
author_association: MEMBER
diff_hunk: @@ -100,6 +107,13 @@
 		} else {
 			console.debug('Logged in', res.data);
 			authStatus = true;
+
+			// set the auth token in the preferences store
+			preferences.update((p) => ({
+				...p,
+				prices: { ...p.prices, authToken: res?.data?.access_token ?? null }
+			}));
+
path: src/routes/products/[barcode]/Prices.svelte
user: login: heymitali
body: done
created_at: 2025-04-24T18:01:56Z
author_association: MEMBER
diff_hunk: @@ -37,7 +40,12 @@ export const load: PageLoad = async ({ params, fetch }) => {
 	const folksonomyTags = folkApi.getProduct(params.barcode);
 	const folksonomyKeys = folkApi.getKeys();
 
-	const pricesApi = new PricesApi(fetch);
+	const baseUrl = import.meta.env.VITE_PRICES_API_URL;
+
+	const pricesApi = new PricesApi(fetch, {
+		baseUrl,
+		authToken: `${get(preferences)?.prices?.authToken}`
+	});
path: src/routes/products/[barcode]/+page.ts
user: login: heymitali
body: done, thanks for review.
created_at: 2025-04-24T18:02:15Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,24 @@
+import { get } from 'svelte/store';
+import { preferences } from '$lib/settings';
+import { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
+
+const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
+
+export function isConfigured() {
+	return BASE_URL != null;
+}
+
+export const createPricesApi = (fetch: typeof window.fetch): PricesApi => {
+    const pricesApi = new PricesApi(fetch, {
+        baseUrl: BASE_URL,
+        authToken: `${get(preferences)?.prices?.authToken}`
+    });
+    return pricesApi;
+}
+
+export const updatePricesAuthToken = async (token: string) => {
path: src/lib/api/prices.ts
user: login: VaiTon
body: Why `async?
created_at: 2025-04-24T18:21:14Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,24 @@
+import { get } from 'svelte/store';
+import { preferences } from '$lib/settings';
+import { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
+
+const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
+
+export function isConfigured() {
+	return BASE_URL != null;
+}
+
+export const createPricesApi = (fetch: typeof window.fetch): PricesApi => {
+    const pricesApi = new PricesApi(fetch, {
+        baseUrl: BASE_URL,
+        authToken: `${get(preferences)?.prices?.authToken}`
+    });
+    return pricesApi;
+}
+
+export const updatePricesAuthToken = async (token: string) => {
+    preferences.update((p) => ({
+        ...p,
+        prices: { ...p.prices, authToken: token ?? null }
path: src/lib/api/prices.ts
user: login: VaiTon
body: `token` is a string, it can't be null
created_at: 2025-04-24T18:21:31Z
author_association: MEMBER
diff_hunk: @@ -109,10 +106,7 @@
 			authStatus = true;
 
 			// set the auth token in the preferences store
-			preferences.update((p) => ({
-				...p,
-				prices: { ...p.prices, authToken: res?.data?.access_token ?? null }
-			}));
+			updatePricesAuthToken(res?.data?.access_token ?? '');
path: src/routes/products/[barcode]/Prices.svelte
user: login: VaiTon
body: We should decide if we use an empty string (not good) or null (better)
created_at: 2025-04-24T18:22:06Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,24 @@
+import { get } from 'svelte/store';
+import { preferences } from '$lib/settings';
+import { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
+
+const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
+
+export function isConfigured() {
+	return BASE_URL != null;
+}
+
+export const createPricesApi = (fetch: typeof window.fetch): PricesApi => {
+    const pricesApi = new PricesApi(fetch, {
+        baseUrl: BASE_URL,
+        authToken: `${get(preferences)?.prices?.authToken}`
+    });
+    return pricesApi;
+}
+
+export const updatePricesAuthToken = async (token: string) => {
path: src/lib/api/prices.ts
user: login: heymitali
body: sorry forget to remove this. done.
created_at: 2025-04-25T06:27:12Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,24 @@
+import { get } from 'svelte/store';
+import { preferences } from '$lib/settings';
+import { PricesApi } from '@openfoodfacts/openfoodfacts-nodejs';
+
+const BASE_URL = import.meta.env.VITE_PRICES_API_URL;
+
+export function isConfigured() {
+	return BASE_URL != null;
+}
+
+export const createPricesApi = (fetch: typeof window.fetch): PricesApi => {
+    const pricesApi = new PricesApi(fetch, {
+        baseUrl: BASE_URL,
+        authToken: `${get(preferences)?.prices?.authToken}`
+    });
+    return pricesApi;
+}
+
+export const updatePricesAuthToken = async (token: string) => {
+    preferences.update((p) => ({
+        ...p,
+        prices: { ...p.prices, authToken: token ?? null }
path: src/lib/api/prices.ts
user: login: heymitali
body: done.
created_at: 2025-04-25T06:27:29Z
author_association: MEMBER
diff_hunk: @@ -109,10 +106,7 @@
 			authStatus = true;
 
 			// set the auth token in the preferences store
-			preferences.update((p) => ({
-				...p,
-				prices: { ...p.prices, authToken: res?.data?.access_token ?? null }
-			}));
+			updatePricesAuthToken(res?.data?.access_token ?? '');
path: src/routes/products/[barcode]/Prices.svelte
user: login: heymitali
body: I agree - null would be better and it will keep the authToken value sync with previous code.
created_at: 2025-04-25T06:28:19Z
author_association: MEMBER
diff_hunk: @@ -109,10 +106,7 @@
 			authStatus = true;
 
 			// set the auth token in the preferences store
-			preferences.update((p) => ({
-				...p,
-				prices: { ...p.prices, authToken: res?.data?.access_token ?? null }
-			}));
+			updatePricesAuthToken(res?.data?.access_token ?? '');
path: src/routes/products/[barcode]/Prices.svelte
user: login: heymitali
body: changes done.
created_at: 2025-04-25T06:28:25Z
author_association: MEMBER

PR comments:
user: login: vercel[bot]
created_at: 2025-04-13T14:49:46Z
body: [vc]: #WP9SAEVAPQUk2ITcpjv9SBAVeYsjuMEhvKb2ByPOA8w=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJvcGVuZm9vZGZhY3RzLWV4cGxvcmVyIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL29wZW5mb29kZmFjdHMvb3BlbmZvb2RmYWN0cy1leHBsb3Jlci82SndGclk0ekpnWDNlYm9MUkFna05hWUc5eGdFIiwicHJldmlld1VybCI6Im9wZW5mb29kZmFjdHMtZXhwbG9yZXItZ2l0LWZvcmstaGV5bWl0YWxpLTJiZGMyNi1vcGVuZm9vZGZhY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoib3BlbmZvb2RmYWN0cy1leHBsb3Jlci1naXQtZm9yay1oZXltaXRhbGktMmJkYzI2LW9wZW5mb29kZmFjdHMudmVyY2VsLmFwcCJ9fV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **openfoodfacts-explorer** | ✅ Ready ([Inspect](https://vercel.com/openfoodfacts/openfoodfacts-explorer/6JwFrY4zJgX3eboLRAgkNaYG9xgE)) | [Visit Preview](https://openfoodfacts-explorer-git-fork-heymitali-2bdc26-openfoodfacts.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/openfoodfacts-explorer-git-fork-heymitali-2bdc26-openfoodfacts.vercel.app?via=pr-comment-feedback-link) | Apr 25, 2025 6:26am |


author_association: NONE
user: login: heymitali
created_at: 2025-04-23T04:10:15Z
body: Update: This PR has dependency on nodejs PR - https://github.com/openfoodfacts/openfoodfacts-nodejs/pull/627
Once that NodeJs PR is merged, I will update this PR and open it.
author_association: MEMBER
user: login: heymitali
created_at: 2025-04-24T11:14:46Z
body: @VaiTon  All changes are done. Can you please help in reviewing it?
author_association: MEMBER
user: login: heymitali
created_at: 2025-04-24T18:02:58Z
body: @VaiTon all comments are now resolved. please review.
author_association: MEMBER
user: login: heymitali
created_at: 2025-04-25T11:12:38Z
body: @VaiTon  I have made the required changes. Please review.
author_association: MEMBER