Первоначально опубликовано на deno.com/blog.
Существует множество отличных руководств для всех, кто хочет начать создавать REST API с помощью TypeScript и Express. У этих руководств, какими бы замечательными они ни были, есть два недостатка:
- Они требуют, чтобы вы установили и настроили TypeScript и предоставили все необходимое для этого. Это может занять много времени и стать источником разочарования, особенно для новых разработчиков.
- Они не учитывают необходимость проявлять инициативу в отношении упаковки ненадежного кода; это неудивительно, так как большинство инструментов не поддерживают его.
Вот почему мы создали этот учебник. С Deno вам не нужно настраивать TypeScript, поэтому вы можете приступить к работе с минимальными зависимостями.
Не стесняйтесь посмотреть видеопрохождение этого поста.
Если вы хотите перейти к коду, вы можете сделать это здесь.
Настройка Express и его виды
Давайте создадим main.ts
, который будет содержать логику для нашего API.
В этом файле давайте импортируем Express через спецификатор npm
.
import express, { NextFunction, Request, Response } from "npm:[email protected]";
Это дает нам выражение, но не определения типов. Давайте импортируем определения типов, добавив этот комментарий:
// @deno-types="npm:@types/express@4" import express, { NextFunction, Request, Response } from "npm:[email protected]";
Затем нам нужно определить способ взаимодействия с интерфейсом приложения Express, и нам нужно будет определить порт для его запуска, который мы получим из среды:
const app = express(); const port = Number(Deno.env.get("PORT")) || 3000;
Давайте определим тестовый маршрут, который будет приветствовать при получении запроса GET, который мы сейчас просто сделаем базовым маршрутом по умолчанию:
app.get("/", (_req, res) => { res.status(200).send("Hello from Deno and Express!"); });
Теперь мы построили простую логику, нам просто нужно, чтобы она слушала и начинала обслуживать запросы! Для этого мы будем использовать .listen()
, как показано ниже:
app.listen(port, () => { console.log(`Listening on ${port} ...`); });
И теперь мы готовы к работе!
Безопасный запуск сервера
Запускаем наш сервер:
Когда мы разрабатываем API, нам приходится использовать все виды кода, от геоинформации, ИИ, рекламных серверов и любых других входных данных, которые должны собраться вместе, чтобы создать то, что требуется. Конечно, мы не ожидаем, что Express создаст уязвимости, но Express — это лишь часть стека, который вам понадобится, чтобы что-то сделать.
Если бы он запросил доступ к системной информации, таймерам с высоким разрешением или доступ за пределы каталога, это было бы тревожным сигналом. Вы можете указать разрешения различными способами, включая шебанг в сценариях.
На данный момент у нас есть работающая служба API, которую мы можем запросить с помощью curl:
Теперь мы уверены, что фреймворк работает правильно, поэтому мы уверены в нашей установке и во всем остальном. Но пока это не очень хорошая рабочая среда, поэтому давайте настроим наш файл deno.jsonc
, чтобы определить несколько вспомогательных скриптов:
Это работает аналогично package.json
скриптам (на самом деле Deno может использовать даже package.json
скрипты, но рекомендуется deno.jsonc
), где у нас одна задача на разработку, а другая на запуск сервера без просмотра и перезагрузки по изменениям.
Увидев вывод deno task
, мы можем подтвердить, что у нас есть два доступных скрипта:
$ deno task Available tasks: - dev deno run --allow-read --allow-net --allow-env --watch main.ts - start deno run --allow-read --allow-net --allow-env main.ts
Мы можем использовать deno task dev
и deno task start
соответственно.
Добавление ведения журнала
Следующее, что нам понадобится, — это какая-то функция ведения журнала, чтобы мы могли устранять неполадки в наших запросах во время их создания, и это отличное введение в концепцию промежуточного программного обеспечения в Express.
Промежуточное ПО — это функция, которая может читать и даже изменять объекты req
и res
. Мы используем промежуточное программное обеспечение, чтобы делать все, от ведения журнала до внедрения заголовков или даже ограничения скорости и проверки авторизации. Промежуточное программное обеспечение должно сделать одну из двух вещей, когда это будет сделано:
- Он должен закрыть соединение с ответом, если это уместно, или
- Он должен вызвать
next()
, который сообщает Express, что пора передать объекты следующей промежуточной функции.
Промежуточное ПО принимает 3 аргумента: req
и res
, как и следовало ожидать, а также next
, который указывает на следующую подходящую функцию промежуточного ПО (или возвращает управление функции-обработчику).
Вместо того, чтобы console.log()
что-то в каждом обработчике, который мы пишем, давайте определим первую промежуточную функцию как регистратор и скажем Express, что мы хотели бы ее использовать. В main.ts
:
const reqLogger = function (req, _res, next) { console.info(`${req.method} request to "${req.url}" by ${req.hostname}`); next(); };
Вы можете иметь столько промежуточного программного обеспечения, сколько захотите, и организовать его удобным для вас способом. Просто помните, что скорость ваших ответов зависит от того, насколько быстро ваша цепочка промежуточного программного обеспечения возвращает управление фреймворку. Промежуточное ПО выполняется в том порядке, в котором о нем сообщается фреймворку.
Генерация данных
Так что сейчас мы находимся в отличном месте, чтобы начать разработку. Запустите команду ./generate_data.ts
(deno run -A ./generate_data.ts
, если шебанг не сработает для вас), которая сгенерирует некоторые фиктивные пользовательские данные в data_blob.json
, которые мы можем безопасно использовать, как и любое другое хранилище данных только для чтения, через утверждения типа импорта Deno:
import demoData from "./data_blob.json" assert { type: "json" };
Теперь у нас есть доступ к demoData.users
в наших обработчиках, так что давайте напишем два обработчика:
- один
/users
, который возвращает все содержимое объекта пользователей, и - дополнительный динамический маршрут, который позволяет нам искать одного пользователя по идентификатору
app.get("/users", (_req, res) => { res.status(200).json(demoData.users); }); app.get("/users/:id", (req, res) => { const idx = Number(req.params.id); for (const user of demoData.users) { if (user.id === idx) { res.status(200).json(user); } } res.status(400).json({ msg: "User not found" }); });
Мы также можем убрать маршрут по умолчанию hello world, что оставляет нам хорошую отправную точку для API:
// @deno-types="npm:@types/express@4" import express, { NextFunction, Request, Response } from "npm:[email protected]"; import demoData from "./data_blob.json" assert { type: "json" }; const app = express(); const port = Number(Deno.env.get("PORT")) || 3000; const reqLogger = function (req, _res, next) { console.info(`${req.method} request to "${req.url}" by ${req.hostname}`); next(); }; app.use(reqLogger); app.get("/users", (_req, res) => { res.status(200).json(demoData.users); }); app.get("/users/:id", (req, res) => { const idx = Number(req.params.id); for (const user of demoData.users) { if (user.id === idx) { res.status(200).json(user); } } res.status(400).json({ msg: "User not found" }); }); app.listen(port, () => { console.log(`Listening on ${port} ...`); });
Обратите внимание, что обработчик Hello, world!
для /
был удален (и отсутствует в связанном репозитории).
Что дальше?
У нас есть отличная отправная точка для REST API, состоящая менее чем из 30 строк кода. Теперь вы можете добавить обработчик POST
с помощью app.post()
, обработчик PUT с app.put()
или любые другие методы, которые вы хотите.
В следующем посте мы расскажем, как мы можем использовать инструменты запуска тестов и тестов Deno, чтобы нам было удобнее переводить наш код из проверки концепции в то, чему мы будем доверять в производстве. После этого мы закончим серию тем, как мы можем развернуть наш проект.
Застрял? Получите помощь в нашем Discord!