Back to Docs

Framework Examples

Ready-to-use code examples for React, Vue, Node.js, and more.

Framework Examples

This page provides ready-to-use code examples for popular frameworks and libraries. Copy these snippets to quickly integrate SheetsJSON into your projects.

React

Basic Data Fetching with Hooks

import { useState, useEffect } from 'react';

// Custom hook for fetching sheet data
function useSheetsJSON(accountSlug, sheetSlug, options = {}) {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      setError(null);

      try {
        const url = new URL(
          `https://api.sheetsjson.com/api/sheets/${accountSlug}/${sheetSlug}`
        );

        // Add query parameters
        if (options.limit) url.searchParams.set('limit', options.limit);
        if (options.offset) url.searchParams.set('offset', options.offset);
        if (options.sort) url.searchParams.set('sort', options.sort);
        
        // Add filters
        if (options.filters) {
          Object.entries(options.filters).forEach(([key, value]) => {
            url.searchParams.set(`filter[${key}]`, value);
          });
        }

        const response = await fetch(url, {
          headers: options.apiKey 
            ? { 'Authorization': `Bearer ${options.apiKey}` }
            : {}
        });

        if (!response.ok) {
          throw new Error(`HTTP error: ${response.status}`);
        }

        const result = await response.json();
        setData(result.data || []);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [accountSlug, sheetSlug, JSON.stringify(options)]);

  return { data, loading, error };
}

// Example usage in a component
function ProductList() {
  const { data: products, loading, error } = useSheetsJSON(
    'my-account',
    'products',
    {
      apiKey: process.env.REACT_APP_SHEETSJSON_API_KEY,
      sort: '-price',
      filters: { in_stock: 'true' }
    }
  );

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div className="grid grid-cols-3 gap-4">
      {products.map((product) => (
        <div key={product.id} className="border rounded-lg p-4">
          <h3 className="font-bold">{product.name}</h3>
          <p className="text-gray-600">{product.description}</p>
          <p className="text-xl font-bold mt-2">${product.price}</p>
        </div>
      ))}
    </div>
  );
}

export default ProductList;

React with TanStack Query (React Query)

For more advanced data fetching with caching, refetching, and mutations:

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

const API_BASE = 'https://api.sheetsjson.com/api/sheets';
const ACCOUNT = 'my-account';
const SHEET = 'products';
const API_KEY = process.env.REACT_APP_SHEETSJSON_API_KEY;

// Fetch function
async function fetchProducts(filters = {}) {
  const url = new URL(`${API_BASE}/${ACCOUNT}/${SHEET}`);
  
  Object.entries(filters).forEach(([key, value]) => {
    if (value) url.searchParams.set(key, value);
  });

  const response = await fetch(url, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  });

  if (!response.ok) {
    throw new Error('Failed to fetch products');
  }

  return response.json();
}

