<?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: Veaceslav</title>
    <description>The latest articles on DEV Community by Veaceslav (@veaceslav).</description>
    <link>https://dev.to/veaceslav</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%2F626211%2Fa82c2dbb-0d46-4384-8dc7-e440b8114ef7.jpg</url>
      <title>DEV Community: Veaceslav</title>
      <link>https://dev.to/veaceslav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veaceslav"/>
    <language>en</language>
    <item>
      <title>A 40% Faster Fault-Detection Pipeline Without WebSockets — the Polling Trick We Shipped</title>
      <dc:creator>Veaceslav</dc:creator>
      <pubDate>Tue, 06 Oct 2026 07:01:21 +0000</pubDate>
      <link>https://dev.to/veaceslav/a-40-faster-fault-detection-pipeline-without-websockets-the-polling-trick-we-shipped-e28</link>
      <guid>https://dev.to/veaceslav/a-40-faster-fault-detection-pipeline-without-websockets-the-polling-trick-we-shipped-e28</guid>
      <description>&lt;p&gt;title: A 40% Faster Fault-Detection Pipeline Without WebSockets — the Polling Trick We Shipped&lt;br&gt;
published: false&lt;br&gt;
description: Real-time monitoring for 1,000+ network nodes over plain REST — a change-flag endpoint, RTK Query cache discipline, and an operator map that shows the causal chain, not the noise.&lt;br&gt;
tags: react, rtkquery, architecture&lt;/p&gt;

&lt;p&gt;This is the dev.to version of a story I first published as a LinkedIn article — restructured for a code-first audience. It's a real production system: real-time monitoring for 1,000+ GSM service stations, built for an international partner. And one honest constraint at its core: &lt;strong&gt;we could not touch the backend protocol.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The ticket-queue blind spot
&lt;/h2&gt;

&lt;p&gt;The worst part of a monitoring outage isn't the failure itself. It's not being able to tell the difference between two situations that look identical on a ticket queue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a node recovered &lt;strong&gt;by itself&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;a node is &lt;strong&gt;still broken&lt;/strong&gt;, but nobody noticed yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our network engineers — a field team plus a back office — lived inside that ambiguity for years. The back office created tickets from Grafana data, and a ticket meant &lt;em&gt;wait&lt;/em&gt;: someone opened it, someone processed it, and a certain amount of time always passed before the incident became visible to people who could act on it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The constraint that reframed everything
&lt;/h2&gt;

&lt;p&gt;I checked the obvious answers first: whether the Romanian backend team could support &lt;strong&gt;WebSockets or SSE&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They couldn't. The backend was a monolith, and a deep refactor to introduce a new transport protocol was off the table. Not "hard" — &lt;em&gt;off the table&lt;/em&gt;. Different project, different budget, different risk.&lt;/p&gt;

&lt;p&gt;If the client can't be &lt;em&gt;pushed&lt;/em&gt; updates, then maybe it can be &lt;em&gt;woken up&lt;/em&gt; cheaply.&lt;/p&gt;
&lt;h2&gt;
  
  
  The reframe: change the data granularity, not the protocol
&lt;/h2&gt;

&lt;p&gt;My proposal split the information flow into two layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A cheap change-flag.&lt;/strong&gt; The backend exposes a tiny flag/version field on an ordinary REST endpoint — a number that increments whenever anything relevant changes underneath. We designed this together: they implemented a small flags feature to my spec, I tracked it from the frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A heavy data pull.&lt;/strong&gt; The actual node states — statuses, incidents, telemetry the dashboard renders.&lt;/p&gt;

&lt;p&gt;The frontend polls &lt;strong&gt;only the flag&lt;/strong&gt; on a light interval. When the flag hasn't changed, the request costs nothing meaningful. When it has — RTK Query re-fetches the heavy data &lt;em&gt;and&lt;/em&gt; its cache is immediately invalidated and repopulated. The dashboard behaves as if it were pushed to, while under the hood everything is still plain REST.&lt;/p&gt;

