<?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: nly.kr</title>
    <description>The latest articles on DEV Community by nly.kr (@nly-kr).</description>
    <link>https://dev.to/nly-kr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3746385%2F7ac2b81e-520c-417c-bbb3-611864578ee7.png</url>
      <title>DEV Community: nly.kr</title>
      <link>https://dev.to/nly-kr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nly-kr"/>
    <language>en</language>
    <item>
      <title>How to Manage Long UTM URLs in Developer Workflows</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Tue, 25 Aug 2026 03:39:15 +0000</pubDate>
      <link>https://dev.to/nly-kr/how-to-manage-long-utm-urls-in-developer-workflows-1eah</link>
      <guid>https://dev.to/nly-kr/how-to-manage-long-utm-urls-in-developer-workflows-1eah</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffl24q8nra0w6m617mgct.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffl24q8nra0w6m617mgct.jpeg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
Long URLs are not always a problem.&lt;/p&gt;

&lt;p&gt;Sometimes they are exactly what you want.&lt;/p&gt;

&lt;p&gt;A campaign URL like this contains useful information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/pricing?utm_source=newsletter&amp;amp;utm_medium=email&amp;amp;utm_campaign=august_launch&amp;amp;utm_content=cta_button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem starts when the same URL has to move through several parts of a workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an email system&lt;/li&gt;
&lt;li&gt;a notification service&lt;/li&gt;
&lt;li&gt;a CMS&lt;/li&gt;
&lt;li&gt;a QR code&lt;/li&gt;
&lt;li&gt;an internal dashboard&lt;/li&gt;
&lt;li&gt;a social post&lt;/li&gt;
&lt;li&gt;a database&lt;/li&gt;
&lt;li&gt;an analytics pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, manually creating, copying, checking, and shortening URLs becomes repetitive.&lt;/p&gt;

&lt;p&gt;Instead of treating URL shortening as a separate manual step, we can build a small workflow around it.&lt;/p&gt;

&lt;p&gt;In this post, we'll create a practical JavaScript flow that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Destination URL
      ↓
Add UTM parameters
      ↓
Validate the URL
      ↓
Shorten it
      ↓
Store both URLs
      ↓
Use the short URL for distribution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not the URL shortener itself.&lt;/p&gt;

&lt;p&gt;The important part is &lt;strong&gt;keeping the original tracking URL correct while making the distribution URL easier to handle&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Build the UTM URL programmatically
&lt;/h2&gt;

&lt;p&gt;You could concatenate strings manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/pricing&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;?utm_source=newsletter&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;&amp;amp;utm_medium=email&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;&amp;amp;utm_campaign=august_launch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, but it becomes easy to introduce encoding mistakes as the number of parameters grows.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;URL&lt;/code&gt; and &lt;code&gt;URLSearchParams&lt;/code&gt; APIs give us a cleaner option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildCampaignUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_medium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_campaign&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_term&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create a campaign URL like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildCampaignUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/pricing&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="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;august_launch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cta_button&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will contain the complete tracking URL.&lt;/p&gt;

&lt;p&gt;This approach has another useful property: values are encoded correctly by the URL API instead of relying on manual string manipulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Validate before shortening
&lt;/h2&gt;

&lt;p&gt;A short URL should not hide a broken long URL.&lt;/p&gt;

&lt;p&gt;Before sending anything to a shortening service, validate the destination.&lt;/p&gt;

&lt;p&gt;A basic validator can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isHttpUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isHttpUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid campaign URL&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only validates the structure.&lt;/p&gt;

&lt;p&gt;It does not guarantee that the destination actually exists.&lt;/p&gt;

&lt;p&gt;If your workflow is important enough, you may also want a separate check for the destination before distributing the link.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Shorten the complete URL, not half of it
&lt;/h2&gt;

&lt;p&gt;One mistake that makes tracking URLs harder to reason about is splitting the workflow like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Destination
    ↓
Shorten URL
    ↓
Add tracking parameters somewhere later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a typical campaign workflow, I prefer this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Destination
    ↓
Add UTM parameters
    ↓
Validate
    ↓
Shorten the COMPLETE URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you one canonical campaign URL before the shortening layer is introduced.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/pricing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/pricing?utm_source=newsletter&amp;amp;utm_medium=email&amp;amp;utm_campaign=august_launch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;strong&gt;that complete URL&lt;/strong&gt; becomes the input to the shortener.&lt;/p&gt;

&lt;p&gt;This makes the system easier to debug later.&lt;/p&gt;

&lt;p&gt;If something goes wrong, you still have the exact campaign destination that was originally created.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Put the shortener behind a function
&lt;/h2&gt;

&lt;p&gt;Your application should not need to know the details of the shortening service everywhere.&lt;/p&gt;

&lt;p&gt;Instead, isolate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// call your URL shortening service here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the rest of your application only needs to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small abstraction is useful if you ever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change providers&lt;/li&gt;
&lt;li&gt;add retries&lt;/li&gt;
&lt;li&gt;add logging&lt;/li&gt;
&lt;li&gt;introduce caching&lt;/li&gt;
&lt;li&gt;disable shortening temporarily&lt;/li&gt;
&lt;li&gt;use different providers in different environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The campaign creation logic stays independent from the shortener implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Example using a URL shortening API
&lt;/h2&gt;

&lt;p&gt;As one concrete example, nly.kr provides a URL shortening API.&lt;/p&gt;

&lt;p&gt;Its current documented endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://nly.kr/api/shorten
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API accepts the original URL and a required category, and API-key authentication can be sent using the &lt;code&gt;X-API-Key&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;For developer-related links, &lt;code&gt;dev&lt;/code&gt; is one of the documented categories.&lt;/p&gt;

