Cele lekcji

Po tej lekcji:

  • rozumiesz, czym jest webservice (API),
  • wiesz, czym różni się strona internetowa od API,
  • potrafisz uruchomić serwer Node.js w Replit,
  • potrafisz odczytać dane z pliku JSON przez endpoint /players,
  • wiesz, jak aplikacja (gra, strona) może pobierać dane z serwera.

Wprowadzenie

Na początku ustalamy jedno bardzo ważne rozróżnienie:

Strona internetowa pokazuje coś człowiekowi.
API (webservice) zwraca dane, które czyta program.

Dzisiaj budujemy API, a nie klasyczną stronę.


Zrobimy to za pomocą on-linowego środowiska replit

zakładamy konto

Krok 1. Utworzenie projektu Node.js w Replit

Przechodzimy w tryb Developer Frameworks

Wybieramy Node.js

Rys. Tworzymy nową aplikację w Replit, wybierając Node.js jako technologię.

Po utworzeniu projektu widzisz:

  • plik index.js,
  • plik package.json,
  • konsolę uruchomieniową.

Rys. ekran z wyborem Node.js / startowy widok projektu w Replit


Krok 2. Pierwszy działający serwer

W pliku index.js uruchamiamy najprostszy serwer:

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Webservice działa");
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log("Serwer działa na porcie", port);
});

W tools&files należy dodać Preview

Uruchamiamy projekt przyciskiem Run.

Jeśli wszystko działa poprawnie:

  • w konsoli pojawia się komunikat o porcie,
  • w podglądzie (Preview) widać tekst „Webservice działa 🚀”.

Rys. konsola + Preview z napisem „Webservice działa”


Krok 3. Dodanie pliku z danymi – db.json

Tworzymy nowy plik o nazwie db.json.

To jest nasza prosta baza danych w formacie JSON:

{
  "players": [
    { "id": 1, "name": "Tymek", "score": 1200 },
    { "id": 2, "name": "Adam", "score": 950 }
  ]
}

Rys. Lista plików z widocznym db.json + zawartość pliku

Wyjaśniamy:

  • JSON to format danych, nie strona,
  • takie dane czyta aplikacja, a nie człowiek.

Krok 4. Odczyt danych z JSON przez API

Rozszerzamy index.js, aby serwer:

  • czytał dane z db.json,
  • zwracał je pod adresem /players.
const express = require("express");
const fs = require("fs");

const app = express();
app.use(express.json());

function readDB() {
  return JSON.parse(fs.readFileSync("db.json", "utf8"));
}

app.get("/players", (req, res) => {
  const db = readDB();
  res.json(db.players);
});

app.get("/", (req, res) => {
  res.send("Webservice działa  Wejdź na /players");
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log("Serwer działa na porcie", port);
});

Po uruchomieniu:

players

  • / pokazuje komunikat tekstowy,
  • /players zwraca czysty JSON.

Otwieramy przeglądarkę w new tab

Pojawi się ten sam monit co wyżej, ale w przeglądarce

dopisujemy /players

Rys. Adres /players w przeglądarce z widocznym JSON-em


Krok 5. Dlaczego to NIE jest strona?

W przeglądarce widzisz dane w formie:

[
  { "id": 1, "name": "Tymek", "score": 1200 },
  { "id": 2, "name": "Adam", "score": 950 }
]

To wygląda „jak tekst”, bo:

  • to nie jest dla człowieka,
  • to jest dla programu (gry, strony, aplikacji).

Tak właśnie działają:

  • gry online,
  • rankingi,
  • panele admina,
  • aplikacje mobilne.

Podsumowanie lekcji

Dzisiaj:

  • uruchomiłeś prawdziwy serwer,
  • stworzyłeś API,
  • połączyłeś kod z plikiem JSON,
  • zobaczyłeś, skąd aplikacje biorą dane.

To dokładnie ten sam mechanizm, który stoi za dużymi serwisami — tylko w uproszczonej, edukacyjnej wersji.


Co dalej (zapowiedź kolejnej lekcji)

