Öğreticiler

Renk API'si Tasarlamak: Renk Verisi için REST Uç Noktaları

9 dk okuma

Bir renk API'si, kaç tane aracın renk verisi tükettiğini düşünene kadar niş görünür: dönüşüme ihtiyaç duyan tasarım araçları, kontrastı doğrulayan erişilebilirlik denetleyicileri, adlandırılmış renk araması gerektiren renk seçici bileşenler ve uyum rengi üreten palet oluşturucular. Bunlardan herhangi birini inşa ediyorsanız ya da diğer geliştiricilere renk verisi sağlıyorsanız, iyi tasarlanmış bir REST API'ye ihtiyacınız var.

Bu kılavuz, tam tasarım yüzeyini kapsıyor: şema kararları, uç nokta yapısı, yanıt formatları, hata yönetimi ve hız sınırlaması — ColorFYI'daki Renk Dönüştürücü ve Kontrast Denetleyici'ye güç veren uç noktaları inşa eden birinin bakış açısından.


Renk Verisi için API Şema Tasarımı

Temel Renk Nesnesi

Bir rengin birden fazla gösterimi vardır — hex, RGB, HSL, CMYK, OKLCH — ve birden fazla meta veri alanı. Kanonik yanıt nesnesi, istemcilerin ek istekler yapmak zorunda kalmadan ihtiyaç duydukları formatı kullanmalarına olanak tanıyarak hepsini içermelidir:

{
  "hex": "#FF5733",
  "rgb": {
    "r": 255,
    "g": 87,
    "b": 51,
    "css": "rgb(255, 87, 51)"
  },
  "hsl": {
    "h": 11,
    "s": 100,
    "l": 60,
    "css": "hsl(11, 100%, 60%)"
  },
  "hsv": {
    "h": 11,
    "s": 80,
    "v": 100
  },
  "cmyk": {
    "c": 0,
    "m": 66,
    "y": 80,
    "k": 0
  },
  "oklch": {
    "l": 0.63,
    "c": 0.19,
    "h": 27.5,
    "css": "oklch(0.63 0.19 27.5)"
  },
  "luminance": 0.215,
  "is_dark": false,
  "name": "Coral Red",
  "name_source": "color_name_database",
  "name_distance": 0.04
}

is_dark alanını (parlaklıktan türetilmiş) eklemek, istemcilerin WCAG parlaklık hesaplamasını kendileri uygulamak zorunda kalmamasını sağlar. name ve name_distance alanları, istemcilerin ayrı bir API çağrısı yapmadan en yakın adlandırılmış rengi göstermesine olanak tanır.

Adlandırma Kuralları

JSON anahtarları için snake_case kullanın (çoğu REST API ile tutarlı). İlgili alanları düzleştirmek yerine nesnelere yerleştirin (rgb, hsl, cmyk). İç içe nesneler daha okunabilir, JavaScript'te yapılandırması daha kolay ve yazılı arayüzlerle temiz bir şekilde eşleşir:

interface ColorResponse {
  hex: string;
  rgb: { r: number; g: number; b: number; css: string };
  hsl: { h: number; s: number; l: number; css: string };
  oklch: { l: number; c: number; h: number; css: string };
  luminance: number;
  is_dark: boolean;
  name: string | null;
  name_source: string | null;
  name_distance: number | null;
}

Hex Normalleştirme

Hex renkleri birden fazla formatta kabul edin — # ile veya olmadan, 3 basamaklı veya 6 basamaklı, büyük harf veya küçük harf — ve herhangi bir işlemden önce API katmanında normalleştirin:

# Python/Django örneği
import re

def normalize_hex(raw: str) -> str:
    """Hex girişini # olmadan 6 basamaklı büyük harfe normalleştir."""
    clean = raw.strip().lstrip('#').upper()
    if not re.match(r'^[0-9A-F]{3}$|^[0-9A-F]{6}$', clean):
        raise ValueError(f"Geçersiz hex rengi: {raw!r}")
    # 3 basamaklıyı 6 basamaklıya genişlet
    if len(clean) == 3:
        clean = ''.join(c * 2 for c in clean)
    return clean

Yanıtta normalleştirilmiş formu döndürün — bu belirsizliği ortadan kaldırır ve aşağı akış önbelleğini daha öngörülebilir hale getirir.


