diff --git a/README.md b/README.md index 0558ad5..f082b1b 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ Repository for team IT2810-H26-T25 in IT2810-H26. 2. [Working in this repository](#working-in-this-repository) 3. [Project Description](#project-description) 4. [Planned Functionality](#planned-functionality) -5. [Stored User-Generated Data](#stored-user-generated-data) -6. [Technology Choices](#technology-choices) -7. [AI Usage](#ai-usage) +5. [GraphQL API Design](#graphql-api-design) +6. [Stored User-Generated Data](#stored-user-generated-data) +7. [Technology Choices](#technology-choices) +8. [AI Usage](#ai-usage) ## Running the Project @@ -108,6 +109,22 @@ replaced by GraphQL queries against the backend, so that sorting and filtering always cover the whole dataset and not only what has been loaded in the browser. +## GraphQL API Design + +The schema is in `backend/src/schema.ts`. It is a proposal: the resolvers and +database connection are not written yet, so only `health` returns data. + +- `sighting(id)` returns one sighting, or null if it does not exist. +- `sightings(search, filter, sort, first, after)` returns a page of sightings + with `totalCount` and `pageInfo`. Search matches city, shape and comments. + Filters are shape, country, state and a date range. Sort is date (newest or + oldest), city or shape. All of it runs in the database. + +**Pagination: cursor based "load more".** With about 80,000 rows, `LIMIT/OFFSET` +gets slower the further you page, while a cursor continues from where the last +page ended. It also fits a "load more" button, so the client only downloads +what it shows, and Apollo Client can merge the pages into one list. The trade-off is that users cannot jump to a specific page. + ## Stored User-Generated Data The main data users create are their own sighting reports. A report will have diff --git a/backend/src/schema.ts b/backend/src/schema.ts index 40df036..1ac4e56 100644 --- a/backend/src/schema.ts +++ b/backend/src/schema.ts @@ -1,12 +1,71 @@ -/** - * Placeholder schema that only proves the server is running. The real - * Sighting types and queries will be agreed on by the group and added - * in a separate issue. - */ export const typeDefs = `#graphql + "A single UFO sighting report." + type Sighting { + id: ID! + "Local time where it was seen, no timezone, e.g. 2005-01-01T00:30:00." + datetime: String + city: String + state: String + country: String + shape: String + durationSeconds: Int + comments: String + datePosted: String + latitude: Float + longitude: Float + } + + enum SightingSort { + DATE_DESC + DATE_ASC + CITY_ASC + SHAPE_ASC + } + + "All fields are optional and combined with AND." + input SightingFilter { + shape: String + country: String + state: String + "ISO 8601 date, inclusive." + dateFrom: String + "ISO 8601 date, inclusive." + dateTo: String + } + + type SightingEdge { + "Opaque. Pass it as \`after\` to get the next page." + cursor: String! + node: Sighting! + } + + type PageInfo { + hasNextPage: Boolean! + endCursor: String + } + + type SightingConnection { + edges: [SightingEdge!]! + pageInfo: PageInfo! + "Number of matches across all pages." + totalCount: Int! + } + type Query { "Returns \\"ok\\" when the server is up." health: String! + + "Returns null if the id does not exist." + sighting(id: ID!): Sighting + + "\`search\` matches city, shape and comments. \`first\` is limited to 1-100." + sightings( + search: String + filter: SightingFilter + sort: SightingSort = DATE_DESC + first: Int = 20 + after: String + ): SightingConnection! } `