Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
FREEDOM251

Freedom251 › Blog › API Development

How to Add XP and Badge Rules to a Choose-Your-Own-Adventure API in NestJS

By Freedom251 Team · · 4 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To turn stored story progress into game mechanics in NestJS, keep three jobs separate: persist the player’s progress, calculate level from total XP, and award badges only when the player does not already have them. In this installment of the Grimoire API series, the choice’s existing xpReward and optional badgeUnlocked fields begin driving those behaviors.

That division keeps ProgressService focused on progress and gives each game rule a clear place to change and test. The level formula and badge flow below describe the tutorial’s implementation, not universal game-design standards.

As an Amazon Associate I earn from qualifying purchases.

What changes when story choices become game mechanics?

Part 1 of the Grimoire API series established a validated, read-only endpoint; Part 2 added player progress persistence. Part 3 connects the reward metadata already attached to story choices to player state. Choosing an available option now adds its XP reward, updates the current page, saves progress, and checks the destination page for a badge award.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rather than adding XP calculations and badge-specific conditionals to ProgressService, the tutorial separates the rules into dedicated services. That follows NestJS’s provider model: controllers handle HTTP requests and delegate application logic to providers, which are wired through dependency injection. See the NestJS controllers documentation and providers documentation.

How does the tutorial calculate a level from XP?

The tutorial’s XpService derives a player’s level from total XP using a square-threshold formula: the threshold for level N is N² × 100 XP. Under that project-specific rule, level 2 starts at 400 XP and level 3 starts at 900 XP. The tutorial’s level-1 example places the player below 400 XP.

The service also provides an xpForNextLevel value for the next threshold. Since level can be derived from total XP, there is no need to persist a separate level value that could drift out of sync with XP. This formula is the tutorial author’s chosen progression curve; it is not an industry standard.

Why keep XP calculation independent?

XP-to-level conversion is a calculation, not a database operation. Keeping it pure means it can be tested against boundary values without setting up persistence: for example, verify the result immediately below and at each threshold. It also avoids coupling the progression curve to the code that saves player records.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How does the API prevent duplicate badge awards?

The tutorial’s BadgesService checks for an existing player-badge record before creating one. If the player already has the badge, the service returns without issuing it again. The progress flow invokes this check for a badge associated with the destination page after saving the updated progress.

That read-then-create check handles repeated calls in the illustrated sequential flow, but it is not by itself safe against concurrent requests: two requests can both observe no record and then attempt to insert one. A production database should enforce uniqueness for the player-and-badge pair, or use an equivalent concurrency-safe mechanism. The tutorial does not demonstrate such a constraint.

What happens when a player advances?

  1. Read and validate the requested choice identifier at the HTTP boundary.
  2. Confirm that the requested choice is actually available on the player’s current page; a valid identifier alone does not make a choice available in that state.
  3. Add the selected option’s xpReward to the player’s total XP and update the current page to the option’s destination.
  4. Save the updated progress.
  5. Check the destination page’s optional badgeUnlocked value and award the badge if the player does not already hold it.

The order matters: the badge rule in this flow belongs to the destination page reached by the choice, and it runs after the progress update is saved. The article also introduces an InvalidChoiceException derived from NestJS’s BadRequestException. That gives the domain failure a more precise name while retaining a 400-class HTTP response.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Where should choice validation happen?

Validate and normalize caller-supplied choice identifiers at the request boundary, then let the game logic check whether that choice belongs to the player’s current page. NestJS documents both ValidationPipe for decorated DTO classes and StandardSchemaValidationPipe for compatible validation schemas, as well as parsing pipes for individual values. See the NestJS validation documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

These checks answer different questions: boundary validation establishes that the input has the expected shape, while the game service enforces the state-dependent rule that the player may take only an available choice. A custom invalid-choice exception makes that latter failure distinguishable in the application code.

What belongs in the next installment, not this game-rule layer?

The series uses a temporary default player in this stage; its next installment moves progress to authenticated accounts. Authentication and authorization therefore remain a separate concern from calculating XP or deciding whether a badge is already owned. NestJS guards are designed for request authorization checks using execution context, and run after middleware but before interceptors or pipes. See the NestJS guards documentation.

The tutorial uses PostgreSQL with TypeORM for persistence, but neither is required by NestJS or by these game rules. NestJS documents integrations for TypeORM, Drizzle, Mongoose, Prisma, MikroORM, and Sequelize. See the NestJS database documentation. The XP calculation can stay independent of whichever persistence integration stores player progress and badge records.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

API DevelopmentBadgesgame logicNestJSTypeScriptXP progression

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.