Hex/RGB/HSL Dönüşüm Uç Noktaları

GET /api/color/{hex}

Birincil uç nokta, belirli bir hex kodu için tam renk nesnesini döndürür. Renk Dönüştürücü'nün tüm format alanlarını aynı anda doldurmak için kullandığı budur:

GET /api/color/FF5733
GET /api/color/%23FF5733   (URL kodlu #)
GET /api/color/f57          (3 basamaklı, küçük harf)

Üçü de #FF5733 için aynı yanıtı döndürmelidir.

Yanıt:

{
  "hex": "#FF5733",
  "rgb": { "r": 255, "g": 87, "b": 51, "css": "rgb(255, 87, 51)" },
  "hsl": { "h": 11, "s": 100, "l": 60, "css": "hsl(11, 100%, 60%)" },
  "cmyk": { "c": 0, "m": 66, "y": 80, "k": 0 },
  "oklch": { "l": 0.63, "c": 0.19, "h": 27.5, "css": "oklch(0.63 0.19 27.5)" },
  "luminance": 0.215,
  "is_dark": false,
  "name": "Coral Red",
  "name_source": "css_extended",
  "name_distance": 0.08
}

POST /api/convert

Hex olmayan keyfi girdileri dönüştürmek için — CSS renk adları, RGB üçlüleri, HSL değerleri:

POST /api/convert
Content-Type: application/json

{
  "input": "rgb(255, 87, 51)",
  "from": "rgb"
}

Yanıt: Aynı tam renk nesnesi.

from değerleri olarak şunları kabul edin: hex, rgb, hsl, cmyk, oklch, name. Girdi formatının bildirilen from türüyle eşleştiğini doğrulayın ve uyumsuzluk durumunda net bir hata mesajıyla 422 döndürün.

Django ViewSet Örneği

# apps/api/views.py
from django.http import JsonResponse
from django.views import View
from apps.colors.engine import ColorEngine

class ColorDetailView(View):
    def get(self, request, hex_code: str):
        try:
            normalized = normalize_hex(hex_code)
        except ValueError as e:
            return JsonResponse(
                {"error": "invalid_hex", "message": str(e)},
                status=422
            )

        engine = ColorEngine(normalized)
        data = {
            "hex": f"#{normalized}",
            "rgb": engine.to_rgb_dict(),
            "hsl": engine.to_hsl_dict(),
            "cmyk": engine.to_cmyk_dict(),
            "oklch": engine.to_oklch_dict(),
            "luminance": engine.relative_luminance(),
            "is_dark": engine.is_dark(),
            "name": engine.nearest_name(),
            "name_source": engine.nearest_name_source(),
            "name_distance": engine.nearest_name_distance(),
        }
        response = JsonResponse(data)
        response["Cache-Control"] = "public, max-age=86400, stale-while-revalidate=604800"
        return response

Renk verilerini agresif biçimde önbelleğe alın — belirli bir hex kodu her zaman aynı çıktıyı üretir, bu nedenle önbellek çok uzun ömürlü olabilir.


Renk Arama ve Otomatik Tamamlama API'si

GET /api/search

Adlandırılmış renk araması, renk seçicilerdeki ve arama alanlarındaki otomatik tamamlamayı çalıştırır. Sorgu parametresi kısmi bir renk adıdır:

GET /api/search?q=coral
GET /api/search?q=teal&limit=5
GET /api/search?q=%230d&source=css   // Hex önekiyle arama

Yanıt:

{
  "results": [
    {
      "hex": "#FF7F50",
      "name": "Coral",
      "source": "css_named",
      "score": 1.0
    },
    {
      "hex": "#FF6B6B",
      "name": "Light Coral",
      "source": "css_extended",
      "score": 0.85
    },
    {
      "hex": "#E8735A",
      "name": "Terra Cotta",
      "source": "crayola",
      "score": 0.62
    }
  ],
  "total": 3,
  "query": "coral"
}

Arama Uygulaması Değerlendirmeleri

Küçük adlandırılmış renk veritabanları (~2.000 kayıt) için, bulanık eşleştirmeli bellek içi önek araması yeterince hızlıdır:

from difflib import SequenceMatcher

def search_colors(query: str, limit: int = 10) -> list[dict]:
    q = query.lower().strip()
    results = []

    for color in NAMED_COLORS:  # Önceden yüklenmiş liste
        name = color["name"].lower()
        if q in name:
            # Tam alt dizi eşleşmesi: konuma göre puan
            pos = name.index(q)
            score = 1.0 - (pos / len(name)) * 0.3
        else:
            # Bulanık eşleşme
            score = SequenceMatcher(None, q, name).ratio()
            if score < 0.5:
                continue

        results.append({**color, "score": round(score, 3)})

    results.sort(key=lambda x: x["score"], reverse=True)
    return results[:limit]

10.000'den fazla kayıt içeren veritabanları için, milisaniyenin altında bulanık arama için trigram indekslemeli PostgreSQL'in pg_trgm uzantısını kullanın:

-- Uzantıyı etkinleştir
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- İndeks ekle
CREATE INDEX idx_color_name_trgm ON named_colors USING gin(name gin_trgm_ops);

-- Sorgu
SELECT hex, name, source, similarity(name, 'coral') AS score
FROM named_colors
WHERE name % 'coral'
ORDER BY score DESC
LIMIT 10;

Otomatik Tamamlama Optimizasyonu

Canlı yazarken arama otomatik tamamlama için düşük gecikme süresini optimize edin:

  1. İstemcide geri tepme — İstek göndermeden önce minimum 150 ms.
  2. Minimum sorgu uzunluğu belirleyin — 2 karakterden kısa sorguları reddedin (hemen boş sonuç döndürün).
  3. Sonuçları sorguya göre önbelleğe alın — Farklı kullanıcılardan gelen aynı sorgu aynı sonucu döndürür. Cache-Control: public, max-age=3600 kullanın.
  4. Sonuç yoksa hızlı yanıt verin — Tam bulanık tarama yapmak yerine hemen {"results": [], "total": 0} yanıtı verin.

Kontrast Denetleme Uç Noktası

GET /api/contrast

Kontrast API'si, Kontrast Denetleyici'nin motorudur. İki hex kodu alır ve her seviye için geçti/kaldı durumunu içeren WCAG kontrast oranını döndürür:

GET /api/contrast?fg=FF5733&bg=FFFFFF
GET /api/contrast?fg=000000&bg=FFFFFF

Yanıt:

{
  "ratio": 3.02,
  "ratio_formatted": "3.02:1",
  "foreground": {
    "hex": "#FF5733",
    "luminance": 0.215
  },
  "background": {
    "hex": "#FFFFFF",
    "luminance": 1.0
  },
  "wcag": {
    "aa_normal": false,
    "aa_large": true,
    "aaa_normal": false,
    "aaa_large": false
  },
  "level": "AA Large",
  "passes": true
}

level alanı, çiftin ulaştığı en yüksek WCAG seviyesini döndürür ("AAA", "AA", "AA Large" veya "Fail"). passes alanı, herhangi bir WCAG seviyesi karşılandığında true değerini alır — basit yeşil/kırmızı gösterge için kullanışlıdır.

Kontrast Hesaplama

def relative_luminance(hex_code: str) -> float:
    r, g, b = hex_to_rgb(hex_code)
    def linearize(v: int) -> float:
        s = v / 255
        return s / 12.92 if s <= 0.04045 else ((s + 0.055) / 1.055) ** 2.4
    return 0.2126 * linearize(r) + 0.7152 * linearize(g) + 0.0722 * linearize(b)

def contrast_ratio(hex1: str, hex2: str) -> float:
    L1 = relative_luminance(hex1)
    L2 = relative_luminance(hex2)
    lighter = max(L1, L2)
    darker = min(L1, L2)
    return round((lighter + 0.05) / (darker + 0.05), 2)

def wcag_result(ratio: float) -> dict:
    return {
        "aa_normal": ratio >= 4.5,
        "aa_large": ratio >= 3.0,
        "aaa_normal": ratio >= 7.0,
        "aaa_large": ratio >= 4.5,
    }

Erişilebilir Alternatifler Önermek

Kullanışlı bir uzantı: arka plana karşı kontrastı geçemeyen bir ön plan rengi verildiğinde, WCAG AA'yı elde eden minimum parlaklık ayarını önerin:

def suggest_accessible_foreground(fg_hex: str, bg_hex: str, target_ratio: float = 4.5) -> str:
    """Hedef oranı geçen fg_hex'in parlaklık ayarlı versiyonunu döndür."""
    hsl = rgb_to_hsl(*hex_to_rgb(fg_hex))
    bg_lum = relative_luminance(bg_hex)
    is_dark_bg = bg_lum < 0.179

    step = -2 if is_dark_bg else 2  # Açık arka planda koyulaştır, koyu arka planda aydınlat
    current_l = hsl[2]

    for _ in range(50):
        current_l = max(0, min(100, current_l + step))
        adjusted_hex = hsl_to_hex(hsl[0], hsl[1], current_l)
        ratio = contrast_ratio(adjusted_hex, bg_hex)
        if ratio >= target_ratio:
            return adjusted_hex

    return '#000000' if not is_dark_bg else '#FFFFFF'

Renk API'leri için Hız Sınırlaması

Renk API'lerinin Neden Hız Sınırlamasına İhtiyacı Var

Renk API'leri düşük riskli görünebilir, ancak şu saldırıların hedefi olabilirler: - Numaralandırma saldırıları: Tam renk adı veritabanını elde etmek için 16,7 milyon hex kodunu yinelemek. - Kazıma: Dönüşüm mantığınızı ücretsiz bir hesaplama kaynağı olarak kullanmak. - Kötüye kullanım: Diğerleri için hizmet kalitesini düşüren tek bir istemciden yüksek hacimli istekler.

Uygulama Stratejisi

Django API için django-ratelimit, Redis arka ucuyla dekoratör tabanlı hız sınırlaması sağlar:

# settings.py
RATELIMIT_USE_CACHE = 'default'  # Django'nun önbellek arka ucunu kullanır (Redis önerilir)

# views.py
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='100/m', method='GET', block=True)
def color_detail_view(request, hex_code: str):
    ...

