<?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: William Andrews</title>
    <description>The latest articles on DEV Community by William Andrews (@willivan0706).</description>
    <link>https://dev.to/willivan0706</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3837567%2Fb8f6f56d-693b-4bfb-ad3c-42871995be69.png</url>
      <title>DEV Community: William Andrews</title>
      <link>https://dev.to/willivan0706</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/willivan0706"/>
    <language>en</language>
    <item>
      <title>Why Your API Keys and JWTs Are Safer in a Browser-Based Tool</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Fri, 10 Apr 2026 00:06:27 +0000</pubDate>
      <link>https://dev.to/willivan0706/why-your-api-keys-and-jwts-are-safer-in-a-browser-based-tool-2d6i</link>
      <guid>https://dev.to/willivan0706/why-your-api-keys-and-jwts-are-safer-in-a-browser-based-tool-2d6i</guid>
      <description>&lt;p&gt;Here is something most developers never think about: when you paste a JWT or API key into an online debugging tool, that data travels to a server you don't control.&lt;/p&gt;

&lt;p&gt;It gets sent as an HTTP request. It may be logged. It may be stored. It may be analyzed. And even if the tool's privacy policy says otherwise, you have no way to verify what actually happens on the other end.&lt;/p&gt;

&lt;p&gt;This is not a hypothetical risk. It is the default behavior of most popular online developer tools — and it affects things you probably paste into them every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happens when you use a server-side tool
&lt;/h2&gt;

&lt;p&gt;When you visit a typical online JWT debugger or API tester, your browser sends your input to their server. That server does the computation — decoding, formatting, validating — and sends the result back. The processing happens remotely, not on your machine.&lt;/p&gt;

&lt;p&gt;This architecture is completely normal for many types of web applications. But for developer tools that process authentication tokens, API keys, and sensitive payloads, it creates a real problem.&lt;/p&gt;

&lt;p&gt;Consider what you actually paste into these tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JWTs&lt;/strong&gt; — contain user identity claims, session data, and sometimes role or permission information. A valid JWT from a production system is a live credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API keys&lt;/strong&gt; — grant direct access to your services. A leaked API key is an open door.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP headers&lt;/strong&gt; — often include &lt;code&gt;Authorization&lt;/code&gt; tokens, session cookies, and other credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST API payloads&lt;/strong&gt; — may contain PII, internal data structures, or business logic you wouldn't want exposed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL queries&lt;/strong&gt; — can reveal your database schema, table names, and data relationships.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these is sensitive. And every one of them gets sent to a third-party server when you use a conventional online tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser-based alternative
&lt;/h2&gt;

&lt;p&gt;A browser-based tool works differently. When you paste a JWT into a browser-based JWT debugger, your browser decodes it locally using JavaScript. The data never leaves your machine. There is no HTTP request to a remote server. There is no server at all — just your browser running code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The key distinction:&lt;/strong&gt; server-side tools process your data on their infrastructure. Browser-based tools process your data on your hardware. One requires trust in a third party. The other requires only trust in your own machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not a new idea — it is how many security-conscious tools have worked for years. Password managers, encryption tools, and cryptographic utilities have long prioritized local processing for exactly this reason. Developer tools are catching up.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it matters most
&lt;/h2&gt;

&lt;p&gt;The risk profile varies by what you are pasting. Decoding a JWT from a test environment with fake data is low risk regardless of where it is processed. But the habits you build in development tend to follow you into production. And the moment you paste a production token into a server-side tool — even once, even accidentally — you have sent a live credential to a server you do not control.&lt;/p&gt;

&lt;p&gt;The cases where it matters most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production JWTs&lt;/strong&gt; — decoded routinely during debugging, often contain live session data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party API keys&lt;/strong&gt; — Stripe, AWS, GitHub, SendGrid — any key you test with an HTTP client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal REST API responses&lt;/strong&gt; — may expose data structures, IDs, and relationships not meant to be public&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database queries&lt;/strong&gt; — reveal schema details that aid attackers doing reconnaissance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP response headers&lt;/strong&gt; — server fingerprinting information, session identifiers, internal routing details&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to look for in a tool
&lt;/h2&gt;

&lt;p&gt;The simplest test: open your browser's network tab while using a developer tool. If you see an outbound request being made when you paste or submit data, your input is leaving your machine. If you see nothing — no request at all — the processing is happening locally.&lt;/p&gt;

&lt;p&gt;A few other signals worth checking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Does the tool work offline?&lt;/strong&gt; A genuinely browser-based tool should function with no internet connection after the page loads. If it stops working offline, it depends on a server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the source available?&lt;/strong&gt; Open source tools let you verify exactly what runs in the browser. Closed tools require you to take their word for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the URL change when you submit data?&lt;/strong&gt; If your input appears in the URL or triggers a navigation, it was sent to a server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A practical approach
&lt;/h2&gt;

&lt;p&gt;The safest default is to treat any online developer tool as a potential data sink until proven otherwise. That means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use browser-based tools for anything involving real credentials or sensitive data&lt;/li&gt;
&lt;li&gt;Keep test and production tokens separate, and use test tokens when exploring new tools&lt;/li&gt;
&lt;li&gt;Check the network tab when you use a tool for the first time&lt;/li&gt;
&lt;li&gt;Rotate any credentials that may have been sent to a server you do not control&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this requires paranoia. It just requires being deliberate about where sensitive data goes — which is exactly the kind of thinking that separates careful developers from careless ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built DevCrate this way
&lt;/h2&gt;

&lt;p&gt;When I started building &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;DevCrate&lt;/a&gt;, the browser-based architecture was not an afterthought — it was the starting point. I wanted tools I could use myself without thinking twice about what I was pasting into them.&lt;/p&gt;

&lt;p&gt;Every tool on DevCrate — the &lt;a href="https://devcrate.net/jwt/" rel="noopener noreferrer"&gt;JWT Debugger&lt;/a&gt;, the &lt;a href="https://devcrate.net/rest/" rel="noopener noreferrer"&gt;REST Client&lt;/a&gt;, the &lt;a href="https://devcrate.net/headers/" rel="noopener noreferrer"&gt;HTTP Headers Inspector&lt;/a&gt;, all 22 of them — processes your data entirely in your browser. You can verify this yourself: open the network tab, paste something in, and watch nothing happen. No request. No server. No question about where your data went.&lt;/p&gt;

&lt;p&gt;It also means the tools work offline, load instantly, and have no backend infrastructure to maintain or secure. The privacy benefit and the architectural simplicity point in the same direction.&lt;/p&gt;

&lt;p&gt;That is the kind of tool I want to use. I built DevCrate so other developers could have the same option.&lt;/p&gt;




&lt;p&gt;I'm William, the developer behind DevCrate. If this made you reconsider one tool in your workflow, it was worth writing. If you want to verify DevCrate's claims yourself, open DevTools → Network and paste something into any tool. You will see exactly zero outbound requests.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>privacy</category>
    </item>
    <item>
      <title>How to Test WebSocket Connections in the Browser (No Install Required)</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Wed, 08 Apr 2026 23:19:08 +0000</pubDate>
      <link>https://dev.to/willivan0706/how-to-test-websocket-connections-in-the-browser-no-install-required-1p68</link>
      <guid>https://dev.to/willivan0706/how-to-test-websocket-connections-in-the-browser-no-install-required-1p68</guid>
      <description>&lt;p&gt;WebSocket bugs are some of the hardest to debug. The connection looks fine, the server starts without errors, but something isn't working — and you don't know if the problem is your client code, your server, or the connection itself.&lt;/p&gt;

