portaldacalheta.pt
  • Основен
  • Agile Talent
  • Растеж На Приходите
  • Мобилен Дизайн
  • Управление На Проекти
Back-End

Изграждане на Node.js / TypeScript REST API, Част 1: Express.js



Как да напиша REST API в Node.js?

Когато се изгражда заден край за REST API, Express.js често е първият избор сред Node.js рамките. Въпреки че поддържа и изграждане на статичен HTML и шаблони, в тази серия ще се съсредоточим върху back-end разработката с помощта на TypeScript. Полученият REST API ще бъде такъв, който всяка фронт-енд рамка или външна задна услуга би могла да поиска.

Ще имаш нужда:



  • Основни познания по JavaScript и TypeScript
  • Основни познания за Node.js
  • Основни познания за REST архитектурата (вж. този раздел от предишната ми статия за REST API, ако е необходимо)
  • Готова инсталация на Node.js (за предпочитане версия 14+)

В терминал (или команден ред) ще създадем папка за проекта. От тази папка стартирайте npm init. Това ще създаде някои от основните Node.js проектни файлове, от които се нуждаем.



какви са гещалт принципите на перцептивната организация

След това ще добавим рамката Express.js и някои полезни библиотеки:



npm install --save express debug winston express-winston cors

Има сериозни причини тези библиотеки да са Node.js разработчик любими:

  • debug е модул, който ще използваме, за да избегнем извикването console.log() докато разработваме нашето приложение. По този начин можем лесно да филтрираме изявления за отстраняване на грешки по време на отстраняване на неизправности. Те също могат да бъдат изключени изцяло в производството, вместо да се налага да бъдат премахнати ръчно.
  • winston отговаря за регистрирането на заявките към нашия API и връщаните отговори (и грешки). express-winston интегрира се директно с Express.js, така че всички стандартни свързани с API winston регистрационният код вече е направен.
  • cors е част от междинния софтуер Express.js, който ни позволява да го активираме споделяне на ресурси с различен произход . Без това нашият API ще може да се използва само от предни краища, обслужвани от точно същия поддомейн като нашия заден край.

Нашият заден край използва тези пакети, когато се изпълнява. Но ние също трябва да инсталираме някои развитие зависимости за нашата конфигурация на TypeScript. За това ще изпълним:



npm install --save-dev @types/cors @types/express @types/debug source-map-support tslint typescript

Тези зависимости са необходими за активиране на TypeScript за собствения код на нашето приложение, заедно с типовете, използвани от Express.js и други зависимости. Това може да спести много време, когато използваме IDE като WebStorm или VSCode, като ни позволява да завършим някои функционални методи автоматично, докато кодираме.

Крайните зависимости в package.json трябва да бъде така:



'dependencies': { 'debug': '^4.2.0', 'express': '^4.17.1', 'express-winston': '^4.0.5', 'winston': '^3.3.3', 'cors': '^2.8.5' }, 'devDependencies': { '@types/cors': '^2.8.7', '@types/debug': '^4.1.5', '@types/express': '^4.17.2', 'source-map-support': '^0.5.16', 'tslint': '^6.0.0', 'typescript': '^3.7.5' }

След като инсталирахме всички необходими зависимости, нека започнем да изграждаме собствен код!

Структура на проекта на API на TypeScript REST

За този урок ще създадем само три файла:



  1. ./app.ts
  2. ./common/common.routes.config.ts
  3. ./users/users.routes.config.ts

Идеята на двете папки на структурата на проекта (common и users) е да има отделни модули, които имат свои собствени отговорности. В този смисъл в крайна сметка ще имаме някои или всички от следните за всеки модул:

  • Конфигурация на маршрута за да дефинираме заявките, с които може да се справи нашият API
  • Услуги за задачи като свързване с нашите модели бази данни, извършване на заявки или свързване с външни услуги, които се изискват от конкретната заявка
  • Middleware за провеждане на конкретни валидации на заявки преди крайният контролер на маршрута да се справи със спецификите му
  • Модели за дефиниране на модели на данни, съответстващи на дадена схема на база данни, за улесняване на съхранението и извличането на данни
  • Контролери за отделяне на конфигурацията на маршрута от кода, който накрая (след всеки междинен софтуер) обработва заявка за маршрут, извиква горните функции на услугата, ако е необходимо, и дава отговор на клиента