Hız Sınırı Yanıt Formatı

Hız sınırı aşıldığında, net bir yanıt gövdesi ve Retry-After başlığıyla 429 Too Many Requests döndürün:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708012345
Content-Type: application/json

{
  "error": "rate_limit_exceeded",
  "message": "Dakikada 100 istek hız sınırını aştınız.",
  "retry_after": 60
}

Kademeli Hız Sınırları

Makul bir kademeli yapı:

Kademe Sınır Anahtar
Anonim 60 istek / dakika IP adresi
API anahtarı (ücretsiz) 300 istek / dakika API anahtarı
API anahtarı (ücretli) 3000 istek / dakika API anahtarı

API anahtarları Authorization: Bearer {key} veya X-API-Key: {key} olarak iletilir. Kimliği doğrulanmış istekler için API anahtarını hız sınırı anahtarı olarak kullanın:

@ratelimit(key='header:x-api-key', rate='300/m', method='GET')
@ratelimit(key='ip', rate='60/m', method='GET')
def color_detail_view(request, hex_code: str):
    # django-ratelimit her ikisini de kontrol eder; kimliği doğrulanmış istekler için daha geniş sınır kazanır
    ...

Yanıt Formatı En İyi Uygulamaları

Tutarlı Hata Şeması

Her hata yanıtı — doğrulama hatası, hız sınırı, bulunamadı — aynı şemayı izlemelidir:

