Javid
·14 min read

Building Real-World Services with SelfDevKit: A Developer Case Study

Cover Image for Building Real-World Services with SelfDevKit: A Developer Case Study

Building Software with Privacy-First Tools

Every production service I build relies on the same core set of developer utilities: JSON formatters for API debugging, URL encoders for query parameters, hash generators for cache keys, and regex testers for data validation. After years of using scattered online tools for these tasks, I switched to SelfDevKit - and it fundamentally changed how I approach development. This is the story of building Is It Visible, a real-time mountain visibility forecasting platform, using offline-first developer tools.

When you're building a service that handles user data and integrates with external APIs, the last thing you want is to paste sensitive information into random websites. Yet that's exactly what most developers do - copying API responses into json-formatter.com, pasting tokens into jwt.io, and encoding URLs through online tools that log every request.

I created SelfDevKit to solve this problem for myself and other developers. But theory only goes so far. In this post, I'll walk through how I actually use these tools while building real production services, using my latest project Is It Visible as a concrete example.

Table of contents

  1. About Is It Visible
  2. The development workflow
  3. JSON tools for API debugging
  4. URL encoding for API requests
  5. Hash generation for caching
  6. Regex testing for data validation
  7. Base64 for data handling
  8. Unix timestamps for weather data
  9. Color tools for visibility scores
  10. UUID generation for unique identifiers
  11. The offline advantage in practice
  12. Lessons learned
  13. In summary

About Is It Visible

Is It Visible is a real-time mountain visibility forecasting platform that helps travelers and photographers determine the best times to view iconic mountains. The service currently covers:

  • Mt. Fuji, Japan - Visible from Tokyo, Yokohama, and Lake Kawaguchiko
  • Mount Rainier, Washington - The iconic peak visible from Seattle
  • Denali, Alaska - Only 30% of visitors ever see this mountain

The platform analyzes weather data every 15 minutes to generate visibility scores from 0-100, helping users plan their trips around optimal viewing conditions. It integrates with multiple weather APIs, processes altitude-specific cloud data, and provides 10-day forecasts with hourly breakdowns.

Building this service required constant interaction with APIs, data transformation, URL construction, and debugging - exactly the tasks where developer tools become essential.

The development workflow

Modern web development involves a continuous cycle of:

  1. API Integration - Fetching data from external services
  2. Data Transformation - Parsing, validating, and reformatting responses
  3. Debugging - Identifying issues in data flow
  4. Testing - Validating patterns and edge cases
  5. Optimization - Implementing caching and performance improvements

Each of these steps benefits from having the right tools immediately accessible. Let me walk through specific examples from building Is It Visible.

JSON tools for API debugging

Weather APIs return complex nested JSON structures. When building Is It Visible, I integrate with the Open-Meteo API, which returns forecasts in this format:

{"latitude":35.6762,"longitude":139.6503,"generationtime_ms":0.051021575927734375,"utc_offset_seconds":32400,"timezone":"Asia/Tokyo","timezone_abbreviation":"JST","elevation":40.0,"hourly_units":{"time":"iso8601","temperature_2m":"°C","relative_humidity_2m":"%","precipitation_probability":"%","cloud_cover":"%","cloud_cover_low":"%","cloud_cover_mid":"%","cloud_cover_high":"%","visibility":"m","wind_speed_10m":"km/h"},"hourly":{"time":["2026-01-31T00:00","2026-01-31T01:00","2026-01-31T02:00"],"temperature_2m":[5.2,4.8,4.5],"relative_humidity_2m":[65,68,71],"precipitation_probability":[0,0,5],"cloud_cover":[25,30,35],"cloud_cover_low":[10,15,20],"cloud_cover_mid":[5,10,15],"cloud_cover_high":[10,5,0],"visibility":[24140,24140,20000],"wind_speed_10m":[8.5,7.2,6.8]}}

That's unreadable. Using the JSON Formatter, I instantly transform it into:

