Skip to content

JS/TS: fetch/axios URLs built from template literals over module-level constants are not extracted as HTTP_CALLS #1147

Open

Description

@paolo-blocklabs

Version

main @ e678b2b (v0.9.0-221)

Platform

macOS (Apple Silicon)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

In a production Next.js/React 19 monorepo (pnpm workspaces, ~2,800 nodes indexed), the graph contains only 10 HTTP_CALLS edges — all literal page paths from tests (/en-gb/slots, /games/list, …). None of the application's actual API calls are extracted, because every endpoint is a template literal composed from module-level string constants.

With the callee side now minting Route nodes correctly (#1059), this caller-side gap is the last blocker for CROSS_HTTP_CALLS: the linking pass still produces zero edges fleet-wide. Possibly related to the native-fetch under-extraction tracked in #856 / #706, but this is specifically about template-literal URL composition.

Suggested handling (in increasing order of ambition)

  1. Constant folding of template literals: when every ${...} part of a template literal resolves (within the module, or via an imported const) to a compile-time string, fold and extract the result. `${BASE}/users/me` with const BASE = 'api' → api/users/me. This alone covers the central-endpoints-map idiom below — the most common frontend pattern we know of.
  2. Imported-constant resolution at the call site: fetch(API_ENDPOINTS.USER) / client.post(API_ENDPOINTS.GAME_LAUNCH_URL, ...) — resolve the member access to the folded constant from step 1 across module boundaries.
  3. Unresolvable-prefix canonicalization: when a leading ${...} can't be folded (e.g. ${getApiBaseUrl()}), mint the edge with the resolvable suffix (/api/users/me) and mark the prefix as dynamic (property url_prefix_dynamic: true), so cross-repo matching can still suffix-match against Route identities.

Steps 1+2 would take our monorepo from 0 extracted API calls to full coverage of the endpoints table; step 3 covers the wrapper-client pattern generically.

Environment

Next.js / React 19 micro-frontends, pnpm workspaces; calls via native fetch and a thin axios-style wrapper. Built from source on Linux x86_64, gcc, make -f Makefile.cbm cbm.

Reproduction

  1. Code: dummy snippet (mirrors our real endpoint-constants module + call sites):

endpoints.ts:

const BASE = 'api'
export const API_ENDPOINTS = {
  LOGIN: `${BASE}/users/login`,
  USER: `${BASE}/users/me`,
  GAME_LAUNCH_URL: `${BASE}/games/launch`,
}
export const getApiBaseUrl = () => `${API_PROTOCOL}://${process.env.API_DOMAIN}/`

client.ts:

import { API_ENDPOINTS, getApiBaseUrl } from './endpoints'
const response = await fetch(`${getApiBaseUrl()}/${API_ENDPOINTS.USER}`, { method: 'GET' })
const result = await apiClient.post(API_ENDPOINTS.GAME_LAUNCH_URL, payload)
  1. Command: codebase-memory-mcp cli index_repository --repo-path . then
    codebase-memory-mcp cli query_graph with MATCH ()-[r:HTTP_CALLS]->() RETURN r.url_path

  2. Result: no HTTP_CALLS edge for any of these call sites (only unrelated literal-string paths from tests are minted) / Expected: edges with url_path api/users/me, api/games/launch (or suffix-canonicalized equivalents)

Logs


Diagnostics trajectory (memory / performance / leak issues)


Project scale (if relevant)

2,845 nodes / 5,476 edges (Next.js pnpm-workspaces monorepo)

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Activity

  1. added
    parsing/qualityGraph extraction bugs, false positives, missing edges
    ux/behaviorDisplay bugs, docs, adoption UX
    windowsWindows-specific issues
    on Jul 17, 2026
  2. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    and removed
    windowsWindows-specific issues
    on Jul 18, 2026
  3. added this to the 0.9.1-rc milestone on Jul 18, 2026
  4. DeusData commented on Jul 18, 2026

    @DeusData
    Owner

    Thanks for separating this from literal template parsing. This is tracked as a high-priority 0.9.1-rc extraction gap under #592. It is related to #1006/#1008, but not a duplicate: this report requires module-local and imported constant/member resolution before the HTTP call edge can be minted. The RED matrix should keep fully foldable constants, imported constants, and unresolved dynamic prefixes separate; unresolved expressions must not be promoted to exact URLs.

  5. rehanazher commented on Aug 22, 2026

    @rehanazher

    Reproduces on v0.10.8 (npm build, linux-amd64), a month after this was
    filed. Adding a data point with a control repo, which we think isolates the
    variable more sharply than the original report could.

    The affected repo

    A production Next.js/React frontend. Its HTTP layer is a central axios wrapper
    plus a service module that composes endpoints from a baseURL constant.
    search_code finds it easily:

    query hits top file
    axios 35 src/services/HttpClient.ts
    baseURL 28 src/services/GeneralService.ts

    The graph disagrees:

    Route nodes in this repo:                 60 total,  0 in src/services
    Channel nodes:                             0
    HTTP_CALLS edges originating in src/services: 0
    

    Of the repo's 94 HTTP_CALLS edges, exactly one comes from the service
    layer (r.callee = GeneralService.getInstance()). The other 93 are
    client-side navigation — that is #1250, filed separately.

    So the module that demonstrably performs every backend call in this
    application contributes 1 edge, and the endpoints themselves never appear.

    The control

    A sibling frontend in the same estate — same fleet, same indexer version, same
    day — calls axios.* with literal URLs rather than composed ones:

    repo HTTP_CALLS edges callees cross-repo links resolved
    Frontend A (template literals) 94 93 navigation, 1 real 0
    Frontend B (literal URLs) 43 43 × axios.get/post/patch/put/delete 18

    Frontend B's edges are all real and resolve to 18 cross-repo links against its
    own backend. Frontend A resolves to zero.

    Both repos are indexed by the same binary in the same run, and consumed by the
    same downstream linker. The only difference is whether the URL is a literal or
    a template literal composed from a constant. We think that makes this a clean
    isolation of the defect described in the issue.

    Why it matters downstream

    We derive a service dependency map from HTTP_CALLS. For Frontend A the map is
    empty — not wrong, empty — while the application in reality calls its backend
    on nearly every screen. An empty result is particularly awkward for us because
    "no integrations found" and "the extractor could not see them" are
    indistinguishable to a reader, and we deliberately never present absence as a
    confident answer.

    We can detect and discard the navigation noise (#1250) using r.callee. There
    is no equivalent local remedy for this one: the endpoints are simply not in the
    graph, so nothing downstream can recover them.

    On the suggested fix

    Option 1 in the issue body — constant folding of template literals where every
    ${...} part resolves within the module or via an imported const — would
    cover this repo exactly. The repo matches the "central endpoints map" idiom the original reporter
    describes: a single service module holding the base URL, with per-endpoint
    methods composing paths onto it.

    We have no opinion on the more ambitious options.

    To be precise about what we did and did not verify: we observed the shape
    from the index — GeneralService.ts is a class whose constructor matches
    baseURL (a two-line match inside an 11-line constructor) and whose methods
    (saveData and siblings) are the call sites — but we did not retrieve the
    literal source text, so we are not quoting it. The claim we stand behind is the
    measurable one: that module contributes 1 HTTP_CALLS edge and no endpoints,
    while a sibling repo using literal URLs contributes 43 and resolves 18 links.

    Version detail

    codebase-memory-mcp 0.10.8   (npm, linux-amd64)
    

    Confirmed by process linkage from the running service, not by the
    /usr/local/bin symlink — worth mentioning because on our host that symlink
    pointed at a much older version while the service used an explicitly
    versioned path.

  6. DeusData commented on Oct 19, 2026

    @DeusData
    Owner

    Thank you @rehanazher for the versioned control comparison and for distinguishing observed graph output from source you did not retrieve. It strengthens the missing composed-endpoint case while keeping navigation noise in #1250 separate. I have no new fix or verification result to add.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.ux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions