Action

Type

Resolved On

Configurable API Redirects refactoring - - -

Configurable API Redirects

Overview

API routes hardcode redirect paths, which creates issues when working with historical data and reduces flexibility for different use cases.

Problem

Routes like api/open/task.ts hardcode redirect paths:

// src/pages/api/open/task.ts:19
return redirect("/benben/projects/" + project + '/' + level);

// src/pages/api/close/task.ts:19
return redirect("/benben/projects/" + project + '/' + level);

// src/pages/api/update/backlog.ts:26
return redirect("/benben/projects");

// src/pages/api/open/goal.ts:17 (with TODO comment)
// TODO - ISSUE (MINOR): when updating goal of previous month, it always redirects to latest goals
return redirect("/" + owner + "/weeks");

Issues

  1. Historical Data Problem: When updating data from a previous month, the redirect goes to the current month/week
  2. No Flexibility: Client cannot specify where to redirect after action
  3. Hardcoded Routes: URL structure changes require updating multiple API files

Solution

Pass the return URL as a hidden form field or use a redirectTo query parameter.

Option 1: Form Field Approach

// API route
export const POST: APIRoute = async ({ request, redirect }) => {
  const formData = await request.formData();
  const returnUrl = String(formData.get("returnUrl")) || "/benben/projects";

  // ... perform action ...

  return redirect(returnUrl);
};

Option 2: Query Parameter Approach

export const POST: APIRoute = async ({ request, redirect }) => {
  const url = new URL(request.url);
  const returnUrl = url.searchParams.get("returnTo") || "/benben/projects";

  // ... perform action ...

  return redirect(returnUrl);
};

Option 3: Smart Redirect Utility

// src/lib/redirect.ts
export function calculateRedirectUrl(
  action: 'open' | 'close' | 'update',
  entity: 'task' | 'goal' | 'idea' | 'backlog',
  params: {
    project?: string;
    owner?: string;
    year?: number;
    month?: number;
    level?: number;
    returnUrl?: string;
  }
): string {
  // Use explicit return URL if provided
  if (params.returnUrl) {
    return params.returnUrl;
  }

  // Otherwise calculate based on entity and context
  if (entity === 'task' && params.project) {
    return `/benben/projects/${params.project}/${params.level ?? ''}`;
  }

  if (entity === 'goal' && params.owner) {
    return `/${params.owner}/weeks`;
  }

  // Default fallback
  return '/benben/projects';
}

Benefits

  • Contextual Redirects: Return to the page where the action originated
  • Historical Data Support: Return to the correct month/week after updates
  • Flexibility: Client controls redirect destination
  • Maintainability: URL patterns centralized in one location
  • User Experience: Users stay in context after actions

Implementation Notes

  • Form field approach is more secure (can’t be tampered with via URL)
  • Query parameter approach is simpler for direct links
  • Consider validating return URLs to prevent open redirect vulnerabilities
  • Default to safe fallback if no return URL specified
  • src/pages/api/open/task.ts
  • src/pages/api/close/task.ts
  • src/pages/api/open/goal.ts
  • src/pages/api/close/goal.ts
  • src/pages/api/update/backlog.ts