{
  "latitude": 35.6762,
  "longitude": 139.6503,
  "generationtime_ms": 0.051021575927734375,
  "utc_offset_seconds": 32400,
  "timezone": "Asia/Tokyo",
  "timezone_abbreviation": "JST",
  "elevation": 40.0,
  "hourly_units": {
    "time": "iso8601",
    "temperature_2m": "°C",
    "relative_humidity_2m": "%",
    "precipitation_probability": "%",
    "cloud_cover": "%",
    "cloud_cover_low": "%",
    "cloud_cover_mid": "%",
    "cloud_cover_high": "%",
    "visibility": "m",
    "wind_speed_10m": "km/h"
  },
  "hourly": {
    "time": [
      "2026-01-31T00:00",
      "2026-01-31T01:00",
      "2026-01-31T02:00"
    ],
    "temperature_2m": [5.2, 4.8, 4.5],
    "relative_humidity_2m": [65, 68, 71],
    "precipitation_probability": [0, 0, 5],
    "cloud_cover": [25, 30, 35],
    "cloud_cover_low": [10, 15, 20],
    "cloud_cover_mid": [5, 10, 15],
    "cloud_cover_high": [10, 5, 0],
    "visibility": [24140, 24140, 20000],
    "wind_speed_10m": [8.5, 7.2, 6.8]
  }
}

Now I can see the structure clearly:

  • The API returns separate cloud cover percentages for low (0-3km), mid (3-8km), and high (8km+) altitude layers
  • Visibility is in meters (24140m = about 15 miles)
  • All timestamps are in ISO 8601 format with the local timezone

Debugging API response issues

During development, I encountered an issue where visibility scores were unexpectedly low. The formatted JSON revealed the problem:

{
  "cloud_cover_high": [95, 98, 100],
  "visibility": [50000, 50000, 50000]
}

High-altitude clouds (above 8km) were at 95-100% coverage, but ground-level visibility was excellent. The original algorithm weighted all cloud layers equally, but for mountain viewing, high-altitude clouds directly obscure the summit even when surface visibility is clear.

Without proper JSON formatting, this discrepancy would have taken much longer to identify. The JSON Tools made the data structure immediately apparent.

Validating API responses

Before processing weather data, I validate the JSON structure to catch API changes or malformed responses:

interface WeatherResponse {
  hourly: {
    time: string[];
    cloud_cover: number[];
    cloud_cover_low: number[];
    cloud_cover_mid: number[];
    cloud_cover_high: number[];
    visibility: number[];
    precipitation_probability: number[];
  };
}

function validateWeatherData(data: unknown): data is WeatherResponse {
  if (!data || typeof data !== 'object') return false;
  const response = data as Record<string, unknown>;

  if (!response.hourly || typeof response.hourly !== 'object') return false;

  const hourly = response.hourly as Record<string, unknown>;
  const requiredArrays = [
    'time', 'cloud_cover', 'cloud_cover_low',
    'cloud_cover_mid', 'cloud_cover_high',
    'visibility', 'precipitation_probability'
  ];

  return requiredArrays.every(key => Array.isArray(hourly[key]));
}

When validation fails, I paste the actual API response into the JSON formatter to compare against the expected structure. This workflow happens multiple times per development session.

For a complete guide on JSON formatting and validation, see our JSON Formatter, Viewer & Validator Guide.

URL encoding for API requests

Weather APIs require geographic coordinates and various parameters in the URL. For Is It Visible, I construct URLs like:

https://api.open-meteo.com/v1/forecast?latitude=35.3606&longitude=138.7274&hourly=temperature_2m,relative_humidity_2m,precipitation_probability,cloud_cover,cloud_cover_low,cloud_cover_mid,cloud_cover_high,visibility,wind_speed_10m&timezone=Asia/Tokyo&forecast_days=10

The timezone parameter Asia/Tokyo contains a forward slash that must be URL-encoded as Asia%2FTokyo for the request to work correctly.

Building complex query strings

The URL Encoder/Decoder helps me verify that parameters are properly encoded:

const params = new URLSearchParams({
  latitude: '35.3606',
  longitude: '138.7274',
  hourly: [
    'temperature_2m',
    'relative_humidity_2m',
    'precipitation_probability',
    'cloud_cover',
    'cloud_cover_low',
    'cloud_cover_mid',
    'cloud_cover_high',
    'visibility',
    'wind_speed_10m'
  ].join(','),
  timezone: 'Asia/Tokyo',
  forecast_days: '10'
});

const url = `https://api.open-meteo.com/v1/forecast?${params.toString()}`;

When debugging failed requests, I decode the full URL to verify each parameter:

