npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ng-nutrition-score

v1.0.1

Published

Nigeria nutrition scoring engine. Converts raw product nutrition facts into a letter grade (A–E) using WHO AFRO nutrient profile thresholds calibrated to the Nigerian dietary context.

Readme

Nigeria-nutrition-score

Nigeria nutrition scoring engine. Converts food product nutrition facts into a letter grade A–E using WHO AFRO nutrient profile thresholds calibrated to the Nigerian dietary context.

Features

  • Letter grades A–E — based on WHO nutrient profile model for the African region and NAFDAC Food Labelling Regulations 2005
  • Category-aware thresholds — beverages, cereals, dairy, meat, oils, sauces, and more each have their own grade boundaries
  • Full scoring breakdown — per-nutrient points for transparency
  • Dual ESM + CJS — works in modern Node (ESM), legacy Node (CommonJS), and bundlers
  • Optional MongoDB pipeline — a /db subpath maps raw DbProduct documents to scored results without any MongoDB runtime dependency
  • Zero dependencies — pure TypeScript, no external runtime packages

Installation

npm install ng-nutrition-score

Quick Start

Scoring from NutritionFacts directly

Use this if you already have nutrition data in the right shape (e.g. from a REST API, form input, or CSV).

import { calculateNutritionScore } from "ng-nutrition-score";

const result = calculateNutritionScore({
  energyKcal:         364,
  totalFatG:          1.2,
  saturatedFatG:      0.3,
  transFatG:          0,
  totalCarbohydratesG: 80,
  totalSugarsG:       0.5,
  dietaryFibreG:      2.0,
  proteinG:           7.0,
  sodiumMg:           2,
  category:           "cereal",
});

console.log(result.grade);           // "B"
console.log(result.label);           // "Good"
console.log(result.recommendation);  // "Good nutritional profile..."
console.log(result.rawScore);        // e.g. 5
console.log(result.breakdown);       // per-nutrient point array

MongoDB pipeline (server-side and client-side)

Use this if your product data comes from MongoDB in the DbProduct shape. The /db subpath handles all label normalisation: per-serving → per-100g, unit conversions (kJ→kcal, salt→sodium, mg→g), 30+ nutrient name aliases, and sanity clamps.

import { runScoringPipeline } from "ng-nutrition-score/db";

// products: DbProduct[] fetched from MongoDB
const scored = runScoringPipeline(products);

for (const p of scored) {
  console.log(`${p.productName}: ${p.score.grade} (${p.score.label})`);
  if (p.warnings.length) {
    console.log("  Conversion notes:", p.warnings);
  }
}

You can also use the mapper independently:

import {
  mapDbProductToNutritionFacts,
  hasNoNutritionData,
} from "ng-nutrition-score/db";

if (!hasNoNutritionData(product)) {
  const { facts, warnings } = mapDbProductToNutritionFacts(product);
  // facts is a NutritionFacts object ready for calculateNutritionScore()
}

API Reference

Main entry point (ng-nutrition-score)

calculateNutritionScore(facts: NutritionFacts): NutritionScoreResult

Validates and scores a NutritionFacts object. Throws if required fields are missing or nutritionally impossible (e.g. saturated fat > total fat).

Algorithm:

rawScore = negativePoints − positivePoints

| Negative nutrients (penalised) | Positive nutrients (rewarded) | |-------------------------------|-------------------------------| | Energy (kcal) | Dietary fibre (g) | | Saturated fat (g) | Protein (g) | | Trans fat (g) | | | Total/added sugars (g) | | | Sodium (mg) | |

UNSCORED_RESULT: NutritionScoreResult

A sentinel result with grade: "N/A" and scorable: false. Returned by the pipeline when a product has no nutrition data on its label.

saltToSodium(saltG: number): number

Converts salt in grams to sodium in mg (saltG × 400).

resolveGrade(rawScore, category): { grade, label, recommendation }

Maps a raw score to a letter grade for a given food category.

DB subpath (ng-nutrition-score/db)

When to use: Import from /db only if your data originates from a MongoDB collection in the DbProduct shape described below. If you are constructing NutritionFacts yourself, use the main entry point.

runScoringPipeline(products: DbProduct[]): ScoredProduct[]

Full pipeline from MongoDB documents to scored results. Products with empty nutrition arrays receive UNSCORED_RESULT.

mapDbProductToNutritionFacts(product: DbProduct): MappedNutritionResult

Maps a single DbProduct to NutritionFacts. Returns { facts, warnings } where warnings lists any unit conversions applied.

Transformations applied:

  • Per-serving → per-100g normalisation (Per 50g, Per 25cl, Per 1 tsp, etc.)
  • Energy: kJ → kcal (÷ 4.184)
  • Sodium: g → mg (× 1000), or derived from salt (salt g × 400)
  • Macronutrients declared as %: treated as g/100g
  • Protein/fibre/fat declared in mg: converted to g
  • 30+ nutrient name variants aliased to 10 canonical keys
  • Sanity clamps: saturated fat ≤ total fat; sugars ≤ total carbohydrates

hasNoNutritionData(product: DbProduct): boolean

Returns true if the product's nutrition array is empty (e.g. table salt, pure spices).

Food Categories

| Category value | Description | |-------------------|--------------------------------------------------| | general | Default; snacks, confectionery, cooking ingredients | | beverage | Drinks — stricter sugar thresholds (WHO AFRO) | | dairy | Dairy products, dairy alternatives, medical nutrition drinks | | cereal | Cereals, grains, staples, ready meals, baby food | | meat | Meat, poultry, seafood | | fruit_vegetable | Fruits and vegetables — more lenient boundaries | | fat_oil | Oils, fats, spreads — higher fat thresholds | | sauce_condiment | Sauces, condiments, seasonings |

Grade Scale

| Grade | Label | Meaning | |-------|-----------|-------------------------------------------------------| | A | Excellent | Strong nutritional quality; suitable for regular consumption | | B | Good | Good profile; suitable as part of a balanced diet | | C | Fair | Moderate quality; consume in reasonable portions | | D | Poor | Low quality; limit intake | | E | Avoid | Poor profile; avoid regular consumption |

Grade boundaries are category-specific. For example, beverages are judged more strictly than oils.

NutritionFacts Shape

interface NutritionFacts {
  energyKcal: number;           // kcal per 100g/100ml
  totalFatG: number;            // g per 100g
  saturatedFatG: number;        // g per 100g
  transFatG: number;            // g per 100g
  totalCarbohydratesG: number;  // g per 100g
  totalSugarsG: number;         // g per 100g
  addedSugarsG?: number;        // g per 100g (optional; falls back to totalSugars)
  dietaryFibreG: number;        // g per 100g
  proteinG: number;             // g per 100g
  sodiumMg: number;             // mg per 100g
  category?: FoodCategory;      // defaults to "general"
}

Regulatory Basis

Thresholds are based on:

  • WHO Nutrient Profile Model for the African Region (2022)
  • NAFDAC Food Labelling Regulations 2005 (as amended)
  • Calibrated to the Nigerian dietary context where staple foods (yam, cassava, plantain, rice) sit naturally in the B–C range

License

MIT