Na następnych zajęciach możemy:

  • dodać prostą stronę HTML, która pobiera dane z /players,
  • połączyć API z grą lub Twoim projektem,
  • rozbudować API (dodawanie / usuwanie graczy),
  • porozmawiać o bezpieczeństwie danych.

Czyli wykonujemy te wszystkie czynności w środowisku on-linowym, który moglibyśmy uruchomić lokalnie.

Opis plików

index.js

// Importujemy bibliotekę express.
// Express to framework, który pozwala bardzo łatwo tworzyć serwer HTTP.
const express = require("express");

// Importujemy moduł fs (file system).
// Pozwala on czytać i zapisywać pliki na dysku (u nas: db.json).
const fs = require("fs");

// Tworzymy obiekt aplikacji serwera.
// Od tej chwili 'app' reprezentuje nasz serwer.
const app = express();

// Mówimy serwerowi, że będzie odbierał dane w formacie JSON.
// Dzięki temu możemy czytać req.body w zapytaniach POST.
app.use(express.json());

// ======================================================
// FUNKCJE POMOCNICZE – OBSŁUGA PLIKU db.json
// ======================================================

// Funkcja do odczytu danych z pliku db.json.
// Czytamy plik jako tekst, a potem zamieniamy go na obiekt JavaScript.
function readDB() {
return JSON.parse(
fs.readFileSync("db.json", "utf8")
);
}

// Funkcja do zapisu danych do pliku db.json.
// Zamieniamy obiekt JavaScript na JSON i zapisujemy do pliku.
// 'null, 2' sprawia, że plik jest czytelnie sformatowany.
function writeDB(db) {
fs.writeFileSync(
"db.json",
JSON.stringify(db, null, 2)
);
}

// ======================================================
// ENDPOINTY API
// ======================================================

// ------------------------------------------------------
// GET /players
// Ten endpoint zwraca listę wszystkich graczy.
// Używany np. przez stronę, grę lub aplikację.
app.get("/players", (req, res) => {

// Odczytujemy dane z pliku db.json
const db = readDB();

// Zwracamy tylko tablicę graczy w formacie JSON
res.json(db.players);
});

// ------------------------------------------------------
// POST /players
// Ten endpoint dodaje nowego gracza do "bazy danych".
app.post("/players", (req, res) => {

// Pobieramy dane wysłane w zapytaniu (z body).
// Oczekujemy, że będzie tam name i score.
const { name, score } = req.body;

// Sprawdzamy, czy przesłano imię gracza.
// Jeśli nie – zwracamy błąd 400 (złe dane).
if (!name) {
return res
.status(400)
.json({ error: "Brak name" });
}

// Odczytujemy aktualne dane z pliku db.json
const db = readDB();

// Wyznaczamy ID nowego gracza.
// Jeśli gracze już istnieją – bierzemy największe ID i dodajemy 1.
// Jeśli nie ma żadnych graczy – zaczynamy od ID = 1.
const nextId = db.players.length
? Math.max(…db.players.map(p => p.id)) + 1
: 1;

// Tworzymy obiekt nowego gracza.
const player = {
id: nextId,
name: name,
score: Number(score) || 0 // jeśli score nie podano – ustawiamy 0
};

// Dodajemy nowego gracza do tablicy
db.players.push(player);

// Zapisujemy zmienione dane z powrotem do db.json
writeDB(db);

// Zwracamy dodanego gracza oraz kod 201 (utworzono)
res.status(201).json(player);
});

// ======================================================
// START SERWERA
// ======================================================

// Ustalamy port serwera.
// Replit ustawia PORT automatycznie, lokalnie będzie to 3000.
const port = process.env.PORT || 3000;

// Endpoint główny "/".
// Nie jest to API, tylko informacja dla człowieka.
app.get("/", (req, res) => {
res.send("Webservice działa. Wejdź na /players");
});

// Uruchamiamy serwer i zaczynamy nasłuchiwać na porcie.
app.listen(port, () => {
console.log("Serwer działa na porcie", port);
});

JSON

Zasada działania

Jak działa ten webservice? (od środka)