{
  "error": "invalid_hex",
  "message": "'gggggg' değeri geçerli bir hex rengi değil. 3 veya 6 basamaklı hex dizesi bekleniyor.",
  "field": "hex_code",
  "value": "gggggg"
}
Alan Amaç
error Makine tarafından okunabilir hata kodu (snake_case)
message İnsan tarafından okunabilir açıklama
field Hataya neden olan giriş alanı (doğrulama hataları için)
value Geçersiz değer (hata ayıklamaya yardımcı olur)

Koleksiyon Uç Noktaları için Sayfalandırma

limit ve offset ile arama uç noktası:

{
  "results": [...],
  "pagination": {
    "total": 47,
    "limit": 10,
    "offset": 0,
    "next": "/api/search?q=blue&limit=10&offset=10",
    "prev": null
  }
}

Her zaman total ekleyin, böylece istemciler tam sonuç kümesini bilmeden sayfalandırma kontrollerini gösterebilir.

Sürümleme Stratejisi

API'nizi bir başlıkta değil URL yolunda sürümleyin. URL sürümlemesi açık, CDN'ler tarafından önbelleğe alınabilir ve istemcilerin özel başlıklar ayarlamasını gerektirmez:

/api/v1/color/FF5733    # Sürüm 1
/api/v2/color/FF5733    # Sürüm 2 (büyük değişiklikler gerektiğinde)