Тази структура на папките предоставя ранна начална точка за останалата част от тази серия уроци и достатъчно, за да започнете да практикувате.



Файл с общи маршрути в TypeScript

В common папка, нека създадем common.routes.config.ts файл, за да изглежда по следния начин:

import express from 'express'; export class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; } getName() { return this.name; } }

Начинът, по който създаваме маршрутите тук, е незадължителен. Но тъй като работим с TypeScript, сценарият ни за маршрути е възможност да практикуваме използването на наследяване с extends ключова дума, както ще видим скоро. В този проект всички файлове с маршрути имат едно и също поведение: Те имат име (което ще използваме за отстраняване на грешки) и достъп до главния Express.js Application обект.



Сега можем да започнем да създаваме потребителски маршрут файл. В users папка, нека създадем users.routes.config.ts и започнете да го кодирате така:

import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } }

Тук импортираме CommonRoutesConfig клас и го разширява до новия ни клас, наречен UsersRoutes. С конструктора изпращаме приложението (основния express.Application обект) и името UsersRoutes до конструктора на CommonRoutesConfig

Този пример е доста прост, но при мащабиране, за да създадем няколко маршрутни файла, това ще ни помогне да избегнем дублиращ се код.

Да предположим, че бихме искали да добавим нови функции в този файл, като регистриране. Бихме могли да добавим необходимото поле към CommonRoutesConfig клас, а след това всички маршрути, които се простират CommonRoutesConfig ще имат достъп до него.

Използване на абстрактни функции на TypeScript за подобна функционалност в различните класове

Ами ако бихме искали да имаме някаква функционалност, която е подобен между тези класове (като конфигуриране на крайните точки на API), но това се нуждае от различно изпълнение за всеки клас? Една от опциите е да се използва функция TypeScript, наречена абстракция .

Нека създадем много проста абстрактна функция, която UsersRoutes клас (и бъдещи класове за маршрутизиране) ще наследят от CommonRoutesConfig. Да кажем, че искаме да принудим всички маршрути да имат функция (за да можем да я извикаме от нашия общ конструктор) с име configureRoutes() Там ще декларираме крайните точки на ресурса на всеки клас на маршрутизация.

За целта ще добавим три бързи неща към common.routes.config.ts:

  1. Ключовата дума abstract към нашите class линия, за да се позволи абстракция за този клас.
  2. Нова декларация за функция в края на нашия клас, abstract configureRoutes(): express.Application;. Това принуждава всеки клас да се разширява CommonRoutesConfig за да осигури изпълнение, съответстващо на този подпис - ако не го направи, компилаторът TypeScript ще изведе грешка.
  3. Обаждане до this.configureRoutes(); в края на конструктора, тъй като вече можем да сме сигурни, че тази функция ще съществува.

Резултатът:

import express from 'express'; export abstract class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; this.configureRoutes(); } getName() { return this.name; } abstract configureRoutes(): express.Application; }

С това всеки клас, удължаващ CommonRoutesConfig трябва да има функция, наречена configureRoutes() което връща express.Application обект. Това означава users.routes.config.ts има нужда от актуализиране:

