setu / Frontend /lib /api-usage-example.md
khagu's picture
chore: finally untrack large database files
3998131

API Client Usage Guide

This guide shows how to use the API client for making authenticated requests to the Nepal Justice Weaver backend.

Backend API Information

  • Base URL: http://localhost:8000
  • API Prefix: /api/v1
  • Authentication: Supabase Auth with JWT tokens

Basic Setup

The API client is already configured and can be imported from @/lib/api-client:

import { apiClient } from "@/lib/api-client"
import { setAuthData, getAccessToken, clearAuthData } from "@/lib/auth-utils"

Authentication

Register (Sign Up)

Endpoint: POST /api/v1/auth/signup

The registration now accepts only basic information:

import { apiClient } from "@/lib/api-client"
import { setAuthData } from "@/lib/auth-utils"

try {
  const response = await apiClient.register(
    "john@example.com",      // email
    "securePassword123",      // password (min 6 characters)
    "John Doe"                // full_name
  )

  // Response: { access_token, refresh_token?, user }
  setAuthData(response.access_token, response.refresh_token, response.user)

  console.log("Registered successfully:", response.user)
} catch (error) {
  console.error("Registration failed:", error.message)
}

Login

Endpoint: POST /api/v1/auth/login

import { apiClient } from "@/lib/api-client"
import { setAuthData } from "@/lib/auth-utils"

try {
  const response = await apiClient.login(
    "john@example.com",
    "securePassword123"
  )

  // Store authentication data
  setAuthData(response.access_token, response.refresh_token, response.user)

  console.log("Logged in successfully:", response.user)
} catch (error) {
  console.error("Login failed:", error.message)
}

Logout

import { clearAuthData } from "@/lib/auth-utils"

// Clear all authentication data from localStorage
clearAuthData()

// Optionally redirect to login page
router.push("/login")

Protected Features

These features require authentication (access token must be stored in localStorage).

1. Letter Generation

Endpoint: POST /api/v1/letter/generate (requires auth)

Generate legal letters based on user input:

try {
  const response = await apiClient.post("/api/v1/letter/generate", {
    letterType: "complaint",
    recipientName: "District Court",
    subject: "Property Dispute",
    details: "Description of the legal matter..."
  })

  console.log("Generated letter:", response)
} catch (error) {
  console.error("Letter generation failed:", error.message)
}

2. Law Explanation Chatbot

Endpoint: POST /api/v1/law-explanation/chat (requires auth)

Get explanations about Nepali laws:

try {
  const response = await apiClient.post("/api/v1/law-explanation/chat", {
    query: "What are my rights as a tenant in Nepal?",
    context: "rental agreement dispute"
  })

  console.log("Chatbot response:", response)
} catch (error) {
  console.error("Chatbot request failed:", error.message)
}

3. Bias Detection

Endpoint: POST /api/v1/bias-detection/analyze (requires auth)

Analyze legal documents or text for potential bias:

try {
  const response = await apiClient.post("/api/v1/bias-detection/analyze", {
    text: "Legal document text to analyze for bias...",
    documentType: "court_decision"
  })

  console.log("Bias analysis:", response)
} catch (error) {
  console.error("Bias detection failed:", error.message)
}

Generic Protected Requests

The API client provides generic methods for protected endpoints:

GET Request

try {
  const data = await apiClient.get("/api/v1/some-endpoint")
  console.log("Data:", data)
} catch (error) {
  console.error("Failed:", error.message)
}

POST Request

try {
  const result = await apiClient.post("/api/v1/some-endpoint", {
    field1: "value1",
    field2: "value2"
  })
  console.log("Result:", result)
} catch (error) {
  console.error("Failed:", error.message)
}

PUT Request

try {
  const updated = await apiClient.put("/api/v1/some-endpoint", {
    field: "updated value"
  })
  console.log("Updated:", updated)
} catch (error) {
  console.error("Failed:", error.message)
}

DELETE Request

try {
  const result = await apiClient.delete("/api/v1/some-endpoint")
  console.log("Deleted:", result)
} catch (error) {
  console.error("Failed:", error.message)
}

Using in React Components

Example: Fetching Data on Component Mount

"use client"

import { useEffect, useState } from "react"
import { apiClient } from "@/lib/api-client"

export default function ProfilePage() {
  const [profile, setProfile] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    const fetchProfile = async () => {
      try {
        const data = await apiClient.get("/users/me")
        setProfile(data)
      } catch (err) {
        setError(err.message)
      } finally {
        setLoading(false)
      }
    }

    fetchProfile()
  }, [])

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

  return <div>Welcome, {profile?.name}</div>
}

Example: Form Submission

"use client"

import { useState } from "react"
import { apiClient } from "@/lib/api-client"

export default function CreateQueryForm() {
  const [title, setTitle] = useState("")
  const [description, setDescription] = useState("")
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  const handleSubmit = async (e) => {
    e.preventDefault()
    setLoading(true)
    setError(null)

    try {
      const result = await apiClient.post("/legal-queries", {
        title,
        description
      })
      console.log("Query created:", result)
      // Reset form or redirect
      setTitle("")
      setDescription("")
    } catch (err) {
      setError(err.message)
    } finally {
      setLoading(false)
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Title"
      />
      <textarea
        value={description}
        onChange={(e) => setDescription(e.target.value)}
        placeholder="Description"
      />
      {error && <div className="error">{error}</div>}
      <button type="submit" disabled={loading}>
        {loading ? "Submitting..." : "Submit"}
      </button>
    </form>
  )
}

Authentication Utilities

The @/lib/auth-utils module provides helper functions for managing authentication:

Store Authentication Data

import { setAuthData } from "@/lib/auth-utils"

// Store access token, refresh token, and user info
setAuthData(accessToken, refreshToken, userData)

Get Access Token

import { getAccessToken } from "@/lib/auth-utils"

const token = getAccessToken()
if (token) {
  console.log("User is authenticated")
}

Get Refresh Token

import { getRefreshToken } from "@/lib/auth-utils"

const refreshToken = getRefreshToken()

Get User Data

import { getUser } from "@/lib/auth-utils"

const user = getUser()
if (user) {
  console.log("User email:", user.email)
}

Check Authentication Status

import { isAuthenticated } from "@/lib/auth-utils"

if (isAuthenticated()) {
  // User has valid access token
  // Allow access to protected features
} else {
  // Redirect to login
  router.push("/login")
}

Clear Authentication (Logout)

import { clearAuthData } from "@/lib/auth-utils"

// Remove all auth data from localStorage
clearAuthData()

Token Storage Details

Authentication data is stored in localStorage with these keys:

  • access_token - JWT access token from Supabase
  • refresh_token - JWT refresh token (optional)
  • user - JSON stringified user object

The access token is automatically:

  • Stored when you register or login
  • Included in all authenticated requests with the Authorization: Bearer <token> header
  • Retrieved from localStorage for each protected request

Error Handling

The API client throws errors with meaningful messages:

  • Network errors: Connection failures
  • API errors: Returns the detail field from backend response
  • Validation errors: Invalid input or missing required fields
  • Authentication errors: Invalid or expired token (401)

Always wrap API calls in try-catch blocks to handle errors gracefully:

try {
  const result = await apiClient.post("/api/v1/some-endpoint", data)
} catch (error) {
  if (error instanceof Error) {
    // Display user-friendly error message
    toast.error(error.message)
  }
}

Environment Variables

Set the backend URL in your .env.local file:

NEXT_PUBLIC_BACKEND_URL=http://localhost:8000

If not set, it defaults to http://localhost:8000.

Summary

Registration Flow:

  1. User fills out registration form with name, email, password
  2. Form validates password match and minimum length
  3. Call apiClient.register(email, password, name)
  4. Store tokens with setAuthData(access_token, refresh_token, user)
  5. Redirect to dashboard

Login Flow:

  1. User enters email and password
  2. Call apiClient.login(email, password)
  3. Store tokens with setAuthData(access_token, refresh_token, user)
  4. Redirect to dashboard

Using Protected Features:

  1. Check authentication with isAuthenticated()
  2. Make API calls to letter generation, chatbot, or bias detection endpoints
  3. API client automatically includes the Bearer token
  4. Handle success/error responses appropriately