&lt;p&gt;That's the whole trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naive polling:            [timer] → pull full payload → render   (every N seconds, always)
flag-driven polling:      [timer] → pull 1 number  ─┬─ unchanged → nothing
                                                    └─ changed   → invalidate → pull full payload → render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The RTK Query side: cache and refetch discipline
&lt;/h2&gt;

&lt;p&gt;Two things came out of the RTK Query migration itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caching.&lt;/strong&gt; Identical requests were served from cache instead of re-hitting the API — removing redundant traffic the legacy thunk-based store generated on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refetch discipline.&lt;/strong&gt; Refetches happened &lt;em&gt;on demand&lt;/em&gt; — flag changed, cache invalidated, window refocused — not on a dumb interval burning everyone's patience and the backend's CPU.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The polling setup in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// poll the flag only — light interval, e.g. 5s&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePollChangeFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// the heavy query is keyed on the flag value:&lt;/span&gt;
&lt;span class="c1"&gt;// flag moves → new cache key → automatic refetch + repopulate&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGetNodesQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The exact API shape was refactored a few times since — the pattern is the deliverable, not the signature. The heavy-fetch key follows the flag; refetch happens when and only when the flag moves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Showing pain, not data: the map
&lt;/h2&gt;

&lt;p&gt;Raw node counts are useless to a human operator. 1,000+ stations on one screen is noise.&lt;/p&gt;

&lt;p&gt;The stations had real topology: &lt;strong&gt;parent–child hierarchy&lt;/strong&gt;. A parent going down didn't just break itself — it degraded its children. So we drew the map differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the map shows &lt;strong&gt;only the affected subtree&lt;/strong&gt;, not the whole network;&lt;/li&gt;
&lt;li&gt;when a parent fails, its dependent children light up as &lt;em&gt;potentially damaged&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;in a typical impact window the map displayed &lt;strong&gt;around 400 nodes&lt;/strong&gt; — the ones potentially hurt through connectivity — instead of 1,000+.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An engineer opens the map and sees a causal chain, not a cloud of dots. And the back office, which had been hand-writing tickets out of Grafana, now pulled the incident structure — parent, children, order of failure — from a single source.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;We validated the improvement with two existing instruments: &lt;strong&gt;Grafana metrics&lt;/strong&gt; and &lt;strong&gt;the ticket system itself&lt;/strong&gt; (opening-to-resolution timestamps, across the before/after periods).&lt;/p&gt;

&lt;p&gt;Result: &lt;strong&gt;incident detection time improved by ~40%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being precise about what the number means — never defend a fuzzy metric in an interview: the &lt;em&gt;clock&lt;/em&gt; we shortened is "failure happens → failure is actionable" — previously dominated by ticket latency, now dominated by flag-poll latency. Nodes that self-recovered no longer polluted the queue and no longer &lt;em&gt;hid&lt;/em&gt; the ones that didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you can't change the protocol, change the granularity
&lt;/h2&gt;

&lt;p&gt;You don't always need WebSockets. You need the &lt;em&gt;cheapest possible signal that something changed&lt;/em&gt; — a version number, a hash, an etag — and a client disciplined enough to pull the real payload only when that signal moves. Pair it with cache-first architecture (RTK Query, React Query — whatever your stack provides) so heavy data is fetched rarely and reused often.&lt;/p&gt;

&lt;p&gt;And when you present it to a human operator: don't show them the network. Show them the pain — the affected subtree, the causal chain. A map of 400 hurting nodes is worth more than a map of 1,000 quiet ones.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with React, TypeScript, RTK Query — and with backend partners who were straightforward about what the system could and couldn't give us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I write about React patterns and the engineering behind my AI tooling — &lt;a href="https://t.me/novamind_hub" rel="noopener noreferrer"&gt;building in public on Telegram&lt;/a&gt;, the LinkedIn version of this story is &lt;a href="https://www.linkedin.com/pulse/how-i-cut-fault-detection-time-40-1000-node-network-without-bezusco-fmpgf" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How do you do "real-time" when the protocol won't budge — flag-polling, SSE workarounds, or plain polling? Curious what others landed on.&lt;/p&gt;

</description>
      <category>react</category>
      <category>rtkquery</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop prompting for valid JSON: an LLM output layer that does not break</title>
      <dc:creator>Veaceslav</dc:creator>
      <pubDate>Tue, 29 Sep 2026 08:45:52 +0000</pubDate>
      <link>https://dev.to/veaceslav/stop-prompting-for-valid-json-an-llm-output-layer-that-does-not-break-5264</link>
      <guid>https://dev.to/veaceslav/stop-prompting-for-valid-json-an-llm-output-layer-that-does-not-break-5264</guid>
      <description>&lt;p&gt;I'm building JobCopilot: a small Python agent that reads a job description, compares it to my resume, and tells me what skills I'm missing. Like a skeptical recruiter in a terminal.&lt;/p&gt;

&lt;p&gt;I want the agent's brain to be an LLM call. But my program needs an &lt;strong&gt;answer in a defined shape&lt;/strong&gt; — like a form with three fields — not a wall of prose. And there's the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem in one picture
&lt;/h2&gt;

&lt;p&gt;I asked the model: &lt;em&gt;return only raw JSON, no explanation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It agreed politely. Then it sent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sure! Here's the breakdown in raw JSON:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "verdict": "partial",
  "match_score": 68,
  "missing_skills": ["FastAPI", "Alemdic"]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;See it? The JSON is wrapped in a &lt;strong&gt;hello + a code fence&lt;/strong&gt; (the JSON-fence block). Python's &lt;code&gt;json.loads()&lt;/code&gt; says there's no JSON there at all — it sees the words "Sure! Here's..." and dies. If I ask the model again, it does the same thing slightly differently. You can write "I really mean it this time!!!" in the prompt. It does not help. I tried for an hour.&lt;/p&gt;

&lt;p&gt;And when the model &lt;em&gt;does&lt;/em&gt; send clean JSON one night, there are three more ways it betrays you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wrong key names&lt;/strong&gt; — it sends &lt;code&gt;skills_missing&lt;/code&gt; today and &lt;code&gt;missing_skills&lt;/code&gt; tomorrow. Your code searched for one of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong types&lt;/strong&gt; — the score arrives as &lt;code&gt;87.5&lt;/code&gt;, or even &lt;code&gt;105&lt;/code&gt;, when your program expects an integer up to 100.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct format, wrong content&lt;/strong&gt; — &lt;code&gt;{"verdict": "no", "missing_skills": []}&lt;/code&gt;: shape is perfect, logic is nonsense.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"A prompt is a suggestion. Only code is a contract." So instead of fighting the prompt, I built a small &lt;strong&gt;validation layer&lt;/strong&gt; — a wall between the model and my program. Whatever comes out of the model, my program either receives a valid form or a precise error report. Here it is, in four steps any beginner can follow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Unwrap the JSON (8 lines of Python)
&lt;/h2&gt;

&lt;p&gt;The model keeps wrapping JSON in JSON-fence fences no matter what I write in the prompt. Fine — let the code unwrap it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# match (.*?) between ```
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;newlines&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s DOTALL
_FENCE_RE = re.compile(r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
{% raw %}
```(?:json)?\s*(.*?)\s*```

&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, re.DOTALL)


def extract_json(raw: str) -&amp;gt; dict:
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Takes the model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Returns&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    try:
        return json.loads(raw)      # best case: it was already clean JSON
    except json.JSONDecodeError:
        pass                        # no? look for the fence below...

    match = _FENCE_RE.search(raw)
    if match is None:
        raise ValueError(f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no JSON found in: {raw[:200]}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)

    loaded = json.loads(match.group(1))
    if not isinstance(loaded, dict):
        raise ValueError(f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected a JSON object, got {type(loaded).__name__}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)
    return loaded


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This function tries the honest path first (&lt;code&gt;json.loads(raw)&lt;/code&gt;), and if the text has chatty greetings, it grabs whatever sits between the triple-backtick marks and parses THAT.&lt;/p&gt;

