Skip to content

BgeeDB/bgee_web

Repository files navigation

DOI DOI Bluesky Mastodon

🐝 Bgee website

Tests

Code for the Bgee website available at https://www.bgee.org. Bgee is a database for retrieval and comparison of gene expression patterns across multiple animal species.

This website uses React Router 7 to serve pages with server-side rendering (SSR).

πŸ› οΈ Development

πŸ“₯ Installation

Requirements: we recommend using the latest NodeJS LTS (22+), but anything after 18 should work.

Install dependencies:

npm i

Note

This will automatically setup git pre-commit hooks to run formatter, linter and type checker on every commit and make sure the commited code is in good shape.

Tip

Alternatively, install dependencies without running the script to install playwright for tests:

npm i --ignore-scripts

⚑ Start server

Start development server at http://localhost:5173

npm run dev

🧹 Format, lint and check types

Format and lint with prettier and eslint:

npm run fmt

Check types with TypeScript:

npm run typecheck

Note

Formatting and type checking will be run automatically when you commit with husky and lint-staged.

βœ… Tests

Run the tests with playwright:

npm test

Run everything (format, type check, tests):

npm run all

⏫ Upgrade dependencies

Upgrade dependencies to their latest available version in the package.json file:

npm run upgrade

Warning

bulma breaks when upgraded to v1+, the rest can be usually upgraded without problem. It has been excluded from the upgrade script.

🌐 Deployment

πŸ“¦ Build for production

Create a production build:

npm run build

Start the production server:

npm start
If you're familiar with deploying Node applications, the built-in app server is production-ready.

Make sure to deploy the output of npm run build

β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ build/
β”‚   β”œβ”€β”€ client/  # Static assets
β”‚   └── server/  # Server-side code

Prepare the application to be deployed as an archive:

npm run archive

Important

Be careful with the version set in config.json, it will impact the app in production or in archive.

🐳 Docker deployment

Build:

docker build -t bgee-web .

Run:

docker run -p 3000:3000 bgee-web

🧢 Multithreaded deployment

With pm2, build, then deploy with a load balancer on http://localhost:3000/

npm run build
npm run cluster

Check logs:

npm run logs

Stop all processes:

npm run stop:all

Run stresstest (after starting the cluster in development):

npm run stresstest

πŸ’‘ FAQ

πŸ“„ Add a new page

2 routing approaches are available:

  • File-based routes in src/routes/
    • We recommend to use this approach for new pages
    • Currently used for all routes to markdown files, the gene items, and raw data pages
    • To add a route to /gene/XYZ:
      • Create a file named gene.$geneId.tsx (see example below)
      • Create a folder named gene.$geneId with a route.tsx file in it
  • Manually defined routes in src/routes.ts
    • Currently used for most routes defined in src/pages
    • Link a URL path to a component file using the route(path, file) function

When creating a new route the file resolving this route can contain special exported functions used for SSR:

  • loader(): data returned by this function will be used to prerender the page on the server (SSR). The served page will contain the html depending on the data from the loader, so really useful for SEO and load speed.
  • meta() function to define the page metadata (can use the data returned by loader())

Important

When creating new files only create .ts or .tsx files, they provide much more help through IDE completion and reliability than .js files.

Here is an example where we make multiple API calls in parallel in the loader, and use its results to define the page metadata and page content:

import api from '~/api';
import config from '~/config.json';
import PATHS from '~/paths/paths';
import { geneToLdJSON } from '~/helpers/schemaDotOrg';
import { getMetadata } from '~/helpers/metadata';

/** Function executed on the server to render the DOM using the data retrieved */
export async function loader({ params, request }) {
  try {
    const [genesResp, speciesResp] = await Promise.all([
      api.search.genes.getGeneralInformation(params.geneId),
      api.search.species.name(params.speciesId),
    ]);
    return {
      genes: genesResp.data.genes,
      species: speciesResp.data.species,
      requestUrl: request.url,
    };
  } catch (error: any) {
    throw new Response(error.data?.message || error.message || 'Page not found', { status: 404 });
  }
}

/** Define the metadata of the page, can use data retrieved in the loader */
export function meta({ data }) {
  return getMetadata({
    title: `${data.genes.name} expression in ${data.species.name}`,
    description: `Gene expression for ${data.genes.name} in ${data.species.name}`,
    keywords: `gene expression, ${data.genes.name}, ${data.species.name}`,
    link: data.requestUrl,
    schemaorg: [geneToLdJSON(data.genes)],
  });
}

/** The component for the page, can use data retrieved in the loader  */
export default function Page({ loaderData }) {
  const { genes, species } = loaderData;

  return <GeneDisplay genes={genes} species={species} />;
}

Important

Try to avoid using too many useEffect with different dependency arrays, it can trigger many rerenders, and it gets quite complex to understand what is happening. Instead prefer loading as much data as possible from the loader function, especially for static content like a gene page.

For search pages with a lot of dynamic elements, like raw-data, you will still need to use useEffect, but do it with parsimony, if you have more than 3 of them, then you're probably doing something wrong.

πŸ“ƒ Add a new markdown page

To add a markdown page you will need to create a new route in src/routes/ with a .tsx file, import the .md, and define the metadata in the route file, e.g.:

import { getMetadata } from '~/helpers/metadata';
import Markdown from '~/markdown/support/data-curation/data-curation.md';

export function meta() {
  return getMetadata({
    title: 'Bgee data curation tutorial',
    description: 'Bgee Tutorial about data curation and annotation',
    keywords: 'Tutorial, data curation, annotation',
  });
}

export default function Page() {
  return <Markdown />;
}

πŸ–ΌοΈ Images and icons

The images are stored externally of the project. You will find the path of the images in the src/config.json file at the key imageDomain. Be careful, the image used for the 'external icon' link is directly defined in the SCSS. If you are moving it, don't forget to change the path.

If you need to add new icons you can find them there: https://lucide.dev/icons and use them like that:

import { ChevronDown } from 'lucide-react';

<ChevronDown size={15} color="black" fill="white" />;

πŸ“ Font size matrix

$size-7: 12px;
$size-6: 1rem (= 14px)
$size-5: 1.1rem (= 15.4px)
$size-4: 1.2rem (= 16.8px)
$size-3: 1.5rem (= 21px)

β˜‘οΈ To do

  • Fix script scripts/archiveCreation.js
  • Update the project docs in the docs folder? Or delete it, a well maintained README.md is better.
  • In a gene item page, if change filters for Expression table > click update > reload page with new URL filters > Expression Graph is broken
  • Improve presence/absence expression loading speed: the 2 API calls to get expression data for a gene takes 2s. This is too much for SSR (the browser freezes 2s before loading the page). If you want to include them in SSR (and in metadata) you will need to make the calls to get expression data faster.
  • Hydration issue in the raw-data page, due to react-select using CSS-in-JS emotion library that is not compatible with SSR (see issue).
  • Upgrade bulma dependency from 0.9 to 1+. We managed to make it compile in this commit, but still work to do to get the exact same style right (default colors are broken for many elements, and dark theme causes problems for those who have it enabled system-wide).
    • Would be also a good idea to migrate off SASS and use standard CSS. There are many warnings when running in dev due to using deprecated SASS features. CSS has evolved and now supports many features from SASS (e.g. variables and nested CSS). postcss could be interesting to make sure older browsers support newer CSS features
  • It would be nice to have have proper args and return types on api functions in src/api/prod (but the java API return types are not defined anywhere, the OpenAPI specs are not up-to-date)
  • Search for TODO: to fix in the code, and : any types to define properly.

About

React code of the Bgee frontend

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 15