Büyük sürüm yayınından sonra en az bir yıl boyunca önceki sürümleri koruyun. Eski sürümlerde kullanımdan kaldırma başlıkları kullanın:

Deprecation: Sun, 01 Jan 2026 00:00:00 GMT
Sunset: Sun, 01 Jan 2027 00:00:00 GMT
Link: </api/v2/color/FF5733>; rel="successor-version"

CORS Yapılandırması

Tarayıcı istemcileri tarafından tüketilen genel bir API için:

# settings.py (django-cors-headers kullanarak)
CORS_ALLOWED_ORIGINS = [
    "https://yoursite.com",
    "https://app.yoursite.com",
]

# Veya tamamen genel bir API için:
CORS_ALLOW_ALL_ORIGINS = True

# Her zaman yöntemleri ve başlıkları kısıtlayın
CORS_ALLOW_METHODS = ['GET', 'POST', 'OPTIONS']
CORS_ALLOW_HEADERS = ['Content-Type', 'Authorization', 'X-API-Key']

Önbellek Mimarisi

Renk verisi yüksek oranda önbelleğe alınabilir. #FF5733 için renk nesnesi hiçbir zaman değişmez — dönüşüm matematiği belirleyicidir. Önbellek stratejinizi buna göre yapılandırın:

Uç Nokta Cache-Control CDN TTL
/api/color/{hex} public, max-age=86400, stale-while-revalidate=604800 1 gün
/api/search?q={q} public, max-age=3600 1 saat
/api/contrast?fg=X&bg=Y public, max-age=86400 1 gün
/api/palette/{hex} public, max-age=86400 1 gün

Koşullu istekler için hex koduna dayalı bir ETag başlığı ekleyin:

response["ETag"] = f'"{hex_code}"'
response["Last-Modified"] = "Thu, 01 Jan 2026 00:00:00 GMT"  # Statik — içerik hiçbir zaman değişmez

If-None-Match: "FF5733" gönderen istemciler, gövde olmadan 304 Not Modified yanıtı alır — tekrarlanan isteklerde bant genişliğinden tasarruf edilir.


Temel Çıkarımlar

  • Kanonik renk nesnesi, istemcilerin farklı formatları almak için birden fazla istek yapmasına gerek kalmadan tüm format gösterimlerini (hex, RGB, HSL, CMYK, OKLCH) tek bir yanıtta içermelidir.
  • Hex girişini agresif biçimde normalleştirin: # kaldırın, 3 basamaklıyı genişletin, büyük harfe dönüştürün. Herhangi bir işlemden önce regex ile doğrulayın.
  • Kontrast uç noktası herhangi bir erişilebilirlik aracının çekirdeğidir — WCAG oranlarını, seviye başına geçti/kaldı durumunu ve tek bir yanıtta en yüksek geçen seviyeyi döndürün.
  • Adlandırılmış renk araması küçük veritabanları için bellek içi önek aramasıyla güvenilir biçimde çalışır; büyük olanlar için PostgreSQL pg_trgm kullanın.
  • Kimliği doğrulanmamış istekler için IP'ye göre hız sınırı uygulayın (60/dakika makuldür); daha yüksek katmanlar için API anahtarları kullanın.
  • Her hata yanıtı en azından error (makine tarafından okunabilir) ve message (insan tarafından okunabilir) içeren aynı şemayı izlemelidir.
  • Renk verisi yüksek oranda önbelleğe alınabilir — dönüşüm uç noktaları için 24 saatlik CDN önbelleği uygundur; verimli önbellek geçersiz kılma için ETag ve stale-while-revalidate kullanın.
  • Bu API uç noktalarını çalışırken görmek için Renk Dönüştürücü ve Kontrast Denetleyici'yi deneyin.

İlgili Renkler

İlgili Markalar

İlgili Araçlar