Skip to content

Harmonize the /lookup and /synonyms response shapes #291

Description

@gaurav

/lookup and /synonyms describe the same concept in two different shapes. Nothing here is wrong on its own, but a client that uses both endpoints has to special-case each one, and every divergence is a thing we have to explain in the API documentation rather than something a user can guess.

This came up while documenting the field lists on the improve-documentation branch — the taxa difference in particular is subtle enough that it needed its own paragraph in documentation/Babel.md.

Divergences

Verified against a local Solr loaded with tests/data/test-synonyms.json. For CHEBI:48407:

// GET /lookup?string=antiparkinson&limit=1
{"curie": "CHEBI:48407", "label": "antiparkinson agent", "synonyms": [...], "taxa": [], "types": ["biolink:NamedThing"]}

// GET /synonyms?preferred_curies=CHEBI:48407
{"curie": "CHEBI:48407", "preferred_name": "antiparkinson agent", "names": [...], "types": ["NamedThing"]}
  1. Different names for the same field. label vs preferred_name, and synonyms vs names.
  2. types is biolink:-prefixed by /lookup and bare by /synonyms. A client joining the two has to normalize the prefix itself.
  3. Absent vs empty. /lookup builds a LookupResult and so always emits taxa, defaulting to []. /synonyms returns the raw Solr document, and Solr omits an empty multi-valued field entirely — so the key is missing rather than empty.
  4. /synonyms is untyped. /lookup has response_model=List[LookupResult]; /synonyms is Dict[str, Dict], so its shape appears nowhere in the OpenAPI schema and is not validated.
  5. Solr internals leak through /synonyms. id, _version_ and curie_suffix are implementation detail of the index, not of the concept.
  6. Fields only one endpoint has. /synonyms returns shortest_name_length and taxon_specific, which /lookup drops; /lookup returns score, highlighting, explain and debug, which have no meaning for a direct CURIE fetch.
  7. A CURIE that isn't in the index comes back from /synonyms as {"FAKE:9999": {}} — an empty object rather than an omitted key or an explicit null.

Suggested direction

Give /synonyms a real Pydantic response model and make it return the same view of a concept that /lookup does, keeping the search-only fields (score, highlighting, explain, debug) exclusive to /lookup and dropping the Solr internals. That means picking one name per field and one convention for types.

That is a breaking change for /synonyms consumers, so it probably wants to ride along with a version bump, and it may be worth doing at the same time as #227 (bulk-synonyms) so that a new endpoint isn't added in the old shape.

Worth deciding explicitly rather than by default:

  • Which field names win (label/preferred_name, synonyms/names)
  • Whether types is biolink:-prefixed everywhere or bare everywhere
  • Whether absent-vs-empty is normalized in both directions
  • Whether /synonyms keeps shortest_name_length and taxon_specific, and whether /lookup gains them
  • What a not-found CURIE looks like

Once this is settled, the endpoint-specific caveats in documentation/API.md and the taxa paragraph in documentation/Babel.md can be deleted rather than maintained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions