Remote Fetch

The remoteFetchAsync function sends HTTP requests from NocoDB scripts to external services. Use it to integrate with third-party APIs, get external data, and connect your base to other systems.

Overview

The remoteFetchAsync function is similar to the standard web Fetch API. It runs in the secure context of NocoDB Scripts. Use it to:

  • Send GET, POST, PUT, and DELETE requests to external APIs
  • Send and receive JSON data, text, and other formats
  • Set custom headers, for example authentication tokens
  • Integrate your base with external services

This function is asynchronous and returns a Promise. Use it with await to get the result.

Basic Syntax

const response = await remoteFetchAsync(url, options);

Parameters:

  • url (string): The URL to send the request to
  • options (object, optional): Options for the request:
    • method ('GET' | 'POST' | 'PUT' | 'DELETE', optional): The HTTP method to use (default: 'GET')
    • headers (object, optional): Headers to include in the request
    • body (string | null, optional): The body content to send with the request

Returns: Promise<any> - A promise that resolves to the response data or rejects with an axios-like error object

Error Handling

The errors of remoteFetchAsync use the same structure as axios errors. When a request fails, the rejected error object contains:

  • message (string): The error message
  • response (object): The response details:
    • data: Response data from the server
    • status: HTTP status code
    • statusText: HTTP status text
    • headers: Response headers
    • config: Request configuration
  • config (object): The original request configuration
  • code (string): Error code (if available)
  • name (string): Error name (defaults to 'AxiosError')

Handling Errors

try {
  const response = await remoteFetchAsync('https://api.example.com/data');
  output.inspect(response.data);
} catch (error) {
  // Error object mimics axios structure
  output.text(`Error: ${error.message}`, 'error');
  
  if (error.response) {
    // Server responded with error status
    output.text(`Status: ${error.response.status}`, 'error');
    output.text(`Status Text: ${error.response.statusText}`, 'error');
    output.inspect(error.response.data);
  } else {
    // Network error or other issue
    output.text('Network error or request failed', 'error');
  }
}

Basic Examples

Making a GET Request

// Simple GET request
const response = await remoteFetchAsync('https://api.example.com/data');
output.text(`Response status: ${response.status}`);
output.inspect(response.data);

Making a POST Request with JSON Data

// POST request with JSON data
const response = await remoteFetchAsync('https://api.example.com/items', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'New Item',
    description: 'This is a new item',
    quantity: 10
  })
});

if (response.status === 201) {
  output.text('Item created successfully!');
  output.inspect(response.data);
} else {
  output.text(`Error creating item: ${response.status}`, 'error');
}

Making a Request with Authentication

// GET request with authorization header
const apiKey = 'your_api_key_here';

const response = await remoteFetchAsync('https://api.example.com/secure-data', {
  headers: {
    'Authorization': `Bearer ${apiKey}`
  }
});

if (response.status === 200) {
  output.text('Successfully fetched secure data');
  output.table(response.data);
} else if (response.status === 401) {
  output.text('Authentication failed: Invalid API key', 'error');
} else {
  output.text(`Request failed with status: ${response.status}`, 'error');
}

Common Use Cases

Fetching Data from a Public API

// Fetch weather data from a public API
async function getWeatherData(city) {
  try {
    const response = await remoteFetchAsync(
      `https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${encodeURIComponent(city)}`
    );
    
    if (response.status !== 200) {
      throw new Error(`API returned status ${response.status}`);
    }
    
    const weatherData = response.data;
    
    output.markdown(`# Weather for ${weatherData.location.name}, ${weatherData.location.country}`);
    output.text(`Temperature: ${weatherData.current.temp_c}°C / ${weatherData.current.temp_f}°F`);
    output.text(`Condition: ${weatherData.current.condition.text}`);
    output.text(`Wind: ${weatherData.current.wind_kph} km/h, ${weatherData.current.wind_dir}`);
    output.text(`Humidity: ${weatherData.current.humidity}%`);
    output.text(`Last Updated: ${weatherData.current.last_updated}`);
    
    return weatherData;
  } catch (error) {
    output.text(`Error fetching weather data: ${error.message}`, 'error');
    return null;
  }
}