&lt;p&gt;A Node.js helper could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NLY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NLY_API_KEY is not configured&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://nly.kr/api/shorten&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&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="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="s2"&gt;`Shortening failed: HTTP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the API key comes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NLY_API_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than being hardcoded.&lt;/p&gt;

&lt;p&gt;Do not put a private API key into browser-side JavaScript.&lt;/p&gt;

&lt;p&gt;If authentication is required, make the request from your backend.&lt;/p&gt;

&lt;p&gt;You can check the current endpoint, authentication options, parameters, and response format in the &lt;a href="https://nly.kr/api" rel="noopener noreferrer"&gt;nly.kr URL Shortener API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Combine the pieces
&lt;/h2&gt;

&lt;p&gt;Now we can turn the separate steps into one function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCampaignLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;content&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildCampaignUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isHttpUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid campaign URL&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;campaignUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;shortUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createCampaignLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/pricing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;august_launch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cta_button&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, the result looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;campaignUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...?utm_source=...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;shortUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have three separate pieces of information instead of throwing the original URL away.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Store the long URL too
&lt;/h2&gt;

&lt;p&gt;This is probably the most important operational detail.&lt;/p&gt;

&lt;p&gt;Do not store only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;short_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store enough information to reconstruct what happened.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;august_launch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newsletter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;original_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/pricing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;campaign_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/pricing?...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because three months later, a short URL alone tells you very little.&lt;/p&gt;

&lt;p&gt;You may want to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which campaign created this link?&lt;/li&gt;
&lt;li&gt;Which destination did it point to?&lt;/li&gt;
&lt;li&gt;Which UTM parameters were used?&lt;/li&gt;
&lt;li&gt;Was a new short URL generated later?&lt;/li&gt;
&lt;li&gt;Which channel received it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping the original campaign URL makes debugging and reporting much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Avoid generating duplicates unnecessarily
&lt;/h2&gt;

&lt;p&gt;Imagine your application runs this code every time someone opens an admin page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could end up generating multiple short links for exactly the same campaign URL.&lt;/p&gt;

&lt;p&gt;A better workflow may be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create campaign URL
       ↓
Search existing record
       ↓
Found?
 ├─ Yes → reuse saved short URL
 └─ No  → create short URL and save it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple pseudo implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOrCreateShortUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findByCampaignUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shortUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;campaignUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;shortUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;shortUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also reduces unnecessary external API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Treat shortening as optional infrastructure
&lt;/h2&gt;

&lt;p&gt;Your main product probably should not stop working just because a URL shortening API is temporarily unavailable.&lt;/p&gt;

&lt;p&gt;Consider this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create content
     ↓
Generate campaign URL
     ↓
Try shortening
     ↓
Success → use short URL
Failure → keep long URL / retry later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on your use case, falling back to the original URL may be better than failing the entire operation.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeShortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;shortenUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;URL shortening failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whether this fallback makes sense depends on your application.&lt;/p&gt;

&lt;p&gt;For a marketing batch that must use predefined short links, you might prefer to stop the job instead.&lt;/p&gt;

&lt;p&gt;For a non-critical notification, using the original URL may be acceptable.&lt;/p&gt;

&lt;p&gt;The key is to decide deliberately.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Add timeouts and retries carefully
&lt;/h2&gt;

&lt;p&gt;External APIs can fail.&lt;/p&gt;

&lt;p&gt;Your application should expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;network errors&lt;/li&gt;
&lt;li&gt;authentication errors&lt;/li&gt;
&lt;li&gt;bad input&lt;/li&gt;
&lt;li&gt;server errors&lt;/li&gt;
&lt;li&gt;timeouts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retries are useful for temporary failures, but do not blindly retry every error.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;400 Bad Request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;usually suggests that changing nothing and retrying the same request will not help.&lt;/p&gt;

&lt;p&gt;A temporary server or network error may be different.&lt;/p&gt;

&lt;p&gt;A production workflow could use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  ↓
Success → save result

Temporary failure
  ↓
retry with backoff

Permanent/input failure
  ↓
log + require correction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters even for a feature as small as URL shortening.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Keep API keys on the server
&lt;/h2&gt;

&lt;p&gt;If your shortener requires authentication, avoid this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DON'T do this in browser code&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-secret-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything delivered to the browser should be considered visible to the user.&lt;/p&gt;

&lt;p&gt;A safer architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   ↓
Your backend
   ↓
URL Shortener API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your backend reads the key from an environment variable or secret manager.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NLY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser never needs to receive the credential.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. A more realistic campaign workflow
&lt;/h2&gt;

&lt;p&gt;Once everything is combined, an internal system might work like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Editor creates campaign
        ↓
Destination selected
        ↓
UTM parameters generated
        ↓
Campaign URL validated
        ↓
Existing short URL lookup
        ↓
Short URL generated if needed
        ↓
Both URLs stored
        ↓
Short URL sent to email/SMS/QR workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more reliable than manually moving URLs between browser tabs.&lt;/p&gt;

&lt;p&gt;It also creates a clean boundary between different concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tracking
   = UTM parameters

Distribution
   = short URL

Analytics
   = campaign/traffic data

Storage
   = mapping between them
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping those responsibilities separate makes the workflow easier to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would implement first
&lt;/h2&gt;

&lt;p&gt;If I were adding this to a small project, I would not start with a complicated link-management system.&lt;/p&gt;

&lt;p&gt;I'd begin with four pieces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. buildCampaignUrl()
2. isHttpUrl()
3. shortenUrl()
4. save the long + short URL pair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add caching, retries, dashboards, or batch processing only when the project actually needs them.&lt;/p&gt;