&lt;p&gt;Three small details that each cost me an evening of confusion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;re.DOTALL&lt;/code&gt;&lt;/strong&gt; — without it, the dot in my regex stops at the first line break, and the fenced JSON spans many lines. My first version matched nothing, silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;(?:json)?&lt;/code&gt;&lt;/strong&gt; — the model writes the language hint or a bare triple-backtick fence. This part makes the hint optional, so both work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;search&lt;/code&gt;, not &lt;code&gt;fullmatch&lt;/code&gt;&lt;/strong&gt; — because there's prose around the JSON: "Sure!", intro sentences, sometimes a closing joke.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One bonus detail I did not know: in Python, &lt;code&gt;JSONDecodeError&lt;/code&gt; is a subclass of &lt;code&gt;ValueError&lt;/code&gt;. That means &lt;code&gt;except ValueError&lt;/code&gt; catches both — I'll use this in Step 3.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Check the form field by field (pydantic)
&lt;/h2&gt;

&lt;p&gt;The JSON parses now. But parsing is not validating: &lt;code&gt;{"match_score": 105}&lt;/code&gt; parses perfectly fine. So the next layer checks the &lt;em&gt;shape of the data&lt;/em&gt; field by field:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
from typing import Literal
from pydantic import BaseModel, Field, field_validator, model_validator


class JobMatch(BaseModel):
    verdict: Literal["strong", "partial", "no"]   # ONLY these three values
    match_score: int = Field(ge=0, le=100)         # int, 0..100, nothing else
    missing_skills: list[str]                      # a list of strings

    @field_validator("missing_skills")
    @classmethod
    def clean_skills(cls, skills):
        # model sent ["React", "react", "  CSS ", ""] once. Clean it:
        seen = set()
        out = []
        for s in skills:
            key = s.strip().lower()
            if not key or key in seen:
                continue
            seen.add(key)
            out.append(s.strip())
        return out

    @model_validator(mode="after")
    def skeptical_check(self):
        # "no" verdict with empty list = the model gave up
        if self.verdict == "no" and not self.missing_skills:
            raise ValueError("verdict 'no' but no missing skills — model gave up")
        return self


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Read this as a &lt;strong&gt;form with three fields&lt;/strong&gt;, each field guarded:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The verdict must be exactly &lt;code&gt;"strong"&lt;/code&gt;, &lt;code&gt;"partial"&lt;/code&gt; or &lt;code&gt;"no"&lt;/code&gt; — the model once answered &lt;code&gt;"maybe"&lt;/code&gt;; with &lt;code&gt;Literal&lt;/code&gt; that becomes impossible.&lt;/li&gt;
&lt;li&gt;The score must be an integer between 0 and 100. The model sent &lt;code&gt;105&lt;/code&gt; once. Now that is impossible too.&lt;/li&gt;
&lt;li&gt;The skills list gets cleaned: duplicated &lt;code&gt;"React"&lt;/code&gt; and &lt;code&gt;"react"&lt;/code&gt; collapse into one, empty strings are dropped.&lt;/li&gt;
&lt;li&gt;The final check catches nonsense logic: if the verdict says "no match" but lists zero missing skills, the model gave up instead of analyzing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a value breaks a rule, pydantic produces an error that is &lt;em&gt;specific&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
match_score: Input should be a valid integer, got a number with a fractional
             part [type=int_from_float, input_value=87.5]
verdict:     Input should be 'strong', 'partial' or 'no'
             [type=literal_error, input_value='maybe']


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Notice we threw out about thirty lines of handwritten &lt;code&gt;if "verdict" not in data: ...&lt;/code&gt; code. The declarative model does the same job, and the errors are not "value error" mush — they name the field, the problem and the offending value.&lt;/p&gt;

&lt;p&gt;One honest limit: this layer guarantees the &lt;strong&gt;shape&lt;/strong&gt;, not the truth. It will not catch &lt;code&gt;"Alemdic"&lt;/code&gt; — a misspelling of &lt;code&gt;Alembic&lt;/code&gt; that is perfectly well-formed JSON. Catching wrong-but-valid content is the job of an eval set, and that is the next article in this series.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Return an error as a &lt;em&gt;result&lt;/em&gt;, not a crash
&lt;/h2&gt;

