<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mubashir Ali</title>
    <description>The latest articles on DEV Community by Mubashir Ali (@mubashir_ali_d95e1c5d09ff).</description>
    <link>https://dev.to/mubashir_ali_d95e1c5d09ff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4157834%2F2cd78288-6284-4355-b4bf-7817fca5d5a6.png</url>
      <title>DEV Community: Mubashir Ali</title>
      <link>https://dev.to/mubashir_ali_d95e1c5d09ff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mubashir_ali_d95e1c5d09ff"/>
    <language>en</language>
    <item>
      <title>Why Your JSON Parse Failed, and Why Decoding a JWT Isn't Verifying It</title>
      <dc:creator>Mubashir Ali</dc:creator>
      <pubDate>Thu, 29 Oct 2026 11:31:39 +0000</pubDate>
      <link>https://dev.to/mubashir_ali_d95e1c5d09ff/why-your-json-parse-failed-and-why-decoding-a-jwt-isnt-verifying-it-37f8</link>
      <guid>https://dev.to/mubashir_ali_d95e1c5d09ff/why-your-json-parse-failed-and-why-decoding-a-jwt-isnt-verifying-it-37f8</guid>
      <description>&lt;p&gt;Why Your JSON Parse Failed, and Why Decoding a JWT Isn't Verifying It&lt;br&gt;
The API returned 200. Your frontend is dead anyway.&lt;br&gt;
SyntaxError: Unexpected token } in JSON at position 118&lt;br&gt;
Somewhere between the server and your parser there is a payload that looks like JSON, reads like JSON, and is not JSON. Here is how to find the break quickly, plus the JWT version of the same trap, because the two failures travel together in real debugging sessions.&lt;br&gt;
Read the error as coordinates, not as an insult&lt;br&gt;
A JSON parse error gives you a position. Use it. In Python the message is wordier and even more helpful:&lt;br&gt;
import json&lt;/p&gt;

&lt;p&gt;json.loads(payload)&lt;/p&gt;

&lt;h1&gt;
  
  
  json.decoder.JSONDecodeError: Expecting property name
&lt;/h1&gt;

&lt;h1&gt;
  
  
  enclosed in double quotes: line 4 column 3 (char 61)
&lt;/h1&gt;