&lt;p&gt;A URL shortener is a small component.&lt;/p&gt;

&lt;p&gt;The useful part is the workflow around it.&lt;/p&gt;

&lt;p&gt;When you create the full campaign URL first, validate it, shorten it behind a small abstraction, and keep the original URL in storage, long UTM links become much easier to manage without losing the information that made them useful in the first place.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>api</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Generate Short URLs with JavaScript and a URL Shortening API</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Tue, 18 Aug 2026 09:14:43 +0000</pubDate>
      <link>https://dev.to/nly-kr/how-to-generate-short-urls-with-javascript-and-a-url-shortening-api-2g3d</link>
      <guid>https://dev.to/nly-kr/how-to-generate-short-urls-with-javascript-and-a-url-shortening-api-2g3d</guid>
      <description>&lt;p&gt;Long URLs are often inconvenient when building web applications.&lt;/p&gt;

&lt;p&gt;You may want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate short links for social media posts&lt;/li&gt;
&lt;li&gt;create links for QR codes&lt;/li&gt;
&lt;li&gt;shorten URLs automatically from an admin dashboard&lt;/li&gt;
&lt;li&gt;create campaign links programmatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, we'll use a URL shortening API with both &lt;code&gt;curl&lt;/code&gt; and JavaScript's &lt;code&gt;fetch()&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;For the examples, I'll use the API from &lt;strong&gt;nly.kr&lt;/strong&gt;, a URL shortener that I develop and operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  API endpoint
&lt;/h2&gt;

&lt;p&gt;The endpoint used in this tutorial is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://nly.kr/api/shorten
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Type: application/x-www-form-urlencoded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authentication is handled with an API key.&lt;/p&gt;

&lt;p&gt;The recommended header is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-API-Key: your_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bearer authentication is also supported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: Bearer your_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For JSON responses, send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accept: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Required parameters
&lt;/h2&gt;

&lt;p&gt;Two parameters are required when creating a short URL:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original HTTP/HTTPS URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Category key for the link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For this example, we'll use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category=dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a short URL with curl
&lt;/h2&gt;

&lt;p&gt;The quickest way to test the API is with curl.&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 &lt;span class="s2"&gt;"https://nly.kr/api/shorten"&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-API-Key: your_API_KEY"&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;"Accept: 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="s2"&gt;"url=https://example.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"category=dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your_API_KEY&lt;/code&gt; with your actual API key.&lt;/p&gt;

&lt;p&gt;A successful request returns JSON similar to 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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_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://nly.kr/Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"original_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://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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"member_idx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dev"&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;For most applications, the value you'll care about is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;short_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Calling the API with JavaScript
&lt;/h2&gt;

&lt;p&gt;You can make the same request using &lt;code&gt;fetch()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_API_KEY&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;longUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&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;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://nly.kr/api/shorten&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;category&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Short URL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important detail here is that the API expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application/x-www-form-urlencoded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than a JSON request body.&lt;/p&gt;

&lt;p&gt;That's why we're using &lt;code&gt;URLSearchParams&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Bearer authentication
&lt;/h2&gt;

&lt;p&gt;If you prefer the &lt;code&gt;Authorization&lt;/code&gt; header, you can use:&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 &lt;span class="s2"&gt;"https://nly.kr/api/shorten"&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;"Authorization: Bearer your_API_KEY"&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;"Accept: 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="s2"&gt;"url=https://example.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"category=dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For most cases, using the recommended &lt;code&gt;X-API-Key&lt;/code&gt; header is simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling API errors
&lt;/h2&gt;

&lt;p&gt;If a required parameter is missing, the API returns an error response.&lt;/p&gt;

&lt;p&gt;For example:&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Category is required."&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;Possible HTTP responses include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&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;400&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bad Request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;401&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unauthorized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;405&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Method Not Allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Server Error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In production code, it's a good idea to check both the HTTP status code and the returned JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't expose private API keys in frontend code
&lt;/h2&gt;

&lt;p&gt;The JavaScript example above contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for demonstrating the request, but private API keys should generally not be embedded directly in frontend JavaScript.&lt;/p&gt;

&lt;p&gt;Anything sent to the browser can be inspected by users.&lt;/p&gt;

&lt;p&gt;For production applications, consider calling the URL shortening API from your backend instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   ↓
Your server
   ↓
URL shortening API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets your server keep the API key private.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical use cases
&lt;/h2&gt;

&lt;p&gt;Once you receive &lt;code&gt;short_url&lt;/code&gt;, you can use it in many workflows.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store it in your database&lt;/li&gt;
&lt;li&gt;include it in email campaigns&lt;/li&gt;
&lt;li&gt;generate QR codes&lt;/li&gt;
&lt;li&gt;create social media links&lt;/li&gt;
&lt;li&gt;generate short links automatically from a CMS&lt;/li&gt;
&lt;li&gt;create campaign-specific tracking links&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A URL shortening API makes it easy to automate link generation.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;receive the original URL&lt;/li&gt;
&lt;li&gt;send it to the API&lt;/li&gt;
&lt;li&gt;get the &lt;code&gt;short_url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;store or share the generated link&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nly.kr/en/api" rel="noopener noreferrer"&gt;https://nly.kr/en/api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you just want to shorten a URL directly in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nly.kr/en/create-short-url" rel="noopener noreferrer"&gt;https://nly.kr/en/create-short-url&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I develop and operate nly.kr. I used its API here as a practical example for this tutorial.&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>API로 URL 단축하기: JavaScript와 cURL로 긴 링크 자동으로 줄이는 방법</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Sun, 16 Aug 2026 23:51:06 +0000</pubDate>
      <link>https://dev.to/nly-kr/apiro-url-dancughagi-javascriptwa-curlro-gin-ringkeu-jadongeuro-julineun-bangbeob-4bjp</link>
      <guid>https://dev.to/nly-kr/apiro-url-dancughagi-javascriptwa-curlro-gin-ringkeu-jadongeuro-julineun-bangbeob-4bjp</guid>
      <description>&lt;h1&gt;
  
  
  API로 URL 단축하기: JavaScript와 cURL로 긴 링크 자동으로 줄이는 방법