// Create function (Write API)
async function createProduct(productData) {
  const response = await fetch(`${API_BASE}/${ACCOUNT}/${SHEET}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(productData)
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'Failed to create product');
  }

  return response.json();
}

// Component using React Query
function ProductManager() {
  const queryClient = useQueryClient();

  // Query for fetching products
  const { data, isLoading, error } = useQuery({
    queryKey: ['products'],
    queryFn: () => fetchProducts({ sort: 'name' }),
    staleTime: 60000, // Consider data fresh for 1 minute
  });

  // Mutation for creating products
  const createMutation = useMutation({
    mutationFn: createProduct,
    onSuccess: () => {
      // Invalidate and refetch products after creating
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });

  const handleAddProduct = () => {
    createMutation.mutate({
      name: 'New Product',
      price: '29.99',
      category: 'Electronics'
    });
  };

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <button 
        onClick={handleAddProduct}
        disabled={createMutation.isPending}
        className="bg-blue-600 text-white px-4 py-2 rounded mb-4"
      >
        {createMutation.isPending ? 'Adding...' : 'Add Product'}
      </button>

      {createMutation.isError && (
        <div className="text-red-600 mb-4">
          Error: {createMutation.error.message}
        </div>
      )}

      <div className="grid grid-cols-3 gap-4">
        {data?.data?.map((product) => (
          <div key={product.id} className="border rounded p-4">
            <h3>{product.name}</h3>
            <p>${product.price}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

export default ProductManager;

Vue.js

Vue 3 Composition API

<template>
  <div>
<!-- Loading State -->

    <div v-if="loading" class="text-center py-8">
      <p>Loading products...</p>
    </div>

<!-- Error State -->

    <div v-else-if="error" class="text-red-600 p-4 bg-red-50 rounded">
      {{ error }}
    </div>

<!-- Products Grid -->

    <div v-else class="grid grid-cols-3 gap-4">
      <div 
        v-for="product in products" 
        :key="product.id"
        class="border rounded-lg p-4 hover:shadow-lg transition-shadow"
      >
        <img 
          :src="product.image_url" 
          :alt="product.name"
          class="w-full h-48 object-cover rounded mb-4"
        >
        <h3 class="font-bold text-lg">{{ product.name }}</h3>
        <p class="text-gray-600 text-sm mt-1">{{ product.description }}</p>
        <p class="text-xl font-bold mt-2">${{ product.price }}</p>
        <button 
          @click="addToCart(product)"
          class="w-full mt-4 bg-blue-600 text-white py-2 rounded hover:bg-blue-700"
        >
          Add to Cart
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, computed } from 'vue';

// Configuration
const API_BASE = 'https://api.sheetsjson.com/api/sheets';
const ACCOUNT_SLUG = 'my-account';
const SHEET_SLUG = 'products';
const API_KEY = import.meta.env.VITE_SHEETSJSON_API_KEY;

// State
const products = ref([]);
const loading = ref(true);
const error = ref(null);

// Fetch products
async function fetchProducts(params = {}) {
  loading.value = true;
  error.value = null;

  try {
    const url = new URL(`${API_BASE}/${ACCOUNT_SLUG}/${SHEET_SLUG}`);
    
    // Add query parameters
    Object.entries(params).forEach(([key, value]) => {
      if (value) url.searchParams.set(key, value);
    });

    const response = await fetch(url, {
      headers: API_KEY ? { 'Authorization': `Bearer ${API_KEY}` } : {}
    });

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const data = await response.json();
    products.value = data.data || [];
  } catch (err) {
    error.value = err.message;
    console.error('Failed to fetch products:', err);
  } finally {
    loading.value = false;
  }
}

// Add to cart
function addToCart(product) {
  console.log('Added to cart:', product);
  // Implement your cart logic here
}

// Lifecycle
onMounted(() => {
  fetchProducts({ sort: '-price', 'filter[in_stock]': 'true' });
});
</script>

Vue 3 Composable (Reusable)

Create a composable for reusable data fetching:

// composables/useSheetsJSON.js
import { ref, watch } from 'vue';

export function useSheetsJSON(accountSlug, sheetSlug, options = {}) {
  const data = ref([]);
  const loading = ref(false);
  const error = ref(null);
  const meta = ref(null);

  const API_BASE = 'https://api.sheetsjson.com/api/sheets';

  async function fetchData(params = {}) {
    loading.value = true;
    error.value = null;

    try {
      const url = new URL(`${API_BASE}/${accountSlug}/${sheetSlug}`);

      // Merge default options with params
      const allParams = { ...options, ...params };

      Object.entries(allParams).forEach(([key, value]) => {
        if (value !== undefined && value !== null && key !== 'apiKey') {
          url.searchParams.set(key, value);
        }
      });

      const headers = {};
      if (options.apiKey) {
        headers['Authorization'] = `Bearer ${options.apiKey}`;
      }

      const response = await fetch(url, { headers });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error || `HTTP error: ${response.status}`);
      }

      const result = await response.json();
      data.value = result.data || [];
      meta.value = result.meta || null;

      return result;
    } catch (err) {
      error.value = err.message;
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function createRow(rowData) {
    loading.value = true;
    error.value = null;

    try {
      const response = await fetch(`${API_BASE}/${accountSlug}/${sheetSlug}`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${options.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(rowData)
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error || 'Failed to create row');
      }

      // Refresh data after creation
      await fetchData();

      return response.json();
    } catch (err) {
      error.value = err.message;
      throw err;
    } finally {
      loading.value = false;
    }
  }

  return {
    data,
    loading,
    error,
    meta,
    fetchData,
    createRow,
    refresh: fetchData
  };
}

Usage in a component:

<script setup>
import { onMounted } from 'vue';
import { useSheetsJSON } from '@/composables/useSheetsJSON';

const { data: products, loading, error, fetchData, createRow } = useSheetsJSON(
  'my-account',
  'products',
  { apiKey: import.meta.env.VITE_SHEETSJSON_API_KEY }
);

onMounted(() => {
  fetchData({ sort: 'name' });
});

async function handleAddProduct() {
  await createRow({
    name: 'New Product',
    price: '19.99',
    category: 'General'
  });
}
</script>

Node.js

Express.js API Proxy

Create a secure backend proxy to protect your API key:

// server.js
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

const SHEETSJSON_API_KEY = process.env.SHEETSJSON_API_KEY;
const SHEETSJSON_ACCOUNT = process.env.SHEETSJSON_ACCOUNT;
const API_BASE = 'https://api.sheetsjson.com/api/sheets';

// Proxy GET requests
app.get('/api/sheets/:sheet', async (req, res) => {
  try {
    const { sheet } = req.params;
    const url = new URL(`${API_BASE}/${SHEETSJSON_ACCOUNT}/${sheet}`);

    // Forward query parameters
    Object.entries(req.query).forEach(([key, value]) => {
      url.searchParams.set(key, value);
    });

    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${SHEETSJSON_API_KEY}`
      }
    });

    const data = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(data);
    }

    res.json(data);
  } catch (error) {
    console.error('Proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Proxy POST requests (Write API)
app.post('/api/sheets/:sheet', async (req, res) => {
  try {
    const { sheet } = req.params;

    const response = await fetch(`${API_BASE}/${SHEETSJSON_ACCOUNT}/${sheet}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${SHEETSJSON_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(req.body)
    });

    const data = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(data);
    }

    res.status(201).json(data);
  } catch (error) {
    console.error('Proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Proxy PUT requests
app.put('/api/sheets/:sheet/:row', async (req, res) => {
  try {
    const { sheet, row } = req.params;

    const response = await fetch(`${API_BASE}/${SHEETSJSON_ACCOUNT}/${sheet}/${row}`, {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${SHEETSJSON_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(req.body)
    });

    const data = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(data);
    }

    res.json(data);
  } catch (error) {
    console.error('Proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Proxy DELETE requests
app.delete('/api/sheets/:sheet/:row', async (req, res) => {
  try {
    const { sheet, row } = req.params;

    const response = await fetch(`${API_BASE}/${SHEETSJSON_ACCOUNT}/${sheet}/${row}`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${SHEETSJSON_API_KEY}`
      }
    });

    const data = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(data);
    }

    res.json(data);
  } catch (error) {
    console.error('Proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Node.js Client Library

Create a reusable client for your Node.js applications:

// lib/sheetsjson-client.js
class SheetsJSONClient {
  constructor(config) {
    this.apiBase = 'https://api.sheetsjson.com/api/sheets';
    this.accountSlug = config.accountSlug;
    this.apiKey = config.apiKey;
  }

  async _request(method, path, body = null) {
    const url = `${this.apiBase}/${this.accountSlug}${path}`;

    const options = {
      method,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`
      }
    };

    if (body) {
      options.headers['Content-Type'] = 'application/json';
      options.body = JSON.stringify(body);
    }

    const response = await fetch(url, options);
    const data = await response.json();

    if (!response.ok) {
      const error = new Error(data.error || `HTTP error: ${response.status}`);
      error.code = data.code;
      error.status = response.status;
      throw error;
    }

    return data;
  }

  // Read operations
  async getRows(sheetSlug, params = {}) {
    const url = new URL(`${this.apiBase}/${this.accountSlug}/${sheetSlug}`);
    
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        url.searchParams.set(key, value);
      }
    });

    const response = await fetch(url, {
      headers: { 'Authorization': `Bearer ${this.apiKey}` }
    });

    const data = await response.json();

    if (!response.ok) {
      const error = new Error(data.error || `HTTP error: ${response.status}`);
      error.code = data.code;
      error.status = response.status;
      throw error;
    }

    return data;
  }

  async getRow(sheetSlug, filter) {
    const result = await this.getRows(sheetSlug, {
      ...Object.fromEntries(
        Object.entries(filter).map(([k, v]) => [`filter[${k}]`, v])
      ),
      limit: 1
    });

    return result.data?.[0] || null;
  }

  // Write operations
  async createRow(sheetSlug, data) {
    return this._request('POST', `/${sheetSlug}`, data);
  }

  async createRows(sheetSlug, rows) {
    return this._request('POST', `/${sheetSlug}`, rows);
  }

  async updateRow(sheetSlug, rowNumber, data) {
    return this._request('PUT', `/${sheetSlug}/${rowNumber}`, data);
  }

  async deleteRow(sheetSlug, rowNumber) {
    return this._request('DELETE', `/${sheetSlug}/${rowNumber}`);
  }
}

module.exports = SheetsJSONClient;

Usage:

const SheetsJSONClient = require('./lib/sheetsjson-client');

const client = new SheetsJSONClient({
  accountSlug: 'my-account',
  apiKey: process.env.SHEETSJSON_API_KEY
});

// Get all products
async function getProducts() {
  const result = await client.getRows('products', {
    sort: '-price',
    limit: 10
  });
  console.log('Products:', result.data);
}

// Get a single product
async function getProduct(id) {
  const product = await client.getRow('products', { id });
  console.log('Product:', product);
}

// Create a product
async function createProduct() {
  const result = await client.createRow('products', {
    name: 'New Product',
    price: '49.99',
    category: 'Electronics'
  });
  console.log('Created:', result);
}

// Update a product
async function updateProduct(rowNumber) {
  const result = await client.updateRow('products', rowNumber, {
    price: '39.99'
  });
  console.log('Updated:', result);
}

// Delete a product
async function deleteProduct(rowNumber) {
  const result = await client.deleteRow('products', rowNumber);
  console.log('Deleted:', result);
}

Next.js

API Routes (App Router)

// app/api/products/route.ts
import { NextResponse } from 'next/server';

const API_BASE = 'https://api.sheetsjson.com/api/sheets';
const ACCOUNT = process.env.SHEETSJSON_ACCOUNT!;
const API_KEY = process.env.SHEETSJSON_API_KEY!;

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  
  const url = new URL(`${API_BASE}/${ACCOUNT}/products`);
  
  // Forward search params
  searchParams.forEach((value, key) => {
    url.searchParams.set(key, value);
  });

  const response = await fetch(url, {
    headers: { 'Authorization': `Bearer ${API_KEY}` },
    next: { revalidate: 60 } // Cache for 60 seconds
  });

  const data = await response.json();

  if (!response.ok) {
    return NextResponse.json(data, { status: response.status });
  }

  return NextResponse.json(data);
}

export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch(`${API_BASE}/${ACCOUNT}/products`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  });

  const data = await response.json();

  if (!response.ok) {
    return NextResponse.json(data, { status: response.status });
  }

  return NextResponse.json(data, { status: 201 });
}

Server Component

// app/products/page.tsx
async function getProducts() {
  const res = await fetch(
    `https://api.sheetsjson.com/api/sheets/${process.env.SHEETSJSON_ACCOUNT}/products`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.SHEETSJSON_API_KEY}`
      },
      next: { revalidate: 60 }
    }
  );

  if (!res.ok) {
    throw new Error('Failed to fetch products');
  }

  return res.json();
}

export default async function ProductsPage() {
  const { data: products } = await getProducts();

  return (
    <div className="grid grid-cols-3 gap-4 p-8">
      {products.map((product: any) => (
        <div key={product.id} className="border rounded-lg p-4">
          <h3 className="font-bold">{product.name}</h3>
          <p className="text-gray-600">{product.description}</p>
          <p className="text-xl font-bold mt-2">${product.price}</p>
        </div>
      ))}
    </div>
  );
}

Python (Flask)

# app.py
from flask import Flask, jsonify, request
import requests
import os

app = Flask(__name__)

API_BASE = 'https://api.sheetsjson.com/api/sheets'
ACCOUNT = os.environ.get('SHEETSJSON_ACCOUNT')
API_KEY = os.environ.get('SHEETSJSON_API_KEY')

def get_headers():
    return {'Authorization': f'Bearer {API_KEY}'}

@app.route('/api/products', methods=['GET'])
def get_products():
    url = f'{API_BASE}/{ACCOUNT}/products'
    params = dict(request.args)
    
    response = requests.get(url, headers=get_headers(), params=params)
    
    return jsonify(response.json()), response.status_code

@app.route('/api/products', methods=['POST'])
def create_product():
    url = f'{API_BASE}/{ACCOUNT}/products'
    
    response = requests.post(
        url,
        headers={**get_headers(), 'Content-Type': 'application/json'},
        json=request.json
    )
    
    return jsonify(response.json()), response.status_code

@app.route('/api/products/<int:row>', methods=['PUT'])
def update_product(row):
    url = f'{API_BASE}/{ACCOUNT}/products/{row}'
    
    response = requests.put(
        url,
        headers={**get_headers(), 'Content-Type': 'application/json'},
        json=request.json
    )
    
    return jsonify(response.json()), response.status_code

@app.route('/api/products/<int:row>', methods=['DELETE'])
def delete_product(row):
    url = f'{API_BASE}/{ACCOUNT}/products/{row}'
    
    response = requests.delete(url, headers=get_headers())
    
    return jsonify(response.json()), response.status_code

if __name__ == '__main__':
    app.run(debug=True)

Security Best Practices

  1. Never expose API keys in client-side code — Use a backend proxy
  2. Use environment variables — Store keys in .env files
  3. Implement rate limiting — Protect your proxy endpoints
  4. Validate input — Sanitize data before sending to the API
  5. Use HTTPS — Always use secure connections

Related Documentation

Was this page helpful? |