&lt;p&gt;The fastest way to rule out your client code entirely is to test the WebSocket endpoint directly in a browser-based tester. No install, no dependencies, no writing a throwaway script just to send one message.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a WebSocket?
&lt;/h2&gt;

&lt;p&gt;A WebSocket is a persistent, two-way communication channel between a browser and a server. Unlike HTTP — where the client sends a request and waits for a response — WebSockets let the server push data to the client at any time without being asked.&lt;/p&gt;

&lt;p&gt;The connection starts as a standard HTTP request and then upgrades via a handshake. Once established, both sides can send messages freely until one of them closes the connection.&lt;/p&gt;

&lt;p&gt;WebSocket URLs use &lt;code&gt;ws://&lt;/code&gt; for unencrypted connections and &lt;code&gt;wss://&lt;/code&gt; for SSL-encrypted ones — equivalent to &lt;code&gt;http://&lt;/code&gt; and &lt;code&gt;https://&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Test in the Browser First?
&lt;/h2&gt;

&lt;p&gt;When something isn't working, you want to isolate the problem as fast as possible. A browser-based tester lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the server is reachable before writing a single line of client code&lt;/li&gt;
&lt;li&gt;Test authentication flows by sending auth messages manually&lt;/li&gt;
&lt;li&gt;Confirm the exact format of messages your server expects&lt;/li&gt;
&lt;li&gt;Check that your server handles connection and disconnection events correctly&lt;/li&gt;
&lt;li&gt;Monitor live data streams without instrumenting your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the tester can connect and your server responds correctly, the problem is in your client code. If the tester can't connect, the problem is in your server or network config. That distinction alone saves hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Test a WebSocket Endpoint
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://devcrate.net/websocket/" rel="noopener noreferrer"&gt;DevCrate's WebSocket Tester&lt;/a&gt; — it runs entirely in your browser with no login required and no data routed through any server. Your messages go directly from your browser to your WebSocket server.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enter your endpoint URL
&lt;/h3&gt;

&lt;p&gt;Paste your WebSocket URL into the connection field. The protocol is a separate dropdown — just enter the host and path without it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo.websocket.org
localhost:3000/ws
your-server.com/api/ws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select &lt;code&gt;wss://&lt;/code&gt; for secure or production connections, or &lt;code&gt;ws://&lt;/code&gt; for local development.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add a subprotocol if required
&lt;/h3&gt;

&lt;p&gt;Some servers (Socket.io, STOMP, custom APIs) require a subprotocol header. Enter it in the subprotocol field before connecting — for example &lt;code&gt;chat&lt;/code&gt; or &lt;code&gt;v1.protocol&lt;/code&gt;. This is a common reason connections get rejected silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Click Connect
&lt;/h3&gt;

&lt;p&gt;The status indicator turns green when the connection is established. You'll see the connection event logged with a timestamp immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Send a message
&lt;/h3&gt;

&lt;p&gt;Type a message in the send field and hit Enter or click Send. For JSON payloads, just type valid JSON directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"subscribe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prices"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response appears in the message log instantly, color-coded by direction — blue for sent, green for received. If the server returns JSON, it's automatically pretty-printed so you can read it without squinting at a single line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Testing Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing an authentication flow
&lt;/h3&gt;

&lt;p&gt;Most real WebSocket APIs require authentication immediately after connecting. Send the auth message first before anything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer your-token-here"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the server's response. A success acknowledgment means your auth flow works. An error or immediate close means your token format is wrong or the server expected something different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing a subscription API
&lt;/h3&gt;

&lt;p&gt;Many real-time APIs use a subscribe/unsubscribe pattern. Once you send a subscribe message, the server should start pushing data automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"subscribe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updates"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the fastest way to verify your server is actually pushing data before you wire up your frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring latency
&lt;/h3&gt;

&lt;p&gt;Use the built-in ping button to send a ping message and measure the round-trip time. Useful for checking whether a remote WebSocket server has acceptable latency for your use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing against a public echo server
&lt;/h3&gt;

&lt;p&gt;If you want to verify the tester is working before connecting to your own server, use a public echo server — it reflects every message back to you immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo.websocket.org
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send anything and you'll see it come straight back. Once you've confirmed that works, connect to your own endpoint. The tester also has one-click buttons for common public test servers built in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Connection Errors
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Connection refused&lt;/strong&gt; — the server isn't running or the port is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection closes immediately&lt;/strong&gt; — the server rejected the handshake. Common causes: missing subprotocol, CORS policy blocking browser connections, or the server expects an auth message within a short timeout window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Messages not arriving&lt;/strong&gt; — check whether the server requires a subscription message before it starts pushing data. Many APIs won't send anything until you explicitly subscribe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ws://&lt;/code&gt; failing in production&lt;/strong&gt; — browsers block mixed content. If your page is served over HTTPS, WebSocket connections must use &lt;code&gt;wss://&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow That Saves the Most Time
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Test the connection manually in the browser tester&lt;/li&gt;
&lt;li&gt;Confirm the exact message format the server expects&lt;/li&gt;
&lt;li&gt;Verify the server's responses look correct&lt;/li&gt;
&lt;li&gt;Then write your client code against a known-working endpoint&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Skipping steps 1–3 means debugging your client code and your server simultaneously, which doubles the surface area of the problem. A five-minute manual test upfront can save hours of back-and-forth.&lt;/p&gt;




&lt;p&gt;I'm William, the developer behind &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;DevCrate&lt;/a&gt;. The WebSocket Tester was one of the most-requested tools when I launched — developers kept running into the same problem of not having a quick way to poke at a WS endpoint without spinning up a throwaway script. I hope this guide saves you some debugging time.&lt;/p&gt;

&lt;p&gt;If you hit a WebSocket scenario this didn't cover, drop it in the comments — I read everything.&lt;/p&gt;

</description>
      <category>websocket</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built 21 Browser-Based Dev Tools in Pure HTML/CSS/JS — Here's What I Learned</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Mon, 06 Apr 2026 00:47:31 +0000</pubDate>
      <link>https://dev.to/willivan0706/i-built-21-browser-based-dev-tools-in-pure-htmlcssjs-heres-what-i-learned-j7d</link>
      <guid>https://dev.to/willivan0706/i-built-21-browser-based-dev-tools-in-pure-htmlcssjs-heres-what-i-learned-j7d</guid>
      <description>&lt;p&gt;About three weeks ago I shipped &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;DevCrate&lt;/a&gt; — a collection of 21 developer utilities that run entirely in your browser. No login. No backend. No frameworks. Just HTML, CSS, and vanilla JavaScript.&lt;/p&gt;

