Vylepšite svoje aplikácie Express.js pomocou robustnej typovej bezpečnosti s TypeScript. Táto príručka pokrýva definície obsluhy trás, typovanie middleware a osvedčené postupy pre škálovateľné API.
Integrácia TypeScript s Express: Typová bezpečnosť obsluhy trás
TypeScript sa stal základným kameňom moderného vývoja JavaScriptu a ponúka možnosti statického typovania, ktoré zvyšujú kvalitu kódu, jeho udržiavateľnosť a škálovateľnosť. V kombinácii s Express.js, populárnym frameworkom webových aplikácií pre Node.js, môže TypeScript výrazne zlepšiť robustnosť vašich backendových API. Táto komplexná príručka skúma, ako využiť TypeScript na dosiahnutie typovej bezpečnosti obsluhy trás v aplikáciách Express.js, pričom poskytuje praktické príklady a osvedčené postupy pre vytváranie robustných a udržiavateľných API pre globálne publikum.
Prečo typová bezpečnosť v Express.js záleží
V dynamických jazykoch, ako je JavaScript, sa chyby často zachytávajú za behu, čo môže viesť k neočakávanému správaniu a ťažko laditeľným problémom. TypeScript to rieši zavedením statického typovania, ktoré vám umožňuje zachytiť chyby počas vývoja ešte predtým, ako sa dostanú do produkcie. V kontexte Express.js je typová bezpečnosť obzvlášť dôležitá pre obsluhy trás, kde pracujete s objektmi požiadaviek a odpovedí, parametrami dopytu a telami požiadaviek. Nesprávne spracovanie týchto prvkov môže viesť k zlyhaniu aplikácie, poškodeniu údajov a bezpečnostným zraniteľnostiam.
- Včasná detekcia chýb: Zachyťte chyby súvisiace s typmi počas vývoja, čím sa zníži pravdepodobnosť neočakávaných problémov za behu.
- Lepšia udržiavateľnosť kódu: Anotácie typov uľahčujú pochopenie a refaktorovanie kódu.
- Vylepšené automatické dopĺňanie kódu a nástroje: IDE môžu poskytovať lepšie návrhy a kontrolu chýb s informáciami o typoch.
- Menej chýb: Typová bezpečnosť pomáha predchádzať bežným programovacím chybám, ako je napríklad odovzdávanie nesprávnych dátových typov funkciám.
Nastavenie projektu TypeScript Express.js
Pred ponorením sa do typovej bezpečnosti obsluhy trás si nastavíme základný projekt TypeScript Express.js. Ten bude slúžiť ako základ pre naše príklady.
Predpoklady
- Nainštalovaný Node.js a npm (Node Package Manager). Môžete si ich stiahnuť z oficiálnej webovej stránky Node.js. Uistite sa, že máte najnovšiu verziu pre optimálnu kompatibilitu.
- Editor kódu ako Visual Studio Code, ktorý ponúka vynikajúcu podporu TypeScript.
Inicializácia projektu
- Vytvorte nový adresár projektu:
mkdir typescript-express-app && cd typescript-express-app - Inicializujte nový npm projekt:
npm init -y - Nainštalujte TypeScript a Express.js:
npm install typescript express - Nainštalujte súbory deklarácií TypeScript pre Express.js (dôležité pre typovú bezpečnosť):
npm install @types/express @types/node - Inicializujte TypeScript:
npx tsc --init(Tým sa vytvorí súbortsconfig.json, ktorý konfiguruje kompilátor TypeScript.)
Konfigurácia TypeScript
Otvorte súbor tsconfig.json a primerane ho nakonfigurujte. Tu je ukážková konfigurácia:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Kľúčové konfigurácie, ktoré si treba všimnúť:
target: Určuje cieľovú verziu ECMAScriptu.es6je dobrý štartovací bod.module: Určuje generovanie kódu modulu.commonjsje bežná voľba pre Node.js.outDir: Určuje výstupný adresár pre skompilované súbory JavaScriptu.rootDir: Určuje koreňový adresár vašich zdrojových súborov TypeScript.strict: Povoľuje všetky možnosti prísneho typového kontrolovania pre zvýšenú typovú bezpečnosť. Toto je vysoko odporúčané.esModuleInterop: Povoľuje interoperabilitu medzi modulmi CommonJS a ES.
Vytvorenie vstupného bodu
Vytvorte adresár src a pridajte súbor index.ts:
mkdir src
touch src/index.ts
Naplňte src/index.ts základným nastavením servera Express.js:
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
app.get('/', (req: Request, res: Response) => {
res.send('Hello, TypeScript Express!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Pridanie skriptu na zostavenie
Pridajte skript na zostavenie do súboru package.json na kompiláciu kódu TypeScript:
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "npm run build && npm run start"
}
Teraz môžete spustiť npm run dev na zostavenie a spustenie servera.
Typová bezpečnosť obsluhy trás: Definícia typov požiadaviek a odpovedí
Jadrom typovej bezpečnosti obsluhy trás je správna definícia typov pre objekty Request a Response. Express.js poskytuje generické typy pre tieto objekty, ktoré vám umožňujú špecifikovať typy parametrov dopytu, telo požiadavky a parametre trasy.
Základné typy obsluhy trás
Začnime jednoduchou obsluhou trasy, ktorá očakáva meno ako parameter dopytu:
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
interface NameQuery {
name: string;
}
app.get('/hello', (req: Request<any, any, any, NameQuery>, res: Response) => {
const name = req.query.name;
if (!name) {
return res.status(400).send('Parameter Name je vyžadovaný.');
}
res.send(`Ahoj, ${name}!`);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
Request<any, any, any, NameQuery>definuje typ pre objekt požiadavky.- Prvé
anypredstavuje parametre trasy (napr./users/:id). - Druhé
anypredstavuje typ tela odpovede. - Tretie
anypredstavuje typ tela požiadavky. NameQueryje rozhranie, ktoré definuje štruktúru parametrov dopytu.
Definovaním rozhrania NameQuery môže TypeScript teraz overiť, že vlastnosť req.query.name existuje a má typ string. Ak sa pokúsite pristupovať k neexistujúcej vlastnosti alebo priradiť hodnotu nesprávneho typu, TypeScript zobrazí chybu.
Spracovanie tiel požiadaviek
Pre trasy, ktoré prijímajú telá požiadaviek (napr. POST, PUT, PATCH), môžete definovať rozhranie pre telo požiadavky a použiť ho v type Request:
import express, { Request, Response } from 'express';
import bodyParser from 'body-parser';
const app = express();
const port = 3000;
app.use(bodyParser.json()); // Dôležité pre spracovanie JSON tiel požiadaviek
interface CreateUserRequest {
firstName: string;
lastName: string;
email: string;
}
app.post('/users', (req: Request<any, any, CreateUserRequest>, res: Response) => {
const { firstName, lastName, email } = req.body;
// Validácia tela požiadavky
if (!firstName || !lastName || !email) {
return res.status(400).send('Chýbajú požadované polia.');
}
// Spracovanie vytvorenia používateľa (napr. uloženie do databázy)
console.log(`Creating user: ${firstName} ${lastName} (${email})`);
res.status(201).send('Užívateľ bol úspešne vytvorený.');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
CreateUserRequestdefinuje štruktúru očakávaného tela požiadavky.app.use(bodyParser.json())je kľúčové pre spracovanie JSON tiel požiadaviek. Bez neho budereq.bodynedefinovaný.- Typ
Requestje terazRequest<any, any, CreateUserRequest>, čo naznačuje, že telo požiadavky by malo zodpovedať rozhraniuCreateUserRequest.
TypeScript teraz zabezpečí, že objekt req.body obsahuje očakávané vlastnosti (firstName, lastName a email) a že ich typy sú správne. Toto výrazne znižuje riziko chýb za behu spôsobených nesprávnymi údajmi v tele požiadavky.
Spracovanie parametrov trasy
Pre trasy s parametrami (napr. /users/:id) môžete definovať rozhranie pre parametre trasy a použiť ho v type Request:
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
interface UserParams {
id: string;
}
interface User {
id: string;
firstName: string;
lastName: string;
email: string;
}
const users: User[] = [
{ id: '1', firstName: 'John', lastName: 'Doe', email: 'john.doe@example.com' },
{ id: '2', firstName: 'Jane', lastName: 'Smith', email: 'jane.smith@example.com' },
];
app.get('/users/:id', (req: Request<UserParams>, res: Response) => {
const userId = req.params.id;
const user = users.find(u => u.id === userId);
if (!user) {
return res.status(404).send('Užívateľ nenájdený.');
}
res.json(user);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
UserParamsdefinuje štruktúru parametrov trasy a špecifikuje, že parameteridby mal byť reťazec.- Typ
Requestje terazRequest<UserParams>, čo naznačuje, že objektreq.paramsby mal zodpovedať rozhraniuUserParams.
TypeScript teraz zabezpečí, že vlastnosť req.params.id existuje a má typ string. Toto pomáha predchádzať chybám spôsobeným prístupom k neexistujúcim parametrom trasy alebo ich použitím s nesprávnymi typmi.
Špecifikácia typov odpovedí
Zatiaľ čo zameranie sa na typovú bezpečnosť požiadaviek je kľúčové, definícia typov odpovedí tiež zlepšuje zrozumiteľnosť kódu a pomáha predchádzať nekonzistenciám. Môžete definovať typ údajov, ktoré posielate späť v odpovedi.
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
interface User {
id: string;
firstName: string;
lastName: string;
email: string;
}
const users: User[] = [
{ id: '1', firstName: 'John', lastName: 'Doe', email: 'john.doe@example.com' },
{ id: '2', firstName: 'Jane', lastName: 'Smith', email: 'jane.smith@example.com' },
];
app.get('/users', (req: Request, res: Response<User[]>) => {
res.json(users);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Tu Response<User[]> špecifikuje, že telo odpovede by malo byť pole objektov User. To pomáha zabezpečiť, že v odpovediach vášho API konzistentne posielate správnu štruktúru údajov. Ak sa pokúsite poslať údaje, ktoré nezodpovedajú typu `User[]`, TypeScript vydá varovanie.
Typová bezpečnosť middleware
Middleware funkcie sú nevyhnutné pre spracovanie prierezových záujmov v aplikáciách Express.js. Zabezpečenie typovej bezpečnosti v middleware je rovnako dôležité ako v obsluhách trás.
Typovanie funkcií middleware
Základná štruktúra funkcie middleware v TypeScript je podobná ako u obsluhy trás:
import express, { Request, Response, NextFunction } from 'express';
function authenticationMiddleware(req: Request, res: Response, next: NextFunction) {
// Autentizačná logika
const isAuthenticated = true; // Nahraďte skutočnou kontrolou autentizácie
if (isAuthenticated) {
next(); // Pokračovať k ďalšiemu middleware alebo obsluhe trasy
} else {
res.status(401).send('Neautorizované');
}
}
const app = express();
const port = 3000;
app.use(authenticationMiddleware);
app.get('/', (req: Request, res: Response) => {
res.send('Ahoj, autentizovaný užívateľ!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
NextFunctionje typ poskytovaný Express.js, ktorý predstavuje ďalšiu funkciu middleware v reťazci.- Funkcia middleware prijíma rovnaké objekty
RequestaResponseako obsluhy trás.
Rozšírenie objektu požiadavky
Niekedy môžete chcieť pridať vlastné vlastnosti do objektu Request vo vašom middleware. Napríklad autentizačné middleware môže pridať vlastnosť user do objektu požiadavky. Aby ste to urobili typovo bezpečným spôsobom, musíte rozšíriť rozhranie Request.
import express, { Request, Response, NextFunction } from 'express';
interface User {
id: string;
username: string;
email: string;
}
// Rozšírenie rozhrania Request
declare global {
namespace Express {
interface Request {
user?: User;
}
}
}
function authenticationMiddleware(req: Request, res: Response, next: NextFunction) {
// Autentizačná logika (nahraďte skutočnou kontrolou autentizácie)
const user: User = { id: '123', username: 'johndoe', email: 'john.doe@example.com' };
req.user = user; // Pridanie používateľa do objektu požiadavky
next(); // Pokračovať k ďalšiemu middleware alebo obsluhe trasy
}
const app = express();
const port = 3000;
app.use(authenticationMiddleware);
app.get('/', (req: Request, res: Response) => {
const username = req.user?.username || 'Gość';
res.send(`Ahoj, ${username}!`);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
- Používame globálnu deklaráciu na rozšírenie rozhrania
Express.Request. - Do rozhrania
Requestpridáme voliteľnú vlastnosťusertypuUser. - Teraz môžete pristupovať k vlastnosti
req.uservo vašich obsluhách trás bez toho, aby TypeScript hlásil chybu. `?` v `req.user?.username` je kľúčové pre spracovanie prípadov, keď používateľ nie je autentizovaný, čím sa predchádza možným chybám.
Osvedčené postupy pre integráciu TypeScript s Express
Na maximalizáciu výhod TypeScriptu vo vašich aplikáciách Express.js dodržiavajte tieto osvedčené postupy:
- Povoľte prísny režim: Použite možnosť
"strict": truevo vašom súboretsconfig.jsonna povolenie všetkých možností prísneho typového kontrolovania. Toto pomáha včas zachytiť potenciálne chyby a zabezpečuje vyššiu úroveň typovej bezpečnosti. - Používajte rozhrania a typové aliasy: Definovajte rozhrania a typové aliasy na reprezentáciu štruktúry vašich údajov. Toto robí váš kód čitateľnejším a udržiavateľnejším.
- Používajte generické typy: Využite generické typy na vytváranie znovupoužiteľných a typovo bezpečných komponentov.
- Píšte jednotkové testy: Píšte jednotkové testy na overenie správnosti vášho kódu a zabezpečenie presnosti vašich typových anotácií. Testovanie je kľúčové pre udržanie kvality kódu.
- Používajte linter a formátovač: Používajte linter (ako ESLint) a formátovač (ako Prettier) na presadzovanie konzistentných štýlov kódovania a zachytávanie potenciálnych chýb.
- Vyhnite sa typu
any: Minimalizujte používanie typuany, pretože obchádza typové kontrolovanie a popiera účel používania TypeScriptu. Používajte ho iba vtedy, keď je to absolútne nevyhnutné, a vždy, keď je to možné, zvážte použitie špecifickejších typov alebo generík. - Logicky štruktúrujte svoj projekt: Usporiadajte svoj projekt do modulov alebo priečinkov podľa funkčnosti. Tým sa zlepší udržiavateľnosť a škálovateľnosť vašej aplikácie.
- Použite injekciu závislostí: Zvážte použitie kontajnera na injekciu závislostí na správu závislostí vašej aplikácie. Toto môže urobiť váš kód testovateľnejším a udržiavateľnejším. Knižnice ako InversifyJS sú populárne voľby.
Pokročilé koncepty TypeScriptu pre Express.js
Používanie dekorátorov
Dekorátory poskytujú stručný a expresívny spôsob pridávania metadát do tried a funkcií. Dekorátory môžete použiť na zjednodušenie registrácie trás v Express.js.
Najprv musíte povoliť experimentálne dekorátory vo vašom súbore tsconfig.json pridaním "experimentalDecorators": true do compilerOptions.
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true
}
}
Potom môžete vytvoriť vlastný dekorátor na registráciu trás:
import express, { Router, Request, Response } from 'express';
function route(method: string, path: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
if (!target.__router__) {
target.__router__ = Router();
}
target.__router__[method](path, descriptor.value);
};
}
class UserController {
@route('get', '/users')
getUsers(req: Request, res: Response) {
res.send('Zoznam používateľov');
}
@route('post', '/users')
createUser(req: Request, res: Response) {
res.status(201).send('Užívateľ bol vytvorený');
}
public getRouter() {
return this.__router__;
}
}
const userController = new UserController();
const app = express();
const port = 3000;
app.use('/', userController.getRouter());
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
- Dekorátor
routeprijíma ako argumenty HTTP metódu a cestu. - Registroval dekorovanú metódu ako obsluhu trasy na routeri spojenom s triedou.
- Tým sa zjednoduší registrácia trás a váš kód bude čitateľnejší.
Používanie vlastných typových strážcov
Typoví strážcovia sú funkcie, ktoré zúžujú typ premennej v rámci konkrétneho rozsahu. Vlastné typoví strážcovia môžete použiť na validáciu tiel požiadaviek alebo parametrov dopytu.
interface Product {
id: string;
name: string;
price: number;
}
function isProduct(obj: any): obj is Product {
return typeof obj === 'object' &&
obj !== null &&
typeof obj.id === 'string' &&
typeof obj.name === 'string' &&
typeof obj.price === 'number';
}
import express, { Request, Response } from 'express';
import bodyParser from 'body-parser';
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.post('/products', (req: Request, res: Response) => {
if (!isProduct(req.body)) {
return res.status(400).send('Neplatné údaje o produkte');
}
const product: Product = req.body;
console.log(`Creating product: ${product.name}`);
res.status(201).send('Produkt bol vytvorený');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
V tomto príklade:
- Funkcia
isProductje vlastný typový strážca, ktorý kontroluje, či objekt zodpovedá rozhraniuProduct. - Vo vnútri obsluhy trasy
/productssa používa funkciaisProductna validáciu tela požiadavky. - Ak je telo požiadavky platným produktom, TypeScript vie, že
req.bodymá v rámci blokuiftypProduct.
Zohľadnenie globálnych aspektov v návrhu API
Pri navrhovaní API pre globálne publikum by sa mali zvážiť viaceré faktory na zabezpečenie dostupnosti, použiteľnosti a kultúrnej citlivosti.
- Lokalizácia a internacionalizácia (i18n a L10n):
- Vyjednávanie obsahu: Podpora viacerých jazykov a regiónov prostredníctvom vyjednávania obsahu na základe hlavičky
Accept-Language. - Formátovanie dátumu a času: Použite formát ISO 8601 na reprezentáciu dátumov a časov, aby ste sa vyhli nejednoznačnosti naprieč rôznymi regiónmi.
- Formátovanie čísel: Spracujte formátovanie čísel podľa lokality používateľa (napr. desatinné a tisícové oddeľovače).
- Spracovanie mien: Podpora viacerých mien a poskytnite informácie o výmenných kurzoch, ak je to potrebné.
- Smer textu: Ubytujte pravopis zprava doľava (RTL) ako arabčina a hebrejčina.
- Vyjednávanie obsahu: Podpora viacerých jazykov a regiónov prostredníctvom vyjednávania obsahu na základe hlavičky
- Časové zóny:
- Ukladajte dátumy a časy na strane servera v UTC (koordinovaný univerzálny čas).
- Umožnite používateľom špecifikovať ich preferovanú časovú zónu a podľa toho konvertujte dátumy a časy na strane klienta.
- Použite knižnice ako
moment-timezonena spracovanie konverzií časových zón.
- Kódovanie znakov:
- Použite kódovanie UTF-8 pre všetky textové údaje na podporu širokej škály znakov z rôznych jazykov.
- Uistite sa, že vaša databáza a iné systémy na ukladanie údajov sú nakonfigurované na používanie UTF-8.
- Dostupnosť:
- Dodržiavajte pokyny pre dostupnosť (napr. WCAG), aby ste svoje API sprístupnili používateľom so zdravotným postihnutím.
- Poskytujte jasné a opisné chybové hlásenia, ktoré sú ľahko zrozumiteľné.
- Používajte sémantické HTML prvky a atribúty ARIA vo svojej dokumentácii API.
- Kultúrna citlivosť:
- Vyhnite sa používaniu kultúrne špecifických odkazov, idiomov alebo humoru, ktoré nemusia byť pochopené všetkými používateľmi.
- Buďte si vedomí kultúrnych rozdielov v komunikačných štýloch a preferenciách.
- Zvážte potenciálny vplyv vášho API na rôzne kultúrne skupiny a vyhnite sa šíreniu stereotypov alebo predsudkov.
- Ochrana osobných údajov a bezpečnosť:
- Dodržiavajte predpisy o ochrane osobných údajov, ako sú GDPR (General Data Protection Regulation) a CCPA (California Consumer Privacy Act).
- Implementujte silné mechanizmy autentizácie a autorizácie na ochranu údajov používateľov.
- Šifrujte citlivé údaje počas prenosu aj v pokoji.
- Poskytnite používateľom kontrolu nad ich údajmi a umožnite im prístup k ich údajom, ich úpravu a vymazanie.
- Dokumentácia API:
- Poskytnite komplexnú a dobre organizovanú dokumentáciu API, ktorá je ľahko zrozumiteľná a prehľadná.
- Použite nástroje ako Swagger/OpenAPI na generovanie interaktívnej dokumentácie API.
- Zahrňte príklady kódu vo viacerých programovacích jazykoch, aby ste uspokojili rôznorodé publikum.
- Preložte svoju dokumentáciu API do viacerých jazykov, aby ste oslovili širšie publikum.
- Spracovanie chýb:
- Poskytnite konkrétne a informatívne chybové hlásenia. Vyhnite sa generickým chybovým hláseniam ako „Niečo sa pokazilo.“
- Použite štandardné HTTP stavové kódy na označenie typu chyby (napr. 400 pre zlú požiadavku, 401 pre neautorizované, 500 pre internú chybu servera).
- Zahrňte chybové kódy alebo identifikátory, ktoré možno použiť na sledovanie a ladenie problémov.
- Logujte chyby na strane servera na účely ladenia a monitorovania.
- Obmedzenie rýchlosti: Implementujte obmedzenie rýchlosti na ochranu vášho API pred zneužitím a zabezpečenie spravodlivého používania.
- Verzionovanie: Použite verzionovanie API na povolenie spätne kompatibilných zmien a zabránenie poškodeniu existujúcich klientov.
Záver
Integrácia TypeScriptu s Expressom výrazne zlepšuje spoľahlivosť a udržiavateľnosť vašich backendových API. Využitím typovej bezpečnosti v obsluhách trás a middleware môžete včas zachytiť chyby v procese vývoja a budovať robustnejšie a škálovateľnejšie aplikácie pre globálne publikum. Definovaním typov požiadaviek a odpovedí zabezpečíte, že vaše API bude dodržiavať konzistentnú štruktúru údajov, čím sa zníži pravdepodobnosť chýb za behu. Nezabudnite dodržiavať osvedčené postupy, ako je povolenie prísneho režimu, používanie rozhraní a typových aliasov a písanie jednotkových testov, aby ste maximalizovali výhody TypeScriptu. Vždy zvážte globálne faktory, ako je lokalizácia, časové zóny a kultúrna citlivosť, aby ste zaistili, že vaše API budú celosvetovo dostupné a použiteľné.