Skip to content

API URL Handling

Overview

This guide explains how the API URL handling works in the tracker system, particularly focusing on the interaction between FastAPI, the frontend, and the nginx proxy.

FastAPI URL Handling

FastAPI is configured with redirect_slashes=False, which means:

  1. Endpoints will work exactly as defined, with or without trailing slashes
  2. No redirects will be issued for trailing slash mismatches
  3. Clients can use either form (with or without trailing slashes) consistently

This configuration eliminates the 307 redirects that were previously occurring when a request was made to an endpoint with a trailing slash format different from how it was defined.

Previously, FastAPI had a specific behavior regarding URL trailing slashes:

  1. Most API endpoints in FastAPI were defined with trailing slashes (e.g., /api/v1/users/)
  2. When a request was made to an endpoint without a trailing slash (e.g., /api/v1/users), FastAPI automatically redirected to the version with a trailing slash using a 307 Temporary Redirect
  3. Some specific endpoints were defined without trailing slashes (e.g., /api/v1/auth/login/json)

This behavior was consistent with the OpenAPI specification, but caused issues with proxying and required complex workarounds in the frontend and nginx configuration.

Frontend URL Handling

The frontend client (in tracker-admin/src/api/client.ts) has logic to handle URL trailing slashes:

// Handle URL trailing slashes based on endpoint type
if (config.url && config.url !== "") {
  // Skip handling for static file URLs
  if (!config.url.startsWith("/static/")) {
    // Split URL into path and query parts
    const [path, query] = config.url.split("?");

    // List of endpoints that don't want trailing slashes
    const noTrailingSlashEndpoints = [
      "/auth/login/json",
      "/auth/refresh-cookie",
      "/auth/refresh",
      "/auth/logout",
    ];

    // Check if this URL matches any endpoint that doesn't want a trailing slash
    const shouldRemoveTrailingSlash = noTrailingSlashEndpoints.some(
      (endpoint) => path.endsWith(endpoint + "/"),
    );

    // Check if this URL needs a trailing slash added
    const shouldAddTrailingSlash =
      !path.endsWith("/") &&
      !noTrailingSlashEndpoints.some((endpoint) => path.endsWith(endpoint));

    // Apply the appropriate transformation
    if (shouldRemoveTrailingSlash) {
      // Remove trailing slash
      const newPath = path.slice(0, -1);
      config.url = query ? `${newPath}?${query}` : newPath;
      console.log("Adapter: Removed trailing slash from URL:", config.url);
    } else if (shouldAddTrailingSlash) {
      // Add trailing slash
      const newPath = `${path}/`;
      config.url = query ? `${newPath}?${query}` : newPath;
      console.log("Adapter: Added trailing slash to URL:", config.url);
    }
  }
}

This logic attempts to add trailing slashes to all endpoints except for a few specific ones, but it may not work correctly in all cases.

Nginx URL Handling

The nginx configuration handles URL trailing slashes in two ways:

  1. Special case handling for specific endpoints that don't use trailing slashes (e.g., /api/v1/auth/login/json)
  2. Automatic handling of redirects for all other endpoints
# Special case for auth login endpoint (no trailing slash)
location = /api/v1/auth/login/json {
    proxy_pass __API_URL__/api/v1/auth/login/json;
    # ... other proxy settings ...
}

# Handle API requests
location /api/ {
    proxy_pass __API_URL__/api/;
    # ... other proxy settings ...

    # Follow redirects for trailing slashes
    proxy_redirect off;
    proxy_intercept_errors on;
    error_page 301 302 307 = @handle_redirect;
}

# Handle redirects
location @handle_redirect {
    set $saved_redirect_location $upstream_http_location;
    proxy_pass $saved_redirect_location;
}

This configuration ensures that:

  1. Requests to /api/v1/auth/login/json are sent directly to the API server without modification
  2. All other API requests are sent to the API server, and any redirects (including 307 redirects for trailing slashes) are automatically followed

HTTPS Considerations

When the application is running behind a load balancer that terminates SSL, there are additional considerations:

  1. The X-Forwarded-Proto header must be set to "https" for all API requests
  2. The Host header must be set correctly to ensure proper routing
  3. The nginx configuration must handle IPv6 connectivity issues

See the HTTPS Behind a Load Balancer guide for more details.

Troubleshooting

If you encounter issues with API URL handling, check the following:

  1. Network Requests: Use the browser's developer tools to inspect network requests and verify that endpoints are being accessed directly without redirects
  2. Nginx Logs: Check the nginx logs for errors or unusual behavior
  3. API Logs: Check the FastAPI logs for information about incoming requests

Common issues include:

  1. Incorrect Host Header: If the Host header is not set correctly, the API server may not be able to route requests properly
  2. IPv6 Connectivity Issues: If nginx is trying to connect to the API server using IPv6, but IPv6 is not available, the connection will fail
  3. Legacy Code: The frontend and nginx may still have legacy code that handles trailing slashes unnecessarily

Best Practices

  1. Frontend: With redirect_slashes=False, the frontend can use either form (with or without trailing slashes) consistently. Consider standardizing on one approach for clarity.
  2. API: Be consistent with trailing slashes in API endpoint definitions for readability, even though both forms will work.
  3. Nginx: Simplify the nginx configuration by removing special cases and redirect handling that are no longer needed.
  4. Documentation: Keep the OpenAPI specification up to date with the correct endpoint definitions.
  5. Cleanup: Gradually remove the now-unnecessary trailing slash handling code from the frontend and nginx configuration as confidence in the new approach grows.