&lt;/h1&gt;

&lt;p&gt;긴 URL을 한두 번 줄이는 정도라면 URL 단축 사이트에 접속해서 직접 처리하면 됩니다.&lt;/p&gt;

&lt;p&gt;하지만 개발 과정에서 URL을 반복해서 생성해야 한다면 이야기가 달라집니다.&lt;/p&gt;

&lt;p&gt;예를 들어 이런 경우입니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;게시물이 등록될 때 공유용 짧은 링크를 자동 생성&lt;/li&gt;
&lt;li&gt;긴 UTM 링크를 생성한 뒤 자동으로 URL 단축&lt;/li&gt;
&lt;li&gt;관리자 페이지에서 버튼 한 번으로 공유 링크 생성&lt;/li&gt;
&lt;li&gt;사내 도구나 개인 프로젝트에서 짧은 링크 생성&lt;/li&gt;
&lt;li&gt;여러 URL을 처리하는 자동화 작업&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이럴 때는 웹페이지에서 하나씩 URL을 입력하는 것보다 &lt;strong&gt;URL 단축 API를 사용하는 방식&lt;/strong&gt;이 편합니다.&lt;/p&gt;

&lt;p&gt;이번 글에서는 nly.kr의 URL 단축 API를 이용해 cURL과 JavaScript에서 긴 링크를 짧게 만드는 기본 흐름을 정리해보겠습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  URL 단축 API를 사용하면 무엇이 달라질까?
&lt;/h2&gt;

&lt;p&gt;일반적인 URL 단축 과정은 다음과 같습니다.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;URL 단축 사이트를 연다.&lt;/li&gt;
&lt;li&gt;긴 URL을 붙여넣는다.&lt;/li&gt;
&lt;li&gt;단축 버튼을 누른다.&lt;/li&gt;
&lt;li&gt;생성된 짧은 URL을 복사한다.&lt;/li&gt;
&lt;li&gt;필요한 곳에 붙여넣는다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;한두 번이라면 전혀 문제가 없습니다.&lt;/p&gt;

&lt;p&gt;하지만 프로그램에서 URL이 계속 생성된다면 이 과정을 사람이 반복할 필요는 없습니다.&lt;/p&gt;

&lt;p&gt;API를 사용하면 프로그램이&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;긴 URL 생성
↓
URL 단축 API 호출
↓
짧은 URL 응답
↓
DB 저장 또는 화면 표시
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;처럼 처리할 수 있습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  nly.kr URL 단축 API 기본 구조
&lt;/h2&gt;

&lt;p&gt;nly.kr에서는 다음 Endpoint로 URL 단축 요청을 보낼 수 있습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://nly.kr/api/shorten
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;요청 데이터는 &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; 형식으로 전송합니다.&lt;/p&gt;

&lt;p&gt;API Key는 헤더에 포함할 수 있습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-API-Key: YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;단축 요청에는 기본적으로 두 가지 값이 필요합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url
category
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;url&lt;/code&gt;은 줄이려는 원본 주소이고, &lt;code&gt;category&lt;/code&gt;는 해당 링크의 분류입니다.&lt;/p&gt;

&lt;p&gt;예를 들어 개발 관련 문서라면 &lt;code&gt;dev&lt;/code&gt; 카테고리를 사용할 수 있습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  cURL로 URL 단축하기
&lt;/h2&gt;

&lt;p&gt;터미널에서 가장 간단하게 테스트하려면 cURL을 사용할 수 있습니다.&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 &lt;span class="s2"&gt;"https://nly.kr/api/shorten"&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-API-Key: YOUR_API_KEY"&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;"Accept: 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;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"url=https://example.com/docs/getting-started"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"category=dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;정상적으로 처리되면 결과는 JSON 형태로 받을 수 있습니다.&lt;/p&gt;

&lt;p&gt;예를 들면 이런 구조입니다.&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_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://nly.kr/Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"original_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://example.com/docs/getting-started"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dev"&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;code&gt;short_url&lt;/code&gt; 값을 가져와 저장하거나 사용자에게 보여주면 됩니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  JavaScript에서 URL 단축하기
&lt;/h2&gt;

&lt;p&gt;JavaScript에서도 같은 방식으로 요청할 수 있습니다.&lt;/p&gt;

&lt;p&gt;아래는 Node.js 등 서버 측 JavaScript에서 사용할 수 있는 간단한 예입니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_API_KEY&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/docs/getting-started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createShortUrl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://nly.kr/api/shorten&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;URL shortening failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Short URL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;createShortUrl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;성공하면 &lt;code&gt;data.short_url&lt;/code&gt;을 이용해 이후 작업을 이어갈 수 있습니다.&lt;/p&gt;

&lt;p&gt;예를 들어 DB에 저장하거나 이메일, 알림 메시지, 관리자 페이지 등에 짧은 주소를 표시할 수 있습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Key는 프론트엔드에 노출하지 않는 것이 좋다
&lt;/h2&gt;

&lt;p&gt;JavaScript 예제를 볼 때 주의할 점이 하나 있습니다.&lt;/p&gt;