&lt;p&gt;Now the top layer. My rule: the model can fail, and the program must handle that failure like any other value. If a function raises an exception, the caller has to catch it — and if the caller forgets, the whole program dies. If a function &lt;em&gt;returns&lt;/em&gt; the error, the caller just decides what to do.&lt;/p&gt;

&lt;p&gt;If you write TypeScript, you know &lt;code&gt;zod.safeParse()&lt;/code&gt; — it returns a result object with a status. We can build the same in Python with two dataclasses:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
from dataclasses import dataclass


@dataclass
class ParseOk:
    match: JobMatch                       # valid data


@dataclass
class ParseError:
    stage: str      # "json" — couldn't extract, or "schema" — invalid
    message: str    # exact reason, for me AND for the next LLM call


def parse_match(raw: str) -&amp;gt; ParseOk | ParseError:
    try:
        data = extract_json(raw)          # step 1
    except ValueError as exc:             # JSONDecodeError IS a ValueError
        return ParseError(stage="json", message=str(exc))

    try:
        return ParseOk(match=JobMatch.model_validate(data))   # step 2
    except ValidationError as exc:
        return ParseError(stage="schema", message=str(exc))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The type &lt;code&gt;ParseOk | ParseError&lt;/code&gt; works like a union type from TypeScript: every result is definitely one or the other, and the caller checks with &lt;code&gt;isinstance(result, ParseOk)&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: The retry — give the exact error back to the model
&lt;/h2&gt;

&lt;p&gt;There is one more trick with &lt;code&gt;ParseError.message&lt;/code&gt;. The model &lt;strong&gt;can repair itself&lt;/strong&gt; if you tell it exactly what failed. "It failed" teaches nothing. But:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;match_score: Input should be a valid integer, got a number with a fractional part [type=int_from_float, input_value=87.5]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...the model fixes almost every time. So the retry loop feeds the exact pydantic error back into the next call:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
FIX_PROMPT = """Below was your previous output and the validation error.
Return corrected JSON only.

Previous output:
{previous}

Validation error:
{error}"""


def structured_match(chat, jd_text: str, max_retries: int = 2):
    prompt = MATCH_PROMPT.format(job=jd_text)

    result = None
    for attempt in range(1 + max_retries):
        raw = chat(prompt)
        result = parse_match(raw)
        if isinstance(result, ParseOk):
            return result
        prompt = FIX_PROMPT.format(previous=raw, error=result.message)

    return result   # after max retries: ParseError as data — never None


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retry at most twice, then give up — but give up &lt;em&gt;as a result&lt;/em&gt;, not a crash. The caller decides: log it, message the user, or end the run.&lt;/p&gt;

