Logo Light
Published on

Fitnotes (1. díl) - Postgres + Prisma

Nastavení Prisma s PostgreSQL v Next.js aplikaci

V této sérii článků postavím webovou aplikaci pro sledování tréninků z CSV souborů vygenerovaných v mobilní aplikaci Fitnotes. V tomto příspěvku popíšu, jak jsem nastavoval Prisma s PostgreSQL v projektu Next.js — včetně modelu databáze, konfigurace a jednoduchého testu. Struktura csv souboru s daty je následující:

Date,Exercise,Category,Weight,Weight Unit,Reps,Distance,Distance Unit,Time
2024-10-11,Incline Machine Press,Chest,70.0,kgs,10,,,
2024-10-11,Incline Machine Press,Chest,100.0,kgs,16,,,
2024-10-11,Pectoral Fly,Chest,33.0,kgs,8,,,
2024-10-11,Pectoral Fly,Chest,40.0,kgs,8,,,
2024-10-11,Lying Leg Curl Machine,Legs,19.0,kgs,8,,,
2024-10-11,Lying Leg Curl Machine,Legs,26.0,kgs,8,,,
2024-10-11,Lying Leg Curl Machine,Legs,33.0,kgs,10,,,
2024-10-16,Pectoral Fly,Chest,54.0,kgs,8,,,
2024-10-16,Pectoral Fly,Chest,54.0,kgs,8,,,
2024-10-16,Prone Leg Curl,Legs,55.0,kgs,8,,,
2024-10-16,Prone Leg Curl,Legs,100.0,kgs,6,,,
2024-10-18,Military Press,Shoulders,45.0,kgs,7,,,
2024-10-18,Military Press,Shoulders,45.0,kgs,6,,,

Krok 0: Instalace Postgres databáze

Z oficiálního webu Postgresu jsem stáhnul instalační soubor a nainstaloval databázový server. Zvolil jsem jméno a heslo, které později použiju pro připojení z Prisma klienta a vytvořil novou databázi. Pro správu lokálního PostgreSQL serveru používám pgAdmin.

Krok 1: Inicializace projektu

Projekt jsem vytvořil v Next.js:

npx create-next-app@latest

Při vytváření projektu jsem zvolil následující možnosti:

    √ What is your project named? ... fitnotes-webapp?
    √ Would you like to use TypeScript? ... No / Yes
    √ Would you like to use ESLint? ... No / Yes
    √ Would you like to use Tailwind CSS? ... No / Yes
    √ Would you like your code inside a src/ directory? ... No / Yes
    √ Would you like to use App Router? (recommended) ... No / Yes
    √ Would you like to use Turbopack for next dev? ... No / Yes
    √ Would you like to customize the import alias (@/* by default)? ... No / Yes


Krok 2: Instalace a konfigurace Prismy

npm install prisma --save-dev
npm install prisma @prisma/client
npx prisma init

Tento krok vygeneroval:

  • .env soubor s proměnnou DATABASE_URL
  • prisma/schema.prisma soubor pro definici modelu databáze

V souboru .env jsem nastavil připojení k PostgreSQL:

DATABASE_URL="postgresql://uzivatel:heslo@localhost:5432/nazev_databaze"

Krok 3: Definice modelu databáze

V souboru schema.prisma jsem vytvořil model podle struktury CSV:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Workout {
  id           Int      @id @default(autoincrement())
  date         DateTime
  exercise     String
  category     String
  weight       Float?
  weightUnit   String?
  reps         Int?
  distance     Float?
  distanceUnit String?
  time         String?

  createdAt    DateTime @default(now())
}

Spustil jsem migraci:

npx prisma migrate dev --name init

Tímto krokem se vytvořila tabulka v databázi PostgreSQL (ve výchozím schématu public). V terminálu dostaneme odpověď v tomto smyslu:

    Environment variables loaded from .env
    Prisma schema loaded from prisma\schema.prisma
    Datasource "db": PostgreSQL database "fitnotes", schema "public" at "localhost:5432"
    Applying migration 20250625133828_init
    The following migration(s) have been created and applied from new schema changes:
    migrations/
      └─ 20250625133828_init/
        └─ migration.sql
    Your database is now in sync with your schema.
    ✔ Generated Prisma Client (v6.10.1) to .\node_modules@prisma\client in 74ms


Krok 4: Otestování pomocí skriptu

Pro rychlé ověření funkčnosti jsem napsal skript scripts/testPrisma.ts:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  const newWorkout = await prisma.workout.create({
    data: {
      date: new Date('2024-10-11'),
      exercise: 'Flat Barbell Bench Press',
      category: 'Chest',
      weight: 70,
      weightUnit: 'kgs',
      reps: 5,
    },
  })

  console.log('Nový workout:', newWorkout)

  const allWorkouts = await prisma.workout.findMany()
  console.log('Všechny workouty:', allWorkouts)
}

main()
  .catch(console.error)
  .finally(() => prisma.$disconnect())

Spuštění skriptu:

npx tsx scripts/testPrisma.ts

Pokud se zobrazily výsledky, znamená to, že databáze i Prisma klient fungují správně.


Poznámky

1. Názvy tabulek v Prisma

  • Standardně se v Prisma vytváří názvy tabulek začínající velkými písmeny (např. "Workout")
model Workout {
  id Int @id @default(autoincrement())
  ...
}

V PostgreSQL pak musím používat uvozovky u PascalCase názvů.

Tohle je ok:

SELECT * FROM public."Workout";
SELECT * FROM "Workout";

Tohle skončí chybou:

SELECT * FROM Workout; - PostgreSQL si myslí, že se jedná o tabulku workout
SELECT * FROM workout; - table not found


2. Adresářová struktura pro testy

Proč jsem umístil testovací script do adresáře /scripts a nikoliv do /tests?

Kdy použít adresář /scripts?

  • píšu manuální jednorázový skript (jako import dat do databáze nebo test konektivity apod. - je to kód, který spouštím npx tsx ...)
  • nepoužívám test runner (Jest, Vitest apod.)

Kdy použít adresář /tests?

  • používám test runner (Jest, Vitest apod.)
  • chci, aby testy z adresáře byly zpracované CI pipelinou

Co bude příště?

V dalším článku ukážu, jak zprovoznit testovací framework Jest pro testování Prisma modelu.