&lt;p&gt;API Key가 필요한 API라면 실제 서비스에서는 API Key를 브라우저 JavaScript 코드에 그대로 넣지 않는 것이 좋습니다.&lt;/p&gt;

&lt;p&gt;브라우저에 전달된 코드는 사용자가 확인할 수 있기 때문입니다.&lt;/p&gt;

&lt;p&gt;따라서 실제 서비스에서는 보통 다음과 같은 구조가 더 적합합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   ↓
내 서버
   ↓
URL Shortener API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API Key는 서버에 저장하고 브라우저에서는 자신의 서버를 호출하도록 구성하는 방식입니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  UTM 링크 자동화에도 활용할 수 있다
&lt;/h2&gt;

&lt;p&gt;URL 단축 API가 특히 유용한 경우 중 하나가 UTM 링크입니다.&lt;/p&gt;

&lt;p&gt;마케팅이나 유입 분석을 위해 URL에 여러 파라미터를 붙이면 주소가 금방 길어집니다.&lt;/p&gt;

&lt;p&gt;예를 들어:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/product/123
?utm_source=newsletter
&amp;amp;utm_medium=email
&amp;amp;utm_campaign=summer_event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;실제로는 한 줄의 URL이지만 사람이 보기에는 상당히 길어질 수 있습니다.&lt;/p&gt;

&lt;p&gt;이런 시스템을 만들고 있다면&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;원본 URL
↓
UTM 파라미터 생성
↓
완성된 UTM URL
↓
URL 단축 API
↓
짧은 공유 URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;순서로 자동화할 수 있습니다.&lt;/p&gt;

&lt;p&gt;여기서 중요한 점은 &lt;strong&gt;UTM 파라미터까지 포함된 완성된 원본 URL을 먼저 만든 후 단축하는 것&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;p&gt;사용자는 짧은 링크를 공유하지만, 링크를 통해 이동할 때는 원래 목적지의 UTM 정보를 그대로 사용할 수 있는 구조가 됩니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  직접 URL을 줄일 때와 API를 사용할 때
&lt;/h2&gt;

&lt;p&gt;두 방식 중 무조건 하나가 좋은 것은 아닙니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  웹 URL 단축기가 편한 경우
&lt;/h3&gt;

&lt;p&gt;몇 개의 링크만 빠르게 줄이고 싶다면 굳이 코드를 작성할 필요가 없습니다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr 무료 URL 단축기&lt;/a&gt;에서 긴 URL을 입력해 바로 짧은 링크를 만들 수 있습니다.&lt;/p&gt;

&lt;p&gt;웹 방식은 직접 URL을 줄이거나 QR 코드까지 함께 활용하고 싶을 때 편합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  API가 편한 경우
&lt;/h3&gt;

&lt;p&gt;반대로 아래처럼 반복적인 처리가 있다면 API 방식이 더 적합합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;게시물 등록
→ 짧은 공유 URL 자동 생성

상품 등록
→ 공유 링크 자동 생성

UTM 링크 생성
→ 짧은 캠페인 링크 자동 생성

관리자 도구
→ 버튼 클릭 시 URL 단축
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;결국 기준은 간단합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;사람이 가끔 만드는 링크라면 웹 도구, 프로그램이 반복해서 만들어야 한다면 API&lt;/strong&gt;입니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  URL 단축과 SEO는 별개의 문제다
&lt;/h2&gt;

&lt;p&gt;URL을 짧게 만들었다고 해서 원본 페이지의 Google 검색 순위가 올라가는 것은 아닙니다.&lt;/p&gt;

&lt;p&gt;URL 단축은 주로&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;긴 링크 정리&lt;/li&gt;
&lt;li&gt;SNS 및 메신저 공유&lt;/li&gt;
&lt;li&gt;문서에 넣을 링크 정리&lt;/li&gt;
&lt;li&gt;UTM 주소 단순화&lt;/li&gt;
&lt;li&gt;QR 코드 활용&lt;/li&gt;
&lt;li&gt;프로그램 자동화&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;같은 목적에 사용하는 것이 적절합니다.&lt;/p&gt;

&lt;p&gt;검색 노출을 늘리려면 결국 검색엔진이 평가하는 실제 콘텐츠와 원본 페이지를 개선해야 합니다.&lt;/p&gt;

&lt;p&gt;즉, &lt;strong&gt;단축 URL은 SEO 기법이라기보다 링크를 공유하고 관리하는 도구&lt;/strong&gt;에 가깝습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  마무리
&lt;/h2&gt;

&lt;p&gt;URL 단축은 기능 자체는 단순합니다.&lt;/p&gt;

&lt;p&gt;하지만 API와 연결하면 활용 범위가 꽤 넓어집니다.&lt;/p&gt;

&lt;p&gt;단순히 긴 주소를 줄이는 것을 넘어&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;콘텐츠 생성
→ URL 생성
→ UTM 추가
→ URL 단축
→ 저장 및 공유
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;과정 전체를 자동화할 수 있기 때문입니다.&lt;/p&gt;

&lt;p&gt;직접 URL을 줄이고 싶다면:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL 단축기&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;프로젝트에서 자동으로 단축 URL을 생성하고 싶다면:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://nly.kr/api/" rel="noopener noreferrer"&gt;nly.kr URL 단축 API 문서&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;를 참고할 수 있습니다.&lt;/p&gt;