latitude=35.3606
longitude=138.7274
hourly=temperature_2m,relative_humidity_2m,precipitation_probability,cloud_cover,cloud_cover_low,cloud_cover_mid,cloud_cover_high,visibility,wind_speed_10m
timezone=Asia/Tokyo
forecast_days=10

Debugging redirect URLs

Is It Visible includes canonical URLs for SEO. When testing social sharing, I need to verify that encoded URLs resolve correctly:

https://is-it-visible.com/fuji?utm_source=twitter&utm_medium=social&utm_campaign=launch

The URL encoder helps me quickly verify that special characters in campaign names or referrer URLs are properly handled.

Hash generation for caching

Weather data updates every 15 minutes, but I don't want to fetch from the API on every page load. Is It Visible implements intelligent caching using hash-based cache keys.

Creating cache keys

Each cache entry needs a unique key based on the request parameters:

import { createHash } from 'crypto';

function generateCacheKey(
  mountain: string,
  coordinates: { lat: number; lng: number },
  forecastDays: number
): string {
  const keyData = JSON.stringify({
    mountain,
    lat: coordinates.lat.toFixed(4),
    lng: coordinates.lng.toFixed(4),
    days: forecastDays,
    // Round to 15-minute intervals
    timestamp: Math.floor(Date.now() / (15 * 60 * 1000))
  });

  return createHash('sha256').update(keyData).digest('hex').slice(0, 16);
}

// Example: "3a7f2b9c8d1e4f0a"
const cacheKey = generateCacheKey('fuji', { lat: 35.3606, lng: 138.7274 }, 10);

The Hash Generator helps me verify that my cache key implementation produces consistent results. I paste the input JSON and confirm the expected hash output.

Verifying hash consistency

During development, I discovered that floating-point precision caused cache misses:

// These produce different hashes!
{ lat: 35.360600000000001, lng: 138.7274 }
{ lat: 35.3606, lng: 138.7274 }

Using the hash generator to compare outputs revealed the issue immediately. The fix was to round coordinates to a fixed decimal precision before hashing.

For more details on hash algorithms and use cases, see our Hash Generator Guide.

Regex testing for data validation

Is It Visible accepts user input for location searches and feedback forms. Regular expressions validate this input before processing.

Validating coordinate formats

Users sometimes paste coordinates from Google Maps in various formats:

35.3606, 138.7274
35.3606,138.7274
35°21'38.2"N 138°43'38.6"E
N 35.3606, E 138.7274

The Regex Tester helps me build and test patterns for each format:

// Decimal degrees pattern
const decimalPattern = /^(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)$/;

// Test cases
const testInputs = [
  '35.3606, 138.7274',      // ✓ Match
  '35.3606,138.7274',       // ✓ Match
  '-35.3606, -138.7274',    // ✓ Match (negative coordinates)
  '35.3606 138.7274',       // ✗ No match (missing comma)
];

Extracting weather alert patterns

Weather APIs sometimes include alert messages that need parsing:

"WIND ADVISORY IN EFFECT FROM 6 PM THIS EVENING TO 3 AM PST FRIDAY"

Building a regex to extract the time range:

const alertTimePattern = /FROM\s+(\d+\s*(?:AM|PM))\s+.*?TO\s+(\d+\s*(?:AM|PM))/i;
const match = alertText.match(alertTimePattern);
// match[1] = "6 PM", match[2] = "3 AM"

The regex tester lets me iterate on patterns quickly, testing against multiple real alert messages without writing a full test harness.

For a complete guide to regular expressions, see our Regex Tester Guide.

Base64 for data handling

Several features in Is It Visible use Base64 encoding for data handling.

Encoding state in URLs

The share feature encodes current view settings into the URL:

interface ViewSettings {
  mountain: string;
  unit: 'metric' | 'imperial';
  theme: 'light' | 'dark';
  zoom: number;
}

function encodeSettings(settings: ViewSettings): string {
  const json = JSON.stringify(settings);
  return btoa(json);
}

function decodeSettings(encoded: string): ViewSettings {
  const json = atob(encoded);
  return JSON.parse(json);
}

// URL: https://is-it-visible.com/fuji?s=eyJtb3VudGFpbiI6ImZ1amkiLCJ1bml0IjoibWV0cmljIiwidGhlbWUiOiJsaWdodCIsInpvb20iOjEwfQ==

The Base64 Tools help me debug shared URLs by decoding the settings parameter to see what configuration was shared.

