<?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: Doug silkstone</title>
    <description>The latest articles on DEV Community by Doug silkstone (@dougwithseismic).</description>
    <link>https://dev.to/dougwithseismic</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%2F2849980%2Fc0fa5143-c3de-4145-9fdc-6eafdf97325b.png</url>
      <title>DEV Community: Doug silkstone</title>
      <link>https://dev.to/dougwithseismic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dougwithseismic"/>
    <language>en</language>
    <item>
      <title>Spin up a License Key API on new Railway Functions In Less Than Two Minutes!</title>
      <dc:creator>Doug silkstone</dc:creator>
      <pubDate>Tue, 11 Feb 2025 23:07:26 +0000</pubDate>
      <link>https://dev.to/dougwithseismic/spin-up-a-license-key-api-on-new-railway-functions-in-less-than-two-minutes-4m5i</link>
      <guid>https://dev.to/dougwithseismic/spin-up-a-license-key-api-on-new-railway-functions-in-less-than-two-minutes-4m5i</guid>
      <description>&lt;p&gt;Railway's new &lt;a href="https://docs.railway.com/reference/functions" rel="noopener noreferrer"&gt;Functions&lt;/a&gt; feature makes it incredibly easy to deploy single-file TypeScript code without the overhead of managing infrastructure or repositories. &lt;/p&gt;

&lt;p&gt;In this article, we'll walk through building a license API using &lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;Hono&lt;/a&gt;, Redis, and the Bun runtime—all in a single file. No more excuses, it's time to build software!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Railway Functions are perfect for small tasks like handling webhooks, cron jobs, or simple APIs. What makes them even better is how they interact with the rest of the Railway ecosystem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Customizing Your Prefixes
&lt;/h2&gt;

&lt;p&gt;Before diving in, make sure to edit the prefix values in the code to suit your needs. The default prefixes, such as &lt;code&gt;WTHSEISMIC_&lt;/code&gt;, are placeholders and should be replaced with values relevant to your project to ensure uniqueness and avoid conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Railway account.&lt;/li&gt;
&lt;li&gt;Basic knowledge of TypeScript.&lt;/li&gt;
&lt;li&gt;Familiarity with serverless concepts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up a New Project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a New Project:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Head over to your Railway dashboard and create a new project. Choose the &lt;strong&gt;Functions&lt;/strong&gt; service to get started.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Select the Functions Environment:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Railway will automatically set up the environment using the Bun runtime for your function. No need for additional configuration!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Provision Redis
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a Redis Service:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In your Railway project, provision a Redis service. This will be used to store license data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Share Environment Variables:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
After provisioning Redis, click on the Redis service, then head to &lt;strong&gt;Variables&lt;/strong&gt;. Use the "Share Variables" feature to expose the following environment variables to your function:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;REDISHOST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REDISPORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REDISUSER&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REDISPASSWORD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;(Optional) &lt;code&gt;REDIS_TLS&lt;/code&gt; if your Redis requires TLS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Create Your Function
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a New Function:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In the Railway dashboard, click &lt;strong&gt;New Function&lt;/strong&gt;. This opens the built-in editor which supports TypeScript using Bun.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Up Additional Environment Variables:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Add any additional variables your API requires. For example, generate unique values (e.g., using a UUID generator) for:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ADMIN_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HMAC_SECRET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PAYMENT_SECRET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally, set &lt;code&gt;PORT&lt;/code&gt; (defaults to 3000 if not provided).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Paste and Save the Code
&lt;/h2&gt;

&lt;p&gt;Copy and paste the following complete code into the &lt;strong&gt;Source Code&lt;/strong&gt; tab of your Railway Function. Save your changes using &lt;code&gt;⌘+S&lt;/code&gt; (or &lt;code&gt;Ctrl+S&lt;/code&gt; on Windows). Railway will automatically deploy your changes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hono/cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rateLimiter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hono-rate-limiter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Custom prefix configuration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LICENSE_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WTHSEISMIC_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LICENSE_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WTHSEISMIC_LICENSES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WEBHOOK_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WTHSEISMIC_WEBHOOKS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Updated LicenseTier type to include "unlimited"&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LicenseTier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;basic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enterprise&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unlimited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Test Your Deployment
&lt;/h2&gt;

&lt;p&gt;Once deployed, Railway provides a URL for your function. Use the following &lt;code&gt;curl&lt;/code&gt; commands to test each endpoint:&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1: Test the Root Endpoint
&lt;/h3&gt;

&lt;p&gt;Returns the current timestamp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET https://&amp;lt;your-function-url&amp;gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2: Create a New License
&lt;/h3&gt;

&lt;p&gt;Creates a new license key for a user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;your-function-url&amp;gt;/api/licenses &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email": "user@example.com", "tier": "pro", "durationDays": 90}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3: Validate a License
&lt;/h3&gt;

&lt;p&gt;Validates an existing license key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET https://&amp;lt;your-function-url&amp;gt;/api/licenses/WTHSEISMIC_xxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.4: Handle Payment Webhook
&lt;/h3&gt;

&lt;p&gt;Simulates a payment webhook to extend a license's duration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;your-function-url&amp;gt;/webhooks/payment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: text/plain"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Payment-Signature: &amp;lt;signature&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"licenseKey": "WTHSEISMIC_xxx", "durationDays": 30}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.5: Extend License via Admin Endpoint
&lt;/h3&gt;

&lt;p&gt;Extends the license duration using an admin key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://&amp;lt;your-function-url&amp;gt;/admin/licenses/WTHSEISMIC_xxx/extend &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Admin-Key: your-secret-key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"durationDays": 30}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use these examples to verify that your function is working as expected. &lt;/p&gt;

&lt;p&gt;So there you have it, in less than two minutes, we've put together a license key server on Railway using Redis, Hono and Bun - Demonstrating how headache-free the whole process is, and how (not so) small snippets of code can live within your Railway ecoystem within minutes.&lt;/p&gt;

&lt;p&gt;Great feature, great platform.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>javascript</category>
      <category>bunjs</category>
      <category>railway</category>
    </item>
  </channel>
</rss>
