-
Notifications
You must be signed in to change notification settings - Fork 0
feat(backend): Satt opp GraphQL-backend med PostgreSQL #4
Merged
+2,248
−0
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Kopier denne fila til .env og fyll inn verdiene. .env skal ALDRI committes. | ||
|
|
||
| # Adressen til databasen: postgres://bruker:passord@vert:port/databasenavn | ||
| DATABASE_URL=postgres://localhost:5432/framerate | ||
|
|
||
| # Porten GraphQL-serveren lytter på | ||
| PORT=3001 | ||
|
|
||
| # TMDB "API Read Access Token" (lang tekst), fra https://www.themoviedb.org/settings/api | ||
| # Brukes bare av importskriptet, ikke av serveren. | ||
| TMDB_READ_TOKEN= | ||
|
|
||
| # Hvor mange filmer importskriptet henter (maks 10000) | ||
| IMPORT_MOVIE_COUNT=5000 | ||
|
|
||
| # Sett til "production" på VM-en, så skjuler Apollo interne feildetaljer | ||
| NODE_ENV=development |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| node_modules | ||
| .env | ||
| *.log |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # Frame Rate – backend | ||
|
|
||
| GraphQL-server (Apollo Server) med egen PostgreSQL-database. Filmene importeres **én gang** fra TMDB med et skript; serveren spør bare vår egen database. | ||
|
|
||
| ## Kom i gang | ||
|
|
||
| Krever Node 24.6+ og PostgreSQL. | ||
|
|
||
| ```bash | ||
| cd backend | ||
| npm install | ||
| cp .env.example .env # fyll inn DATABASE_URL og TMDB_READ_TOKEN | ||
| createdb framerate # lager en tom database (første gang) | ||
| npm run db:migrate # lager tabellene | ||
| npm run import:movies # henter ~5000 filmer fra TMDB (tar noen minutter) | ||
| npm run dev # starter serveren på http://localhost:3001 | ||
| ``` | ||
|
|
||
| Åpne http://localhost:3001 i nettleseren for å teste spørringer i Apollo Sandbox. | ||
|
|
||
| ## Skript | ||
|
|
||
| | Kommando | Hva den gjør | | ||
| | ----------------------- | -------------------------------------------------------------------------- | | ||
| | `npm run dev` | Starter serveren og restarter den når du lagrer en fil | | ||
| | `npm start` | Starter serveren (bruk denne på VM-en) | | ||
| | `npm run typecheck` | Sjekker TypeScript-typer | | ||
| | `npm run db:migrate` | Lager/oppdaterer tabellene fra `db/schema.sql`. Trygt å kjøre flere ganger | | ||
| | `npm run import:movies` | Henter filmer fra TMDB. Kan kjøres på nytt uten duplikater | | ||
|
|
||
| ## Eksempel | ||
|
|
||
| ```graphql | ||
| query { | ||
| searchMovies(search: "star", genreIds: [878], yearFrom: 1970, sort: RATING_DESC, limit: 20, offset: 0) { | ||
| totalCount | ||
| movies { | ||
| id | ||
| title | ||
| releaseYear | ||
| posterPath | ||
| averageRating | ||
| genres { name } | ||
| } | ||
| } | ||
| movie(id: 11) { | ||
| title | ||
| director | ||
| runtime | ||
| ageRating | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Plakater: `https://image.tmdb.org/t/p/w185{posterPath}` (liste) eller `w500` (detaljside). | ||
|
|
||
| ## Mappestruktur | ||
|
|
||
| ``` | ||
| backend/ | ||
| ├── db/schema.sql Tabeller: movies, genres, movie_genres, users, reviews, saved_movies | ||
| ├── scripts/migrate.ts Kjører schema.sql | ||
| ├── scripts/import-movies.ts Engangsimport fra TMDB | ||
| └── src/ | ||
| ├── index.ts Starter serveren | ||
| ├── schema.ts GraphQL-typene (hva API-et tilbyr) | ||
| ├── resolvers.ts Henter data fra databasen | ||
| └── db.ts Tilkobling til PostgreSQL | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| -- Databaseskjema for Frame Rate (PostgreSQL). | ||
| -- Kjøres med `npm run db:migrate`. Trygt å kjøre flere ganger (IF NOT EXISTS). | ||
|
|
||
| -- pg_trgm gjør at søk med ILIKE '%tekst%' kan bruke en indeks. | ||
| CREATE EXTENSION IF NOT EXISTS pg_trgm; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS genres ( | ||
| id INTEGER PRIMARY KEY, -- TMDB sin sjanger-ID | ||
| name TEXT NOT NULL | ||
| ); | ||
|
|
||
| CREATE TABLE IF NOT EXISTS movies ( | ||
| id INTEGER PRIMARY KEY, -- TMDB sin film-ID, hindrer duplikater ved ny import | ||
| title TEXT NOT NULL, | ||
| original_title TEXT, | ||
| overview TEXT, | ||
| release_date DATE, | ||
| release_year INTEGER GENERATED ALWAYS AS (EXTRACT(YEAR FROM release_date)::INTEGER) STORED, | ||
| runtime INTEGER, -- minutter | ||
| director TEXT, | ||
| age_rating TEXT, -- f.eks. "15" (NO) eller "PG-13" (US) | ||
| poster_path TEXT, | ||
| backdrop_path TEXT, | ||
| popularity REAL NOT NULL DEFAULT 0, | ||
| -- Regnes ut fra våre egne anmeldelser, ikke fra TMDB | ||
| average_rating NUMERIC(3, 2), | ||
| review_count INTEGER NOT NULL DEFAULT 0, | ||
| imported_at TIMESTAMPTZ NOT NULL DEFAULT now() | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS movies_title_trgm_idx ON movies USING gin (title gin_trgm_ops); | ||
| CREATE INDEX IF NOT EXISTS movies_release_year_idx ON movies (release_year); | ||
| CREATE INDEX IF NOT EXISTS movies_popularity_idx ON movies (popularity DESC); | ||
| CREATE INDEX IF NOT EXISTS movies_average_rating_idx ON movies (average_rating DESC NULLS LAST); | ||
|
|
||
| CREATE TABLE IF NOT EXISTS movie_genres ( | ||
| movie_id INTEGER NOT NULL REFERENCES movies (id) ON DELETE CASCADE, | ||
| genre_id INTEGER NOT NULL REFERENCES genres (id) ON DELETE CASCADE, | ||
| PRIMARY KEY (movie_id, genre_id) | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS movie_genres_genre_idx ON movie_genres (genre_id); | ||
|
|
||
| -- Brukere: innlogging bygges senere, men tabellen finnes så anmeldelser og lister kan peke hit. | ||
| CREATE TABLE IF NOT EXISTS users ( | ||
| id SERIAL PRIMARY KEY, | ||
| username TEXT NOT NULL UNIQUE, | ||
| password_hash TEXT NOT NULL, | ||
| created_at TIMESTAMPTZ NOT NULL DEFAULT now() | ||
| ); | ||
|
|
||
| CREATE TABLE IF NOT EXISTS reviews ( | ||
| id SERIAL PRIMARY KEY, | ||
| user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, | ||
| movie_id INTEGER NOT NULL REFERENCES movies (id) ON DELETE CASCADE, | ||
| rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 6), -- terningkast | ||
| text TEXT CHECK (char_length(text) <= 2000), | ||
| created_at TIMESTAMPTZ NOT NULL DEFAULT now(), | ||
| updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), | ||
| UNIQUE (user_id, movie_id) -- én anmeldelse per bruker per film | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS reviews_movie_idx ON reviews (movie_id, created_at DESC); | ||
|
|
||
| DO $$ | ||
| BEGIN | ||
| CREATE TYPE list_type AS ENUM ('WATCHLIST', 'SEEN', 'FAVORITE'); | ||
| EXCEPTION | ||
| WHEN duplicate_object THEN NULL; | ||
| END | ||
| $$; | ||
|
|
||
| -- Én tabell for alle tre listene; list_type sier hvilken liste filmen ligger i. | ||
| CREATE TABLE IF NOT EXISTS saved_movies ( | ||
| user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, | ||
| movie_id INTEGER NOT NULL REFERENCES movies (id) ON DELETE CASCADE, | ||
| list_type list_type NOT NULL, | ||
| added_at TIMESTAMPTZ NOT NULL DEFAULT now(), | ||
| PRIMARY KEY (user_id, movie_id, list_type) | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS saved_movies_user_list_idx ON saved_movies (user_id, list_type, added_at DESC); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.