&lt;p&gt;작은 자동화라도 반복되는 복사·붙여넣기를 없애면 개발 흐름이 훨씬 단순해집니다.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;DEV.to Tags&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webdev&lt;/code&gt; &lt;code&gt;api&lt;/code&gt; &lt;code&gt;javascript&lt;/code&gt; &lt;code&gt;productivity&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Shorten URLs from Your App with a Simple API</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Fri, 14 Aug 2026 02:54:27 +0000</pubDate>
      <link>https://dev.to/nly-kr/how-to-shorten-urls-from-your-app-with-a-simple-api-2apf</link>
      <guid>https://dev.to/nly-kr/how-to-shorten-urls-from-your-app-with-a-simple-api-2apf</guid>
      <description>&lt;p&gt;When building a small app or side project, there are times when you need to generate short links automatically.&lt;/p&gt;

&lt;p&gt;You could build your own URL shortening system, but for many projects that means adding a database, redirect logic, unique code generation, and analytics just for one small feature.&lt;/p&gt;

&lt;p&gt;A simpler option is to use a URL shortening API.&lt;/p&gt;

&lt;p&gt;I’ve been working with the &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt;, which also provides an API for creating short URLs programmatically.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show a few simple examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Endpoint
&lt;/h2&gt;

&lt;p&gt;The endpoint for creating a short URL is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://nly.kr/api/shorten
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API expects form-encoded data.&lt;/p&gt;

&lt;p&gt;Two parameters are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt; — the original HTTP or HTTPS URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;category&lt;/code&gt; — a category key for the link&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also need an API key for authentication.&lt;/p&gt;

&lt;p&gt;The recommended header is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-API-Key: your_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the full API documentation here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nly.kr/api" rel="noopener noreferrer"&gt;nly.kr URL Shortener API&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Example with cURL
&lt;/h2&gt;

&lt;p&gt;The quickest way to test the API is with cURL.&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 &lt;span class="s2"&gt;"https://nly.kr/api/shorten"&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-API-Key: your_API_KEY"&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;"Accept: 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="s2"&gt;"url=https://example.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"category=dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response looks similar to 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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_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://nly.kr/Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"original_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://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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ab3XkQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"member_idx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dev"&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;Once you have the &lt;code&gt;short_url&lt;/code&gt;, you can store it, display it to the user, or use it anywhere your application needs a shorter shareable link.&lt;/p&gt;




&lt;h2&gt;
  
  
  JavaScript Example
&lt;/h2&gt;

&lt;p&gt;You can call the same endpoint with &lt;code&gt;fetch()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your_API_KEY&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;longUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&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;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://nly.kr/api/shorten&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;category&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to shorten URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Short URL:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be useful when you want to generate short links from an internal tool, dashboard, CMS, or other web application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Python Example
&lt;/h2&gt;

&lt;p&gt;The same request is also straightforward with Python and the &lt;code&gt;requests&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&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="n"&gt;long_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://example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="sh"&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://nly.kr/api/shorten&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-API-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;data&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;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;long_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP:&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&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;p&gt;If the request succeeds, the JSON response contains the generated short URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Categories
&lt;/h2&gt;

&lt;p&gt;The API requires a category when creating a link.&lt;/p&gt;

&lt;p&gt;Some available category keys include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ai
dev
design
productivity
pdf
shopping
news
education
media
community
finance
travel
download
tools
etc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, a developer documentation link could use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category=dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while an AI-related service could use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category=ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Handling Errors
&lt;/h2&gt;

&lt;p&gt;It’s also worth checking the HTTP status instead of assuming every request succeeded.&lt;/p&gt;

&lt;p&gt;For example, the API may return an error response such as:&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;Typical API errors can include invalid parameters, missing authentication, unsupported methods, or server errors.&lt;/p&gt;

&lt;p&gt;In production code, always handle both HTTP errors and malformed responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Is a URL Shortening API Useful?
&lt;/h2&gt;

&lt;p&gt;I find this kind of API useful for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically generating links from a CMS&lt;/li&gt;
&lt;li&gt;Creating short links for notification messages&lt;/li&gt;
&lt;li&gt;Sharing long campaign or tracking URLs&lt;/li&gt;
&lt;li&gt;Generating links from internal admin tools&lt;/li&gt;
&lt;li&gt;Creating cleaner URLs for documentation&lt;/li&gt;
&lt;li&gt;Automating links in side projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For one-off links, using a web interface is usually faster.&lt;/p&gt;

&lt;p&gt;You can create one directly with the &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But when link generation becomes part of an application workflow, an API is much easier to automate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;URL shortening is a small feature, but implementing and maintaining a complete shortening service yourself can be unnecessary for many projects.&lt;/p&gt;

&lt;p&gt;A simple API lets you keep the application logic focused on your actual product while handling short-link generation separately.&lt;/p&gt;

&lt;p&gt;If you want to experiment with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;Try the nly.kr URL Shortener&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nly.kr/api" rel="noopener noreferrer"&gt;Read the nly.kr API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For small apps, automation scripts, and side projects, that’s often all you need.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why I Couldn't Start Telecom Ads (Landing Page Was the Problem)</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Thu, 16 Apr 2026 10:45:55 +0000</pubDate>
      <link>https://dev.to/nly-kr/why-i-couldnt-start-telecom-ads-landing-page-was-the-problem-2h2p</link>
      <guid>https://dev.to/nly-kr/why-i-couldnt-start-telecom-ads-landing-page-was-the-problem-2h2p</guid>
      <description>&lt;p&gt;I tried to run telecom ads a while ago, and surprisingly, the hardest part wasn’t the ads themselves.&lt;/p&gt;

&lt;p&gt;Setting up ads is actually not that difficult. There are tons of guides, and most platforms are pretty straightforward.&lt;/p&gt;

&lt;p&gt;The real problem was something else.&lt;/p&gt;

&lt;p&gt;Landing pages.&lt;/p&gt;

&lt;p&gt;At first, I thought I could just collect leads through messages or chat.&lt;br&gt;
But once I actually ran ads, I realized people don’t convert that way.&lt;/p&gt;

&lt;p&gt;They want to:&lt;/p&gt;

&lt;p&gt;check details&lt;br&gt;
compare options&lt;br&gt;
feel some level of trust&lt;/p&gt;

&lt;p&gt;Without a proper landing page, they just leave.&lt;/p&gt;

&lt;p&gt;So I tried building one myself.&lt;/p&gt;

&lt;p&gt;That’s where things got messy.&lt;/p&gt;

&lt;p&gt;I didn’t know:&lt;/p&gt;

&lt;p&gt;what kind of copy to write&lt;br&gt;
how to structure the page&lt;br&gt;
what information should go first&lt;/p&gt;

&lt;p&gt;And outsourcing wasn’t much better.&lt;/p&gt;

&lt;p&gt;expensive&lt;br&gt;
slow to update&lt;br&gt;
annoying when conditions change (which happens a lot in telecom/rental)&lt;/p&gt;

&lt;p&gt;Eventually, I found a different approach.&lt;/p&gt;

&lt;p&gt;Check it here:&lt;br&gt;
&lt;a href="https://adbase.kr/landing/telecom" rel="noopener noreferrer"&gt;landing page tool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1drbpv87xq2fiban4zb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1drbpv87xq2fiban4zb.png" alt=" " width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of building everything from scratch, this kind of setup lets you:&lt;/p&gt;

&lt;p&gt;pick an industry&lt;br&gt;
generate a basic structure&lt;br&gt;
get actual usable copy filled in&lt;/p&gt;

&lt;p&gt;What stood out to me was this:&lt;/p&gt;

&lt;p&gt;It’s not really about building a “perfect” landing page.&lt;/p&gt;

&lt;p&gt;It’s more about&lt;br&gt;
getting something ready fast so you can actually start running ads.&lt;/p&gt;

&lt;p&gt;After trying it, I realized something:&lt;/p&gt;

&lt;p&gt;In telecom ads, speed matters more than perfection.&lt;/p&gt;

&lt;p&gt;If you wait too long to prepare everything, you miss timing.&lt;br&gt;
And by then, competition is already ahead.&lt;/p&gt;

&lt;p&gt;So now I think about it differently.&lt;/p&gt;

&lt;p&gt;Instead of trying to make the perfect landing page,&lt;/p&gt;

&lt;p&gt;I just:&lt;/p&gt;

&lt;p&gt;create something quickly&lt;br&gt;
launch ads&lt;br&gt;
improve based on results&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Generate Punycode in 1 Second with This Simple Online Tool</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:21:46 +0000</pubDate>
      <link>https://dev.to/nly-kr/generate-punycode-in-1-second-with-this-simple-online-tool-3e4o</link>
      <guid>https://dev.to/nly-kr/generate-punycode-in-1-second-with-this-simple-online-tool-3e4o</guid>
      <description>&lt;p&gt;Internationalized domain names look friendly to users, but once you start working with DNS settings or server panels, you quickly run into encoding issues. Many systems don’t accept special characters directly and expect Punycode instead. That usually forces you to pause your workflow and search for a converter.&lt;br&gt;
I’ve had to deal with this often while configuring multilingual sites and testing domain behavior. Instead of jumping between heavy tools, I started using a lightweight generator that focuses purely on fast conversion: &lt;a href="https://nly.kr/tools/punycode%EF%BF%BD" rel="noopener noreferrer"&gt;https://nly.kr/tools/punycode�&lt;/a&gt;. You paste the text, it converts instantly, and you move on. No extra steps.&lt;br&gt;
This becomes especially useful when debugging URL handling or checking how a system processes international domains. During one setup, a hosting panel kept rejecting a domain name without giving a clear error. Converting it to Punycode solved the problem immediately. Situations like that are where a quick one-second tool actually saves time.&lt;br&gt;
There are still a few practical things to watch for. Some admin panels cache domain values, so changes might not apply right away. Also, copying text from formatted editors can introduce hidden characters that break encoding. When accuracy matters, especially for security testing, it’s smart to double-check results using browser developer tools.&lt;br&gt;
For everyday work, though, a simple converter is one of those small utilities that quietly improves your workflow. It removes friction from repetitive tasks and lets you focus on the real problem instead of fighting with encoding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9mzgemo68p2djy72li2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9mzgemo68p2djy72li2.jpg" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Small Tool I Ended Up Using More Than Expected</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Wed, 04 Feb 2026 05:01:12 +0000</pubDate>
      <link>https://dev.to/nly-kr/a-small-tool-i-ended-up-using-more-than-expected-4n3i</link>
      <guid>https://dev.to/nly-kr/a-small-tool-i-ended-up-using-more-than-expected-4n3i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6x6354gi1nzn0savyvir.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6x6354gi1nzn0savyvir.jpg" alt=" " width="799" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t originally plan to build a product.&lt;/p&gt;

&lt;p&gt;It started as one of those small utilities you create for yourself because existing tools feel heavier than they need to be.&lt;/p&gt;

&lt;p&gt;I wanted a simple way to shorten long URLs, generate QR codes, and create shareable documents without going through a complicated setup.&lt;/p&gt;

&lt;p&gt;That eventually became &lt;strong&gt;nly.kr&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple URL Shortener
&lt;/h2&gt;