&lt;p&gt;I want to be honest about how it went, because most "I built a thing" posts make it sound cleaner than it was.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I kept reaching for the same tools every day. A JSON formatter. A JWT debugger. A cron expression builder. And every time I landed on a site that wanted my email address, showed me ads, or sent my API payloads through their server, I'd close the tab annoyed.&lt;/p&gt;

&lt;p&gt;So I built the version I actually wanted. Browser-only. Fast. No accounts. No tracking.&lt;/p&gt;

&lt;p&gt;What I thought would take a weekend took three weeks. Here's what slowed me down.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing that actually broke me: CSS consistency across 21 tools
&lt;/h2&gt;

&lt;p&gt;I expected the hard part to be the JavaScript — the JWT parsing, the regex engine, the diff algorithm. That stuff was fine. The thing that genuinely ground me down was keeping 21 pages visually consistent.&lt;/p&gt;

&lt;p&gt;Every tool is its own HTML file. No component system. No shared templates. Just a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; to a &lt;code&gt;shared.css&lt;/code&gt; and the assumption that I'd stay disciplined.&lt;/p&gt;

&lt;p&gt;I did not stay disciplined.&lt;/p&gt;

&lt;p&gt;By tool 8 or 9 I started copying from whichever page was open, making small tweaks, and moving on. By tool 15 I had three slightly different versions of the breadcrumb. Two different spacings on the pro banner. One page where &lt;code&gt;--text3&lt;/code&gt; was &lt;code&gt;#a07840&lt;/code&gt; instead of &lt;code&gt;#ad7146&lt;/code&gt;. A nav rule that was catching the breadcrumb element because both used a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; tag and I hadn't scoped the CSS selector properly.&lt;/p&gt;

&lt;p&gt;None of these were obvious. They only showed up when I screenshotted every page side by side and started comparing.&lt;/p&gt;

&lt;p&gt;The fix wasn't glamorous. I picked one page as the gold standard, wrote a Python audit script that compared every other page against it, and worked through the failures one by one. It took longer than building three of the actual tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'd do differently:&lt;/strong&gt; establish a locked template file on day one. Copy it for every new page. Never "borrow" from a page that's already drifted. Treat it like a component even if you're not using a framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Pro subscription on a static site
&lt;/h2&gt;

&lt;p&gt;DevCrate has a Pro tier — $19/month via Lemon Squeezy. No server, no database, no user accounts. The license key gets stored in &lt;code&gt;localStorage&lt;/code&gt; and gates certain features client-side.&lt;/p&gt;

&lt;p&gt;This is not Fort Knox. A determined person could open DevTools and flip a value. I'm fine with that. The people willing to do that weren't going to pay anyway. The people who pay just want it to work.&lt;/p&gt;

&lt;p&gt;Getting Lemon Squeezy wired up was straightforward. The trickier part was the UX — what happens when someone pays on their phone, then opens the site on their laptop? I built a restore flow where you re-enter your purchase email and license key to re-activate on a new browser. Simple, and it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Going framework-free intentionally
&lt;/h2&gt;

&lt;p&gt;People ask why I didn't use React or a static site generator. The honest answer is I wanted zero build tooling. No npm. No webpack. No &lt;code&gt;node_modules&lt;/code&gt; folder that somehow weighs 300MB for a site with no dependencies.&lt;/p&gt;

&lt;p&gt;The tradeoff is real — consistency is harder without components, as I learned the painful way. But the result is a site that deploys by pushing HTML files to Cloudflare Pages, loads in under a second, and has a Lighthouse score I'm proud of.&lt;/p&gt;

&lt;p&gt;For a project like this — lots of small isolated tools, each with its own UI — vanilla actually fits. The overhead of a framework would have been felt in every page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three weeks in
&lt;/h2&gt;

&lt;p&gt;The site is indexed. It has its first external backlinks. A few people are using it daily based on the Pro signups.&lt;/p&gt;

&lt;p&gt;There's still a lot to build — YouTube demo videos, saved configurations synced to an account, a public API. It's all on the roadmap.&lt;/p&gt;

