A modern, type-safe wrapper for the official Veganify API with built-in validation, caching, and comprehensive error handling.
- 🎯 Type Safety: Full TypeScript support with Zod validation
- 🚀 Performance: Built-in caching system
- 🛡️ Robust Error Handling: Custom error types for different scenarios
- ⚡ Framework Agnostic: Works with any JavaScript framework
- 🔄 Modern Architecture: Singleton pattern with configurable instances
You can install the package via npm, yarn, or pnpm, bun, etc.:
# Using npm
npm install @frontendnetwork/veganify
# Using yarn
yarn add @frontendnetwork/veganify
# Using pnpm
pnpm add @frontendnetwork/veganify
import Veganify from "@frontendnetwork/veganify";
// Get an instance with default configuration
const veganify = Veganify.getInstance();
// Example: Check if ingredients are vegan
async function checkIngredients() {
try {
const result = await veganify.checkIngredientsListV1("water, sugar, milk");
console.log("Is vegan:", result.data.vegan);
console.log("Non-vegan ingredients:", result.data.not_vegan);
} catch (error) {
if (error instanceof ValidationError) {
console.error("Invalid ingredients format:", error.message);
}
}
}
interface VeganifyConfig {
baseUrl?: string; // Custom API base URL
cacheTTL?: number; // Cache time-to-live in milliseconds
staging?: boolean; // Use staging environment
}
// Example: Configure with custom settings
const veganify = Veganify.getInstance({
cacheTTL: 3600000, // 1 hour cache
staging: true, // Use staging environment
});
Retrieves detailed product information using a barcode number. This method queries multiple databases to gather comprehensive vegan and ethical product data.
Parameters:
barcode
: A string containing the product barcode (must contain only digits)
Returns:
A promise that resolves to a ProductResponse object containing the response type.
try {
const product = await veganify.getProductByBarcode("4066600204404");
console.log(product.product.vegan); // true/false/"n/a"
console.log(product.product.productname); // "Product Name"
} catch (error) {
if (error instanceof NotFoundError) {
console.error("Product not found");
} else if (error instanceof ValidationError) {
console.error("Invalid barcode format");
}
}
Response Type:
interface ProductResponse {
status: number;
product: {
productname: string;
genericname?: string;
vegan?: boolean | "n/a";
vegetarian?: boolean | "n/a";
animaltestfree?: boolean | "n/a";
palmoil?: boolean | "n/a";
nutriscore?: string;
grade?: string;
};
sources: {
processed: boolean;
api: string;
baseuri: string;
};
}
checkIngredientsListV1(ingredientsList: string, preprocessed?: boolean): Promise<IngredientsCheckResponseV1>
Analyzes a list of ingredients to determine their vegan status. The method categorizes ingredients into different groups based on certainty of their vegan status.
Parameters:
ingredientsList
: A string containing comma-separated ingredientspreprocessed
: (Optional) Boolean indicating whether to preprocess the ingredients list (defaults to true)
Returns:
A promise that resolves to an IngredientsCheckResponseV1 return type.
try {
const result = await veganify.checkIngredientsListV1("water, sugar, milk");
console.log("Is vegan:", result.data.vegan);
console.log("Surely vegan:", result.data.surely_vegan);
console.log("Not vegan:", result.data.not_vegan);
console.log("Maybe not vegan:", result.data.maybe_not_vegan);
console.log("Unknown:", result.data.unknown);
} catch (error) {
if (error instanceof ValidationError) {
console.error("Invalid ingredients format");
}
}
Response Type:
interface IngredientsCheckResponseV1 {
code: string;
status: string;
message: string;
data: {
vegan: boolean;
surely_vegan: string[];
not_vegan: string[];
maybe_not_vegan: string[];
unknown: string[];
};
}
Retrieves the current list of PETA-certified cruelty-free brands.
Returns:
A promise that resolves to a PetaCrueltyFreeResponse
object:
interface PetaCrueltyFreeResponse {
LAST_UPDATE: string; // Last database update timestamp
PETA_DOES_NOT_TEST: string[]; // Array of cruelty-free brand names
}
try {
const brands = await veganify.getPetaCrueltyFreeBrands();
console.log("Last update:", brands.LAST_UPDATE);
console.log("Cruelty-free brands:", brands.PETA_DOES_NOT_TEST);
} catch (error) {
console.error("Failed to fetch PETA brands:", error.message);
}
The package provides custom error classes for different scenarios:
import {
ValidationError,
NotFoundError,
VeganifyError,
} from "@frontendnetwork/veganify";
try {
// Your code here
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation errors (400)
} else if (error instanceof NotFoundError) {
// Handle not found errors (404)
} else if (error instanceof VeganifyError) {
// Handle other API errors
} else {
// Handle unexpected errors
}
}
import { useState } from "react";
import Veganify, { ValidationError } from "@frontendnetwork/veganify";
const IngredientsChecker = () => {
const [ingredients, setIngredients] = useState("");
const [result, setResult] = useState(null);
const [error, setError] = useState("");
const veganify = Veganify.getInstance();
const handleCheck = async (e) => {
e.preventDefault();
try {
const analysis = await veganify.checkIngredientsListV1(ingredients);
setResult(analysis);
setError("");
} catch (error) {
setResult(null);
if (error instanceof ValidationError) {
setError("Please enter valid ingredients");
} else {
setError("An error occurred while checking ingredients");
}
}
};
return (
<div>
<form onSubmit={handleCheck}>
<input
value={ingredients}
onChange={(e) => setIngredients(e.target.value)}
placeholder="Enter ingredients (comma-separated)"
/>
<button type="submit">Check Ingredients</button>
</form>
{error && <div className="error">{error}</div>}
{result && (
<div>
<h3>Analysis Results:</h3>
<p>Vegan: {result.data.vegan ? "Yes" : "No"}</p>
{result.data.not_vegan.length > 0 && (
<p>Non-vegan ingredients: {result.data.not_vegan.join(", ")}</p>
)}
{result.data.maybe_not_vegan.length > 0 && (
<p>
Potentially non-vegan: {result.data.maybe_not_vegan.join(", ")}
</p>
)}
</div>
)}
</div>
);
};
export default IngredientsChecker;
<template>
<div>
<form @submit.prevent="checkIngredients">
<input
v-model="ingredients"
placeholder="Enter ingredients (comma-separated)"
/>
<button type="submit">Check</button>
</form>
<div v-if="error" class="error">{{ error }}</div>
<div v-if="result">
<h3>Analysis Results:</h3>
<p>Vegan Status: {{ result.data.vegan ? "Vegan" : "Not Vegan" }}</p>
<p v-if="result.data.not_vegan.length">
Non-vegan ingredients: {{ result.data.not_vegan.join(", ") }}
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Veganify, { ValidationError } from "@frontendnetwork/veganify";
const veganify = Veganify.getInstance();
const ingredients = ref("");
const result = ref(null);
const error = ref("");
async function checkIngredients() {
try {
result.value = await veganify.checkIngredientsListV1(ingredients.value);
error.value = "";
} catch (e) {
result.value = null;
if (e instanceof ValidationError) {
error.value = "Please enter valid ingredients";
} else {
error.value = "An error occurred while checking ingredients";
}
}
}
</script>
The package includes a built-in caching system with configurable TTL:
const veganify = Veganify.getInstance({
cacheTTL: 1800000, // 30 minutes, set to 0 to disable caching
});
// Clear cache if needed
veganify.clearCache();
// The next API call will fetch fresh data
const product = await veganify.getProductByBarcode("4066600204404");
The package exports a utility function that cleans and standardizes ingredient lists for analysis.
Parameters:
ingredientsList
: Raw ingredient list string
Returns:
An array of cleaned and normalized ingredient names
import { preprocessIngredients } from "@frontendnetwork/veganify";
const cleanIngredients = preprocessIngredients(
"water 100%, sugar (organic), salt:"
);
// Result: ['water', 'sugar', 'organic', 'salt']
Contributions are welcome! Please feel free to submit a Pull Request.
MIT © FrontEndNet.work, Philip Brembeck