// Get weather for a city
const city = await input.textAsync('Enter city name:');
await getWeatherData(city);

Sending Data to a Webhook

// Send data to a webhook (e.g., Zapier, Make.com, etc.)
async function sendToWebhook(data) {
  try {
    output.text('Sending data to webhook...');
    
    const response = await remoteFetchAsync('https://hooks.zapier.com/hooks/catch/123456/abcdef/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });
    
    if (response.status >= 200 && response.status < 300) {
      output.text('Data sent successfully!', 'log');
      return true;
    } else {
      throw new Error(`Webhook returned status ${response.status}`);
    }
  } catch (error) {
    output.text(`Error sending to webhook: ${error.message}`, 'error');
    return false;
  }
}

// Example: Send new customer data to a webhook
const customersTable = base.getTable('Customers');
const newCustomersView = customersTable.getView('New Customers');

const customers = await newCustomersView.selectRecordsAsync({
  fields: ['Name', 'Email', 'Phone', 'Signup Date']
});

// Process each customer
for (const customer of customers.records) {
  const customerData = {
    name: customer.getCellValue('Name'),
    email: customer.getCellValue('Email'),
    phone: customer.getCellValue('Phone'),
    signupDate: customer.getCellValue('Signup Date'),
    source: 'NocoDB'
  };
  
  const success = await sendToWebhook(customerData);
  
  if (success) {
    // Update the customer record to mark as processed
    await customersTable.updateRecordAsync(customer.id, {
      'Webhook Sent': true,
      'Processing Date': new Date().toISOString()
    });
  }
}

Integrating with Google Sheets API

// Import data from Google Sheets (simplified example)
// Note: Doesn't work if the spreadsheet is owned by a user with a Google Workspace (formerly G Suite) account
// and requires the Google Sheets API to be enabled in your Google Cloud project. Share sheet with Viewer access publicly
async function importFromGoogleSheets(spreadsheetId, range) {
  const apiKey = 'API_KEY';
  
  try {
    output.text(`Fetching data from Google Sheets (${spreadsheetId})...`);

    const response = await remoteFetchAsync(
      `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}?key=${apiKey}`
    );

    if (response.status !== 200) {
      throw new Error(`API returned status ${response.status}`);
    }

    const sheetData = response.data;

    if (!sheetData.values || sheetData.values.length === 0) {
      output.text('No data found in the specified range.', 'warning');
      return null;
    }

    // Extract headers from the first row
    const headers = sheetData.values[0];

    // Convert the data to an array of objects
    const records = sheetData.values.slice(1).map(row => {
      const record = {};
      headers.forEach((header, index) => {
        record[header] = row[index] || null;
      });
      return record;
    });

    output.text(`Fetched ${records.length} records from Google Sheets.`);
    output.table(records.slice(0, 5)); // Show first 5 records as preview

    return records;
  } catch (error) {
    output.text(`Error fetching data from Google Sheets: ${error.message}`, 'error');
    return null;
  }
}

// Example usage
const spreadsheetId = '1o_HhLwbUYJuwwPok8poOjRZxxxxxIgQF9z5aoEZrzXU'; // Example Spreadsheet ID
const range = 'Sheet1!A1:B10'; // Range to fetch

const sheetsData = await importFromGoogleSheets(spreadsheetId, range);

if (sheetsData) {
  // Import the data into a NocoDB table
  const importTable = base.getTable('Imported Data');

  // Clear confirmation before proceeding
  const confirm = await input.buttonsAsync(
    `Import ${sheetsData.length} records into the "${importTable.name}" table?`,
    [
      { label: 'Import Data', value: true, variant: 'primary' },
      { label: 'Cancel', value: false, variant: 'secondary' }
    ]
  );

  if (confirm) {
    // Import data in batches — createRecordsAsync accepts at most 10 per call
    const batchSize = 10;
    let imported = 0;

    // output.inspect(sheetsData);

    for (let i = 0; i < sheetsData.length; i += batchSize) {
      const batch = sheetsData.slice(i, i + batchSize);

      // Wrap each record in a { fields: ... } object
      const formattedBatch = batch.map(record => ({ fields: record }));
      output.inspect(batch);

      await importTable.createRecordsAsync(formattedBatch);
      imported += batch.length;
      output.text(`Imported ${imported} of ${sheetsData.length} records...`);
    }
    
    output.text(`Import complete! ${imported} records imported.`, 'log');
  } else {
    output.text('Import cancelled.');
  }
}

Working with Different Data Formats

JSON Data

// Working with JSON data
async function fetchJsonData() {
  try {
    const response = await remoteFetchAsync('https://api.example.com/data', {
      headers: {
        'Accept': 'application/json'
      }
    });
    
    if (response.status !== 200) {
      throw new Error(`API returned status ${response.status}`);
    }
    
    // The response data is automatically parsed as JSON
    const data = response.data;
    
    return data;
  } catch (error) {
    output.text(`Error fetching JSON data: ${error.message}`, 'error');
    return null;
  }
}

Form Data

// Submitting form data
async function submitFormData(formData) {
  try {
    // Convert the form data object to URL-encoded format
    const formBody = Object.entries(formData)
      .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
      .join('&');
    
    const response = await remoteFetchAsync('https://api.example.com/submit-form', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: formBody
    });
    
    if (response.status === 200 || response.status === 201) {
      output.text('Form submitted successfully!', 'log');
      return true;
    } else {
      throw new Error(`Server returned status ${response.status}`);
    }
  } catch (error) {
    output.text(`Error submitting form: ${error.message}`, 'error');
    return false;
  }
}

// Example usage
const formData = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  message: 'This is a test message',
  subscribe: 'true'
};

await submitFormData(formData);

Best Practices

  1. Use try/catch blocks - Always put remoteFetchAsync calls in try/catch blocks. This handles connection errors, timeouts, and other issues. The function gives axios-like error objects with detailed error information.

  2. Check error.response for server errors - When you handle an error, check if error.response exists. If it exists, the error is a server error (4xx, 5xx status codes). If not, it is a network/connection error.

  3. Validate response status in success cases - Also for successful requests, validate the HTTP status code before you use the response data.

  4. Set appropriate headers - Add the correct Content-Type and Accept headers to your request.

  5. Handle authentication properly - Use secure methods to store and send authentication credentials.

  6. Implement rate limiting - Know the API rate limits. When necessary, use throttling or backoff strategies.

  7. Cache responses when appropriate - For data that does not change frequently, store the response and use it again.

  8. Log meaningful errors - Write clear error messages. They help you troubleshoot issues.

  9. Validate input data - Make sure that the data you send to external APIs is valid and correctly formatted.

  10. Respect API terms of service - Make sure that your use of external APIs agrees with their terms of service and documentation.

  11. Consider timeouts - A remote request can time out if it takes too long.

Security Considerations

  1. Never expose API keys in output - Do not put sensitive information, for example API keys, in your output.

  2. Use secure endpoints - Use HTTPS URLs, not HTTP, so that the data is encrypted when it is sent.

  3. Validate and sanitize input - Be careful with data from users that you send to external APIs.

  4. Be mindful of data privacy - Think about the privacy risks when you send data to third-party services.

  5. Implement proper error handling - Do not show detailed error messages from APIs. They can reveal sensitive information.

Last updated on

Latest product updates?See Changelog
Stay in the loop? Follow us onLinkedInLinkedInYouTubeYouTubeXX