import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } configureRoutes() { // (we'll add the actual route configuration here next) return this.app; } }

Като обобщение на това, което сме направили:

Първо импортираме common.routes.config файл, след това express модул. След това дефинираме UserRoutes клас, казвайки, че искаме да разшири CommonRoutesConfig базов клас, което предполага, че обещаваме, че ще изпълни configureRoutes().

За изпращане на информация до CommonRoutesConfig клас, ние използваме constructor на класа. Очаква да получи express.Application обект, който ще опишем по-задълбочено в следващата стъпка. С super() преминаваме към конструктора на CommonRoutesConfig приложението и името на нашите маршрути, което в този сценарий е UsersRoutes. (super() от своя страна ще извика изпълнението ни на configureRoutes().)

Конфигуриране на Express.js маршрутите на крайните точки на потребителите

configureRoutes() функция е мястото, където ще създадем крайните точки за потребители на нашия REST API. Там ще използваме приложение и е маршрут функционалности от Express.js.

Идеята за използване на app.route() функцията е да се избегне дублиране на код, което е лесно, тъй като създаваме REST API с добре дефинирани ресурси. Основният ресурс за този урок е потребители . В този сценарий има два случая:

  • Когато повикващият API иска да създаде нов потребител или да изброи всички съществуващи потребители, крайната точка първоначално трябва да има само users в края на заявения път. (В тази статия няма да се занимаваме с филтриране на заявки, страниране или други подобни заявки.)
  • Когато повикващият иска да направи нещо специфично за конкретен потребителски запис, пътят на ресурса на заявката ще следва модела users/:userId

Начинът .route() работи в Express.js ни позволява да обработваме HTTP глаголи с някои елегантни вериги. Това е така, защото .get(), .post() и т.н., всички връщат един и същ екземпляр на IRoute че първата .route() обажда се. Крайната конфигурация ще бъде следната:

configureRoutes() { this.app.route(`/users`) .get((req: express.Request, res: express.Response) => { res.status(200).send(`List of users`); }) .post((req: express.Request, res: express.Response) => { res.status(200).send(`Post to users`); }); this.app.route(`/users/:userId`) .all((req: express.Request, res: express.Response, next: express.NextFunction) => { // this middleware function runs before any request to /users/:userId // but it doesn't accomplish anything just yet--- // it simply passes control to the next applicable function below using next() next(); }) .get((req: express.Request, res: express.Response) => { res.status(200).send(`GET requested for id ${req.params.userId}`); }) .put((req: express.Request, res: express.Response) => { res.status(200).send(`PUT requested for id ${req.params.userId}`); }) .patch((req: express.Request, res: express.Response) => { res.status(200).send(`PATCH requested for id ${req.params.userId}`); }) .delete((req: express.Request, res: express.Response) => { res.status(200).send(`DELETE requested for id ${req.params.userId}`); }); return this.app; }

Горният код позволява на всеки клиент на REST API да извика нашия users крайна точка с POST или a GET заявка. По същия начин позволява на клиента да се обади на /users/:userId крайна точка с GET, PUT, PATCH или DELETE заявка.

Но за /users/:userId, ние също добавихме общ междинен софтуер, използвайки all() функция, която ще се изпълни преди някоя от get(), put(), patch() или delete() функции. Тази функция ще бъде от полза, когато (по-късно в поредицата) създадем маршрути, които са предназначени за достъп само от удостоверени потребители.

Може би сте забелязали, че в нашия .all() функция - както при всеки междинен софтуер - имаме три типа полета: Request, Response и NextFunction.

  • The Заявка е начинът, по който Express.js представлява HTTP заявката, която трябва да бъде обработена. Този тип надгражда и разширява роден Node.js тип заявка.
  • The Отговор е също така как Express.js представлява HTTP отговора, отново разширявайки роден Node.js тип отговор.
  • Не по-малко важно е NextFunction служи като функция за обратно извикване, позволяваща на контрола да преминава през други функции на междинния софтуер. По пътя всички междинни програми ще споделят едни и същи обекти на заявка и отговор, преди контролерът най-накрая да изпрати отговор обратно на заявителя.

Нашият файл за входна точка Node.js, app.ts

След като конфигурирахме някои основни скелети на маршрута, ще започнем да конфигурираме точката за влизане на приложението. Нека създадем app.ts файл в корена на нашата папка на проекта и започнете с този код:

import express from 'express'; import * as http from 'http'; import * as bodyparser from 'body-parser'; import * as winston from 'winston'; import * as expressWinston from 'express-winston'; import cors from 'cors'; import {CommonRoutesConfig} from './common/common.routes.config'; import {UsersRoutes} from './users/users.routes.config'; import debug from 'debug';

Само два от тези вноса са нови към този момент в статията:

  • http е родния модул на Node.js. Необходимо е да стартирате нашето приложение Express.js.
  • body-parser е междинен софтуер, който идва с Express.js. Той анализира заявката (в нашия случай като JSON), преди контролът да премине към нашите собствени обработващи заявки.

След като импортирахме файловете, ще започнем да декларираме променливите, които искаме да използваме:

Raspberry pi 3 сървърни проекти
const app: express.Application = express(); const server: http.Server = http.createServer(app); const port: Number = 3000; const routes: Array = []; const debugLog: debug.IDebugger = debug('app');

express() функцията връща основния обект на приложение Express.js, който ще предаваме в целия ни код, като започнем с добавянето му в http.Server обект. (Ще трябва да стартираме http.Server след конфигуриране на нашите express.Application.)

Ще слушаме на порт 3000, вместо на стандартните портове 80 (HTTP) или 443 (HTTPS), тъй като те обикновено се използват за предния край на приложението.

Защо Port 3000?

Няма правило пристанището да е 3000 - ако не е посочено, произволно пристанище ще бъде назначен - но 3000 се използва в примерите за документация както за Node.js, така и за Express.js, така че ние продължаваме традицията тук.

Може ли Node.js да споделя портове с предния край?

Все още можем да стартираме локално на персонализиран порт, дори когато искаме задният ни край да отговаря на заявки на стандартни портове. Това ще изисква обратен прокси за получаване на заявки на порт 80 или 443 с определен домейн или поддомейн. След това ще ги пренасочи към нашия вътрешен порт 3000.

routes array ще следи файловете ни с маршрути с цел отстраняване на грешки, както ще видим по-долу.

И накрая, debugLog ще завърши като функция, подобна на console.log, но по-добре: По-лесно е да се прецизира, защото автоматично се обхваща до каквото искаме да извикаме контекста на нашия файл / модул. (В този случай сме го нарекли „app“, когато сме го предали в низ на конструктора debug()).

Сега сме готови да конфигурираме всички наши модули за мидълуер Express.js и маршрутите на нашия API:

// here we are adding middleware to parse all incoming requests as JSON app.use(bodyparser.json()); // here we are adding middleware to allow cross-origin requests app.use(cors()); // here we are configuring the expressWinston logging middleware, // which will automatically log all HTTP requests handled by Express.js app.use(expressWinston.logger({ transports: [ new winston.transports.Console() ], format: winston.format.combine( winston.format.colorize(), winston.format.json() ) })); // here we are adding the UserRoutes to our array, // after sending the Express.js application object to have the routes added to our app! routes.push(new UsersRoutes(app)); // here we are configuring the expressWinston error-logging middleware, // which doesn't *handle* errors per se, but does *log* them app.use(expressWinston.errorLogger({ transports: [ new winston.transports.Console() ], format: winston.format.combine( winston.format.colorize(), winston.format.json() ) })); // this is a simple route to make sure everything is working properly app.get('/', (req: express.Request, res: express.Response) => { res.status(200).send(`Server up and running!`) });

Може би сте забелязали, че expressWinston.errorLogger е зададено след ние определяме нашите маршрути. Това не е грешка! Като експрес-winston документация гласи:

Регистраторът трябва да бъде добавен СЛЕД експресния рутер (app.router) и ПРЕДИ някой от вашите персонализирани манипулатори на грешки (express.handler).

И накрая и най-важното:

server.listen(port, () => { debugLog(`Server running at http://localhost:${port}`); routes.forEach((route: CommonRoutesConfig) => { debugLog(`Routes configured for ${route.getName()}`); }); });

Това всъщност стартира нашия сървър. След като стартира, Node.js ще изпълни нашата функция за обратно извикване, която съобщава, че работим, последвана от имената на всички маршрути, които сме конфигурирали - досега, само UsersRoutes

ръководство за стил на материален дизайн на google

Актуализиране package.json на Transpile TypeScript в JavaScript и стартирайте приложението

Сега, когато сме подготвили скелета си за работа, първо се нуждаем от някаква конфигурация на шаблон, за да активираме TypeScript транспилация. Нека добавим файла tsconfig.json в корен на проекта:

{ 'compilerOptions': { 'target': 'es2016', 'module': 'commonjs', 'outDir': './dist', 'strict': true, 'esModuleInterop': true, 'inlineSourceMap': true } }

Тогава просто трябва да добавим последните щрихи към package.json под формата на следните скриптове:

'scripts': { 'start': 'tsc && node ./dist/app.js', 'debug': 'export DEBUG=* && npm run start', 'test': 'echo 'Error: no test specified' && exit 1' },

test script е заместител, който ще заменим по-късно в поредицата.

The tsc в start скриптът принадлежи на TypeScript. Той е отговорен за транпилирането на нашия TypeScript код в JavaScript, който той ще изведе в dist папка. След това просто изпълняваме вградената версия с node ./dist/app.js.

debug скриптът извиква start скрипт, но първо определя a DEBUG променлива на средата. Това води до активиране на всички наши debugLog() оператори (плюс подобни от самия Express.js, който използва същия модул debug, който правим), за да изведе полезни детайли към терминала - детайли, които (удобно) са скрити по друг начин, когато сървърът работи в производствен режим със стандартен npm start.

Опитайте да стартирате npm run debug себе си и след това сравнете това с npm start за да видите как се променя изходът на конзолата.

Съвет: Можете да ограничите изхода за отстраняване на грешки до нашите app.ts файлът е собствен debugLog() извлечения, използващи DEBUG=app вместо DEBUG=*. debug модул обикновено е доста гъвкав и тази функция не е изключение .

Потребителите на Windows вероятно ще трябва да променят export до SET тъй като export е как работи на Mac и Linux. Ако вашият проект трябва да поддържа множество среди за разработка, кръстосания env пакет предоставя директно решение тук.

Тестване на Live Express.js Back End

С npm run debug или npm start все още продължава, нашият REST API ще бъде готов да обслужва заявки на порт 3000. На този етап можем да използваме cURL, Пощальон , Безсъние и т.н., за да тествате задния край.

Тъй като създадохме само скелет за потребителския ресурс, можем просто да изпращаме заявки без тяло, за да видим, че всичко работи според очакванията. Например:

curl --location --request GET 'localhost:3000/users/12345'

Нашият заден край трябва да изпрати обратно отговора GET requested for id 12345.

Що се отнася до POST ing:

curl --location --request POST 'localhost:3000/users' --data-raw ''

Този и всички други видове заявки, за които сме изградили скелети, ще изглеждат доста сходни.

Готово за Rapid Node.js REST API разработка с TypeScript

В тази статия започнахме да създаваме REST API, като конфигурирахме проекта от нулата и се потопихме в основите на рамката Express.js. След това направихме първата си стъпка към овладяване на TypeScript чрез изграждане на шаблон с UsersRoutesConfig удължаване CommonRoutesConfig, модел, който ще използваме повторно за следващата статия от тази поредица. Завършихме, като конфигурирахме нашите app.ts входна точка за използване на нашите нови маршрути и package.json със скриптове за изграждане и стартиране на нашето приложение.

Но дори основите на REST API, направени с Express.js и TypeScript, са доста ангажирани. В следващата част от тази серия ние се фокусираме върху създаването на подходящи контролери за потребителския ресурс и разглеждаме някои полезни модели за услуги, мидълуер, контролери и модели.

Пълният проект е на разположение на GitHub , а кодът от края на тази статия се намира в toptal-article-01 клон.

Разбиране на основите

Мога ли да използвам TypeScript с Node.js?

Абсолютно! Много често за популярните npm пакети (включително Express.js) има съответстващи файлове за дефиниция на TypeScript тип. Това важи за самия Node.js, плюс включени подкомпоненти като пакета за отстраняване на грешки.

Добър ли е Node.js за REST API?

Да. Node.js може да се използва сам за създаване на готови за производство REST API, а има и няколко популярни рамки като Express.js, за да се намали неизбежният шаблон.

Трудно ли е да се научи TypeScript?

Не, не е трудно да започнете да учите TypeScript за тези с модерен JavaScript фон. Още по-лесно е за тези с опит в обектно-ориентираното програмиране. Но овладяването на всички нюанси и най-добри практики на TypeScript отнема време, както при всяко умение.

Трябва ли да използвам TypeScript?

Зависи от проекта, но определено се препоръчва за програмиране на Node.js. Това е по-изразителен език за моделиране на реални проблемни домейни от задната страна. Това прави кода по-четлив и намалява потенциала за грешки.

За какво се използва TypeScript?

TypeScript се използва навсякъде, където се намери JavaScript, но е особено подходящ за по-големи приложения. Той използва JavaScript като основа, добавяйки статично писане и много по-добра поддръжка за парадигмата на обектно-ориентираното програмиране (OOP). Това от своя страна поддържа по-напреднала разработка и опит за отстраняване на грешки.

Помислете S.M.A.R.T. Когато определяте бизнес цели за следващия си UX проект

Ux Дизайн

Помислете S.M.A.R.T. Когато определяте бизнес цели за следващия си UX проект
Изпълнение на I / O от страна на сървъра: Node срещу PHP срещу Java срещу Go

Изпълнение на I / O от страна на сървъра: Node срещу PHP срещу Java срещу Go

Back-End

Популярни Публикации
Урок за скриптове на Google Apps за овладяване на макроси
Урок за скриптове на Google Apps за овладяване на макроси
Теория на цветовете за дизайнери - Crash Course (с инфографика)
Теория на цветовете за дизайнери - Crash Course (с инфографика)
ApeeScape стартира нова специализация DevOps по заявка за обслужване на Enterprise Shift към облака
ApeeScape стартира нова специализация DevOps по заявка за обслужване на Enterprise Shift към облака
Нула до герой: Рецепти за производство на колби
Нула до герой: Рецепти за производство на колби
Как да проведем успешна техническа конференция: Събитието в CordobaJS
Как да проведем успешна техническа конференция: Събитието в CordobaJS
 
Как да накараме отдалечената работа да работи за вас
Как да накараме отдалечената работа да работи за вас
Всеки продукт има теза
Всеки продукт има теза
Комодитизирани смартфони: Привеждане на 4G в развиващите се страни
Комодитизирани смартфони: Привеждане на 4G в развиващите се страни
Запознайте се с Volt, обещаваща Ruby рамка за динамични приложения
Запознайте се с Volt, обещаваща Ruby рамка за динамични приложения
Разширени тактики за силно съвместни, отдалечени екипи
Разширени тактики за силно съвместни, отдалечени екипи
Популярни Публикации
  • урок за node js и angularjs
  • изкупуване с ливъридж (lbo)
  • Резюме на финансовата криза в Гърция
  • назовете инструмента за проектиране, който може да се използва за установяване на пространствена йерархия на съдържанието
  • кое от тях се използва като мярка за общата сума на наличния паричен поток от даден проект?
  • c++ с помощта на заглавни файлове
Категории
  • Agile Talent
  • Растеж На Приходите
  • Мобилен Дизайн
  • Управление На Проекти
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt