bioclients (development version)
ClinVar
-
clinvar_classification()gainsallele, the protein change you mean, such as"V600E"or"p.Val600Glu"(#40). ClinVar’s search often matches several records, and the first is not always the variant you asked for:rs113488022lists BRAF V600G before V600E, the HGVS name of V600E lists BRAF I208V first, andrs121913343lists TP53 R273G before R273C. Withallele, the record that has that protein change is returned, orno_datawhen none has it. - The result has a new column,
n_matches: how many records fit the request. A value above 1 means the row may be for a different variant from the one you meant. Withoutallelethe first record is still returned, so existing code keeps working. - The help page now names the terms that match one record, an SPDI or a VCV accession, and no longer suggests an rsID as the best term.
MyGene
-
mygene_gene()andmygene_genes()now return an Ensembl id for genes that MyGene maps to more than one id, such as PTEN and MUC16. Before this,ensembl_genewasNAfor all of them (#39). The extra ids sit on assembly patches or alternate haplotypes, and the one on a reference chromosome is kept. Taking the first id would not work: for HLA-A it is on an alternate haplotype. A gene whose ids all sit off the reference chromosomes, such as HLA-DRB3, getsNA. Lookups that start fromensembl_gene, such asopentargets_gene_diseases()andhpa_gene(), now work for these genes.
bioclients 0.1.1
CRAN release: 2026-09-15
Ensembl VEP
- Results are matched back on the echoed
inputline and onvcf_stringrather than on a key rebuilt from the reported coordinates. VEP left-trims and renumbers indels, so the rebuilt key matched only SNVs and every indel came back as an empty row. -
vep_variants()takes anoptionslist of request flags, defaulting tovep_default_options().dbNSFPis refused. -
vep_parse_element()addstranscript,gene_id,biotype,hgvsc,hgvsp,canonical,codons,amino_acids,cadd_phred,cadd_raw,revel, the fourspliceai_ds_*scores,spliceai_maxandlof. - New
vep_parse_colocated()reads the dbSNP record beside a variant:rsid, gnomAD genome and exome frequencies with their per-population maximum, and the clinical significance of the allele asked.vep_parse_batch()carries these columns. - New
vep_variants_all()takes any number of variants, chunks them at the 200 VEP accepts per POST and dispatches the chunks throughbiohttp::post_json_many(). One row per input in input order; a failed chunk becomes rows ofNAcarrying the envelope status instatus. -
vep_variants_all()puts the input in genome order and sends a repeated variant once, before chunking, rather than once per occurrence. A duplicate now always carries the same answer at every position it was asked at, which was not guaranteed before when duplicates could land in different chunks and see different transient failures.chunk_sizeand the row count and order the contract promises are unchanged. It also applies a defaultthrottle(one request per second) so an unpaced burst does not trip Ensembl’s rate limiter; passthrottle = NULLto disable it.
gnomAD
- New
gnomad_frequency_by_id()looks a variant up bychrom-pos-ref-alt, which names one allele where an rsID names a site, andgnomad_frequencies()does the same for many ids, alias-batched and chunked likegnomad_constraints().gnomad_variant_id()builds the id. The flat row fromgnomad_parse_variant()carries exome and genomeaf,ac,anandnhomalt, a derivedgrpmax,faf95andfilters. - The 25-alias cost cap was re-verified against the live API for the variant query. The cost is one per alias whatever the selection set.
ClinVar
-
clinvar_classification()throttles by default at the documented E-utilities rate, 3 requests a second or 10 whenNCBI_API_KEYis set, and sendstoolandemailread fromBIOHTTP_CALLER_IDENTITYandBIOHTTP_CONTACT_EMAILwhen they are set.
MyGene
-
mygene_genes()chunks a list longer than the 1000 identifiers MyGene takes per POST, dispatches the chunks throughbiohttp::post_json_many()and merges the hits back in input order.
Packaging
Everything below is for the CRAN submission. Nothing here changes how the package behaves, and no function’s arguments or output shape moved.
Every example that calls a service now runs under
\donttest{}rather than\dontrun{}, 56 of them.\dontrun{}is for an example that genuinely cannot be executed, which none of these are: they are keyless public requests, a client returns an envelope rather than raising, and biohttp’s disk cache is off unless a caller turns it on. So an example run with no network printsNULLand writes nothing.Every help page now cites the service behind it. The 29 service files each carry the canonical publication with a DOI and the service’s own documentation URL, and the other topics in the file inherit it, so all 134 exported topics have a
Referencessection. Each DOI was resolved through CrossRef and checked against the title, author and pagination written here.uniprot_features()carries two, because it calls the EBI Proteins API on a different host to the rest of the file and both are worth naming.The
Descriptionfield cites five of those references, for Ensembl, UniProt, gnomAD, Open Targets and AlphaFold DB, in the form CRAN asks for.biohttpis on CRAN now, at 0.1.2. So theRemotes:line is gone and we ask forbiohttp (>= 0.1.2)instead. I ran the tests against the CRAN build rather than the newer one on GitHub, to be sure nothing here needs a version CRAN cannot give you.cran-comments.mdrecords the submission notes.
bioclients 0.1.0
First release. 29 clients, 125 exported functions, and the behaviour they depend on checked against the live services rather than only against stored responses. 28 of the 29 confirmed; see “Known limits” for the one that did not, which was down at the time rather than wrong.
The envelope contract comes from biohttp and is fixed. What can still move is the shape of a parser’s output, since a service adding a field is a reason to carry it. Anything that changes an existing column is a breaking change and gets a version to say so.
What it covers
- One client per service, 29 of them, covering every web service the app family calls. 125 exported functions in all.
- Every client is split in two. The parser takes an already parsed response body and returns a tibble, or
NULLwhen there is genuinely nothing there. It touches no network, so it is tested directly against a stored response. - The request half calls
biohttp, passes a failing envelope straight through, turns aNULLparse intono_data, and wraps a success inok. No client writes its owntryCatch(). - Where a service supports it, a batch entry point returns one row per input in input order, with a row of
NAfor a miss. A shorter table would silently shift every row after it onto the wrong gene.
Fixtures
- 51 stored response bodies, ported byte for byte out of the apps this package replaces. A test asserts they still match.
- A ported fixture that needs editing is treated as a signal rather than a chore: it means the parser changed behaviour during the port, and that has to be justified rather than fixed by editing the recording.
Verified service behaviour
These cost real debugging time to establish in the apps and are easy to lose in a port, so each one has a comment at the call site and a test where it is testable.
Everything below has now been confirmed against the live service, not only against a stored response. tests/testthat/test-live.R is where that happens. It calls all 29 services once and then makes the specific assertions the claims below depend on. It never runs on its own, and no CI job turns it on:
-
MyVariant silently returns
notfoundfor GRCh38 coordinates when the request omitsassembly=hg38. Silent, not an error, which is exactly why it needs a test. - Ensembl VEP takes exactly 200 per POST, gnomAD exactly 25 GraphQL aliases, MyVariant 1000 per batch. Verified ceilings, not tuning.
-
IMPC’s
genotype-phenotypecore has nohuman_gene_symbolfield, so querying it by symbol returns HTTP 200 with zero documents. A miss isno_dataand never0, because a gene never phenotyped and a gene phenotyped with no significant result are not the same answer. -
Europe PMC and PubTator count zero as an answer, so both come back
ok. A gene with no literature is not an outage. - GTEx needs its own versioned GENCODE id, from GTEx’s own reference lookup. An Ensembl gene id from anywhere else will not work.
-
MyGene returns the HGNC id under
HGNC, upper case. Asking forhgncreturns nothing at all. -
Monarch wants the CURIE colon unencoded, so
HGNC:11998must reach it as-is rather than asHGNC%3A11998. -
PanelApp’s index
searchparameter does not filter, and Reactome answers a gene it does not know with a 404 rather than an empty array. -
STRING replies with
text/json, so the content type check has to be off. -
Ensembl takes
content-typeas a query parameter, not a header. Leave it out and Ensembl serves its HTML browser page with HTTP 200, so the call looks like a success until the JSON parser reaches the first tag. - On the minus strand, Ensembl numbers exons from the highest coordinate, because they are numbered in transcription order rather than by position.
Credentials
-
clinvar_classification()picks upNCBI_API_KEYfrom the environment at call time and passes it throughbiohttp’ssecret_query, which attaches it at dispatch. The key never reaches the cache key, a printed request, or an error message. It is optional; without it NCBI allows 3 requests a second instead of 10.
What the live run changed
Four of the five behaviours that had never been checked against a real server held exactly as the port described them: MyGene’s upper case HGNC, Reactome’s 404, PanelApp’s search that does not filter, and Ensembl’s array wrapping a single record.
The fifth did not, and it was the one with an action already written into it. Monarch was being called on two hosts, api-v3.monarchinitiative.org for search and association and api.monarchinitiative.org for the entity route, because that was how the apps did it and nobody had checked whether they were interchangeable. They are. All three routes answer on both, and the entity payload is identical between them down to the order of the ids. So Monarch is one host now, which also means one circuit breaker instead of two. A host going down used to open only half of them.
Known limits
-
biohttpis not on CRAN, soDESCRIPTIONcarries aRemotes:line. - Pharos was returning HTTP 502 from its own gateway throughout the live run, so its probe is the one service the run could not confirm. That is an outage rather than a finding about the port, and the check is left in place to say so the next time it is run.