NJ Municipality Lookup
CodebaseSrcApp(app)

Bulk

Page for processing multiple addresses at once (up to 1,000), returning municipality information for each address.

Bulk Processing Page (app/bulk/)

Page for processing multiple addresses at once (up to 1,000), returning municipality information for each address.

Purpose

Enables batch geocoding for:

  1. Business registration systems (process entire employee roster)
  2. Data migration (normalize existing address databases)
  3. Bulk validation (verify address data quality)
  4. Reporting (generate municipality statistics)

Route

/bulk - Bulk Address Processing

Features

Input Methods

  1. Text Area Paste: Copy/paste addresses (one per line)
  2. CSV Upload: Upload file with address column
  3. Example Data: Load sample addresses for testing

Processing

  • Parallel Execution: Multiple addresses geocoded simultaneously
  • Rate Limiting: Respects API rate limits automatically
  • Progress Tracking: Real-time progress bar during processing
  • Partial Success: Continues even if some addresses fail

Output

  • Results Table: Municipality, formatted address, coordinates for each input
  • Error Highlighting: Failed addresses marked with error messages
  • CSV Export: Download results with all data columns
  • Copy to Clipboard: Copy results as tab-separated values

Usage Flow

  1. Navigate to /bulk
  2. Enter addresses (paste or upload CSV)
  3. Click "Process Addresses"
  4. View progress bar (updates in real-time)
  5. Review results table
  6. Download CSV or copy to clipboard

Input Format

Text Area (Line-Separated)

323 Dr Martin Luther King Jr Blvd, Newark, NJ 07102
125 West State Street, Trenton, NJ 08608
354 Stockton Street, Princeton, NJ 08540

CSV Upload

address
"323 Dr Martin Luther King Jr Blvd, Newark, NJ 07102"
"125 West State Street, Trenton, NJ 08608"
"354 Stockton Street, Princeton, NJ 08540"

Requirements:

  • Header row must include column named address (case-insensitive)
  • Each row contains one address
  • Maximum 1,000 addresses per file

Output Format

Results Table

AddressMunicipalityCountyFormatted AddressStatus
323 Dr MLK Jr Blvd...NewarkEssex323 DR MARTIN LUTHER KING...✅ Success
125 West State St...TrentonMercer125 WEST STATE STREET...✅ Success
Invalid address---❌ Not found

CSV Export

input_address,municipality,county,formatted_address,latitude,longitude,status,error
"323 Dr Martin Luther King Jr Blvd, Newark, NJ 07102",Newark,Essex,"323 DR MARTIN LUTHER KING JR BLVD, NEWARK, NJ, 07102",40.740623,-74.175383,success,
"Invalid address",,,,,,,error,"Address not found"

Limits and Performance

MetricValue
Maximum addresses1,000 per request
Processing time~2-5 seconds per 100 addresses
Rate limitingEnforced by API adapter
Cache hit rate60-80% for common addresses
Memory usage< 50MB for 1,000 addresses

Error Handling

Address-Level Errors

Individual addresses may fail while others succeed:

  • Address Not Found: Invalid or non-existent address
  • Validation Error: Malformed input (empty, too long, XSS)
  • API Timeout: NJ API didn't respond in time

Failed addresses appear in results table with error details.

Request-Level Errors

Entire request fails if:

  • Input exceeds 1,000 addresses
  • No addresses provided
  • Invalid file format
  • Server error during processing

UI Components

  • Address Input: Large textarea or file upload
  • Process Button: Triggers bulk geocoding server action
  • Progress Bar: Shows completion percentage
  • Results Table: Displays all results with status indicators
  • Export Buttons: Download CSV or copy to clipboard
  • Error Summary: Count of successful/failed addresses

Server Action

Page calls geocodeAddressesBulk() server action:

import { geocodeAddressesBulk } from "@/app/actions/bulk-geocoding";

const addresses = ["Address 1", "Address 2", "Address 3"];
const results = await geocodeAddressesBulk(addresses);

if (results.success) {
  // results.data is array of GeocodingResult | null
  results.data.forEach((result, index) => {
    if (result) {
      console.log(`${addresses[index]}${result.municipality.name}`);
    } else {
      console.log(`${addresses[index]} → FAILED`);
    }
  });
}

Accessibility

  • Keyboard Navigation: Tab through input, buttons, results
  • Screen Reader Support: ARIA labels on all interactive elements
  • Progress Announcements: Live region updates for status changes
  • Error Messages: Associated with specific input fields
  • Focus Management: Focus moves to results after processing

Testing

Bulk processing is tested with:

  1. Unit Tests: Server action with various input sizes
  2. Integration Tests: Full processing flow with cache
  3. E2E Tests: Playwright tests uploading CSV and downloading results
  4. Performance Tests: 1,000 address processing time

Use Cases

Business Registration System

Agency processes employee addresses during business registration:

// Get employee addresses from registration form
const employees = formData.getAll("employee_address");

// Bulk geocode all addresses
const results = await geocodeAddressesBulk(employees);

// Store municipality info with each employee record
employees.forEach((employee, i) => {
  if (results.data[i]) {
    employee.municipality = results.data[i].municipality.name;
    employee.formattedAddress = results.data[i].formattedAddress;
  }
});

Data Migration

Normalize existing address database:

  1. Export addresses from legacy system
  2. Upload CSV to bulk processing page
  3. Download results with municipality info
  4. Import into new system

On this page