Now.js Framework Documentation

Now.js Framework Documentation

HTTP/API Guide - Now.js Framework

EN 31 Oct 2025 01:21

HTTP/API Guide - Now.js Framework

Comprehensive guide for working with HTTP/API in Now.js Framework

📋 Table of Contents

  1. Overview
  2. Available Tools
  3. Comparison Table
  4. Choosing the Right Tool
  5. Common Patterns
  6. Best Practices
  7. Related Links

Overview

Now.js Framework provides multiple tools for working with HTTP/API. Each tool is designed for different purposes, helping you choose the right tool for your needs.

Usage Levels

┌─────────────────────────────────────────────────────────┐
│                    ApiComponent                         │  ← UI Layer (Component Level)
│                 (Declarative HTML)                      │
├─────────────────────────────────────────────────────────┤
│                     ApiService                          │  ← Business Logic Layer (Service Level)
│           (Caching, Retry, Deduplication)              │
├─────────────────────────────────────────────────────────┤
│                     HttpClient                          │  ← Low-Level HTTP Layer (Base Level)
│         (http, httpThrow, simpleFetch)                 │
├─────────────────────────────────────────────────────────┤
│                  ResponseHandler                        │  ← Server Response Layer (Response Level)
│              (Server-driven Actions)                    │
└─────────────────────────────────────────────────────────┘

Available Tools

1. HttpClient (Low-Level)

Low-level HTTP Client built on fetch API with 4 main exports:

  • HttpClient - Class for creating custom instances
  • http - Ready-to-use instance (throwOnError: false)
  • httpThrow - Instance that throws errors automatically (throwOnError: true)
  • simpleFetch - Lightweight utility function for simple use cases

Use when:

  • Need low-level control (interceptors, CSRF, timeout)
  • Working with generic REST APIs
  • Need high flexibility

Don't use when:

  • Need caching, retry, deduplication
  • Working with UI components
  • Need authentication management

📖 Read full documentation: HttpClient

2. ApiService (Service-Level)

High-level HTTP Service with advanced features, suitable for business logic

Key Features:

  • ✅ In-memory caching with expiration
  • ✅ Request deduplication (prevent duplicate API calls)
  • ✅ Retry mechanism (automatic retry on errors)
  • ✅ Authentication management (Bearer, Basic, OAuth, Hybrid)
  • ✅ Request tracking and analytics
  • ✅ CSRF protection
  • ✅ Abort controller management

Use when:

  • Need caching for infrequently changing data
  • Need retry logic for network errors
  • Working with complex authentication
  • Need to prevent duplicate API calls at the same time