&lt;p&gt;Line 4, column 3 is where the parser gave up, and the actual mistake usually sits a character or two before it. Open the payload, go to that line, and look left. Nine times out of ten you will see one of the usual suspects.&lt;br&gt;
The usual suspects, all in one payload&lt;br&gt;
{&lt;br&gt;
  'user': 'ada',&lt;br&gt;
  "roles": ["dev", "admin",],&lt;br&gt;
  "score": undefined,&lt;br&gt;
}&lt;br&gt;
That short block contains three separate failures:&lt;br&gt;
Single quotes. JSON strings need double quotes. 'ada' is JavaScript, not JSON.&lt;br&gt;
A trailing comma. "admin",] and the comma after undefined are both legal in modern JavaScript objects. In JSON they end the parse.&lt;br&gt;
An unquoted literal. undefined and NaN do not exist in JSON. Use null, or leave the key out.&lt;br&gt;
Two more worth memorising because they pass a quick visual scan: comments (// looks fine) aren't allowed, and neither are unquoted keys. If you generated the payload by hand, or a template engine did, check those first.&lt;br&gt;
Pasting the payload into a formatter is the fastest way to see the break in context. I use the JSON Formatter on a site I maintain, because it shows a tree view next to the raw text and the error position stops being abstract. Any formatter you trust does the same job. The point is seeing the structure, not squinting at a 400-character one-line string from a log file.&lt;br&gt;
Now the token that "looks fine"&lt;br&gt;
A JWT is three base64url segments joined by dots:&lt;br&gt;
header.payload.signature&lt;br&gt;
Decoding the first two segments is trivial, and you should know how to do it locally:&lt;br&gt;
const decodePart = (part) =&amp;gt;&lt;br&gt;
  JSON.parse(atob(part.replace(/-/g, "+").replace(/_/g, "/")));&lt;/p&gt;

&lt;p&gt;const [header, payload] = token.split(".").slice(0, 2).map(decodePart);&lt;br&gt;
Watch the padding, by the way. Base64url drops the trailing = characters, and strict decoders in some environments will reject a segment whose length is not padded back to a multiple of 4. Browser atob usually copes. Your mileage will vary, quietly, at the worst moment.&lt;br&gt;
Reading the payload answers the questions that cause most 401s:&lt;br&gt;
exp is in seconds, not milliseconds. Compare it against Date.now() / 1000. Half the "token randomly rejected" bugs I have chased in discussions and issues came down to a unit mismatch or a server clock a few minutes adrift.&lt;br&gt;
iss and aud must match what the API expects, exactly. A token minted for one environment fails against another with no useful error message.&lt;br&gt;
alg in the header tells you what the issuer claims to have signed with. If it says none on a token guarding anything real, stop and go and read your auth library's defaults.&lt;br&gt;
Decoding is not verifying. Say it twice.&lt;br&gt;
Everything above reads the token. None of it proves the token is genuine, because the payload is not encrypted. Anyone holding the string can read every claim in it, and anyone can rewrite those claims, set "role": "admin", and produce a perfectly well-formed token. Only the signature check, done by your server with the issuer's key, separates a real token from a typed one.&lt;br&gt;
Two habits follow from that:&lt;br&gt;
Never put anything secret in a payload. Treat every claim as printed on a postcard.&lt;br&gt;
Never paste a live production token into a random website. For a throwaway dev token, a browser decoder like this JWT Decoder saves a minute. For anything real, decode locally with the snippet above. A token is a credential, and credentials do not go into strangers' text boxes. That includes mine.&lt;br&gt;
Regex: fine for shape, useless for parsing&lt;br&gt;
You'll be tempted to validate payloads with regex. Resist, mostly. JSON needs a parser, and a regex can't give you one. What regex can do is check the shape of a token while you scan logs:&lt;br&gt;
^[A-Za-z0-9_-]+.[A-Za-z0-9_-]+.[A-Za-z0-9_-]+$&lt;br&gt;
Three pitfalls sit inside even this simple pattern:&lt;br&gt;
The dots must be escaped. An unescaped . matches any character, so abcXdefYghi sails through a sloppy version of this test.&lt;br&gt;
Anchors matter. Without ^ and $, the pattern matches a valid-looking substring inside a longer piece of junk and reports success.&lt;br&gt;
Greedy quantifiers hide sins. A .* in the middle will swallow the separators you were trying to count.&lt;br&gt;
I test patterns against a handful of deliberately wrong strings, not just the one good string, in a Regex Tester (same site, disclosure below). A pattern that only ever sees valid input has told you nothing.&lt;br&gt;
The two-minute workflow, in order&lt;br&gt;
Read the parse error's line and column. Look left of that point.&lt;br&gt;
Check for trailing commas, single quotes and an undefined that should be null.&lt;br&gt;
Format the payload and read it as a tree, not a string.&lt;br&gt;
For a 401, decode the token locally and compare exp, iss and aud against what the API expects.&lt;br&gt;
Verify the signature on the server before you trust a single claim.&lt;br&gt;
Do it in that order and most "the API is broken" reports resolve into one tired character in the wrong place. It is rarely the API. It is almost never the API.&lt;br&gt;
Disclosure: I build Xplosole, the site behind the three tools linked above. The tutorial works without them — every step has a local-code route, which for real tokens is the route you should take.&lt;/p&gt;

&lt;p&gt;URLs verified live — 24 Oct 2026 (all HTTP 200)&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/pdf/pdf-merger" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/pdf/pdf-merger&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/image/background-remover" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/image/background-remover&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/image/image-compressor" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/image/image-compressor&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/finance/invoice-generator" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/finance/invoice-generator&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/text/json-formatter" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/text/json-formatter&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.xplosole.com/tools/developer/regex-tester" rel="noopener noreferrer"&gt;https://www.xplosole.com/tools/developer/regex-tester&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>"Compressing a PDF to a Target Size in the Browser", a developer write-up with code from the site's own compressor</title>
      <dc:creator>Mubashir Ali</dc:creator>
      <pubDate>Tue, 06 Oct 2026 16:05:57 +0000</pubDate>
      <link>https://dev.to/mubashir_ali_d95e1c5d09ff/compressing-a-pdf-to-a-target-size-in-the-browser-a-developer-write-up-with-code-from-the-sites-1gp6</link>
      <guid>https://dev.to/mubashir_ali_d95e1c5d09ff/compressing-a-pdf-to-a-target-size-in-the-browser-a-developer-write-up-with-code-from-the-sites-1gp6</guid>
      <description>&lt;p&gt;an exact target file size (e.g., &amp;lt; 2 MB) using WebAssembly, PDF.js, and Canvas?In this write-up, we’ll explore how browser-based PDF compression works, why reaching a specific target size requires a dynamic binary search loop, and how to build a client-side compressor step by step.Target Keywords &amp;amp; SEO StrategyPrimary Keywords: In-browser PDF compression, compress PDF client-side, PDF.js compress PDF to target size, pdf-lib browser compression.Secondary Keywords: Client-side document processing, JavaScript canvas PDF compression, web worker PDF processing.The Architecture: How In-Browser PDF Compression WorksA PDF is essentially a container. While it can contain vector graphics and text streams, scanned documents and image-heavy PDFs make up over 80% of oversized files. The most effective way to shrink a PDF in the browser without altering text layout is to re-encode its raster images at lower resolutions and higher JPEG compression ratios.Here is the high-level workflow:[ Input PDF File ]&lt;br&gt;
       │&lt;br&gt;
       ▼&lt;br&gt;
[ PDF.js / pdf-lib ] ──► Extract &amp;amp; Render Pages to HTML5 Canvas&lt;br&gt;
       │&lt;br&gt;
       ▼&lt;br&gt;
[ Dynamic Compression Loop (Binary Search) ]&lt;br&gt;
  ├── Adjust Image Quality (0.1 – 1.0)&lt;br&gt;
  ├── Adjust Scale / DPI (e.g., 1.0x, 0.75x)&lt;br&gt;
  └── Render Page Frame to JPEG Blob&lt;br&gt;
       │&lt;br&gt;
       ▼&lt;br&gt;
[ Assemble New PDF with pdf-lib ]&lt;br&gt;
       │&lt;br&gt;
       ▼&lt;br&gt;
[ Final Size &amp;lt;= Target Size? ] ──YES──► [ Output Compressed PDF ]&lt;br&gt;
       │&lt;br&gt;
      NO (Refine Quality / Downscale)&lt;br&gt;
Step 1: Loading PDF Pages onto HTML5 CanvasWe use PDF.js to render each PDF page into a hidden standard  element. From the canvas, we can export JPEG image buffers with variable quality levels.JavaScriptimport * as pdfjsLib from 'pdfjs-dist';&lt;/p&gt;

&lt;p&gt;// Set up worker&lt;br&gt;
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.mjs';&lt;/p&gt;

&lt;p&gt;async function renderPageToCanvas(pdfDoc, pageNum, scale = 1.0) {&lt;br&gt;
  const page = await pdfDoc.getPage(pageNum);&lt;br&gt;
  const viewport = page.getViewport({ scale });&lt;/p&gt;

&lt;p&gt;const canvas = document.createElement('canvas');&lt;br&gt;
  const context = canvas.getContext('2d');&lt;br&gt;
  canvas.height = viewport.height;&lt;br&gt;
  canvas.width = viewport.width;&lt;/p&gt;

&lt;p&gt;await page.render({&lt;br&gt;
    canvasContext: context,&lt;br&gt;
    viewport: viewport,&lt;br&gt;
  }).promise;&lt;/p&gt;

&lt;p&gt;return canvas;&lt;br&gt;
}&lt;br&gt;
Step 2: The Target-Size Search AlgorithmStandard compressors ask the user for a static setting (e.g., "Low", "Medium", "High"). But if a user specifically requests "Make this PDF under 2 MB", a static setting might produce 2.4 MB (too big) or 400 KB (unnecessarily degraded quality).To hit a target size accurately, we implement a Binary Search Algorithm over the image quality range [0.1, 0.95]:JavaScriptimport { PDFDocument } from 'pdf-lib';&lt;/p&gt;

&lt;p&gt;async function compressPDFToTargetSize(fileBuffer, targetSizeBytes, maxPasses = 5) {&lt;br&gt;
  const pdfDoc = await pdfjsLib.getDocument({ data: fileBuffer }).promise;&lt;br&gt;
  const pageCount = pdfDoc.numPages;&lt;/p&gt;

&lt;p&gt;let minQuality = 0.1;&lt;br&gt;
  let maxQuality = 0.95;&lt;br&gt;
  let bestBuffer = null;&lt;br&gt;
  let scale = 1.0;&lt;/p&gt;

&lt;p&gt;for (let pass = 0; pass &amp;lt; maxPasses; pass++) {&lt;br&gt;
    const currentQuality = (minQuality + maxQuality) / 2;&lt;br&gt;
    const newPdf = await PDFDocument.create();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (let i = 1; i &amp;lt;= pageCount; i++) {
  const canvas = await renderPageToCanvas(pdfDoc, i, scale);

  // Export canvas frame as JPEG image with target quality factor
  const jpegDataUrl = canvas.toDataURL('image/jpeg', currentQuality);
  const jpegImage = await newPdf.embedJpg(jpegDataUrl);

  const page = newPdf.addPage([canvas.width, canvas.height]);
  page.drawImage(jpegImage, {
    x: 0,
    y: 0,
    width: canvas.width,
    height: canvas.height,
  });
}

const outputPdfBytes = await newPdf.save();
const currentSize = outputPdfBytes.byteLength;

console.log(`Pass ${pass + 1}: Quality=${currentQuality.toFixed(2)}, Size=${(currentSize / 1024 / 1024).toFixed(2)} MB`);

if (currentSize &amp;lt;= targetSizeBytes) {
  bestBuffer = outputPdfBytes;
  // Try to get higher visual quality if room allows
  minQuality = currentQuality; 
} else {
  // Too large, decrease quality threshold
  maxQuality = currentQuality;
}

// Edge case: If quality is already low and file is still too big, downscale dimensions
if (maxQuality &amp;lt; 0.25 &amp;amp;&amp;amp; currentSize &amp;gt; targetSizeBytes) {
  scale *= 0.8;
  minQuality = 0.1;
  maxQuality = 0.8;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;return bestBuffer || (await newPdf.save());&lt;br&gt;
}&lt;br&gt;
Key Performance &amp;amp; Memory OptimizationsProcessing high-resolution PDFs entirely in the browser main thread can lead to UI freezes or out-of-memory errors on mobile devices. Here are key performance strategies:1. Offload Heavy Work to Web WorkersRun PDF rendering and page embedding inside a Web Worker or offscreen canvas context. This keeps the UI thread responsive and smooth at 60 FPS.2. Clean Up DOM ObjectsAlways release canvas objects and image memory explicitly to prevent memory leaks during multi-pass compression loops:JavaScriptcanvas.width = 0;&lt;br&gt;
canvas.height = 0;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Handle Hybrid PDFs GracefullyIf a PDF contains crisp vector text mixed with photos, re-rasterizing every page turns sharp text into bitmaps. A hybrid approach checks stream objects:For image streams: Downsample images directly using pdf-lib.For scanned pages: Render full page frames to .Benefits of Client-Side PDF CompressionFeatureServer-Side ProcessingClient-Side (Browser) ProcessingPrivacy &amp;amp; SecurityFiles uploaded to remote servers100% private; zero bytes leave deviceInfrastructure CostsHigh CPU &amp;amp; bandwidth expenses$0 server footprintSpeedNetwork transfer bottleneckInstant local processing speedOffline SupportRequires continuous internetWorks offline via Progressive Web App (PWA)ConclusionBy combining PDF.js, pdf-lib, and an adaptive binary search algorithm, web applications can achieve exact-target PDF compression completely on the client side. This pattern delivers fast, privacy-preserving performance while reducing backend costs to zero.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>pdf</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Don't Get Math Wrong: Building a Lightweight Multi-Tier Discount Engine</title>
      <dc:creator>Mubashir Ali</dc:creator>
      <pubDate>Fri, 02 Oct 2026 15:12:52 +0000</pubDate>
      <link>https://dev.to/mubashir_ali_d95e1c5d09ff/dont-get-math-wrong-building-a-lightweight-multi-tier-discount-engine-4jc2</link>
      <guid>https://dev.to/mubashir_ali_d95e1c5d09ff/dont-get-math-wrong-building-a-lightweight-multi-tier-discount-engine-4jc2</guid>
      <description>&lt;h1&gt;
  
  
  How I Solved the "Stacked Discount" Logic Problem in Next.js
&lt;/h1&gt;

&lt;p&gt;Retailers love running promotions like &lt;strong&gt;"20% OFF — Plus an Extra 10% at Checkout!"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To most consumers, mental math translates this into $20\% + 10\% = 30\%$ off. But computationally and financially, compound (or stacked) discounts don't work linearly. Each consecutive percentage reduction applies to the &lt;em&gt;remainder&lt;/em&gt; of the previous price, not the base original price.&lt;/p&gt;

&lt;p&gt;When building a utility tool suite for &lt;strong&gt;&lt;a href="https://www.xplosole.com/tools/calculators/discount-calculator" rel="noopener noreferrer"&gt;xplosole&lt;/a&gt;&lt;/strong&gt;, I needed to design a flexible engine that could handle single markdowns, reverse percentages, and arbitrary multi-tier stacked discounts cleanly in the browser.&lt;/p&gt;

&lt;p&gt;Here is how the logic works and how to implement it efficiently.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
