Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lapis-docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export default defineConfig({
label: 'Response format',
link: '/concepts/response-format',
},
{
label: 'Customizable FASTA headers',
link: '/concepts/customizable-fasta-headers',
},
{
label: 'Data versions',
link: '/concepts/data-versions',
Expand All @@ -113,6 +117,10 @@ export default defineConfig({
label: 'Ambiguous symbols',
link: '/concepts/ambiguous-symbols',
},
{
label: 'Lineage queries',
link: '/concepts/lineage-queries',
},
{
label: 'Pango lineage query',
link: '/concepts/pango-lineage-query',
Expand Down
9 changes: 7 additions & 2 deletions lapis-docs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type MetadataType =
export type Metadata = {
name: string;
type: MetadataType;
generateLineageIndex?: boolean;
generateLineageIndex?: string | null;
};

export type Feature = {
Expand Down Expand Up @@ -55,7 +55,12 @@ export function hasFeature(feature: string): boolean {
export function hasPangoLineage(config: Config): boolean {
return config.schema.metadata.some(
(m) =>
m.generateLineageIndex === true &&
m.generateLineageIndex !== undefined &&
m.generateLineageIndex !== null &&
(m.name.toLowerCase().includes('pangolineage') || m.name.toLowerCase().includes('pango_lineage')),
);
}

export function hasLineageFields(config: Config): boolean {
return config.schema.metadata.some((m) => m.generateLineageIndex !== undefined && m.generateLineageIndex !== null);
}
2 changes: 1 addition & 1 deletion lapis-docs/src/content/docs/concepts/advanced-query.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ The syntax is as follows, where `expr1`, ..., `expr5` are any valid expressions:

## Examples

Variant queries can be sent in GET and POST requests just like standard filters.
Advanced queries can be sent in GET and POST requests just like standard filters.

- GET: https://lapis.cov-spectrum.org/open/v2/sample/aggregated?advancedQuery=501T%20and%20country=Switzerland

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: Customizable FASTA headers
description: Customize the header line of FASTA records returned by sequence endpoints
---

When requesting sequences as FASTA, you can customize the header line of each record by setting the
`fastaHeaderTemplate` request property.
The parameter is ignored if the data format is not FASTA.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LAPIS returns an error in this case.


The template allows placeholders in curly braces:

- `{<metadataField>}` — replaced with the value of the metadata field for that sequence (e.g. `{country}`).
The instance's primary key field (configured by the maintainer) can be used as a placeholder in the same way.
- `{.segment}` — replaced with the segment name. Only valid on nucleotide sequence endpoints.
- `{.gene}` — replaced with the gene name. Only valid on amino acid sequence endpoints.

If a metadata value is `null`, the placeholder is replaced with an empty string.

If `fastaHeaderTemplate` is not set, LAPIS uses a default that includes the primary key (and the gene name
for amino acid sequences, and the segment name for multi-segmented nucleotide sequences).

The template contains characters (`{`, `}`, `|`) that are not URL-safe.
When sending the request as a GET, the value of `fastaHeaderTemplate` must be URL-encoded.
In JavaScript, this can be done with the
[encodeURIComponent()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent)
function. POST requests can carry the template unencoded in the JSON body.

## Examples

Assuming the instance's primary key field is named `accession`, include it together with a separator and two
metadata fields. As a POST request, the template can be sent unencoded:

```http
POST /sample/alignedNucleotideSequences

{
"fastaHeaderTemplate": "{accession}|{country}|{date}"
}
```

The same query as a GET request, with the template URL-encoded:

```
GET /sample/alignedNucleotideSequences?fastaHeaderTemplate=%7Baccession%7D%7C%7Bcountry%7D%7C%7Bdate%7D
```

For amino acid sequences, include the gene name (useful when requesting multiple genes in one call):

```http
POST /sample/alignedAminoAcidSequences

{
"fastaHeaderTemplate": "{accession}|{.gene}"
}
```
48 changes: 48 additions & 0 deletions lapis-docs/src/content/docs/concepts/lineage-queries.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: Lineage queries
description: Filtering lineage fields and their descendants
---

import { OnlyIf } from '../../../components/OnlyIf.tsx';
import { getConfig, hasLineageFields } from '../../../config.ts';
import { Code } from '@astrojs/starlight/components';

export const lineageField = 'lineageField';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually tried to write the actual field names into the example, since we assume that the docs page runs next to an actual LAPIS instance. We could search the config for a lineage field and only use this hardcoded value as fallback


{/* prettier-ignore */}
<OnlyIf condition={!hasLineageFields(getConfig())}>
:::note
This LAPIS instance does not provide lineage queries.
:::
</OnlyIf>

A lineage field stores values that form a hierarchy.
For example, if `B.1.1` is a sub-lineage of `B.1`, then a query for `B.1*` can include records assigned to `B.1`,
`B.1.1`, `B.1.1.7`, and other descendants.

## Exact Matches

Without a wildcard, lineage filters match only the exact lineage value:

<Code lang='http' code={`GET /sample/aggregated?${lineageField}=B.1.1`} />

This returns records whose lineage field value is exactly `B.1.1`.

## Including Descendants

To include sub-lineages, add `*` at the end of the lineage:

<Code lang='http' code={`GET /sample/aggregated?${lineageField}=B.1.1*`} />

`B.1.1*` and `B.1.1.*` are equivalent.
Both match `B.1.1` itself and descendants such as `B.1.1.7`.

## Advanced Queries

Lineage-indexed fields can also be used in [advanced queries](../concepts/advanced-query):

<Code lang='http' code={`GET /sample/aggregated?advancedQuery=${lineageField}=B.1.1*`} />

In POST requests, use the same expression as the `advancedQuery` value:

<Code lang='json' code={JSON.stringify({ advancedQuery: `${lineageField}=B.1.1*` }, null, 4)} />
5 changes: 4 additions & 1 deletion lapis-docs/src/content/docs/concepts/pango-lineage-query.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import { getConfig, hasPangoLineage, hasFeature } from '../../../config.ts';
{/* prettier-ignore */}
<OnlyIf condition={!hasPangoLineage(getConfig())}>
:::note
This feature is not available in this LAPIS instance, because none of the fields seems to be a Pango lineage field.
This feature is not available in this LAPIS instance.
:::
</OnlyIf>

Pango lineage names inherit the hierarchical nature of genetic lineages. For example, B.1.1 is a sub-lineage of B.1.
More information about the pango nomenclature can be found on the website of the
[Pango network](https://www.pango.network/).

LAPIS supports Pango lineage queries for Pango lineage fields that have a lineage index.
For general lineage-indexed fields, see [lineage queries](../concepts/lineage-queries).

With the pangoLineage filter<OnlyIf condition={hasFeature('sarsCoV2VariantQuery')}> and in [variant queries](../concepts/variant-query)</OnlyIf>,
it is possible to not only filter for a very specific lineage but also to include its sub-lineages.
To include sub-lineages, add a `*` at the end.
Expand Down
3 changes: 3 additions & 0 deletions lapis-docs/src/content/docs/concepts/response-format.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ You can read line by line without loading the entire file into memory.
Since every line is a valid JSON object, it is usually easier to handle programmatically than FASTA.
:::

The header line of FASTA records can be customized via the `fastaHeaderTemplate` request property.
See [Customizable FASTA headers](../concepts/customizable-fasta-headers) for details.

## Example

To understand the response of the `nucleotideMutation` endpoint, refer to the relevant section in the Swagger UI:
Expand Down
43 changes: 39 additions & 4 deletions lapis-docs/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
---
title: Welcome to LAPIS
description: Get started with LAPIS
description: Lightweight API for Sequences — query genomic data over HTTP
template: splash
hero:
tagline: Instance for SARS-CoV-2
tagline: A fast, lightweight API for querying genomic sequence data
actions:
- text: Introduction
- text: Get started
link: getting-started/introduction
icon: right-arrow
variant: primary
- text: LAPIS on GitHub
- text: Source code on GitHub
link: https://github.com/GenSpectrum/LAPIS
icon: external
variant: minimal
---

## What is LAPIS?

LAPIS (**Lightweight API for Sequences**) is an open-source HTTP API for querying large
collections of pathogen genome sequences. It is built for researchers, public health
analysts and developers who need to filter, aggregate or download genomic data
programmatically.

Under the hood, LAPIS delegates storage and fast filter execution to
[**SILO**](https://github.com/GenSpectrum/LAPIS-SILO), a purpose-built sequence database.

## Known instances

LAPIS is deployed in a number of production systems:

- **[CoV-Spectrum](https://cov-spectrum.org) and [GenSpectrum](https://genspectrum.org)** are interactive dashboards for
analyzing virus variants and mutations, backed by a LAPIS instance over INSDC and Pathoplexus data.
- **[Loculus](https://loculus.org)** is an open-source software for managing and sharing microbial genome data that uses
LAPIS and SILO as its query engine. Production Loculus deployments include
[Pathoplexus](https://pathoplexus.org), an initiative to facilitate sharing of
genetic sequencing data for human viruses of public health importance.
- **[W-ASAP](https://db.wasap.genspectrum.org/)** uses a LAPIS instance for wastewater sequencing data, supporting queries over mutation abundances
from Swiss wastewater samples.

If you are operating a public LAPIS instance and want it to be listed here, please open an issue on
[GitHub](https://github.com/GenSpectrum/LAPIS).

## Citation

If you use LAPIS in your work, please cite:

> Chen, C., Taepper, A., Engelniederhammer, F., Kellerer, J., Roemer, C. & Stadler, T. "LAPIS is a fast web API for
> massive open virus sequencing data" BMC Bioinformatics (2023); doi:
> [10.1186/s12859-023-05364-3](https://doi.org/10.1186/s12859-023-05364-3)
2 changes: 1 addition & 1 deletion lapis-docs/tests/configGenerator.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class ConfigGeneratorPage {

public async goto() {
await this.page.goto(baseUrl);
await this.page.getByRole('link', { name: 'Introduction' }).click();
await this.page.getByRole('link', { name: 'Get started' }).click();
await this.page.getByRole('link', { name: 'Generate your config', exact: true }).click();
}

Expand Down
4 changes: 3 additions & 1 deletion lapis-docs/tests/docs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ const conceptsPages = prependToRelativeUrl(
{ title: 'Authentication', relativeUrl: '/authentication' },
{ title: 'Request methods: GET and POST', relativeUrl: '/request-methods' },
{ title: 'Response format', relativeUrl: '/response-format' },
{ title: 'Customizable FASTA headers', relativeUrl: '/customizable-fasta-headers' },
{ title: 'Data versions', relativeUrl: '/data-versions' },
{ title: 'Request ID', relativeUrl: '/request-id' },
{ title: 'Mutation filters', relativeUrl: '/mutation-filters' },
{ title: 'Insertion filters', relativeUrl: '/insertion-filters' },
{ title: 'Ambiguous symbols', relativeUrl: '/ambiguous-symbols' },
{ title: 'Lineage queries', relativeUrl: '/lineage-queries' },
{ title: 'Pango lineage query', relativeUrl: '/pango-lineage-query' },
{ title: 'Variant query', relativeUrl: '/variant-query' },
{ title: 'Advanced query', relativeUrl: '/advanced-query' },
Expand Down Expand Up @@ -130,7 +132,7 @@ test.describe('The documentation', () => {
test('should show all expected pages via link in navigation', async ({ page }) => {
await page.goto(baseUrl);

await page.getByRole('link', { name: 'Introduction' }).click();
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page).toHaveTitle(/^Introduction/);
await expect(page).toHaveURL(thatDoesNotEndWithSlash);

Expand Down
2 changes: 1 addition & 1 deletion lapis-docs/tests/queryGenerator.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class QueryGeneratorPage {

public async goto() {
await this.page.goto(baseUrl);
await this.page.getByRole('link', { name: 'Introduction' }).click();
await this.page.getByRole('link', { name: 'Get started' }).click();
await this.page.getByRole('link', { name: 'Generate your request', exact: true }).click();
}

Expand Down