<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Daniel Reguero Blog</title>
  <link>https://daniel.reguero.dev</link>
  <atom:link href="https://daniel.reguero.dev/feed.xml" rel="self" type="application/rss+xml" />
  <description>Daniel Reguero&apos;s personal blog and project log</description>
  <language>en-us</language>
  <lastBuildDate>Tue, 18 Aug 2026 13:55:18 GMT</lastBuildDate>
  <item>
    <title>Ten Common Security Mistakes AI-Generated Apps Keep Making</title>
    <link>https://daniel.reguero.dev/blogs/ten-common-security-mistakes-ai-generated-apps-make</link>
    <guid isPermaLink="true">https://daniel.reguero.dev/blogs/ten-common-security-mistakes-ai-generated-apps-make</guid>
    <pubDate>Wed, 19 Aug 2026 00:00:00 GMT</pubDate>
    <category>blogs</category>
    <description><![CDATA[These are the most common security mistakes I have seen AI-generated apps make.]]></description>
    <content:encoded><![CDATA[<p>Shipping an app has never been easier, and that&#x27;s a great thing. AI is giving people with interesting ideas the ability to build software without years of coding experience. But getting an app to work isn&#x27;t the same as making it safe to ship. I don&#x27;t think the answer is dismissing AI-built apps, or blindly trusting what AI produces. There&#x27;s a middle ground.</p>
<p>You don&#x27;t need to be a security expert to build with AI, but you do need to understand what can go wrong. Here are 10 common security mistakes AI-generated apps make, and at the end of this article, I&#x27;ll share a Claude skill I&#x27;ve been using to help catch them.</p>
<p>So let&#x27;s go down the list shall we?</p>
<h2>1. Broken Access Control</h2>
<p>At number one we have broken access control, which is not only the most common issue I have run into, it&#x27;s also OWASP&#x27;s number one: <a href="https://owasp.org/Top10/2021/A01_2021-Broken_Access_Control/">A01:2021 Broken Access Control</a>. In OWASP&#x27;s dataset, 94% of applications were tested for some form of broken access control, and it had over 318,000 occurrences, which is more than any other category. It&#x27;s <em>that</em> big of a problem!</p>
<p>It&#x27;s definitely the most serious issue. This can come in different ways and forms, but the most common versions of this that I have seen are: escalating roles from free to pro without paying and accessing/editing resources you don&#x27;t own.</p>
<h3>The Fix:</h3>
<p>Your server must check on every request that the authenticated user is actually allowed to touch the resource they&#x27;re asking for. Never trust the client to enforce this. But better yet, if your database can support policies, implement them. Supabase, for example, allows you to set <a href="https://supabase.com/docs/guides/database/postgres/row-level-security">Row Level Security</a>, which lets you write policies directly in Postgres that filter which rows each user can read or write (typically keyed off <code>auth.uid()</code>). That way, even if your app code forgets a check, the database itself refuses to hand over rows that don&#x27;t belong to that user. Consider also the idea <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/">zero trust</a> but for your app, where you give the least amount of access to everyone and simply go up from there.</p>
<h2>2. No Server Side Validation</h2>
<p>Endpoints can receive essentially any JSON request body and the app will handle it. What happens when a malicious user sends a Shakespeare novel instead of their first name? It might be funny, but it&#x27;s not something you want to be dealing with. And what if it&#x27;s not a long text but a malicious HTML/script tag that your website then renders on the page? Now you&#x27;re open to <a href="https://owasp.org/www-community/attacks/xss/">XSS</a>.</p>
<p>The nastier version I&#x27;ve seen is when update endpoints accept <em>any</em> field you send them, allowing you to update properties on the database schema without any checks. If there&#x27;s no validation on your &quot;update account&quot; endpoint, a user can simply upgrade their free account to pro for free (which is still part of broken access control and pertains to permission checks). Or what if you have AI features paywalled, but you give a user three tries to try out the AI feature? Well with no request validation, you can simply send a request like so</p>
<pre><code class="language-bash">curl -X PATCH https://myapp.com/api/me \
  -H &quot;Authorization: Bearer &lt;your-own-token&gt;&quot; \
  -d &#x27;{&quot;plan&quot;: &quot;pro&quot;, &quot;aiCreditsRemaining&quot;: 999999}&#x27;
</code></pre>
<p>This won&#x27;t just affect your database, it can also affect third-party APIs that you&#x27;re using. If you aren&#x27;t validating the user&#x27;s input when you send their request to your AI chat feature, you could be inadvertently sending the user&#x27;s submitted Shakespeare novel to your AI API, and thus costing you money now!</p>
<h3>The fix</h3>
<p>Every endpoint should have a dedicated schema validation tool (such as <a href="https://zod.dev">zod</a>, <a href="https://joi.dev">Joi</a>, whatever your stack uses) on what the request body should look like. Also: reject unknown fields instead of silently accepting them (zod&#x27;s <code>.strict()</code>, Joi&#x27;s <code>stripUnknown</code>), enforce maximum lengths on every string, and never spread the raw request body into a database update. Instead, explicitly pick the fields a user is allowed to change.</p>
<h2>3. No Rate Limiting</h2>
<p>It&#x27;s bad enough that people are now sending Shakespeare novels to your app, but what if they wrap this around a looping script and hit all your sensitive endpoints? Your API being called 10,000 times a minute? What will happen exactly? The targets for this are login, password resets, your AI feature endpoints, and anything that can send an e-mail or SMS.</p>
<h3>The fix</h3>
<p>Implement server-side rate limiting, such as a fixed-window, sliding-window, or token-bucket limiter per IP and per user, or at least pull in a dependency that can perform some simple level of it (like <a href="https://www.npmjs.com/package/express-rate-limit"><code>express-rate-limit</code></a> if you&#x27;re on Node). Even bare minimum is better than nothing because you can at least prevent a catastrophic disaster. If you have an endpoint that can send e-mails and/or text messages (e.g., e-mail invite, 2FA), make sure these endpoints have a 30 second to 1 minute cooldown between each request being sent.</p>
<p>Services like Upstash Redis (with their <a href="https://github.com/upstash/ratelimit-js"><code>@upstash/ratelimit</code></a> library) can be enabled on your application in a few lines and give you a sliding-window counter per IP and user.</p>
<h2>4. Bad file upload validation</h2>
<p>Some apps I have seen contain some social media aspect, whether it&#x27;s uploading photos, videos, audio, etc.</p>
<p>What&#x27;s stopping someone from uploading a virus? Sure, it won&#x27;t execute on your server, but your app just became a distribution point: someone downloads an innocent-looking &quot;photo&quot; and it&#x27;s an executable. Or worse... some &quot;image&quot; formats can carry scripts, such as an SVG with JavaScript inside or an HTML file with a renamed extension, and if your app serves that file back and the browser renders it, you&#x27;ve got <a href="https://owasp.org/www-community/attacks/xss/#stored-xss-attacks">stored XSS</a>.</p>
<h3>The Fix</h3>
<p>When uploading media, check file types by actual content (<a href="https://en.wikipedia.org/wiki/List_of_file_signatures">magic bytes</a>), not just extension; enforce size limits; strip metadata; and serve uploads from object storage on a separate domain with the correct <code>Content-Type</code> so the browser never renders them as pages. That combo should kill most stored-XSS risks, which is the realistic threat for most apps.</p>
<p>If your app&#x27;s whole job is distributing files to other users (photo sharing, a marketplace, general file hosting), add actual malware scanning on top.</p>
<h2>5. Enumeration &amp; Injection Vulnerabilities</h2>
<p>We&#x27;ve all heard of <a href="https://owasp.org/www-community/attacks/SQL_Injection">SQL Injection</a>. Funny enough I have seen AI do an OK job here, though it still pops up.</p>
<p>The more interesting one I keep finding are in search and filter features. If your API takes a filter object from the client and passes it more or less directly into a database query, you&#x27;re letting the client write your queries for you. An attacker can send a custom filter that reveals data that should never be reachable, like listing every admin account, or every user with a gmail address. It&#x27;s a bit surprising how often this one has come up.</p>
<h3>The Fix</h3>
<p>Never pass client-supplied query structures to the database. Allowlist which fields and operators are filterable on the server, and build the query yourself from those. We can also look at the queries being sent and think: how can we manipulate this query by changing the initial request? We can go from there on fixing it.</p>
<h2>6. Secrets Leaked</h2>
<p>This one is not so much the AI&#x27;s fault, since it&#x27;s usually pretty good here. It&#x27;s the humans. I can <em>barely</em> understand the justification if you have a private repo. But if that repo ever goes public, rotate those keys immediately. And remember: git history is forever. Deleting the line in a new commit does nothing; the key is still in the history. Rotation is the only fix.</p>
<p>We solved this problem ages ago. Please never hardcode API keys in the UI or the backend.</p>
<h3>The Fix</h3>
<p>Secrets live in environment variables or a proper vault service, and turn on secret scanning (<a href="https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection">GitHub push protection</a> or <a href="https://github.com/gitleaks/gitleaks">gitleaks</a>) so keys never make it into history in the first place.</p>
<h2>7. Exposing PII Data</h2>
<p>This one is a huge problem!! Your API is probably returning more fields than the client needs, and the big one is the customer&#x27;s e-mail.</p>
<p>If you&#x27;re really trying to get users to use your app, you need to make sure you&#x27;re not sending your customers&#x27; sensitive data out on your API. This is especially common on social type apps of course. E-mail is the biggest problem.</p>
<h3>The Fix</h3>
<p>You&#x27;re more than likely storing a customer&#x27;s e-mail; tell your AI to look for <a href="https://en.wikipedia.org/wiki/Personal_data">PII</a> data and see where it&#x27;s being returned on the API. The less information you can provide the better off you are.</p>
<h2>8. No Logging &amp; Monitoring</h2>
<p>Picture this: your app stops working, but how long was it down for? An attacker could be hammering your API and you won&#x27;t even know that it&#x27;s happening.</p>
<p>Or maybe a user hits a problem and reaches out to you for support, but nobody can see what happened, because there&#x27;s no error tracking or metrics. Monitoring isn&#x27;t only for attackers; it&#x27;s how you support real
users. You won&#x27;t be able to help them at all, and now that bug is off to the wind, never to be understood again.</p>
<h3>The Fix</h3>
<p>At minimum, an error tracker (<a href="https://sentry.io/">Sentry</a> or similar), an uptime check, and alerts. This is such an easy thing to add that will save you a TON of problems in the future.</p>
<h2>9. No Backups</h2>
<p>This is essential especially if you&#x27;re going to be iterating a lot on your app. If you get real users, and the AI suddenly decides to do a massive database migration because you are adding or changing a new feature. It will hopefully generate a proper runbook, but things don&#x27;t always go smoothly. If you accidentally destroy your database or environment, the quickest way to restore is with a backup. And make sure you are testing them too, because if you have it but never test it, then you don&#x27;t really know if it&#x27;s going to work.</p>
<h3>The Fix</h3>
<p>Ask your AI what your backup strategy should be for your system. Everything is different, but something needs to be in place. This especially must be done once you have live customers because you do not want to have data loss; it&#x27;s how you lose customers and their trust!</p>
<p>Once in place, have automated scheduled backups, and actually run a restore once in a while to prove they work.</p>
<h2>10. Supply Chain Vulnerabilities</h2>
<p>AI can and will install dependencies that are old, sometimes with known CVEs. It picked those versions because they were common in its training data, not because they&#x27;re current.</p>
<h3>The Fix</h3>
<p>Setup <a href="https://docs.github.com/en/code-security/dependabot">Dependabot</a> (or <a href="https://docs.renovatebot.com/">Renovate</a>), and run <a href="https://docs.npmjs.com/cli/commands/npm-audit"><code>npm audit</code></a> in CI. Neither is perfect, but either one is miles ahead of no CVE checking at all.</p>
<h1>Bonus Interesting Ones</h1>
<p>There are more but I think that this covers the vast majority of them, at least the most important ones. I do have some bonus ones that I included that didn&#x27;t really fit being a major mistake/security concern.</p>
<h2>Admin Panels</h2>
<p>Admin UI living in the same app as customer-facing code. I find it interesting how common this is, and there&#x27;s nothing wrong with it per se. Now this isn&#x27;t something the AI dreamed up on its own; the user explicitly asked for an admin panel, and the AI simply bolted it onto the existing app. But imagine if you have an admin panel and your app contains all the 10 issues I mentioned? If someone could just easily escalate their privileges to admin, they have access to all the sensitive data and controls that your admin panel exposes. This just introduces another unnecessary attack vector in your app.</p>
<h3>The Fix</h3>
<p>This is more personal opinion, but this is how I would solve it: A separate project entirely, with its own credentials and deployment at something like <code>admin.myapp.com</code>, reachable only over the company (or your own) VPN. Yes, it still needs proper auth (VPN access alone isn&#x27;t authorization) but at least the admin portal isn&#x27;t sitting on the public internet. If that&#x27;s too much work, then <em>at least</em> make it a separate app on a separate domain or subdomain so the two don&#x27;t share the same attack surface.</p>
<h2>No staging site</h2>
<p>I don&#x27;t think this is necessary at first, but it definitely should be once you have real users. It can help you have a dedicated place to test and break your app, and avoid the &quot;testing in prod&quot; that you&#x27;re probably doing already.</p>
<h3>The Fix</h3>
<p>Ask your AI what existing structures you have for a staging environment, what steps you need to take to create one, and what it would look like based on your current architecture. Come up with a plan (you can ask AI to help here) on how to have a proper code change -&gt; staging -&gt; test -&gt; production deployment flow. You can set up a dedicated pipeline on GitHub or wherever and have it deploy to a dedicated subdomain of the domain name you own.</p>
<h2>No Privacy Policy, Terms of Service nor GDPR</h2>
<p>This is the more legal &quot;boring&quot; side of things... but it&#x27;s important to have.</p>
<p>You need to explain to the user how you are using their data and for what, especially on apps that are charging money.</p>
<p>On the <a href="https://gdpr.eu/what-is-gdpr/">GDPR</a> front, you more than likely will end up having European users, so it should be taken into account. GDPR applies based on where your <em>users</em> are, not where your company is, and fines can reach €20 million or 4% of global annual revenue, whichever is higher. Start with baby steps.</p>
<h3>The Fix</h3>
<p>Use a policy generator (<a href="https://termly.io/products/terms-and-conditions-generator/">Termly</a>, iubenda, GetTerms, or similar) to get a baseline Privacy Policy and Terms of Service up, and link them in your footer and signup flow. Only collect the data you actually need; the less PII you store, the less you have to worry about. For GDPR specifically: add cookie consent if you&#x27;re running analytics or trackers, and make sure a user can request a copy of their data and have their account deleted (the rights of access and erasure).</p>
<h1>Conclusion + My Skill</h1>
<p>Every chat box carries the same disclaimer: &quot;AI can make mistakes.&quot; These mistakes above are what that looks like in practice, and they will ship with your app unless you go looking for them. To make that easier, I made an AI skill that checks for all ten: <a href="https://github.com/dannyreg/vibechecktech-skills">vibechecktech-skills</a>. This <strong>doesn&#x27;t</strong> replace a proper human code review, audit review and/or pen test, but it will surface these issues so you can act on them before your users (or someone worse) find them first.</p>]]></content:encoded>
  </item>
  <item>
    <title>Another Public API Has Been Walled Off</title>
    <link>https://daniel.reguero.dev/blogs/another-public-api-has-been-walled-off</link>
    <guid isPermaLink="true">https://daniel.reguero.dev/blogs/another-public-api-has-been-walled-off</guid>
    <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
    <category>blogs</category>
    <description><![CDATA[Yahoo, Strava, and Reddit keep gating their APIs, and hobbyists get caught in the middle of it.]]></description>
    <content:encoded><![CDATA[<p>Back in 2024, I created my first real side project, <a href="https://ffawards.app">FFAwards.app</a>, a tool to generate awards for your fantasy football league. It was an incredible feeling to finally get a side project live in production. And truthfully, I really didn&#x27;t care too much about making any money from it; I just wanted to create a fun tool that people would find helpful and useful.</p>
<p>Initially the project started off with Yahoo Fantasy, and I got a lot of great feedback to add more features and more fantasy leagues. So naturally the project grew to support Sleeper and ESPN. This year I spent a good amount of time doing a full rewrite of the UI, and I&#x27;m quite happy with it.</p>
<p>Yahoo Fantasy is the main platform I use for the league with my close friends. But this year, Yahoo decided to make a major change to their developer API. Instead of simply registering a developer account and registering your app, you must now request access to the API.</p>
<p>This wouldn&#x27;t be a problem, if not for the fact that Yahoo can take weeks or even months to respond back. So now it can be a bit of a waiting game until you hear back from them. In the meantime, my app along with other developer apps don&#x27;t work at all with Yahoo Fantasy, since they have revoked API access to existing apps.</p>
<h2>Why has all this changed?</h2>
<p>More and more API providers are starting to gatekeep their APIs, and... I definitely understand why.</p>
<p>These developer tools came with a gentleman&#x27;s agreement kind of trust. They knew for a fact a human would go in and register and make these accounts. But nowadays, I&#x27;m sure these platforms are getting absolutely slammed by more crawlers and scrapers for AI companies. They can&#x27;t rely on the inherent &quot;good nature&quot; of API consumers anymore, so they have to gate access.</p>
<p>I think it makes sense why they gatekeep; they are being forced to. It&#x27;s a resource drain on their compute, it&#x27;s also gatekeeping their data from LLMs simply reading it for free, and it&#x27;s also a way to stop people from using their API to build, say, an AI tool that uses their data when they&#x27;d rather build and monetize it themselves.</p>
<p>But that&#x27;s the danger with building your house on someone else&#x27;s land. While these decisions from Yahoo make sense against companies and apps trying to monetize their data, hobbyists like me just trying to make fun and helpful tools get caught in the middle of this and have to wait until my broken app works again... hopefully.</p>]]></content:encoded>
  </item>
  <item>
    <title>AI is Stealing Your Voice</title>
    <link>https://daniel.reguero.dev/blogs/ai-is-stealing-your-voice</link>
    <guid isPermaLink="true">https://daniel.reguero.dev/blogs/ai-is-stealing-your-voice</guid>
    <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
    <category>blogs</category>
    <description><![CDATA[Don't let AI take away the thing that makes us the most human, our voice.]]></description>
    <content:encoded><![CDATA[<p>Throughout my career, I’ve had the pleasure of working alongside some incredibly intelligent software engineers. They’re the kind of engineers that you could talk for hours on end about any kind of topic. I enjoy hearing what they have to say and when they write something down, it&#x27;s worth reading.</p>
<p>These engineers I&#x27;ve worked with aren&#x27;t the only ones of course, there is the broader online tech community of software engineers as well: Reddit, Hacker News, (...and to an extent, LinkedIn). I genuinely enjoy reading articles and blog posts from these experts in the software community, and the information and knowledge they have to share about a particular subject is not only interesting, but can be an incredible learning tool as well, especially for niche subjects.</p>
<p>However, with AI, these voices and opinions are being replaced by a statistical machine instead.</p>
<h2>&quot;Oh, I&#x27;m Reading AI&quot;</h2>
<p>Now we all have used ChatGPT or some LLM tooling before. Come on, we kind of just know how it sounds. An LLM response usually has telltale signs. I see this especially in LinkedIn and Reddit, where the last sentence contains some engagement bait: &quot;Anyone else? Curious if...&quot;. Even odder is when the original poster replies back to comments with an AI response: &quot;That&#x27;s fair...&quot;, or &quot;You&#x27;re absolutely right...&quot;.</p>
<p>At this point, whenever I read a post online now, I can just <em>tell</em> that it&#x27;s written by some LLM. And as I am reading a post, I have something in the back of my brain that triggers a reflex: <em>&quot;Oh, I&#x27;m reading AI&quot;</em>. Once that comes to mind, I kind of just... die inside lol... This isn&#x27;t something from an expert, this is just an output from an AI, which I could just be asking myself. Now yes, I understand that that is not the case for everyone; some people, believe it or not, can actually write professionally. But in this day and age, it&#x27;s harder and harder to tell what&#x27;s really from a person or not, so rightfully so I will assume that a post is just an AI post if it &quot;quacks and walks like a duck&quot;.</p>
<p>And look, I also fully acknowledge that you can definitely get it to <em>not</em> sound too much like AI. But more often than not, the posts I read just seem like they are copying and pasting the very first LLM response and calling it a day, not even trying to modify it nor make it their own voice again.</p>
<h2>Just Post What You Want To Say</h2>
<p>I definitely am not opposed to using AI, in fact I did have it help me with this very post in itself (could you tell?), but I didn&#x27;t have it write the whole thing for me.</p>
<p>Don&#x27;t let the AI think for you, let it work for you and let it be an extension of your thought, not a total replacement. I know that it&#x27;s efficient and comfortable to just let it type for you, but I would much rather read a typo filled and grammatically incorrect blog post than another generic AI post.</p>
<p>If you have something to share, just write it out and post it. Have AI structure it better for you or don&#x27;t use it at all, but please just post your <strong>own</strong> thoughts. I <strong>want</strong> to hear what you have to say.</p>
<h2>Conclusion</h2>
<p>I thought of many different comparisons... but I guess you can compare this to a painting. If I see a beautiful painting and you tell me it’s AI, I will feel gross about it, why is that? It&#x27;s because there’s no human value to it. I don’t attach my kids&#x27; scribbles on the fridge because they’re a work of art, it’s because a human made it. Imagine if I hung AI art on my fridge instead? That’s how I feel when reading your AI generated posts.</p>
<p>So as AI becomes more and more a part of our lives and work, I think there will be a genuine want and need for &quot;human only&quot; input and feedback again, it will almost seem nostalgic in a way. Yes, I believe that AI is useful and great, and yes I believe that it is here to stay. Just don&#x27;t let it replace your voice.</p>]]></content:encoded>
  </item>
  <item>
    <title>Custom Node.js Errors</title>
    <link>https://daniel.reguero.dev/blogs/custom-node-js-errors</link>
    <guid isPermaLink="true">https://daniel.reguero.dev/blogs/custom-node-js-errors</guid>
    <pubDate>Sat, 04 Mar 2023 00:00:00 GMT</pubDate>
    <category>blogs</category>
    <description><![CDATA[Extending the Node.js error class should be known about and used]]></description>
    <content:encoded><![CDATA[<blockquote>
<p>Update:  I&#x27;ve made a few changes to help clarify a few things. First off, I just wanted to say that everything I&#x27;m sharing here is just based on my own opinions and personal preferences when it comes to using custom Node.js Errors. I hope it&#x27;ll be useful to you too! I&#x27;ve also tweaked the error classes and code examples to make them a bit better - hope this post helps!</p>
</blockquote>
<p>A small yet powerful tool that I believe every Node.js developer should use and know about is the ability to create custom errors within their application.</p>
<p>Creating custom errors gives us a few advantages:</p>
<ol>
<li>Allows us to create more specific unit tests for error scenarios.</li>
<li>Including extra debugging information in the Error class, not limiting us to just <code>Error.message</code>.</li>
<li>Categorizing errors promptly to pinpoint their exact location of occurrence.</li>
</ol>
<h2>Defining a Custom Error</h2>
<p>To define a custom error class, simply extend the Error class. See some examples below.</p>
<pre><code class="language-js">class InvalidInputError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = this.constructor.name;
    this.statusCode = statusCode;
  }
}
class DatabaseError extends Error {
  constructor(message, statusCode, originalError) {
    super(message);
    this.name = this.constructor.name;
    this.statusCode = statusCode;
    this.originalError = originalError;
  }
}
</code></pre>
<p>Now when we extend this class, we can have control over what new and additional properties we can send. So whether it&#x27;s to rethrow an error or capture it in a log, the custom error class can give us extra information on what is going on.</p>
<p>To use the custom error, simply throw it the same way as the built-in error class.</p>
<pre><code class="language-js">const { InvalidInputError, DatabaseError } = require(&#x27;../errors&#x27;);

function validateUser(user) {
  if (!user || !user.email || !user.email.includes(&#x27;@&#x27;)) {
    throw new InvalidInputError(&#x27;Invalid user email&#x27;);
  }

  // This is just an example
  try {
    await someDatabaseCall(user.email);
  } catch(err) {
    throw new DatabaseError(&#x27;Database Error Occurred&#x27;, 500, err);
  }

}
</code></pre>
<p>In this example, we throw a custom error class for invalid user input, and we use a try-catch block to handle any errors that may be thrown during the database call. We throw a typical error message so don&#x27;t divulge too much information to the end user on what exactly happened.</p>
<p>It&#x27;s important to pass the original error when throwing a custom error class like <code>DatabaseError</code>. This way, we don&#x27;t lose any information on the original cause of the error. Of course this may vary depending on the type of error you&#x27;re handling. In the case of <code>InvalidInputError</code>, we don&#x27;t need to pass the original error because we&#x27;re not re-throwing it from the database call.</p>
<h2>Benefit 1: Specific Unit Tests</h2>
<p>Imagine if we were to write a unit test for the method above. How would we test for the error case if we simply threw <code>new Error()</code> for the input validation or re-threw the error from the database call? Well, we&#x27;d have to catch the error based on the string value.</p>
<pre><code class="language-js">describe(&#x27;validate user existing&#x27;, () =&gt; {
  it(&#x27;should throw an error if the user cannot be found in the database&#x27;, async () =&gt; {
    expect(() =&gt; await validateUser(&#x27;someemail@email.com&#x27;).to.throw(/Database Error Occurred/))
  });
});
</code></pre>
<p>But with the added benefit of our custom error, we can write a much cleaner unit test that doesn&#x27;t check for a string value, but rather the class type.</p>
<pre><code class="language-js">const { DatabaseError } = require(&#x27;../errors&#x27;);

describe(&#x27;validate user existing&#x27;, () =&gt; {
  it(&#x27;should throw an error if the user cannot be found in the database&#x27;, async () =&gt; {
    expect(() =&gt; await validateUser(&#x27;someemail@email.com&#x27;).to.throw(DatabaseError));
  });
});

</code></pre>
<p>This version of the unit test is much more specific, and we know the exact reason why this error occurred.</p>
<h2>Benefit 2: Additional Properties</h2>
<p>With us defining the error class now, we can pass as many variables as we need. One valuable variable to include is a timestamp, which can be extremely helpful when errors are captured in your logs.</p>
<p>Moreover, if you have some sort of concept of a tracing/tracking id, you can pass it to the class and the tracing id will be present. This is especially useful if the error is thrown due to an uncaught exception.</p>
<pre><code class="language-js">class DatabaseError extends Error {
  constructor(message, traceId, statusCode, originalError) {
    super(message);
    this.name = this.constructor.name;
    this.statusCode = statusCode;
    this.originalError = originalError;
    this.date = new Date();
  }
}

const findUserById = async (id) =&gt; {
  const traceId = generateRandomUUID(); // some uuid
  logger.info(traceId, &quot;entering database call&quot;);
  let user;
  try {
    user = await someDatabaseCall(id);
  } catch (error) {
    logger.info(traceId, &quot;an error occurred during the database query!&quot;);
    throw new DatabaseError(&quot;Error when getting user.&quot;, traceId, 500, error);
  }
  return user;
}
</code></pre>
<p>The code above will throw an error like so:</p>
<pre><code class="language-text">DatabaseError: Error when getting user.
    at script.js:14:9
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
    at async loadESM (node:internal/process/esm_loader:91:5)
    at async handleMainPromise (node:internal/modules/run_main:65:12) {
  traceId: &#x27;56c84a0d-e8da-40e5-8618-f41a8a00cb65&#x27;,
  date: 2023-02-20T21:35:07.020Z,
  originalError: {Error here}
}
</code></pre>
<h2>Benefit 3: Quicker to Identify</h2>
<p>Notice from the error thrown above, that it contains our class name. This is also a major benefit because right away we can know what type of error this is/where it is coming from.</p>
<p>In this case, since it&#x27;s a DatabaseError, we know that this pertains to an issue with our database.</p>
<p>If errors aren&#x27;t categorized correctly, debugging can be a difficult task since developers have to manually analyze the message and stack trace. But by implementing this simple technique, we can simplify the debugging process tremendously by giving us a context clue right away.</p>
<h2>Custom Errors That Every Dev Should Have</h2>
<p>Generally, I would recommend at least these three types of custom errors. Of course, your use case may vary depending on what you are building.</p>
<h3>Startup Error</h3>
<pre><code class="language-js">
class StartUpError extends Error {
  constructor(message, traceId, originalError) {
    super(message);
    this.name = this.constructor.name;
    this.originalError = originalError;
    this.date = new Date();
  }
}
</code></pre>
<p>This error should be used during application startup, especially when connecting to any backing services, validating environment variables, and other related operations.</p>
<h3>API Error</h3>
<pre><code class="language-js">class ApiError extends Error {
  constructor(message, traceId, originalError, statusCode = 500) {
    super(message);
    this.name = this.constructor.name;
    this.traceId = traceId;
    this.originalError = originalError;
    this.statusCode = statusCode;
    this.date = new Date();
  }
}
</code></pre>
<p>These errors apply to everything that will be sent via our API and will also include any HTTP error status codes.</p>
<p>You can also get extra fancy and extend this base ApiError. This way you can have consistent HTTP error classes depending on the type of HTTP error you will be sending.</p>
<pre><code class="language-js">class NotFoundError extends ApiError {
    constructor(resource, traceId, originalError) {
        super(`Could not find resource ${resource}`, traceId, originalError, 404);
    }
}
</code></pre>
<h3>Application Error</h3>
<pre><code class="language-js">class ApplicationError extends Error {
  constructor(message, traceId, originalError) {
    super(message);
    this.name = this.constructor.name;
    this.originalError = originalError;
    this.date = new Date();
  }
}
</code></pre>
<p>I use Application Errors for &quot;everything else&quot; and/or custom error failure cases that aren&#x27;t necessarily meant for a user. Additionally, in this example I call the class ApplicationError, but I prefer to use the name of my application. So if the name of my app is called &quot;Quiz App&quot;, I&#x27;d call it &quot;QuizAppError&quot;.</p>
<p>You&#x27;ll notice that this class is the same as the StartUpError. But what I am taking advantage of here is the name of the class. When this error is thrown, I know that it is a specific error case that I have handled and caught, so it will contain the additional debugging information I have added.</p>
<h2>Conclusion</h2>
<p>Creating custom errors allows your application to be more flexible and easier to understand. Additionally, implementing them doesn&#x27;t require much extra effort, and I can guarantee you that you, you&#x27;re coworkers, and as well as your users, will appreciate these explicit and detailed error messages.</p>
<h3>Resources and Tools</h3>
<ul>
<li><a href="https://nodejs.org/api/errors.html#class-error">Node.js Error Documentation</a></li>
<li><a href="https://openai.com/">ChatGPT</a> for proof reading this post.</li>
</ul>]]></content:encoded>
  </item>
  <item>
    <title>3 Attempts Later: How I Finally Built My Blog Posting Site</title>
    <link>https://daniel.reguero.dev/projects/blog-posting-site</link>
    <guid isPermaLink="true">https://daniel.reguero.dev/projects/blog-posting-site</guid>
    <pubDate>Sat, 04 Mar 2023 00:00:00 GMT</pubDate>
    <category>projects</category>
    <description><![CDATA[I'd like to talk about how I made this simple markdown blogging website.]]></description>
    <content:encoded><![CDATA[<p>Hey there, this blog post is not quite ready yet. However, the overall gist of what I want to talk about is how it took me over two years to finally publish/make this blogging post site. Every time I picked it up, the dependencies I installed were always out of date, and with it came breaking changes. I&#x27;ll go into more detail about this later. Feel free to check out where this blogging site lives and use it for yourself if you wish. Happy coding!</p>
<p><a href="https://github.com/dannyreg/dannyreg.github.io">Blog Posting Site Code</a></p>]]></content:encoded>
  </item>
</channel>
</rss>