Handling embedded images

The blog section of Is It Visible uses markdown with embedded images. During development, I sometimes need to convert small icons to Base64 data URIs:

![visibility-icon](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCI+PC9zdmc+)

The Base64 Image Tools handle the conversion without needing to upload icons to external services.

For more on Base64 encoding, see our Base64 Encode and Decode Guide.

Unix timestamps for weather data

Weather APIs use Unix timestamps extensively. Is It Visible displays forecasts in local time for each mountain's timezone.

Converting API timestamps

The Open-Meteo API returns ISO 8601 timestamps, but some caching systems use Unix timestamps:

// API returns: "2026-01-31T14:00"
// Cache key needs: 1738332000

function toUnixTimestamp(isoString: string, timezone: string): number {
  const date = new Date(isoString);
  return Math.floor(date.getTime() / 1000);
}

The Unix Timestamp Converter helps me verify conversions, especially when debugging timezone issues.

Timezone-aware display

Each mountain has a different timezone:

  • Mt. Fuji: Asia/Tokyo (JST, UTC+9)
  • Mount Rainier: America/Los_Angeles (PST/PDT, UTC-8/-7)
  • Denali: America/Anchorage (AKST/AKDT, UTC-9/-8)

When a user in New York views the Fuji forecast, times must display in JST. The timestamp converter helps me verify that conversions are correct:

Unix: 1738332000
→ UTC: 2026-01-31T05:00:00Z
→ JST: 2026-01-31T14:00:00+09:00
→ PST: 2026-01-30T21:00:00-08:00

Debugging cache expiration

Cache entries expire after 15 minutes. When debugging stale data issues, I check timestamps:

const cacheEntry = {
  data: weatherData,
  cachedAt: 1738330200,  // When was this cached?
  expiresAt: 1738331100  // When does it expire?
};

// Is it still valid?
const now = Math.floor(Date.now() / 1000);  // Current Unix timestamp
const isValid = now < cacheEntry.expiresAt;

Converting these timestamps to human-readable dates helps me understand cache behavior during debugging.

Color tools for visibility scores

Is It Visible uses a color-coded system to display visibility scores. The Color Picker was essential for designing this system.

Visibility score colors

The score gradient maps 0-100 to a color range:

function getScoreColor(score: number): string {
  if (score >= 80) return '#22c55e';  // Green - Excellent
  if (score >= 60) return '#84cc16';  // Lime - Good
  if (score >= 40) return '#eab308';  // Yellow - Fair
  if (score >= 20) return '#f97316';  // Orange - Poor
  return '#ef4444';                    // Red - Very Poor
}

The color picker helps me:

  • Convert between HEX, RGB, and HSL formats
  • Check color contrast for accessibility
  • Find complementary colors for the UI

Ensuring accessibility

Weather data needs to be accessible to all users. I use the color tools to verify that text remains readable against colored backgrounds:

.score-excellent {
  background-color: #22c55e;
  color: #052e16;  /* Dark green text */
}

.score-poor {
  background-color: #f97316;
  color: #431407;  /* Dark orange text */
}

The contrast ratio between text and background must meet WCAG AA standards (4.5:1 for normal text).

UUID generation for unique identifiers

Is It Visible uses UUIDs for several features:

Session tracking

Anonymous session IDs track user preferences without collecting personal data:

function getOrCreateSessionId(): string {
  let sessionId = localStorage.getItem('session_id');
  if (!sessionId) {
    sessionId = crypto.randomUUID();
    localStorage.setItem('session_id', sessionId);
  }
  return sessionId;
}

Feedback submission IDs

User feedback submissions get unique IDs for deduplication:

interface FeedbackSubmission {
  id: string;  // UUID
  timestamp: number;
  mountain: string;
  message: string;
}

const submission: FeedbackSubmission = {
  id: crypto.randomUUID(),
  timestamp: Date.now(),
  mountain: 'fuji',
  message: 'Great accuracy today!'
};

The UUID Generator helps me generate test UUIDs and verify the format during development.

The offline advantage in practice

Building Is It Visible reinforced why offline tools matter:

Sensitive data never leaves my machine

Weather APIs require API keys. When debugging authentication issues, I need to inspect headers containing these keys:

Authorization: Bearer sk_live_abc123...
X-API-Key: weather_api_key_xyz789...