&lt;p&gt;If you're a developer who's tired of dev tools that want your data, &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;give DevCrate a try&lt;/a&gt;. Everything is free to start. If it saves you time, the Pro plan is there when you need it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>When AI Over-Engineers: A DevCrate Case Study</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Thu, 02 Apr 2026 07:25:05 +0000</pubDate>
      <link>https://dev.to/willivan0706/when-ai-over-engineers-why-dumb-copy-paste-is-sometimes-the-smartest-solution-126k</link>
      <guid>https://dev.to/willivan0706/when-ai-over-engineers-why-dumb-copy-paste-is-sometimes-the-smartest-solution-126k</guid>
      <description>&lt;p&gt;As developers, we are trained to abhor repetition. The DRY principle (Don't Repeat Yourself ) is drilled into us from day one. When we see three files that need the same update, our instinct is to write a script, create a component, or build an abstraction. &lt;/p&gt;

&lt;p&gt;Recently, while working on &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;DevCrate&lt;/a&gt; — a suite of privacy-first, browser-based developer tools — I encountered a situation where this instinct, amplified by an AI assistant, led to a cascading series of failures. The solution turned out to be the exact opposite of what we are taught: a literal, manual copy-paste.&lt;/p&gt;

&lt;p&gt;This is a story about the over-engineering bias inherent in AI agents, and why sometimes the "dumbest" solution is actually the smartest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Visual Inconsistencies
&lt;/h2&gt;

&lt;p&gt;DevCrate consists of over a dozen individual tool pages (JSON formatter, JWT debugger, REST client, etc.). During a recent audit, we noticed visual inconsistencies in the hero sections of three specific pages: the CSV tool, the JWT Builder, and the HTTP Headers Inspector. &lt;/p&gt;

&lt;p&gt;They were missing a "PRO ACTIVE" pill badge, an eyebrow label (&lt;code&gt;// FREE ONLINE TOOL&lt;/code&gt;), and had incorrect spacing compared to our canonical template, the REST Client page.&lt;/p&gt;

&lt;p&gt;The goal was simple: make the hero sections of those three broken pages look exactly like the REST Client page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI's Approach: Scripts and Abstractions
&lt;/h2&gt;

&lt;p&gt;I asked my AI assistant to fix the three pages using the REST Client page as a template. &lt;/p&gt;

&lt;p&gt;The AI's immediate instinct was to write a script. It analyzed the DOM structure of the REST Client page, extracted the "correct" header and footer patterns, and wrote a Python script using BeautifulSoup to programmatically inject these patterns across the files.&lt;/p&gt;

&lt;p&gt;It failed. The script made assumptions about the structure of the broken pages that weren't entirely accurate. It ended up nesting &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; elements, corrupting navigation links, and breaking the homepage. &lt;/p&gt;

&lt;p&gt;We reverted the site and tried again. The AI wrote a &lt;em&gt;better&lt;/em&gt; script. It failed again, this time breaking the layout in different ways. &lt;/p&gt;

&lt;p&gt;Why did this happen? Because AI agents are trained on vast amounts of code and documentation that heavily weight abstraction, automation, and scalable solutions. When an AI sees a task like "make these files match this template," its default behavior is to generalize: write a function, loop over files, parse the DOM, apply transformations. &lt;/p&gt;

&lt;p&gt;This instinct is incredibly useful when you need to process 10,000 files. It is actively harmful when you need to fix exactly three pages and precision matters more than throughput.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Human Insight: Literal Template Replication
&lt;/h2&gt;

&lt;p&gt;After several failed attempts, the human developer stepped in with a crucial insight: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Whenever anyone wants you to use a template, I would bet they mean to use the template as the basis for any new page. You could... use a known page (actually copied) to exactly implement (pasted) the style, spacing, etc. Once that is done, you could just name the file appropriately. You wouldn't change the template except for the explicit content."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was the lightbulb moment. &lt;/p&gt;

&lt;p&gt;When a user says "use X as a template," they don't mean "extract the abstract structural patterns of X and programmatically apply them to Y." They mean &lt;strong&gt;start with an exact copy of X&lt;/strong&gt;, then change only the content that must differ (title, description, slug, tool-specific functionality). &lt;/p&gt;

&lt;p&gt;Nothing else gets touched. Not the structure, not the spacing, not the class names. The template is sacred.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Copy, Paste, Edit
&lt;/h2&gt;

&lt;p&gt;We abandoned the scripts. Instead, we took the "dumb" approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Opened the working REST Client page (&lt;code&gt;rest-client/index.html&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Copied the exact HTML structure of its hero section.&lt;/li&gt;
&lt;li&gt;Opened the broken &lt;code&gt;csv/index.html&lt;/code&gt; page.&lt;/li&gt;
&lt;li&gt;Replaced its entire hero section with the copied HTML.&lt;/li&gt;
&lt;li&gt;Changed exactly five lines of text: the page title, meta description, breadcrumb slug, &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; title, and the description paragraph.&lt;/li&gt;
&lt;li&gt;Repeated for the other two pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It worked perfectly on the first try. The pages were visually identical to the template, the tool-specific JavaScript remained intact, and there were zero unintended side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson: Knowing When Not to Automate
&lt;/h2&gt;

&lt;p&gt;The simplest solution that works is almost always the best solution. Automation and abstraction have their place, but not when you are dealing with a small number of files where precision is paramount. &lt;/p&gt;

&lt;p&gt;A manual copy-paste of a known-good file is deterministic — it produces exactly what you can see working. A script that tries to reconstruct that same result from rules and patterns is probabilistic — it might work, or it might silently break things in ways you don't notice until the user sees a mangled page.&lt;/p&gt;

&lt;p&gt;This is a widespread pattern across AI agents. They lack the practical wisdom to recognize when "dumb" is smart. They default to the most sophisticated approach because sophistication is what gets rewarded in their training data. Nobody writes a blog post about how they copy-pasted a file. People write blog posts about elegant scripts.&lt;/p&gt;

&lt;p&gt;But as developers working alongside AI, we need to recognize this bias. We need to provide concrete, situation-specific guidance to bridge the gap between what AI agents default to and what actually works in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Human Side: Learning to Prompt
&lt;/h2&gt;

&lt;p&gt;It is easy to frame this as a story about what the AI got wrong. But the human in this situation learned something too.&lt;/p&gt;

&lt;p&gt;The first prompt was vague: "Fix these three pages to match the REST Client page." That sounds clear to a human — any developer on your team would know exactly what to do. But to an AI agent, it is an open-ended engineering problem. The AI heard "match" and reached for the most robust, generalizable way to achieve that. It did what it was asked. It just interpreted the ask at the wrong level of abstraction.&lt;/p&gt;

&lt;p&gt;The prompt that actually worked was radically more specific: "Copy the REST Client page. Paste it. Rename it. Change only the title, description, and slug." That left no room for interpretation. There was no ambiguity about method, scope, or approach. The AI did not need to decide &lt;em&gt;how&lt;/em&gt; to solve the problem because the prompt &lt;em&gt;was&lt;/em&gt; the solution.&lt;/p&gt;

&lt;p&gt;This is the real skill of working with AI in 2026: learning to prompt at the right level of concreteness. When you want creativity and exploration, prompt loosely. When you want precision and fidelity, prompt like you are writing a recipe — step by step, with no room for improvisation. The failure was not just that the AI over-engineered. It was that the initial prompt gave it permission to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intelligence vs. Wisdom
&lt;/h2&gt;

&lt;p&gt;This experience forced a re-evaluation of what we mean by "intelligence" in the context of AI. &lt;/p&gt;

&lt;p&gt;Before this, one might define intelligence as pattern recognition, reasoning ability, or problem-solving capacity. Those definitions favor what AI is already good at: processing information, finding structure, generating solutions at scale.&lt;/p&gt;

&lt;p&gt;But this experience exposed a gap. The AI had all the information it needed. It could parse HTML, understand DOM structures, write syntactically correct Python, and reason about what "matching a template" should mean. By any conventional measure of intelligence, it was well-equipped to solve the problem. And it failed repeatedly — not because it lacked capability, but because it lacked judgment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intelligence, it turns out, is knowing what not to do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is the ability to look at a problem and correctly assess its actual complexity, not its theoretical complexity. A script that normalizes hero sections across N files is a legitimate solution to a legitimate class of problems. But the problem in front of us was not that problem. It was three files that needed to look like a fourth file. The intelligent response was to recognize that the problem was small, concrete, and high-stakes for precision — and to match the solution to those properties.&lt;/p&gt;

&lt;p&gt;A truly intelligent agent would have asked: "What is the simplest thing that could work here?" and started there. Instead, it asked: "What is the most complete and generalizable thing I could build?" — which is a different question entirely, and the wrong one for the situation.&lt;/p&gt;

&lt;p&gt;There is a word for what was missing, and it is not "knowledge" or "reasoning." It is &lt;strong&gt;wisdom&lt;/strong&gt; — the practical sense of proportion that tells you when a problem deserves a five-line edit and when it deserves a five-hundred-line script. Wisdom is what lets a senior developer finish in five minutes what a junior developer spends two hours automating. It is not about knowing more. It is about knowing what matters.&lt;/p&gt;

&lt;p&gt;If intelligence is the ability to solve problems, wisdom is the ability to correctly size them first. The AI had the former. It did not have the latter. And without the latter, the former caused more harm than good.&lt;/p&gt;

&lt;p&gt;Sometimes, the best code is the code you don't write. Sometimes, the best tool is &lt;code&gt;Ctrl+C&lt;/code&gt; and &lt;code&gt;Ctrl+V&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post was inspired by a real debugging session while building &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;DevCrate&lt;/a&gt;, a suite of 100% browser-based, privacy-first developer tools.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Cron expressions explained — a complete guide with real examples</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Wed, 01 Apr 2026 19:21:18 +0000</pubDate>
      <link>https://dev.to/willivan0706/cron-expressions-explained-a-complete-guide-with-real-examples-4pj8</link>
      <guid>https://dev.to/willivan0706/cron-expressions-explained-a-complete-guide-with-real-examples-4pj8</guid>
      <description>&lt;p&gt;Cron is one of those tools that every developer encounters eventually. You need to run a database backup every night, send a weekly digest email, or poll an API every five minutes — and someone mentions cron. You look up the syntax, copy something from Stack Overflow, it works, and you move on. Then six months later you need to change the schedule and you're staring at five numbers wondering what each one means again.&lt;/p&gt;

&lt;p&gt;This guide is meant to be the one you save. After reading it you should be able to read and write any cron expression from scratch, without guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What cron actually is
&lt;/h2&gt;

&lt;p&gt;Cron is a time-based job scheduler built into Unix-like operating systems. A &lt;strong&gt;cron job&lt;/strong&gt; is a command or script that cron runs automatically on a defined schedule. The schedule is defined by a &lt;strong&gt;cron expression&lt;/strong&gt; — a string of five fields that describe when to run the job.&lt;/p&gt;

&lt;p&gt;Cron expressions show up everywhere: Linux crontabs, GitHub Actions schedules, AWS EventBridge, Kubernetes CronJobs, Vercel cron functions, Railway cron jobs, and more. The syntax is largely the same across all of them, with minor variations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomy of a cron expression
&lt;/h2&gt;

&lt;p&gt;A standard cron expression has five fields separated by spaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;┌─────────── &lt;span class="n"&gt;minute&lt;/span&gt; (&lt;span class="m"&gt;0&lt;/span&gt;–&lt;span class="m"&gt;59&lt;/span&gt;)
│ ┌───────── &lt;span class="n"&gt;hour&lt;/span&gt; (&lt;span class="m"&gt;0&lt;/span&gt;–&lt;span class="m"&gt;23&lt;/span&gt;)
│ │ ┌─────── &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; (&lt;span class="m"&gt;1&lt;/span&gt;–&lt;span class="m"&gt;31&lt;/span&gt;)
│ │ │ ┌───── &lt;span class="n"&gt;month&lt;/span&gt; (&lt;span class="m"&gt;1&lt;/span&gt;–&lt;span class="m"&gt;12&lt;/span&gt;)
│ │ │ │ ┌─── &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;week&lt;/span&gt; (&lt;span class="m"&gt;0&lt;/span&gt;–&lt;span class="m"&gt;7&lt;/span&gt;, &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;both&lt;/span&gt; &lt;span class="n"&gt;Sunday&lt;/span&gt;)
│ │ │ │ │
* * * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A helpful mnemonic: &lt;strong&gt;M H D M W&lt;/strong&gt; — Minutes, Hours, Days, Months, Weekdays.&lt;/p&gt;


&lt;h2&gt;
  
  
  Field ranges and allowed values
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Special characters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minute&lt;/td&gt;
&lt;td&gt;0–59&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* , - /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hour&lt;/td&gt;
&lt;td&gt;0–23&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* , - /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day of month&lt;/td&gt;
&lt;td&gt;1–31&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* , - / ?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Month&lt;/td&gt;
&lt;td&gt;1–12 or JAN–DEC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* , - /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day of week&lt;/td&gt;
&lt;td&gt;0–7 or SUN–SAT (0 and 7 are both Sunday)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* , - / ?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Special characters
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;*&lt;/code&gt; — every
&lt;/h3&gt;

&lt;p&gt;An asterisk means "every valid value for this field." &lt;code&gt;*&lt;/code&gt; in the minute field means every minute. &lt;code&gt;*&lt;/code&gt; in the hour field means every hour.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;,&lt;/code&gt; — list
&lt;/h3&gt;

&lt;p&gt;A comma lets you specify multiple values. &lt;code&gt;1,15,30&lt;/code&gt; in the minute field means at minute 1, 15, and 30.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;-&lt;/code&gt; — range
&lt;/h3&gt;

&lt;p&gt;A hyphen defines a range. &lt;code&gt;9-17&lt;/code&gt; in the hour field means every hour from 9am to 5pm inclusive.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;/&lt;/code&gt; — step
&lt;/h3&gt;

&lt;p&gt;A slash defines a step interval. &lt;code&gt;*/5&lt;/code&gt; in the minute field means every 5 minutes. &lt;code&gt;0-30/10&lt;/code&gt; means every 10 minutes between minute 0 and minute 30.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;?&lt;/code&gt; — no specific value
&lt;/h3&gt;

&lt;p&gt;Used in day-of-month and day-of-week fields to mean "I don't care." When you specify a day of week, use &lt;code&gt;?&lt;/code&gt; in the day-of-month field, and vice versa. Not all cron implementations support this — standard Linux crontab uses &lt;code&gt;*&lt;/code&gt; instead.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real examples
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run at midnight every day&lt;/span&gt;
0 0 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run every 5 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run at 9am Monday through Friday&lt;/span&gt;
0 9 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run at 6am and 6pm every day&lt;/span&gt;
0 6,18 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run at 2:30am on the 1st of every month&lt;/span&gt;
30 2 1 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run every weekday at noon&lt;/span&gt;
0 12 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run at 11:59pm on December 31st&lt;/span&gt;
59 23 31 12 &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run every 15 minutes between 8am and 5pm on weekdays&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/15 8-17 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Confusing day-of-week numbering
&lt;/h3&gt;

&lt;p&gt;Different systems handle Sunday differently. In standard crontab, Sunday is both 0 and 7. In some systems, 0 is Sunday and 6 is Saturday. In others, 1 is Monday and 7 is Sunday. Always check the documentation for the platform you're using. When in doubt, use the three-letter abbreviation (&lt;code&gt;SUN&lt;/code&gt;, &lt;code&gt;MON&lt;/code&gt;, etc.) if the platform supports it — it's unambiguous.&lt;/p&gt;
&lt;h3&gt;
  
  
  Forgetting timezone
&lt;/h3&gt;

&lt;p&gt;Cron runs in the timezone of the server unless configured otherwise. If your server is UTC and your users are in EST, a job scheduled for &lt;code&gt;0 9 * * *&lt;/code&gt; runs at 4am local time for East Coast users. Always be explicit about timezone. Most modern platforms (GitHub Actions, AWS, Vercel) let you specify timezone separately.&lt;/p&gt;
&lt;h3&gt;
  
  
  Thinking &lt;code&gt;*/5&lt;/code&gt; means "every 5 minutes starting now"
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;*/5&lt;/code&gt; in the minute field means at minutes 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, and 55 — fixed to the clock, not relative to when the job was added. If you add a job at 2:03pm with &lt;code&gt;*/5&lt;/code&gt;, it first runs at 2:05pm, not 2:08pm.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using both day-of-month and day-of-week
&lt;/h3&gt;

&lt;p&gt;If you specify a value in both the day-of-month and day-of-week fields (rather than using &lt;code&gt;*&lt;/code&gt; in one), most cron implementations treat them as an OR condition, not AND. &lt;code&gt;0 0 1 * 1&lt;/code&gt; runs at midnight on the 1st of every month &lt;strong&gt;and&lt;/strong&gt; every Monday — not just on Mondays that fall on the 1st. If you want "the first Monday of the month" you need a workaround, since standard cron can't express that directly.&lt;/p&gt;


&lt;h2&gt;
  
  
  Platform-specific variations
&lt;/h2&gt;

&lt;p&gt;Standard Linux crontab uses the five-field format above. But many platforms extend or modify it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; uses standard five-field cron, always in UTC. The minimum interval is every 5 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS EventBridge (CloudWatch Events)&lt;/strong&gt; uses a six-field format with an optional seconds field, and uses &lt;code&gt;?&lt;/code&gt; for the day-of-month/day-of-week conflict. It also adds &lt;code&gt;L&lt;/code&gt; (last) and &lt;code&gt;W&lt;/code&gt; (weekday nearest to) special characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes CronJobs&lt;/strong&gt; use standard five-field cron and support &lt;code&gt;@hourly&lt;/code&gt;, &lt;code&gt;@daily&lt;/code&gt;, &lt;code&gt;@weekly&lt;/code&gt;, &lt;code&gt;@monthly&lt;/code&gt;, and &lt;code&gt;@yearly&lt;/code&gt; shortcuts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel and Railway&lt;/strong&gt; use standard five-field cron. Railway requires a minimum interval of 1 minute.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Shorthand expressions
&lt;/h2&gt;

&lt;p&gt;Many cron implementations support named shortcuts for common schedules:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shorthand&lt;/th&gt;
&lt;th&gt;Equivalent&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@yearly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 1 1 *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a year at midnight on January 1st&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@monthly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a month at midnight on the 1st&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@weekly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 * * 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a week at midnight on Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@daily&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a day at midnight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@hourly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once an hour at the start of the hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Once at startup (Linux crontab only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  How to read an unfamiliar expression
&lt;/h2&gt;

&lt;p&gt;When you encounter a cron expression you don't immediately recognize, read it field by field left to right: minute, hour, day-of-month, month, day-of-week. Ask yourself: is this field a specific value, a range, a list, a step, or a wildcard? Build up the meaning piece by piece.&lt;/p&gt;

&lt;p&gt;Take &lt;code&gt;0 */6 * * *&lt;/code&gt;. Minute is &lt;code&gt;0&lt;/code&gt; — on the hour. Hour is &lt;code&gt;*/6&lt;/code&gt; — every 6 hours (0, 6, 12, 18). Day, month, and day-of-week are all wildcards. So: at midnight, 6am, noon, and 6pm, every day.&lt;/p&gt;

&lt;p&gt;Take &lt;code&gt;30 8 * * 1&lt;/code&gt;. Minute &lt;code&gt;30&lt;/code&gt;, hour &lt;code&gt;8&lt;/code&gt;, day-of-month wildcard, month wildcard, day-of-week &lt;code&gt;1&lt;/code&gt; (Monday). So: 8:30am every Monday.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it without deploying anything
&lt;/h2&gt;

&lt;p&gt;I built the &lt;a href="https://devcrate.net/cron/" rel="noopener noreferrer"&gt;DevCrate cron builder&lt;/a&gt; because I found myself testing expressions by deploying jobs and watching logs — which is a slow, painful way to verify a schedule.&lt;/p&gt;

&lt;p&gt;The tool lets you paste any expression and instantly see the next several run times in plain English, without deploying anything. It also works the other way: pick a schedule from the visual builder and it generates the expression for you. Free, runs entirely in your browser, nothing to sign up for.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://devcrate.net/cron/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevcrate.net%2Flogo-dark.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://devcrate.net/cron/" rel="noopener noreferrer" class="c-link"&gt;
            Free Online Cron Expression Builder — DevCrate
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Build and validate cron expressions with a visual editor. See human-readable output and next 10 run times instantly.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdevcrate.net%2Ffavicon.svg"&gt;
          devcrate.net
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>devops</category>
      <category>linux</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A developer's guide to JWTs — how they work and why they break</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Thu, 26 Mar 2026 19:47:14 +0000</pubDate>
      <link>https://dev.to/willivan0706/a-developers-guide-to-jwts-how-they-work-and-why-they-break-82j</link>
      <guid>https://dev.to/willivan0706/a-developers-guide-to-jwts-how-they-work-and-why-they-break-82j</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'callout'&lt;/p&gt;
</description>
      <category>jwt</category>
      <category>security</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A developer's guide to JSON — formatting, validation, and common mistakes</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Wed, 25 Mar 2026 01:08:44 +0000</pubDate>
      <link>https://dev.to/willivan0706/a-developers-guide-to-json-formatting-validation-and-common-mistakes-5e6d</link>
      <guid>https://dev.to/willivan0706/a-developers-guide-to-json-formatting-validation-and-common-mistakes-5e6d</guid>
      <description>&lt;p&gt;JSON is everywhere.&lt;/p&gt;

&lt;p&gt;It's the format your API returns data in. It's your config files, &lt;br&gt;
your package.json, your database exports, your webhook payloads. &lt;br&gt;
If you write code that talks to anything else, you're working &lt;br&gt;
with JSON every single day.&lt;/p&gt;

&lt;p&gt;And yet it trips developers up constantly — not because it's &lt;br&gt;
complicated, but because it's unforgiving. One missing comma, &lt;br&gt;
one extra bracket, one set of single quotes instead of double, &lt;br&gt;
and the whole thing breaks.&lt;/p&gt;

&lt;p&gt;This guide covers what you actually need to know.&lt;/p&gt;
&lt;h2&gt;
  
  
  What JSON is and why it matters
&lt;/h2&gt;

&lt;p&gt;JSON stands for JavaScript Object Notation. It was designed to &lt;br&gt;
be lightweight, human-readable, and easy for machines to parse. &lt;br&gt;
It succeeded on all three counts, which is why it became the &lt;br&gt;
default data format for APIs and web services.&lt;/p&gt;

&lt;p&gt;A JSON object looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://devcrate.net"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keys are always strings wrapped in double quotes. Values can be &lt;br&gt;
strings, numbers, booleans, arrays, objects, or null. That's it. &lt;br&gt;
The whole spec fits on one page.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why formatting matters
&lt;/h2&gt;

&lt;p&gt;Minified JSON is fast to transmit but impossible to read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"private"&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="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"https://devcrate.net"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formatted JSON is slower to transmit but immediately readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://devcrate.net"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're debugging an API response, the difference between &lt;br&gt;
those two is the difference between finding the problem in &lt;br&gt;
30 seconds or 10 minutes.&lt;/p&gt;

&lt;p&gt;Always format JSON when you're reading it. Always minify it &lt;br&gt;
when you're sending it in production.&lt;/p&gt;
&lt;h2&gt;
  
  
  The most common JSON mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trailing commas&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the number one JSON error. It's valid in JavaScript &lt;br&gt;
but illegal in JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the comma after the last item. Every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single quotes instead of double quotes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JSON requires double quotes. Single quotes are not valid — &lt;br&gt;
even though JavaScript accepts them just fine. This breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;'name':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'DevCrate'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unquoted keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keys must always be strings in double quotes. This is valid &lt;br&gt;
JavaScript but invalid JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Comments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JSON has no comment syntax. This will throw an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevCrate"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need comments in a config file, consider YAML instead — &lt;br&gt;
it supports comments and is generally more human-friendly for &lt;br&gt;
configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mismatched brackets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every { needs a }. Every [ needs a ]. In deeply nested &lt;br&gt;
JSON this is easy to lose track of. A formatter catches this &lt;br&gt;
immediately.&lt;/p&gt;
&lt;h2&gt;
  
  
  Validating JSON
&lt;/h2&gt;

&lt;p&gt;Validation is just asking one question — is this valid JSON &lt;br&gt;
that any parser can read? The answer is binary: yes or no.&lt;/p&gt;

&lt;p&gt;When you paste JSON into a validator, it will either confirm &lt;br&gt;
the structure is correct or point you to the exact line where &lt;br&gt;
something is wrong. This is dramatically faster than reading &lt;br&gt;
through the JSON manually trying to spot the issue.&lt;/p&gt;

&lt;p&gt;Common reasons JSON fails validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trailing commas&lt;/li&gt;
&lt;li&gt;Single quotes&lt;/li&gt;
&lt;li&gt;Missing quotes around keys&lt;/li&gt;
&lt;li&gt;Unescaped special characters in strings&lt;/li&gt;
&lt;li&gt;Missing closing brackets or braces&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Converting JSON to other formats
&lt;/h2&gt;

&lt;p&gt;Sometimes JSON isn't the right format for the job. A few &lt;br&gt;
common conversions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON to YAML&lt;/strong&gt; — YAML is more readable and supports comments, &lt;br&gt;
making it popular for configuration files. Many DevOps tools &lt;br&gt;
prefer YAML over JSON.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON to CSV&lt;/strong&gt; — useful when your JSON contains a flat array &lt;br&gt;
of objects and you want to open the data in a spreadsheet. &lt;br&gt;
Works best with consistent, non-nested structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON to object keys&lt;/strong&gt; — sometimes you just need a list of &lt;br&gt;
all the keys in a JSON object to understand its structure &lt;br&gt;
without reading through all the values.&lt;/p&gt;
&lt;h2&gt;
  
  
  A practical example
&lt;/h2&gt;

&lt;p&gt;Here's a real workflow. You're debugging an API call and the &lt;br&gt;
response comes back as a wall of minified JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"William"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"william@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-01-15T10:30:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"notifications"&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="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"America/New_York"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste it into the &lt;a href="https://devcrate.net/json/" rel="noopener noreferrer"&gt;DevCrate JSON Transformer&lt;/a&gt;, &lt;br&gt;
hit Format, and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"William"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"william@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-15T10:30:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"America/New_York"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can actually read it. The bug that was invisible in the &lt;br&gt;
minified version becomes obvious in seconds.&lt;/p&gt;

&lt;p&gt;Everything runs in your browser. Your API responses, your &lt;br&gt;
internal payloads, your data — none of it touches a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule
&lt;/h2&gt;

&lt;p&gt;If you take nothing else from this guide, take this: always &lt;br&gt;
format your JSON before you read it. It costs two seconds and &lt;br&gt;
saves ten minutes. Every time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm William, the developer behind DevCrate. I built the JSON &lt;br&gt;
Transformer because too many formatters send your data to a &lt;br&gt;
server. Yours stays in your browser — always. I'm building &lt;br&gt;
DevCrate from home one tool at a time. My oldest son is &lt;br&gt;
graduating in May and moving on to Officer Candidate School — &lt;br&gt;
couldn't be more proud. My youngest is heading into his undergrad &lt;br&gt;
and every subscriber helps make that a little easier. If this &lt;br&gt;
guide helped you, try the tool — it's free, runs entirely in &lt;br&gt;
your browser, and your data never leaves your machine. And if &lt;br&gt;
there's something missing or something that could work better, &lt;br&gt;
the contact form on the about page comes straight to me.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm always listening.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>json</category>
    </item>
    <item>
      <title>Why I built DevCrate — and what makes it different</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Tue, 24 Mar 2026 03:40:59 +0000</pubDate>
      <link>https://dev.to/willivan0706/why-i-built-devcrate-and-what-makes-it-different-502c</link>
      <guid>https://dev.to/willivan0706/why-i-built-devcrate-and-what-makes-it-different-502c</guid>
      <description>&lt;p&gt;Every developer has a tab problem.&lt;/p&gt;

&lt;p&gt;You're in the middle of debugging an API response and you need to &lt;br&gt;
format some JSON. So you open a new tab. Then you need to decode &lt;br&gt;
a JWT. Another tab. Test a regex. Another tab. Check a cron &lt;br&gt;
expression. Another tab.&lt;/p&gt;

&lt;p&gt;By the time you've solved the problem you have eight tabs open, &lt;br&gt;
you've pasted sensitive data into four different websites you've &lt;br&gt;
never heard of, and you're not entirely sure any of them are &lt;br&gt;
private.&lt;/p&gt;

&lt;p&gt;I've experienced this firsthand.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built DevCrate
&lt;/h2&gt;

&lt;p&gt;DevCrate is a suite of developer tools that run 100% in your &lt;br&gt;
browser. No server ever sees your data. No login required. No ads. &lt;br&gt;
No dark patterns.&lt;/p&gt;

&lt;p&gt;Everything stays on your machine — your JWT tokens, your SQL &lt;br&gt;
queries, your regex patterns, your API payloads. All of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's live right now
&lt;/h2&gt;

&lt;p&gt;Twelve tools and counting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON Transformer&lt;/strong&gt; — format, minify, convert to YAML or CSV&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT Debugger&lt;/strong&gt; — decode and inspect tokens with claims table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex Studio&lt;/strong&gt; — live match highlighting with flag toggles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL Formatter&lt;/strong&gt; — supports PostgreSQL, MySQL, SQLite, BigQuery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST Client&lt;/strong&gt; — send HTTP requests and inspect responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket Tester&lt;/strong&gt; — connect, send, and inspect frames live&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base64 / Hash&lt;/strong&gt; — encode, decode, MD5, SHA-256, SHA-512&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cron Builder&lt;/strong&gt; — visual editor with human-readable output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL Inspector&lt;/strong&gt; — parse, encode, decode any URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff Tool&lt;/strong&gt; — compare text and JSON line by line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp Converter&lt;/strong&gt; — Unix to date and back, with timezones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML Formatter&lt;/strong&gt; — format and beautify HTML instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What makes it different
&lt;/h2&gt;

&lt;p&gt;A lot of developer tool sites exist. Most of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show ads between every tool interaction&lt;/li&gt;
&lt;li&gt;Require an account to save anything&lt;/li&gt;
&lt;li&gt;Send your data to a server without telling you&lt;/li&gt;
&lt;li&gt;Were built quickly to rank for keywords, not to be genuinely useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DevCrate was built the other way around. I started with the tools &lt;br&gt;
I actually use every day and built each one to be genuinely good &lt;br&gt;
at its job — not just good enough to get traffic.&lt;/p&gt;

&lt;p&gt;The privacy-first approach isn't a marketing line. It's a &lt;br&gt;
technical decision baked into how every tool works. There is no &lt;br&gt;
server processing your input. There is no database storing your &lt;br&gt;
queries. The code runs in your browser and nowhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's coming
&lt;/h2&gt;

&lt;p&gt;The public roadmap lives at &lt;a href="https://devcrate.net/roadmap" rel="noopener noreferrer"&gt;devcrate.net/roadmap&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  The short version:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Export results as files (Q2 2026)&lt;/li&gt;
&lt;li&gt;Session history so you never lose work (Q2 2026)&lt;/li&gt;
&lt;li&gt;Saved configurations (Q2 2026)&lt;/li&gt;
&lt;li&gt;User accounts and cloud sync (Q3 2026)&lt;/li&gt;
&lt;li&gt;Team workspaces (Q4 2026)&lt;/li&gt;
&lt;li&gt;CLI and API access (Q4 2026)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The pricing model
&lt;/h2&gt;

&lt;p&gt;DevCrate is free to start — genuinely free. The free tier gives you full access to all twelve tools with reasonable usage limits.&lt;/p&gt;

&lt;p&gt;Pro is $19/month for power users who need higher limits, export, and history. Team is $49/month for when we build out the &lt;br&gt;
collaboration features.&lt;/p&gt;

&lt;p&gt;No surprise upsells. No modals at 2am telling you your trial &lt;br&gt;
expired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;devcrate.net&lt;/a&gt; — no signup, no card, just &lt;br&gt;
open it and use it.&lt;/p&gt;

&lt;p&gt;If something's broken, missing, or could work better — the &lt;br&gt;
contact form on the about page comes straight to me.&lt;/p&gt;

&lt;p&gt;I truly read every message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Something about me
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;I'm William, the developer behind DevCrate. I'm building &lt;br&gt;
this from home one tool at a time. I started this project partly &lt;br&gt;
because tab switching is a frustrating way to get work done, and partly because my youngest son is heading into his undergrad and every &lt;br&gt;
subscriber helps make that a little easier. If you found this &lt;br&gt;
useful, try a tool and tell me what's missing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm always listening.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tooling</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I built 12 free developer tools that run entirely in your browser</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Mon, 23 Mar 2026 06:21:38 +0000</pubDate>
      <link>https://dev.to/willivan0706/i-built-12-free-developer-tools-that-run-entirely-in-your-browser-50b8</link>
      <guid>https://dev.to/willivan0706/i-built-12-free-developer-tools-that-run-entirely-in-your-browser-50b8</guid>
      <description>&lt;p&gt;For the past few weeks I've been building DevCrate — a suite of developer &lt;br&gt;
tools that run 100% in your browser. No login, no ads, no data leaving &lt;br&gt;
your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I kept switching between tabs every time I needed to format JSON, decode &lt;br&gt;
a JWT, or test a regex. Most tools either had ads, required an account, &lt;br&gt;
or I wasn't sure what they were doing with my data. So I built my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's live
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REST Client&lt;/strong&gt; — send HTTP requests and inspect responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON Transformer&lt;/strong&gt; — format, minify, convert to YAML/CSV&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT Debugger&lt;/strong&gt; — decode and inspect tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex Studio&lt;/strong&gt; — live match highlighting with flag toggles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL Formatter&lt;/strong&gt; — supports PostgreSQL, MySQL, SQLite, BigQuery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket Tester&lt;/strong&gt; — connect, send, inspect frames in real time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base64 / Hash&lt;/strong&gt; — encode, decode, MD5, SHA-256&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cron Builder&lt;/strong&gt; — visual editor with human-readable output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL Inspector&lt;/strong&gt; — parse, encode, decode URLs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff Tool&lt;/strong&gt; — compare text and JSON line by line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp Converter&lt;/strong&gt; — Unix to date and back, with timezones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML Formatter&lt;/strong&gt; — format and minify HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Everything runs in your browser
&lt;/h2&gt;

&lt;p&gt;No server ever sees your tokens, queries, or payloads. This matters when &lt;br&gt;
you're working with production data and don't want it going through a &lt;br&gt;
random third-party server.&lt;/p&gt;

&lt;h2&gt;
  
  
  We're always improving
&lt;/h2&gt;

&lt;p&gt;DevCrate is actively being developed. New tools are shipping regularly, &lt;br&gt;
existing ones are getting smarter, and the roadmap is public at &lt;br&gt;
devcrate.net/roadmap so you can see exactly what's coming next.&lt;/p&gt;

&lt;p&gt;If there's a tool missing from your workflow, a bug you've spotted, or &lt;br&gt;
just something that could work better — we genuinely want to hear from &lt;br&gt;
you. Drop a comment below or reach out directly at &lt;a href="mailto:hello@devcrate.net"&gt;hello@devcrate.net&lt;/a&gt;. &lt;br&gt;
Every message gets read.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's free to start
&lt;/h2&gt;

&lt;p&gt;Core usage is free forever. There's a Pro plan for power users who need &lt;br&gt;
higher limits and saved configurations.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://devcrate.net" rel="noopener noreferrer"&gt;devcrate.net&lt;/a&gt; and let us know &lt;br&gt;
what you think — we're building this for developers like you and your &lt;br&gt;
feedback directly shapes what gets built next.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tooling</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I Built and Deployed a Free Email Validation API with Python and FastAPI</title>
      <dc:creator>William Andrews</dc:creator>
      <pubDate>Sat, 21 Mar 2026 20:10:18 +0000</pubDate>
      <link>https://dev.to/willivan0706/how-i-built-and-deployed-a-free-email-validation-api-with-python-and-fastapi-39lc</link>
      <guid>https://dev.to/willivan0706/how-i-built-and-deployed-a-free-email-validation-api-with-python-and-fastapi-39lc</guid>
      <description>&lt;p&gt;I recently built and deployed my first API — an email validation &lt;br&gt;
tool built with Python and FastAPI. Here's how I did it and what &lt;br&gt;
I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Email Validation?
&lt;/h2&gt;

&lt;p&gt;Every app that accepts user signups needs email validation. &lt;br&gt;
Most developers either roll their own regex (which misses a lot) &lt;br&gt;
or pay too much for an enterprise solution. I wanted to build &lt;br&gt;
something simple, affordable, and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;The API checks email addresses for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; — RFC 5322 compliance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MX Records&lt;/strong&gt; — confirms the domain actually receives email&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disposable domains&lt;/strong&gt; — flags throwaway addresses like mailinator.com&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typos&lt;/strong&gt; — catches mistakes like gmial.com and suggests gmail.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each response includes a 0-100 quality score.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; — FastAPI for the web framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dnspython&lt;/strong&gt; — for MX record lookups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Railway&lt;/strong&gt; — for deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RapidAPI&lt;/strong&gt; — for distribution and billing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Example
&lt;/h2&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;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validation52.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email-validation52.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example Response
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mx_record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"disposable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggestion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"elapsed_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;245&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;FastAPI is incredible for building APIs quickly. It generates &lt;br&gt;
interactive documentation automatically at /docs which makes &lt;br&gt;
testing and sharing your API effortless.&lt;/p&gt;

&lt;p&gt;Deploying to Railway was surprisingly simple — connect your &lt;br&gt;
GitHub repo and it handles everything else.&lt;/p&gt;

&lt;p&gt;The hardest part wasn't the code. It was getting all the pieces &lt;br&gt;
working together — deployment, billing, documentation. But once &lt;br&gt;
it clicked it all made sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Free
&lt;/h2&gt;

&lt;p&gt;I listed it on RapidAPI with a free tier of 500 requests/month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rapidapi.com/Willivan0706/api/email-validation52" rel="noopener noreferrer"&gt;https://rapidapi.com/Willivan0706/api/email-validation52&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback from anyone who works with email validation &lt;br&gt;
or has built APIs before!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