Ta aplikacja nie jest stroną internetową w klasycznym sensie.
To jest serwer, czyli program, który czeka na zapytania i odpowiada na nie danymi.

Żeby to dobrze zrozumieć, rozbijmy całość na kilka kluczowych pojęć.


1. Co to jest serwer? (app)

Serwer to program działający cały czas w tle, który:

  • nasłuchuje na określonym porcie (np. 3000),
  • odbiera zapytania z Internetu,
  • decyduje, jak na nie odpowiedzieć.

W naszym kodzie serwerem jest ten obiekt:

const app = express();

Od tej chwili:

  • app reprezentuje serwer,
  • wszystko, co piszemy później (app.get, app.post), to instrukcje dla serwera, jak ma się zachować.

Można to porównać do:

recepcji w budynku, która przyjmuje ludzi i kieruje ich do odpowiednich drzwi.

Serwer nie zgaduje, co ma zrobić — ma dokładnie zapisane reguły.


2. Czym jest endpoint? (/players)

Endpoint to konkretny adres, pod którym serwer wykonuje konkretną akcję.

W naszym przypadku:

app.get("/players", ...)

oznacza:

  • jeśli ktoś wejdzie na adres /players,
  • serwer zwróci listę graczy.

Endpoint to:

  • nie cały serwer,
  • nie cała aplikacja,
  • tylko jedno wejście do serwera.

Analogicznie:

  • serwer = cały budynek,
  • endpoint = konkretne drzwi z napisem „Gracze”.

Dlatego:

  • / pokazuje informację tekstową,
  • /players zwraca dane w JSON.

Każdy endpoint ma:

  • adres (/players),
  • metodę (GET, POST),
  • konkretne zachowanie.

3. Różnica między GET a POST

To jest jeden z najważniejszych momentów tej lekcji.

GET – pobieranie danych

app.get("/players", ...)

GET oznacza:

  • „chcę coś dostać”,
  • nie zmieniam danych,
  • tylko odczytuję.

Przykład:

  • strona pobiera listę graczy,
  • gra pobiera ranking,
  • aplikacja mobilna pobiera dane użytkownika.

GET:

  • jest bezpieczny,
  • nie powinien nic zapisywać,
  • można go wywołać zwykłą przeglądarką.

POST – wysyłanie danych

app.post("/players", ...)

POST oznacza:

  • „chcę coś dodać lub zmienić”,
  • wysyłam dane do serwera,
  • serwer zapisuje je w „bazie”.

W naszym przypadku POST:

  • dodaje nowego gracza,
  • zapisuje go do db.json.

POST:

  • zmienia dane,
  • nie jest do „klikania” w pasku adresu,
  • zwykle używa go formularz, gra lub aplikacja.

To dlatego:

  • /players w przeglądarce pokazuje listę,
  • ale dodawanie gracza wymaga POST.

4. Dlaczego JSON to NIE jest strona HTML?

Gdy wchodzisz na /players, widzisz coś takiego:

[
  { "id": 1, "name": "Tymek", "score": 1200 },
  { "id": 2, "name": "Adam", "score": 950 }
]

To nie jest strona internetowa, bo:

  • nie ma HTML,
  • nie ma stylu,
  • nie jest „ładne”.

I bardzo dobrze.

JSON

  • jest dla programów, nie dla ludzi,
  • jest prosty i jednoznaczny,
  • idealnie nadaje się do przesyłania danych.

Dlatego:

  • strona HTML pobiera JSON,
  • gra pobiera JSON,
  • aplikacja mobilna pobiera JSON.

A potem:

  • sama decyduje, jak to wyświetlić.

Serwer:

  • nie dba o wygląd,
  • dba o dane.

Podsumowanie – jak to wszystko działa razem

  1. Serwer (app) działa cały czas.
  2. Endpointy (/players) to konkretne „wejścia”.
  3. GET służy do pobierania danych.
  4. POST służy do ich dodawania.
  5. JSON to format danych dla programów, nie dla ludzi.
  6. Wygląd (HTML, CSS) to kolejny etap, nie obowiązek serwera.

Dlaczego to jest „Internet od środka”?