Don't use when:

  • Need simplicity and lightweight
  • Working with real-time data (shouldn't cache)
  • Don't need retry logic

📖 Read full documentation: ApiService

3. ApiComponent (UI-Level)

UI Component wrapper for declarative API calls via HTML attributes

Key Features:

  • ✅ Declarative API calls (defined via HTML attributes)
  • ✅ Autoload support
  • ✅ Built-in loading/error/empty states
  • ✅ Template rendering
  • ✅ Event handlers (onSuccess, onError, onEmpty)
  • ✅ Pagination support
  • ✅ Polling support

Use when:

  • Need to call API from UI components
  • Need to display loading/error states
  • Need to render data with templates
  • Need pagination or polling

Don't use when:

  • No UI element
  • Need low-level control
  • Working with complex business logic

📖 Read full documentation: ApiComponent

4. ResponseHandler (Response-Level)

Server-driven response handler that processes actions from server (redirect, alert, script execution, etc.)

Key Features:

  • ✅ 17 action types (alert, redirect, script, html, etc.)
  • ✅ Server-driven UI updates
  • ✅ Auto-integration with form submissions
  • ✅ Security features (XSS protection, script validation)

Use when:

  • Server sends actions with response
  • Need server-driven UI updates
  • Working with form submissions

Don't use when:

  • Need to control UI updates yourself
  • Working with generic REST APIs
  • Server doesn't send action-based responses

📖 Read full documentation: ResponseHandler

Comparison Table

Feature HttpClient ApiService ApiComponent ResponseHandler
Usage Level Low-level High-level UI Layer Response Layer
Complexity ⭐️⭐️ ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️
Caching
Retry Logic
Deduplication
Authentication ⚠️ (Manual) ⚠️ (Via ApiService)
CSRF Protection ⚠️ (Via underlying service)
Interceptors
UI Integration
Template Rendering ⚠️ (Via HTML action)
Loading States
Pagination
Polling
Server Actions
File Size Small Large Medium Small
Performance Fast Medium Medium Fast
Learning Curve ⭐️⭐️ ⭐️⭐️⭐️⭐️ ⭐️⭐️⭐️ ⭐️⭐️

Choosing the Right Tool

Scenario 1: Simple API Call

Example: Fetch users from API

// ✅ Best: Use simpleFetch (HttpClient)
const users = await simpleFetch.json('/api/users');
console.log(users);

// ✅ Best: Use http (HttpClient)
const response = await http.get('/api/users');
if (response.ok) {
  console.log(response.data);
}

Reason: Simple, lightweight, fast, no need for advanced features

Scenario 2: API Call with Caching

Example: Fetch categories that don't change frequently

// ✅ Best: Use ApiService
await ApiService.init({
  cache: {
    enabled: true,
    expiry: { get: 300000 } // Cache 5 minutes
  }
});

const categories = await ApiService.get('/api/categories');
console.log(categories.data);

Reason: ApiService has built-in caching with expiration

Scenario 3: API Call with Authentication

Example: Call protected API endpoint

// ✅ Best: Use ApiService
await ApiService.init({
  security: {
    bearerAuth: true,
    bearerTokenKey: 'auth_token', // Token in localStorage
    authStrategy: 'hybrid' // HttpOnly cookie + access token
  }
});

const profile = await ApiService.get('/api/user/profile');
console.log(profile.data);

Reason: ApiService handles authentication automatically

Scenario 4: Display API Data in UI

Example: Display products list with loading state

HTML:

<div data-api-component
     data-api-url="/api/products"
     data-api-method="GET"
     data-api-autoload="true"
     data-api-cache="true"
     data-api-cache-time="60000">

  <template>
    <div class="product">
      <h3>{{name}}</h3>
      <p>{{description}}</p>
      <span class="price">{{price}}</span>
    </div>
  </template>

</div>

JavaScript:

// ✅ Best: Use ApiComponent
const component = ApiComponent.create('[data-api-component]');

Reason: ApiComponent handles UI states, templates, and data binding automatically

Scenario 5: Form Submission with Server Actions

Example: Submit form and let server decide what to do next

HTML:

<form id="loginForm" action="/api/login" method="post">
  <input type="email" name="email" required>
  <input type="password" name="password" required>
  <button type="submit">Login</button>
</form>

Server Response:

{
  "status": "success",
  "message": "Login successful",
  "actions": [
    {
      "action": "alert",
      "params": {
        "message": "Welcome back!",
        "type": "success"
      }
    },
    {
      "action": "redirect",
      "params": {
        "url": "/dashboard",
        "delay": 1000
      }
    }
  ]
}

JavaScript:

// ✅ Best: Use ResponseHandler
// ResponseHandler handles form submissions automatically
// and processes actions from server

Reason: ResponseHandler is designed for server-driven UI updates

Scenario 6: API Call with Retry Logic

Example: Send analytics data that needs retry on network error

// ✅ Best: Use ApiService
await ApiService.init({
  retryCount: 3,
  retryDelay: 1000,
  connection: {
    retryOnNetworkError: true,
    retryStatusCodes: [408, 429, 500, 502, 503, 504]
  }
});

await ApiService.post('/api/analytics', {
  event: 'page_view',
  page: '/products'
});

Reason: ApiService has built-in retry mechanism with exponential backoff

Scenario 7: Prevent Duplicate API Calls

Example: Prevent multiple button clicks that cause multiple API calls

// ✅ Best: Use ApiService
await ApiService.init({
  deduplicate: true
});

// If user clicks multiple times at the same time
// ApiService waits for first request to finish and shares the result
button.addEventListener('click', async () => {
  const result = await ApiService.get('/api/data');
  console.log(result);
});

Reason: ApiService has built-in request deduplication

Scenario 8: Working with Interceptors

Example: Add custom headers or logging

// ✅ Best: Use HttpClient
http.addRequestInterceptor(config => {
  // Add custom header
  config.headers['X-App-Version'] = '1.0.0';

  // Logging
  console.log('Request:', config.method, config.url);

  return config;
});

http.addResponseInterceptor(
  response => {
    console.log('Response:', response.status);
    return response;
  },
  error => {
    console.error('Error:', error);
    throw error;
  }
);

const response = await http.get('/api/data');

Reason: HttpClient has flexible interceptor API

Common Patterns

Pattern 1: Progressive Enhancement

Start with the simplest tool, then add complexity when needed

// Level 1: Simple API call
const users = await simpleFetch.json('/api/users');

// Level 2: Need caching and authentication
await ApiService.init({
  cache: { enabled: true },
  security: { bearerAuth: true }
});
const users = await ApiService.get('/api/users');

// Level 3: Need UI integration
const component = ApiComponent.create('#userList', {
  url: '/api/users',
  autoload: true,
  cache: true
});

Pattern 2: Layer Separation

Separate concerns by responsibility level

// ❌ Bad: Everything mixed together
async function loadUserProfile() {
  const token = localStorage.getItem('token');
  const response = await fetch('/api/user/profile', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const data = await response.json();
  document.getElementById('profile').innerHTML = `
    <h1>${data.name}</h1>
    <p>${data.email}</p>
  `;
}

// ✅ Good: Separate concerns
// 1. API Layer - ApiService handles HTTP and auth
await ApiService.init({
  security: { bearerAuth: true }
});

// 2. Data Layer - Service function
async function getUserProfile() {
  return await ApiService.get('/api/user/profile');
}

// 3. UI Layer - ApiComponent handles rendering
const profileComponent = ApiComponent.create('#profile', {
  url: '/api/user/profile',
  template: 'profileTemplate'
});

Pattern 3: Error Handling Strategy

Choose appropriate error handling strategy

// Strategy 1: Safe mode (doesn't throw error)
const response = await http.get('/api/data');
if (!response.ok) {
  console.error('Error:', response.statusText);
  return;
}

// Strategy 2: Throw mode (throws error)
try {
  const response = await httpThrow.get('/api/data');
  console.log(response.data);
} catch (error) {
  console.error('Error:', error.message);
}

// Strategy 3: Service-level retry
await ApiService.init({
  retryCount: 3,
  connection: { retryOnNetworkError: true }
});
const response = await ApiService.get('/api/data');

Pattern 4: Caching Strategy

Choose appropriate caching strategy for data

// Strategy 1: Long-lived data (categories, config)
await ApiService.init({
  cache: {
    enabled: true,
    expiry: { get: 3600000 } // 1 hour
  }
});

// Strategy 2: Short-lived data (search results)
await ApiService.init({
  cache: {
    enabled: true,
    expiry: { get: 60000 } // 1 minute
  }
});

// Strategy 3: No cache (real-time data)
await ApiService.init({
  cache: { enabled: false }
});

// Strategy 4: Conditional cache
await ApiService.init({
  cache: {
    enabled: true,
    responsePredicate: (response) => {
      // Cache only success responses
      return response.ok && response.status === 200;
    }
  }
});

Best Practices

1. ✅ Choose the right tool for the job

// ❌ Bad: Use ApiService for simple API call
await ApiService.init({ /* many configs */ });
const users = await ApiService.get('/api/users');

// ✅ Good: Use simpleFetch when you don't need advanced features
const users = await simpleFetch.json('/api/users');

2. ✅ Initialize services only once

// ❌ Bad: Initialize every time you call
async function fetchData() {
  await ApiService.init({ /* config */ });
  return await ApiService.get('/api/data');
}

// ✅ Good: Initialize once at app start
await ApiService.init({ /* config */ });

async function fetchData() {
  return await ApiService.get('/api/data');
}

3. ✅ Handle errors appropriately

// ❌ Bad: Don't handle error
const response = await http.get('/api/data');
console.log(response.data); // may be null

// ✅ Good: Check response.ok before using data
const response = await http.get('/api/data');
if (response.ok) {
  console.log(response.data);
} else {
  console.error('Error:', response.statusText);
}

// ✅ Better: Use httpThrow and try-catch
try {
  const response = await httpThrow.get('/api/data');
  console.log(response.data);
} catch (error) {
  console.error('Error:', error.message);
}

4. ✅ Use caching wisely

// ❌ Bad: Cache frequently changing data
await ApiService.init({
  cache: {
    enabled: true,
    expiry: { post: 3600000 } // Cache POST responses?!
  }
});

// ✅ Good: Cache only infrequently changing data
await ApiService.init({
  cache: {
    enabled: true,
    expiry: {
      get: 300000,  // Cache GET 5 minutes
      post: 0,      // Don't cache POST
      put: 0,       // Don't cache PUT
      delete: 0     // Don't cache DELETE
    }
  }
});

5. ✅ Specify correct content type

// ❌ Bad: Send FormData but specify Content-Type
await http.post('/api/upload', formData, {
  headers: {
    'Content-Type': 'multipart/form-data' // Will lose boundary
  }
});

// ✅ Good: Let browser handle Content-Type
await http.post('/api/upload', formData);
// browser sets: Content-Type: multipart/form-data; boundary=...

6. ✅ Use AbortController for long-running requests

// ❌ Bad: No way to cancel request
button.addEventListener('click', async () => {
  const response = await http.get('/api/large-data');
});

// ✅ Good: Can cancel request
const controller = new AbortController();

button.addEventListener('click', async () => {
  const response = await http.get('/api/large-data', {
    signal: controller.signal
  });
});

cancelButton.addEventListener('click', () => {
  controller.abort();
});

7. ✅ Use interceptors for cross-cutting concerns

// ❌ Bad: Add token every time you call
const token = localStorage.getItem('token');
await http.get('/api/data', {
  headers: { 'Authorization': `Bearer ${token}` }
});

// ✅ Good: Use interceptor
http.addRequestInterceptor(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});

await http.get('/api/data');

8. ✅ Use ResponseHandler for server-driven flows

// ❌ Bad: Client decides flow itself
const response = await http.post('/api/delete-item', { id: 123 });
if (response.ok) {
  alert('Deleted successfully');
  window.location.reload();
}

// ✅ Good: Let server decide flow
// Server response:
{
  "status": "success",
  "actions": [
    { "action": "alert", "params": { "message": "Deleted successfully" } },
    { "action": "reload" }
  ]
}
// ResponseHandler handles actions automatically

9. ✅ Set appropriate timeout

// ❌ Bad: Same timeout for all requests
http.setTimeout(5000); // 5 seconds for all requests

// ✅ Good: Timeout by request type
http.setTimeout(30000); // Default 30 seconds

// For long-running requests
await http.post('/api/process-data', data, {
  timeout: 120000 // 2 minutes
});

// For quick requests
await http.get('/api/ping', {
  timeout: 3000 // 3 seconds
});

10. ✅ Use typing and validation

// ❌ Bad: Don't validate response
const response = await http.get('/api/user');
console.log(response.data.name); // may be undefined

// ✅ Good: Validate response before use
const response = await http.get('/api/user');
if (response.ok && response.data) {
  const user = response.data;
  if (user.name && user.email) {
    console.log(user.name);
  } else {
    console.error('Invalid user data');
  }
}

Full Documentation

Usage Examples

Summary

If you need... Use...
Simple API call simpleFetch or http
Caching, Retry, Authentication ApiService
Display API data in UI ApiComponent
Server-driven UI updates ResponseHandler
Interceptors, high flexibility HttpClient (class)

Golden Rule: Start with the simplest tool that works, then add complexity when needed