With SelfDevKit, I can decode, format, and inspect these values without sending them to external servers. Online tools log every request - that's a significant security risk for production credentials.

Speed improves focus

Network latency breaks flow. When I'm debugging a JSON parsing issue, waiting 300ms for an online formatter to respond adds up across hundreds of operations. SelfDevKit processes data in milliseconds, keeping me focused on the actual problem.

Works anywhere

I often work on flights or in cafes with unreliable WiFi. Having all developer tools available offline means I can be productive regardless of connectivity. For a service that helps travelers like Is It Visible, this resonates personally.

No usage limits or rate limiting

Online tools often rate-limit free users. During intensive debugging sessions, hitting these limits at critical moments is frustrating. SelfDevKit has no limits - use the tools as much as needed.

For more on why offline tools matter, see Why Offline-First Developer Tools Matter More Than Ever.

Lessons learned

Building Is It Visible with SelfDevKit taught me several lessons about developer tooling:

1. Consolidation reduces friction

Before SelfDevKit, I had bookmarks for 15+ different online tools. Finding the right bookmark, waiting for the page to load, and context-switching between browser tabs added friction to every operation. Having everything in one offline application streamlines the workflow.

2. Privacy enables experimentation

When tools are private, I'm more willing to paste real production data for debugging. With online tools, I often sanitize data first - replacing real API keys with placeholders, removing actual user data. This sanitization takes time and sometimes obscures the actual issue. Offline tools remove this concern.

3. Reliability matters more than features

A tool that works instantly and consistently beats a feature-rich tool that's slow or occasionally unavailable. SelfDevKit prioritizes reliability - every tool works immediately, every time.

4. Integration opportunities

Having all tools in one application enables future integrations. For example, decoding a JWT could automatically format the payload JSON. Converting a Unix timestamp could offer to copy in multiple formats. These cross-tool workflows are only possible with a unified application.

In summary

Building real-world services like Is It Visible requires constant interaction with data transformation tools. JSON formatters, URL encoders, hash generators, regex testers, Base64 converters, and timestamp utilities are part of every development session.

SelfDevKit provides all these tools in a single offline application:

The key benefits for real-world development:

  1. Privacy - Sensitive data stays on your machine
  2. Speed - No network latency, instant results
  3. Reliability - Works offline, no rate limits
  4. Consolidation - All tools in one place

Whether you're building a weather service like Is It Visible, an e-commerce platform, or a developer API, these tools are essential parts of the development workflow. Having them available offline, instantly, without privacy concerns transforms daily productivity.

Ready to streamline your development workflow?

SelfDevKit includes 50+ developer tools that work entirely offline. Everything runs locally on your machine - no network requests, no data logging, no privacy concerns.

Download SelfDevKit — available for macOS, Windows, and Linux.

Or explore the full toolkit at selfdevkit.com/features to see all available tools including the JSON formatter, JWT decoder, and hash generator.

Start building with confidence, knowing your data never leaves your machine.

Related Articles

Getting Started with SelfDevKit: The Complete Guide to 50+ Offline Developer Tools
DEVELOPER TOOLS

Getting Started with SelfDevKit: The Complete Guide to 50+ Offline Developer Tools

Master SelfDevKit from installation to advanced workflows. Learn how to use JSON tools, JWT decoder, ID generators, and 50+ developer utilities to boost your productivity while keeping your data completely private.

Read →
JSON Formatter, Viewer & Validator: The Complete Guide for Developers
DEVELOPER TOOLS

JSON Formatter, Viewer & Validator: The Complete Guide for Developers

Learn how to format, view, validate, and debug JSON data efficiently. Discover the best JSON tools for developers and why offline formatters protect your sensitive API data.

Read →
Base64 Encode and Decode: Complete Guide for Developers
DEVELOPER TOOLS

Base64 Encode and Decode: Complete Guide for Developers

Learn how to base64 encode and decode text and images. Understand data URIs, when to use Base64, and how to convert images to Base64 strings for HTML, CSS, and APIs.

Read →
Why Offline-First Developer Tools Matter More Than Ever
DEVELOPER TOOLS

Why Offline-First Developer Tools Matter More Than Ever

Discover why privacy-focused, offline developer tools are essential in 2025. Learn how local processing protects your API keys, JWT tokens, and sensitive data while delivering instant performance.

Read →