There is a schema (`backend/src/schema.graphql`), a database, the seed and the search function (`searchCatalog`, PR #52), but **no server**: nothing can call any of it. This issue sets up the GraphQL server and the resolvers for the catalog, so that the search works end to end through GraphQL. The frontend issues (#40–#48) need it, and it is the main part of hand-in 2 ("a running backend and a client with some interaction"). The schema itself and its documentation were done in #29 and #37 (PR #49). This issue used to cover that work, so its text is changed. ## Scope - **Server.** Apollo Server 5 on Express 5 (`@as-integrations/express5`), schema first: it loads `backend/src/schema.graphql`. Port `3001` (`PORT`), endpoint `/graphql`. `graphql` is pinned to version 16, because Apollo Server 5 needs `^16` and the latest release is 17. - **Catalog resolvers.** `apparatus`, `elementGroup` and `Apparatus.groups`, `element`, `vault`, `catalogItem`, and `searchCatalog` as a connection (`edges`, `node`, `cursor`, `pageInfo`, `totalCount`), calling `searchCatalog` and `listElementGroups` from `backend/src/catalog/`. - `CatalogItem` is resolved to `Element` or `Vault` (vault ids start with `VT-`). - `imageUrl` is built from `IMAGE_BASE_URL`, the apparatus folder and the stored file name. - `variants` lists the other variants of the code that are not removed. `removed` comes from `removedAt`. - `value` and `dValue` are converted from `Decimal` to numbers at the end. - **DataLoader** (one set per request) for loading the element or vault behind each search result and for `variants`, so a page of 20 results does not send 20 queries. - **`totalCount` only when the query asks for it** (the count reads every match). - **Errors.** `BadUserInputError` becomes `BAD_USER_INPUT`; a **depth limit** on queries (the schema has cycles). Other unexpected errors become `INTERNAL_SERVER_ERROR` without details. - **Not yet available:** `me` returns `null`. The user-data queries and mutations, and `calculateDScore`, answer with a clear error until the follow-up issues are done (`UNAUTHENTICATED` for the protected ones, because nobody can log in yet). - **Images.** `/images` serves `data/images` for development (cache headers). On the virtual machine Apache serves the images. - **Configuration.** `backend/.env.example`: `PORT`, `IMAGE_BASE_URL`, `CORS_ORIGIN` (the Vite dev server). Scripts `dev` (watch) and `start`. - **Tests.** `executeOperation` against the seeded catalog: catalog queries, a search with filters, sort and cursor to the end, `totalCount` only when asked, every error code, the depth limit, and the user-data operations that are not available yet. Skipped without `DATABASE_URL`, like the search tests. - **Docs.** `docs/API.md` (how to run it, endpoints, variables, error mapping, depth limit) and the README (how to start the server and try a query). ## Done when - [ ] `npm run dev -w backend` starts the server on port 3001, and a `searchCatalog` query returns results from the seeded catalog - [ ] `element`, `vault`, `catalogItem`, `apparatus` and the element groups work, and `imageUrl` points to a file that `/images` serves - [ ] A page of 20 search results needs a fixed number of database queries (DataLoader), not one per result - [ ] `totalCount` is only calculated when it is in the query - [ ] A wrong argument gives `BAD_USER_INPUT`, a query that is too deep is rejected, and unexpected errors show no details - [ ] The tests above pass, and lint, typecheck and build pass - [ ] The README and `docs/API.md` explain how to run and try it ## Not in this issue (follow-ups) - Login with Google and the session token, the resolvers for gymnasts, routines, their lists and `dScore`, and `calculateDScore` - GraphQL Code Generator for the resolver types - Running the server on the virtual machine: process manager and the Apache proxy (#26) - Apollo Client in the frontend - API and end-to-end tests in CI Depends on the search (#24, merged). The schema changes in PR #49 do not block it.