Bo dokładnie tak:

  • działa ranking w grze,
  • działa sklep internetowy,
  • działa Instagram, YouTube, dziennik elektroniczny.

Różnica jest tylko w skali, nie w zasadzie działania.

JSON
Czym jest plik package.json?

Plik package.json to opis projektu, a nie kod programu.

Można o nim myśleć jak o:

  • metryczce aplikacji,
  • instrukcji obsługi dla komputera,
  • liście składników, które są potrzebne, żeby program działał.

Node.js nie zgaduje, czego potrzebuje aplikacja — wszystko ma zapisane właśnie tutaj.


Cały plik package.json

{
  "name": "nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/node": "^22.13.11",
    "express": "^5.2.1"
  }
}

Teraz rozłóżmy to linijka po linijce.


1. Nazwa projektu

"name": "nodejs"

To jest:

  • nazwa aplikacji,
  • identyfikator projektu.

Nie wpływa bezpośrednio na działanie serwera, ale:

  • pozwala rozpoznać projekt,
  • jest ważna przy większych aplikacjach lub publikacji.

-> Można to porównać do nazwy gry lub programu.


2. Wersja aplikacji

"version": "1.0.0"

To numer wersji projektu.

Zapis 1.0.0 oznacza:

  • 1 – główna wersja (duża zmiana),
  • 0 – nowe funkcje,
  • 0 – poprawki błędów.

To jest informacja głównie dla programistów, a nie dla użytkowników.


3. Opis projektu

"description": ""

Miejsce na krótki opis:

  • co robi aplikacja,
  • do czego służy.

U nas jest puste, bo:

  • projekt jest edukacyjny,
  • nie musimy tego opisywać.

4. Plik startowy aplikacji

"main": "index.js"

To jedna z najważniejszych linijek.

Oznacza:

„Gdy uruchamiasz ten projekt, zacznij od pliku index.js.”

Czyli:

  • Node.js wie, od czego zacząć,
  • nie musi zgadywać, który plik jest główny.

Dlatego cały serwer znajduje się właśnie w index.js.


5. Skrypty (czyli polecenia)

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
}

To są gotowe polecenia, które można uruchomić jednym słowem.

Na przykład:

  • npm test
  • npm start (jeśli by istniał)

W tym projekcie:

  • skrypt test jest domyślny,
  • nie korzystamy z niego.

Ważne:
-> Replit uruchamia aplikację bezpośrednio, więc nie musimy tu nic zmieniać.


6. Słowa kluczowe, autor, licencja

"keywords": [],
"author": "",
"license": "ISC"

To są informacje:

  • opisowe,
  • formalne,
  • ważne przy publikowaniu projektu.

To nie wpływa na działanie serwera.

Można to porównać do:

  • autora książki,
  • informacji na okładce.

7. Najważniejsza sekcja – dependencies

"dependencies": {
  "@types/node": "^22.13.11",
  "express": "^5.2.1"
}

To jest lista bibliotek, bez których aplikacja nie zadziała.

-> express

"express": "^5.2.1"

To:

  • framework do tworzenia serwera,
  • bez niego nie byłoby app.get() ani app.post().

Jeśli usuniemy Express:

  • serwer przestaje działać.

-> @types/node

"@types/node": "^22.13.11"

To:

  • informacje pomocnicze o Node.js,
  • ułatwiają pracę edytorowi kodu.

Nie jest to „silnik” aplikacji, tylko:

  • podpowiedzi dla programisty,
  • pomoc w wykrywaniu błędów.

Co robi Replit z package.json?

Gdy tworzysz projekt:

  1. Replit czyta package.json
  2. Widzi dependencies
  3. Automatycznie:
    • pobiera Express,
    • instaluje potrzebne pakiety
  4. Uruchamia index.js

Dlatego:

  • nie musieliśmy nic instalować ręcznie,
  • wszystko zadziałało „samo”.

Podsumowanie – po co jest package.json?

package.json:

  • mówi co to za projekt,
  • mówi od czego zacząć (index.js),
  • mówi jakie biblioteki są potrzebne,
  • pozwala innemu komputerowi uruchomić ten sam projekt.