&lt;p&gt;(A quick note about frameworks: many LLM providers have native JSON mode. If yours does, use it — it kills most fence problems at the root. But keep the pydantic layer anyway: native mode guarantees shape, not logic, and &lt;code&gt;match_score &amp;lt;= 100&lt;/code&gt; survives on every provider.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole picture
&lt;/h2&gt;

&lt;p&gt;Four small pieces, each doing one job:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unwrap&lt;/strong&gt; the code fence — &lt;code&gt;extract_json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; field by field — the &lt;code&gt;JobMatch&lt;/code&gt; pydantic model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convert&lt;/strong&gt; errors into typed data — &lt;code&gt;ParseOk&lt;/code&gt; / &lt;code&gt;ParseError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry&lt;/strong&gt; with the exact error fed back — &lt;code&gt;structured_match&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In one sentence: &lt;strong&gt;code, not the prompt, decides when the model's output is good.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two things I took from building it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A prompt is a suggestion; a pydantic model is a contract.&lt;/li&gt;
&lt;li&gt;"Errors as data" sounds fancy but is 15 lines: two dataclasses and an &lt;code&gt;isinstance&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next Tuesday, part 2: retrieval — embeddings with &lt;code&gt;fastembed&lt;/code&gt;, cosine similarity computed by hand with numpy, and why my top-8 retrieval stopped discriminating on a resume of 20 chunks.&lt;/p&gt;

&lt;p&gt;Follow if you want to see the rest of the copilot get built — including the parts that will break. Build log + daily code: &lt;a href="https://t.me/novamind_hub" rel="noopener noreferrer"&gt;t.me/novamind_hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How do you harden LLM JSON in your projects — native JSON mode, validators, or retries? I'm still collecting failure modes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>llm</category>
      <category>pydantic</category>
    </item>
    <item>
      <title>Stop bloating your AGENTS.md: reference your conventions instead of pasting them</title>
      <dc:creator>Veaceslav</dc:creator>
      <pubDate>Tue, 22 Sep 2026 17:28:35 +0000</pubDate>
      <link>https://dev.to/veaceslav/stop-bloating-your-agentsmd-reference-your-conventions-instead-of-pasting-them-b2i</link>
      <guid>https://dev.to/veaceslav/stop-bloating-your-agentsmd-reference-your-conventions-instead-of-pasting-them-b2i</guid>
      <description>&lt;p&gt;Stop bloating your AGENTS.md: reference your conventions instead of pasting them&lt;/p&gt;

&lt;p&gt;Most AGENTS.md files I see are bloated walls of text. Here is the problem: your agent reads this file at the start of every task. Everything you put in there is passed to the LLM with every prompt. A 400-line AGENTS.md burns context tokens on every single action — even when the agent only needs one rule from it.&lt;/p&gt;

&lt;p&gt;The fix is simple: keep AGENTS.md as a thin index, move the details into separate docs, and reference them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in practice
&lt;/h2&gt;

&lt;p&gt;Here is the real AGENTS.md from my current project (invoice management SPA):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AGENTS.md&lt;/span&gt;

&lt;span class="gu"&gt;## Project&lt;/span&gt;
IOD Invoice — invoice management SPA.
React 18 + TypeScript + Vite, Ant Design 6, Redux Toolkit (RTK Query),
styled-components, i18next (EN/RO), OIDC/Keycloak auth.

&lt;span class="gu"&gt;## Commands (npm; Node 18+)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run dev`&lt;/span&gt; — dev server
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run build`&lt;/span&gt; — tsc + production build
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run test`&lt;/span&gt; — vitest watch; &lt;span class="sb"&gt;`npm run test:ci`&lt;/span&gt; — run + coverage
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt; — eslint, zero warnings tolerated

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
See &lt;span class="sb"&gt;`docs/architecture.md`&lt;/span&gt;

&lt;span class="gu"&gt;## TypeScript Conventions&lt;/span&gt;
See &lt;span class="sb"&gt;`docs/typescript-conventions.md`&lt;/span&gt;

&lt;span class="gu"&gt;## Style&lt;/span&gt;
See &lt;span class="sb"&gt;`docs/style-conventions.md`&lt;/span&gt;

&lt;span class="gu"&gt;## React Components&lt;/span&gt;
See &lt;span class="sb"&gt;`docs/react-components.md`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in &lt;code&gt;docs/&lt;/code&gt; live the actual conventions — architecture decisions, TypeScript rules, styling rules, component patterns.&lt;/p&gt;

&lt;p&gt;The agent loads the index plus &lt;strong&gt;only the docs it actually needs for the current task&lt;/strong&gt;. A TypeScript task? It reads one conventions file, not your entire wiki. That is the whole trick: your conventions stay detailed, your context stays clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  One gotcha: the CLAUDE.md twin problem
&lt;/h2&gt;

&lt;p&gt;Many of us run more than one agent. Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, everything else reads &lt;code&gt;AGENTS.md&lt;/code&gt; — and the two files slowly drift apart until each agent works from different rules.&lt;/p&gt;

&lt;p&gt;The fix is a symlink, so both names point at one file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS / Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; AGENTS.md CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows (order is reversed):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;mklink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CLAUDE.md&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AGENTS.md&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit one file, both agents see the update. No drift, no duplicated maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: monorepos want nested AGENTS.md files
&lt;/h2&gt;

&lt;p&gt;In a monorepo, put an &lt;code&gt;AGENTS.md&lt;/code&gt; in each subfolder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
├── AGENTS.md          # shared rules
├── client/
│   └── AGENTS.md      # frontend-specific rules
└── backend/
    └── AGENTS.md      # backend-specific rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working inside &lt;code&gt;client/&lt;/code&gt;, the agent loads &lt;strong&gt;both&lt;/strong&gt; — the root file and the subfolder file. Shared conventions live once, stack-specific rules live next to the code they govern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Your AGENTS.md is not documentation — it is a &lt;strong&gt;context budget&lt;/strong&gt;. Every line in it is paid on every task. Index + references keeps the rules detailed and the context cheap.&lt;/p&gt;




&lt;p&gt;If this was useful:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;More React + AI tips daily&lt;/strong&gt; in my Telegram channel: t.me/novamind_hub&lt;/p&gt;

&lt;p&gt;I'm building NovaMind — an AI assistant in Telegram that debugs errors from screenshots (the reason my AGENTS.md files matter so much): t.me/mindrorgebot_bot — 30 messages/day free.&lt;/p&gt;

&lt;p&gt;Building with coding agents? Drop your biggest AGENTS.md pain in the comments — curious which conventions you enforce first 👇&lt;/p&gt;

</description>
      <category>agentsmd</category>
      <category>aiengineering</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Handling Dynamic Role Names in Different Environments with useRoleManagement Hook (Part 2)</title>
      <dc:creator>Veaceslav</dc:creator>
      <pubDate>Mon, 02 Sep 2024 07:13:46 +0000</pubDate>
      <link>https://dev.to/veaceslav/handling-dynamic-role-names-in-different-environments-with-userolemanagement-hook-part-2-1jca</link>
      <guid>https://dev.to/veaceslav/handling-dynamic-role-names-in-different-environments-with-userolemanagement-hook-part-2-1jca</guid>
      <description>&lt;p&gt;In the first part of this series, we explored the foundation of implementing role-based access control in React using the useRoleManagement hook. If you haven't read it yet, you can check it out here &lt;br&gt;
&lt;a href="https://dev.to/veaceslav/implementing-role-based-access-control-in-react-a-deep-dive-into-userolemanagement-hook-129i"&gt;Implementing Role-Based Access Control in React: A Deep Dive into useRoleManagement Hook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this second part, we'll dive deeper into managing dynamic role names depending on different environments, such as staging and production. This is crucial for maintaining consistent and secure role management across various stages of development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When developing applications, it's common to have different role names or permissions configurations for various environments. For instance, you might use test or mock role names in development and real, production-ready role names in the production environment. Handling these dynamic role names correctly ensures that your application behaves consistently and securely across different stages of its lifecycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment-Specific Role Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To manage role names dynamically, we can leverage environment variables. These variables allow us to define different role keys for different environments, which can then be used to load the appropriate permissions configuration.&lt;/p&gt;

&lt;p&gt;Here’s how we can achieve this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define Environment Variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your .env.development and .env.production files, specify the role keys for each environment. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.env.development
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;VITE_ROLE_KEYS_MANAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Manager_Test&lt;/span&gt;
&lt;span class="nx"&gt;VITE_ROLE_KEYS_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;USER_Test&lt;/span&gt;
&lt;span class="nx"&gt;VITE_ROLE_KEYS_ADMIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Admin_Test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;.env.production
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;VITE_ROLE_KEYS_MANAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Prod_Manager&lt;/span&gt;
&lt;span class="nx"&gt;VITE_ROLE_KEYS_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Prod_USER&lt;/span&gt;
&lt;span class="nx"&gt;VITE_ROLE_KEYS_ADMIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;Prod_Admin&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This setup ensures that your application uses appropriate role names depending on the environment it’s running in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Update the permissions Object&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use these environment variables to define the permissions object dynamically. The permissions object maps role keys to specific permissions for each role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserPermissions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_ROLE_KEYS_MANAGER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other permissions&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_ROLE_KEYS_USER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other permissions&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_ROLE_KEYS_ADMIN&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other permissions&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The import.meta.env syntax allows you to access environment variables defined in your .env files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use the useRoleManagement Hook&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the first part of this series, we explored the foundation of role-based access control in React using the useRoleManagement hook. If you haven't read it yet, you can check it out &lt;a href="https://dev.to/veaceslav/implementing-role-based-access-control-in-react-a-deep-dive-into-userolemanagement-hook-129i"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The useRoleManagement hook extracts user roles from the decoded JWT token, maps them to permissions using the environment-specific role keys, and then returns the relevant permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useRoleManagement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLocation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Realm&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;decodedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtDecode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Realm&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid token:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;realm_access&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roleExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ifRoleExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rolePermissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;roleExists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/partners&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rolePermissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rolePermissions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By dynamically managing role names through environment variables, you can ensure that your application's role-based access control behaves consistently across different environments. This approach provides flexibility and helps maintain a clear separation between development and production configurations.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Implementing Role-Based Access Control in React: A Deep Dive into useRoleManagement Hook</title>
      <dc:creator>Veaceslav</dc:creator>
      <pubDate>Sun, 14 Jul 2024 18:09:08 +0000</pubDate>
      <link>https://dev.to/veaceslav/implementing-role-based-access-control-in-react-a-deep-dive-into-userolemanagement-hook-129i</link>
      <guid>https://dev.to/veaceslav/implementing-role-based-access-control-in-react-a-deep-dive-into-userolemanagement-hook-129i</guid>
      <description>&lt;p&gt;&lt;strong&gt;Intorduction&lt;/strong&gt;&lt;br&gt;
 Managing user roles and permissions is a critical aspect of web application security and functionality. It ensures that users have appropriate access to different parts of the application based on their roles. In this article, I'll walk you through the implementation of a custom useRoleManagement hook in React, which decodes a JWT token to manage user permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up&lt;/strong&gt;&lt;br&gt;
 Before diving into the implementation, let's set up the necessary tools and libraries. You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;jwt-decode for decoding JWT tokens&lt;/li&gt;
&lt;li&gt;react-router-dom for managing routing and extracting the current path
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install react-router-dom jwt-decode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;br&gt;
 Let's start by defining the interfaces for our permissions and JWT payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JwtPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jwtDecode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jwt-decode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLocation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Realm is for authentication mechanism like Keycloak&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Realm&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;JwtPayload&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;realm_access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Interface representing the user's permissions&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Permissions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;include&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Interface representing the user's permissions for various pages&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserPermissions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="c1"&gt;// More pages can be added here as needed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PermissionKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;UserPermissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we define the permissions for different roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;partners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// Define more pages&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;analyst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define permissions for analyst role&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define permissions for director role&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check if Role Exists&lt;/strong&gt;&lt;br&gt;
We need a function to check if a role exists and return the associated permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ifRoleExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get Permissions for the Role&lt;/strong&gt;&lt;br&gt;
This function retrieves the permissions for a given role and path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roleExists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserPermissions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withoutSlash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PermissionKeys&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;roleExists&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="nx"&gt;withoutSlash&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;pathName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;useRoleManagement Hook&lt;/strong&gt;&lt;br&gt;
Finally, we implement the useRoleManagement hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useRoleManagement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLocation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Realm&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;decodedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtDecode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Realm&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid token:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;realm_access&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roleExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ifRoleExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rolePermissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;roleExists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/partners&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rolePermissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rolePermissions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;deleteRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Usage Example&lt;/strong&gt;&lt;br&gt;
Here's how you can use the useRoleManagement hook in a React component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRoleManagement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./useRoleManagement&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteRow&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRoleManagement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Edit&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;deleteRow&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Delete&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
In this article, we've implemented a custom useRoleManagement hook in React to manage user permissions based on their roles. This approach ensures that your application remains secure and that users have the appropriate access based on their roles. Feel free to customize the permissions and roles according to your application's requirements.&lt;/p&gt;

&lt;p&gt;I hope you found this guide helpful. If you have any questions or suggestions, please leave a comment below!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>react</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