&lt;p&gt;There are plenty of URL shorteners available, but sometimes I only need a very simple workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste a long URL&lt;/li&gt;
&lt;li&gt;Generate a short link&lt;/li&gt;
&lt;li&gt;Copy and share it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt; keeps that basic process simple and does not require signup for basic URL shortening.&lt;/p&gt;

&lt;p&gt;It also generates a QR code for the shortened link, which is useful when sharing links outside the browser.&lt;/p&gt;

&lt;p&gt;I often use short links for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub README files&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Messaging apps&lt;/li&gt;
&lt;li&gt;Presentations&lt;/li&gt;
&lt;li&gt;Long tracking URLs&lt;/li&gt;
&lt;li&gt;QR codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Registered users can optionally access link management and click statistics.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding PDF and Document Tools
&lt;/h2&gt;

&lt;p&gt;After working on the URL shortener, I started running into another small problem.&lt;/p&gt;

&lt;p&gt;Sometimes I needed a quick way to create or share documents without sending files back and forth.&lt;/p&gt;

&lt;p&gt;That led me to add web-based PDF and document tools to nly.kr.&lt;/p&gt;

&lt;p&gt;The PDF service is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nly.kr/pdf/create" rel="noopener noreferrer"&gt;Create and share PDF documents with nly.kr&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on the use case, documents can be created in the browser, downloaded as PDF files, or shared through generated links.&lt;/p&gt;

&lt;p&gt;This has been useful for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Meeting notes&lt;/li&gt;
&lt;li&gt;Simple reports&lt;/li&gt;
&lt;li&gt;Quotations&lt;/li&gt;
&lt;li&gt;Resumes&lt;/li&gt;
&lt;li&gt;Documents that need to be shared through a link&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  It Gradually Became More Than One Tool
&lt;/h2&gt;

&lt;p&gt;What started as a small URL shortener gradually expanded into a collection of lightweight web tools.&lt;/p&gt;

&lt;p&gt;nly.kr now includes features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL shortening&lt;/li&gt;
&lt;li&gt;QR code generation&lt;/li&gt;
&lt;li&gt;Link management&lt;/li&gt;
&lt;li&gt;Click statistics&lt;/li&gt;
&lt;li&gt;PDF and document tools&lt;/li&gt;
&lt;li&gt;Developer APIs&lt;/li&gt;
&lt;li&gt;Chrome extension support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I still try to keep the basic tools simple enough that you can open the page, use what you need, and move on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I’m Sharing It
&lt;/h2&gt;

&lt;p&gt;I built most of these features because I needed them myself.&lt;/p&gt;

&lt;p&gt;Over time, I realized that other developers, side-project builders, and people who regularly share links or documents might find them useful too.&lt;/p&gt;

&lt;p&gt;If you need a lightweight URL shortening tool, you can try the &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the other tools and services, the main site is available at &lt;a href="https://nly.kr/" rel="noopener noreferrer"&gt;nly.kr&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s pretty much how a tiny utility ended up becoming something I use much more often than I originally expected.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Simple URL Shortener for Side Projects (No Signup Required)</title>
      <dc:creator>nly.kr</dc:creator>
      <pubDate>Mon, 02 Feb 2026 04:40:48 +0000</pubDate>
      <link>https://dev.to/nly-kr/a-simple-url-shortener-for-side-projects-no-signup-required-4c14</link>
      <guid>https://dev.to/nly-kr/a-simple-url-shortener-for-side-projects-no-signup-required-4c14</guid>
      <description>&lt;p&gt;When working on small side projects, I often need a quick way to shorten URLs without dealing with complicated dashboards or mandatory signups.&lt;/p&gt;

&lt;p&gt;Sometimes I just want to paste a long URL, get a shorter link, and move on.&lt;/p&gt;

&lt;p&gt;I recently tried the &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt;, a lightweight tool for creating short links without mandatory signup.&lt;/p&gt;

&lt;p&gt;The basic workflow is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste a long URL&lt;/li&gt;
&lt;li&gt;Create a short link&lt;/li&gt;
&lt;li&gt;Copy and share it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No account is required for basic URL shortening.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Like About It
&lt;/h2&gt;

&lt;p&gt;A few things make it convenient for simple projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No signup required to shorten URLs&lt;/li&gt;
&lt;li&gt;Clean and fast interface&lt;/li&gt;
&lt;li&gt;Short and easy-to-share links&lt;/li&gt;
&lt;li&gt;QR code generation&lt;/li&gt;
&lt;li&gt;Optional link management for registered users&lt;/li&gt;
&lt;li&gt;Click statistics for registered users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, nly.kr also provides an API for integrating URL shortening into websites or applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I Use It
&lt;/h2&gt;

&lt;p&gt;I mainly find URL shorteners useful for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub README files&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Messaging apps&lt;/li&gt;
&lt;li&gt;Social media posts&lt;/li&gt;
&lt;li&gt;Long tracking or campaign URLs&lt;/li&gt;
&lt;li&gt;QR codes for offline sharing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is especially useful when a URL contains a lot of parameters and becomes difficult to share cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping Things Simple
&lt;/h2&gt;

&lt;p&gt;There are many URL shorteners with advanced dashboards, marketing features, and paid plans.&lt;/p&gt;

&lt;p&gt;Those features can be useful, but sometimes they are more than I need.&lt;/p&gt;

&lt;p&gt;For small projects, I usually prefer something that lets me create a short link quickly without setting up an account first.&lt;/p&gt;

&lt;p&gt;If you need a simple URL shortening tool, you can try the &lt;a href="https://nly.kr/create-short-url" rel="noopener noreferrer"&gt;nly.kr URL Shortener&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For basic use cases and side projects, it keeps the process simple and gets the job done.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>tools</category>
      <category>webdev</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
