Как стать автором
Обновить

Руководство по Deno: примеры работы с новой средой выполнения серверного JavaScript и TypeScript

Время на прочтение16 мин
Количество просмотров9.5K
Автор оригинала: Flavio Copes

Доброго времени суток, друзья!

Представляю Вашему вниманию перевод статьи «The Deno Handbook: A TypeScript Runtime Tutorial with Code Examples» автора Flavio Copes.

В этой статье мы научимся работать с Deno. Мы сравним его с Node.js и создадим с его помощью простой REST API.

Что такое Deno?


Если вы знакомы с Node.js, популярной экосистемой серверного JavaScript, Deno — это почти тоже самое. Почти, но не совсем.

Давайте начнем со списка возможностей Deno, которые мне больше всего нравятся:

  • Он основан на современном JavaScript
  • Он имеет расширяемую стандартную библиотеку
  • Он имеет стандартную поддержку TypeScript (это означает, что вам не нужно вручную компилировать TypeScript, Deno делает это автоматически)
  • Он поддерживает ES модули
  • Он не имеет пакетного менеджера
  • Он имеет глобальный await
  • Он имеет встроенное средство тестирования
  • Его цель — максимальная совместимость с браузером. Для этого он предоставляет встроенный fetch и глобальный объект window

В данном руководстве мы изучим все эти возможности.

После знакомства с Deno и его возможностями, Node.js покажется вам немного устаревшим.

Особенно по причине того, что Node.js основан на функциях обратного вызова (он был написан до появления промисов и async/await). Едва ли они когда-нибудь там появятся, поскольку это означает необходимость внесения фундаментальных изменений.

Node.js прекрасен и останется фактическим стандартом в мире JavaScript. Однако, я полагаю, что популярность Deno будет быстро расти благодаря его поддержке TypeScript и современной стандартной библиотеке.

Deno может позволить себе современный код, поскольку не нуждается в обеспечении обратной совместимости. Разумеется, гарантии, что этот код будет оставаться современным в ближайшее десятилетие нет, но на сегодня это именно так.

Почему Deno? Почему сейчас?


Deno был анонсирован почти 2 года назад создателем Node.js Ryan Dahl на JSConf EU. Смотрите видео на YouTube, оно очень интересное, и является обязательным к просмотру, если вы работаете с Node.js и JavaScript.

Каждый менеджер (создатель) проекта вынужден принимать решения. Райан жалеет о некоторых ранних решениях в Node. Кроме того, технологии развиваются, и сегодня JavaScript — это совершенно другой язык, чем он был в 2009, когда появился Node. Вспомните о возможностях ES6/2016/2017 и т.д.

Поэтому он решил начать новый проект, своего рода вторую волну приложений на серверном JavaScript.

Причина, по которой я пишу эту статью только сейчас, заключается в том, что требуется довольно много времени для созревания технологии. Наконец, мы получили Deno 1.0 (он был представлен 13 мая 2020 года), первый стабильный релиз.

Это может показаться обычным числом, однако 1.0 означает, что радикальных изменений не будет вплоть до Deno 2.0. При изучении новой технологии вы не хотите, чтобы она слишком быстро менялась.

Следует ли изучать Deno?


Хороший вопрос.

Изучение чего-то нового, такого как Deno, требует больших усилий. Мой совет: если вы только начинаете изучать серверный JS и пока не знаете Node.js, и никогда раньше не писали код на TypeScript, начните с Node.

За выбор Node еще никого не увольняли (перефразирование известной цитаты).

Но если вам нравится TypeScript, не зависящий от тонны npm-пакетов, и вы хотите везде использовать await, Deno может быть тем, что вы ищите.

Заменит ли он Node.js?


Нет. Node.js — это гигант, большой авторитет, невероятно хорошо поддерживаемая технология, которая в ближайшее десятилетие никуда не денется.

Первоклассная поддержка TypeScript


Deno написан на Rust и TypeScript, очень популярных на сегодня языках.

Это означает, что мы получаем много выгод от TypeScript, даже если пишем код на JavaScript.

Запуск TypeScript-кода с помощью Deno не требует предварительной компиляции — Deno делает это автоматически.

Вы не обязаны писать код на TypeScript, однако тот факт, что ядро Deno написано на TypeScript, имеет огромное значение.

Во-первых, большой процент JavaScript-разработчиков любит TypeScript.

Во-вторых, используемые вами инструменты могут получать много информации о ПО, написанном на TypeScript, таком как Deno.

Это означает, что когда мы пишем код в VS Code, например (который имеет тесную интеграцию с TypeScript с момента появления), мы получаем такие преимущества, как проверка типов при написании кода или продвинутые возможности IntelliSense. Другими словами, помощь редактора кода становится гораздо эффективнее.

Отличия от Node.js


Поскольку Deno — это, по сути, замена Node.js, имеет смысл их сравнить.

Общее:

  • Оба основаны на движке V8
  • Оба отлично подходят для разработки серверного JavaScript

Отличия:

  • Node написан на C++ и JavaScript. Deno написан на Rust и TypeScript.
  • Node имеет официальный пакетный менеджер npm. У Deno такого менеджера нет, вместо этого он позволяет импортировать любой модуль с помощью URL.
  • Node использует синтаксис CommonJS для импорта пакетов. Deno использует официальный способ — ES модули.
  • Deno использует современные возможности ECMAScript во всех прикладных интерфейсах и стандартной библиотеке, в то время как Node.js использует основанную на колбеках стандартную библиотеку и не планирует ее обновлять.
  • Deno предлагает уровень (слой) безопасности песочницы через предоставление разрешений. Программа получает разрешение на выполнение определенных действий через пользовательские флаги. Node.js имеет доступ ко всему, к чему имеет доступ пользователь.
  • Deno долгое время искал возможность компиляции программ в выполняемые, т.е. такие, которые можно запускать без внешних зависимостей, как в Go, однако достичь этого пока не удалось. Это изменит правила игры.

Отсутствие пакетного менеджера


Отсутствие пакетного менеджера и использование URL для получения и импорта пакетов имеет свои преимущества и недостатки. Одним из основных преимуществ является большая гибкость, проявляющаяся в возможности создания пакетов без необходимости их опубликования в таком репозитории, как npm.

Думаю, что некая альтернатива пакетному менеджеру в Deno рано или поздно появится.

Официальный сайт Deno имеет хостинг для сторонних пакетов: https://deno.land/x/

Установка Deno


Хватит болтать! Давайте установим Deno.

Простейшим способом это сделать является использование Homebrew:

    brew install deno 



Другие способы установки указаны здесь.

После установки становится доступна команда deno. Вот помощь, которую можно получить, набрав deno --help:

flavio@mbp~> deno --help
deno 0.42.0
A secure JavaScript and TypeScript runtime

Docs: https://deno.land/std/manual.md
Modules: https://deno.land/std/ https://deno.land/x/
Bugs: https://github.com/denoland/deno/issues

To start the REPL, supply no arguments:
  deno

To execute a script:
  deno run https://deno.land/std/examples/welcome.ts
  deno https://deno.land/std/examples/welcome.ts

To evaluate code in the shell:
  deno eval "console.log(30933 + 404)"

Run 'deno help run' for 'run'-specific flags.

USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help
            Prints help information

    -L, --log-level <log-level>
            Set log level [possible values: debug, info]

    -q, --quiet
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -V, --version
            Prints version information


SUBCOMMANDS:
    bundle         Bundle module and dependencies into single file
    cache          Cache the dependencies
    completions    Generate shell completions
    doc            Show documentation for a module
    eval           Eval script
    fmt            Format source files
    help           Prints this message or the help of the given subcommand(s)
    info           Show info about cache or info related to source file
    install        Install script as an executable
    repl           Read Eval Print Loop
    run            Run a program given a filename or url to the module
    test           Run tests
    types          Print runtime TypeScript declarations
    upgrade        Upgrade deno executable to newest version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno's base directory (defaults to $HOME/.deno)
    DENO_INSTALL_ROOT    Set deno install's output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS

Команды Deno


Заметили раздел SUBCOMMANDS? Это список всех команд, которые мы можем запускать. Какие команды у нас есть?

  • bundle — собирает модуль и зависимости проекта в один файл
  • cache — кэширует зависимости
  • completions — генерирует пополнения оболочки
  • doc — показывает документацию по модулю
  • eval — используется для вычисления блока кода, например, deno eval "console.log(1 + 2)"
  • fmt — встроенное средство форматирования кода (такое как goFmt в Go)
  • help — выводит список вспомогательных команд
  • info — показывает информацию о кэше или файле
  • install — устанавливает скрипт как выполняемый
  • repl — цикл «чтение-вычисление-вывод» (по умолчанию)
  • run — запускает программу с заданным именем или URL для модуля
  • test — запускает тесты
  • types — выводит список возможностей TypeScript
  • upgrade — обновляет Deno до последней версии

Вы можете запустить deno <subcommand> help для получения информации об определенной команде, например, deno run --help.

Мы можем использовать команду deno для запуска цикла «чтение-вычисление-вывод»:



Это аналогично запуску deno repl.

Обычно, deno используется для запуска Deno-приложения, содержащегося в TypeScript-файле.

Вы можете запускать как TypeScript-файлы (.ts), так и JavaScript-файлы (.js).

Если вы не знакомы с TypeScript, не переживайте: Deno написан на TypeScript, но вы вполне можете писать свои клиентские приложения на JavaScript.

Первое приложение на Deno


Давайте создадим наше первое приложение.

Для этого нам даже не придется писать код, мы запустим его в терминале посредством URL.

Deno скачивает программу, компилирует ее и запускает:



Разумеется, я бы не советовал запускать случайный код из Интернета. В данном случае, мы запускаем его с официального сайта Deno, плюс Deno имеет песочницу, запрещающую программам делать то, что мы им прямо не разрешили.

Эта программа является очень простой и представляет из себя вызов console.log():

console.log('Welcome to Deno ') // в конце должен быть динозавр, ох уж эти эмодзи

Если вы откроете https://deno.land/std/examples/welcome.ts в браузере, то увидите следующее:



Странно, не правда ли? Вероятно, вы ожидали увидеть TypeScript-файл, а вместо этого получили веб-страницу. Дело в том, что сервер веб-сайта Deno знает, что вы используете браузер, и предоставляет вам более дружелюбную (user friendly) страницу.

Загрузите тот же URL с помощью wget, например, и получите text/plain вместо text/html:



При повторном запуске программы благодаря кэшированию перезагрузка не потребуется:



С помощью флага --reload можно выполнить принудительную перезагрузку:



deno run имеет много разных функций, которые не отображаются посредством deno --help. Для того, чтобы их увидеть, следует выполнить deno run --help:

flavio@mbp~> deno run --help
deno-run
Run a program given a filename or url to the module.

By default all programs are run in sandbox without access to disk, network or
ability to spawn subprocesses.
    deno run https://deno.land/std/examples/welcome.ts

Grant all permissions:
    deno run -A https://deno.land/std/http/file_server.ts

Grant permission to read from disk and listen to network:
    deno run --allow-read --allow-net https://deno.land/std/http/file_server.ts

Grant permission to read whitelisted files from disk:
    deno run --allow-read=/etc https://deno.land/std/http/file_server.ts

USAGE:
    deno run [OPTIONS] <SCRIPT_ARG>...

OPTIONS:
    -A, --allow-all
            Allow all permissions

        --allow-env
            Allow environment access

        --allow-hrtime
            Allow high resolution time measurement

        --allow-net=<allow-net>
            Allow network access

        --allow-plugin
            Allow loading plugins

        --allow-read=<allow-read>
            Allow file system read access

        --allow-run
            Allow running subprocesses

        --allow-write=<allow-write>
            Allow file system write access

        --cached-only
            Require that remote dependencies are already cached

        --cert <FILE>
            Load certificate authority from PEM encoded file

    -c, --config <FILE>
            Load tsconfig.json configuration file

    -h, --help
            Prints help information

        --importmap <FILE>
            UNSTABLE:
            Load import map file
            Docs: https://deno.land/std/manual.md#import-maps
            Specification: https://wicg.github.io/import-maps/
            Examples: https://github.com/WICG/import-maps#the-import-map
        --inspect=<HOST:PORT>
            activate inspector on host:port (default: 127.0.0.1:9229)

        --inspect-brk=<HOST:PORT>
            activate inspector on host:port and break at start of user script

        --lock <FILE>
            Check the specified lock file

        --lock-write
            Write lock file. Use with --lock.

    -L, --log-level <log-level>
            Set log level [possible values: debug, info]

        --no-remote
            Do not resolve remote modules

    -q, --quiet
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -r, --reload=<CACHE_BLACKLIST>
            Reload source code cache (recompile TypeScript)
            --reload
                Reload everything
            --reload=https://deno.land/std
                Reload only standard modules
            --reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts
                Reloads specific modules
        --seed <NUMBER>
            Seed Math.random()

        --unstable
            Enable unstable APIs

        --v8-flags=<v8-flags>
            Set V8 command line options. For help: --v8-flags=--help


ARGS:
    <SCRIPT_ARG>...
            script args

Примеры кода


На сайте Deno имеются и другие примеры, которые можно найти здесь.

На момент написания этой статьи в указанном репозитории можно было найти следующее:

  • cat.ts — выводит содержимое файлов, переданных в качестве аргументов
  • catj.ts — делает тоже самое, что cat.ts, но предварительно совершает некоторые манипуляции с содержимым файлов
  • chat/ — реализация чата
  • colors.ts — пример стилизации текста с помощью модулей
  • curl.ts — простая реализация curl, который выводит содержимое URL, переданного в качестве аргумента
  • echo_server.ts — TCP эхо-сервер
  • gist.ts — программа для размещения файлов в gist.github.com
  • test.ts — программа для тестирования
  • welcome.ts — программа, которую мы запускали
  • xeval.ts — позволяет запускать TypeScript, полученный из любого стандартного источника данных. deno xeval была удалена из списка официальных команд

Первое настоящее приложение на Deno


Давайте писать код.

Первое приложение, которые мы запускали с помощью deno run https://deno.land/std/examples/welcome.ts, уже было написано, так что вы не узнали ничего нового о Deno.

Начнем со стандартного примера, размещенного на сайте Deno:

import { serve } from 'https://deno.land/std/http/server.ts'
const s = serve({ port: 8000 })
console.log('http://localhost:8000/')
for await (const req of s) {
    req.respond({ body: 'Hello World\n' })
}

Здесь мы импортируем функцию serve из модуля http/server. Видите? Нам не пришлось его устанавливать, и он не хранится на нашем компьютере подобно модулям Node. В этом заключается одна из причин быстрой установки Deno.

С помощью https://deno.land/std/http/server.ts мы импортируем последнюю версию модуля. Определенную версию можно импортировать с помощью @VERSION:

import { serve } from 'https://deno.land/std@v0.42.0/http/server.ts'

Вот что из себя представляет функция serve:

/**
 * Create a HTTP server
 *
 *     import { serve } from "https://deno.land/std/http/server.ts";
 *     const body = "Hello World\n";
 *     const s = serve({ port: 8000 });
 *     for await (const req of s) {
 *       req.respond({ body });
 *     }
 */
 export function serve(addr: string | HTTPOptions): Server {
  if (typeof addr === 'string') {
    const [hostname, port] = addr.split(':')
    addr = { hostname, port: Number(port) }
  }

  const listener = listen(addr)
  return new Server(listener)
}

Далее мы вызываем функцию serve() и передаем ей объект со свойством port.

Затем мы запускаем цикл для ответа на каждый запрос от сервера:

for await (const req of s) {
  req.respond({ body: 'Hello World\n' })
}

Обратите внимание, что мы используем ключевое слово await без оборачивания кода в async функцию.

Запустим программу локально. Я использую VS Code, но вы можете использовать любой редактор.

Рекомендую установить расширение от justjavac (там есть еще одно с аналогичным названием, но оно признано устаревшим и может исчезнуть в будущем):



Расширение предоставляет несколько утилит для помощи в написании приложений на Deno.

Создадим файл app.ts и вставим в него наш код:



Запустим его с помощью deno run app.ts:



Deno загрузит все зависимости, которые нужны программе, но сначала ту, которую мы импортируем в файле.

Файл https://deno.land/std/http/server.ts имеет несколько собственных зависимостей:

import { encode } from '../encoding/utf8.ts'
import { BufReader, BufWriter } from '../io/bufio.ts'
import { assert } from '../testing/asserts.ts'
import { deferred, Deferred, MuxAsyncIterator } from '../async/mod.ts'
import {
    bodyReader,
    chunkedBodyReader,
    emptyReader,
    writeResponse,
    readRequest,
} from './_io.ts'
import Listener = Deno.Listener
import Conn = Deno.Conn
import Reader = Deno.Reader

Эти зависимости импортируются автоматически.

В конце у нас возникает проблема:



Что происходит? Мы получили ошибку отсутствия разрешения (permission denied).

Поговорим о песочнице.

Песочница


Как я упоминал ранее, Deno имеет песочницу, которая не позволяет программам делать то, на что они не получили разрешения.

Что это означает?

Как говорит Райан в своем выступлении, иногда вы хотите запустить программу на JavaScript вне браузера и при этом не хотите, чтобы эта программа имела доступ ко всему в вашей системе. Или когда речь идет о внешнем мире, использующем сеть.

Ничто не мешает Node.js-приложению получить ваш SSH-ключ или другую информацию из вашей системы и отправить ее на сервер. Вот почему мы обычно устанавливает Node-пакеты только из проверенных источников. Но как нам узнать о том, что один из используемых нами проектов был взломан?

Deno имитирует систему разрешений, используемую браузером. JavaScript-код, запущенный в браузере, не может ничего делать с вашей системой, пока вы ему этого прямо не разрешите.

Возвращаясь к Deno, если программе нужен доступ к сети, мы должны дать ей на это разрешение.

Это делается с помощью флага --allow-net:

deno run --allow-net app.ts



Теперь сервер запущен на порту 8000:



Другие флаги:

  • --allow-env — разрешает доступ к переменным среды
  • --allow-hrtime — разрешает измерение с высоким разрешением
  • --allow-net=<allow-net> — разрешает доступ к сети
  • --allow-plugin — разрешает загрузку плагинов
  • --allow-read=<allow-read> — разрешает чтение файлов
  • --allow-run — разрешает запуск подпроцессов
  • --allow-write=<allow-write> — разрешает запись файлов
  • --allow-all — предоставляет все разрешения (аналогично -A)

Разрешения для net, read и write могут быть частичными. Например, мы можем разрешить чтение только тех файлов, которые находятся в определенной директории: --allow-read=/dev.

Форматирование кода


Одной из вещей, которые мне нравятся в Go, является команда gofmt. Весь код на Go выглядит одинаково. Все используют gofmt.

JavaScript-разработчики обычно используют Prettier, и deno fmt на самом деле также его использует под «катом».

Допустим, у вас есть такой плохо отформатированный файл:



Вы запускаете deno fmt app.ts и происходит автоматическое форматирование с расстановкой пропущенных точек с запятой:



Стандартная библиотека


Стандартная библиотека Deno является довольно обширной, несмотря на возраст проекта.

Она включает в себя следующее:

  • archieve — утилиты для архивации
  • async — утилиты для работы с асинхронным кодом
  • bytes — вспомогательные функции для разделения байтов
  • datetime — разбор дат/времени
  • encoding — кодирование/декодирование в разные форматы
  • flags — разбор флагов командной строки
  • fmt — формирование и отображение
  • fs — прикладной интерфейс для работы с файловой системой
  • hash — библиотека шифрования
  • http — HTTP-сервер
  • io — библиотека операций ввода/вывода
  • log — утилиты для логгирования
  • mime — поддержка смешанных данных
  • node — слой обратной совместимости с Node
  • path — работа с путями
  • ws — веб-сокеты

Еще один пример


Давайте разберем еще один официальный пример — cat.ts:

const filenames = Deno.args
for (const filename of filenames) {
    const file = await Deno.open(filename)
    await Deno.copy(file, Deno.stdout)
    file.close()
}

Мы присваиваем переменной filenames содержимое Deno.args, которая является переменной, содержащей аргументы, переданные с помощью командной строки.

Мы перебираем их, и для каждого сначала используем Deno.open() для открытия файла, а затем Deno.copy() для копирования содержимого в Deno.stdout. Наконец, мы закрываем файл.

Если вы запустите:

deno run https://deno.land/std/examples/cat.ts

Программа загрузится и скомпилируется, но ничего не произойдет, поскольку мы не передали ни одного аргумента.

Теперь попробуем так:

deno run https://deno.land/std/examples/cat.ts app.ts

Получаем ошибку разрешения:



Deno не имеет доступа к системе по умолчанию. Предоставим ему такое разрешение с помощью --allow-read:

deno run --allow-read=./ https://deno.land/std/examples/cat.ts app.ts



Существует ли Express/Hapi/Koa/* для Deno?


Да, конечно. Взгляните на следующие проекты:


Пример: используем Oak для создания REST API


Я собираюсь создать REST API с помощью Oak. Oak интересен тем, что вдохновлен Koa, популярным промежуточным ПО для NOde.js, и имеет схожий с ним синтаксис.

Наш прикладной интерфейс будет очень простым.

Наш сервер будет хранить в памяти список собак, их имена и возраст.

Мы хотим получить следующий функционал:

  • добавлять новых собак в список
  • получать список всех собак
  • получать информацию об определенной собаке
  • удалять собаку из списка
  • обновлять возраст собаки

Мы будем писать код на Typescript, но ничто не мешает вам делать это на JavaScript — просто не указывайте типы данных.

Создаем файл app.ts.

Начнем с импорта объектов Application и Router из Oak:

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

Получаем переменные среды PORT и HOST:

const env = Deno.env.toObject()
const PORT = env.PORT || 4000
const HOST = env.HOST || '127.0.0.1'

По умолчанию наше приложение будет работать на localhost:4000.

Создаем Oak-приложение и запускаем его:

const router = new Router()

const app = new Application()

app.use(router.routes())
app.use(router.allowedMethods())

console.log(`Listening on port ${PORT}...`)

await app.listen(`${HOST}:${PORT}`)

Теперь приложение должно работать.

Проверяем:

deno run --allow-env --allow-net app.ts

Deno загружает зависимости:



И начинает прослушивать порт 4000.

При повторном запуске этап установки будет пропущен благодаря кэшированию:



Определим интерфейс для собаки, затем определим массив dog, содержащий объекты Dog:

interface Dog {
  name: string
  age: number
}

let dogs: Array<Dog> = [
  {
    name: 'Roger',
    age: 8,
  },
  {
    name: 'Syd',
    age: 7,
  },
]

Займемся реализацией API.

Все на месте. Добавим роутеру несколько функций, которые будут вызываться при обращении к опеределенной конечной точке:

const router = new Router()

router
    .get('/dogs', getDogs)
    .get('/dogs/:name', getDog)
    .post('/dogs', addDog)
    .put('/dogs/:name', updateDog)
    .delete('/dogs/:name', removeDog)

Мы определили следующее:

  • GET /dogs
  • GET /dogs/:name
  • POST /dogs
  • PUT /dogs/:name
  • DELETE /dogs/:name


Реализуем эти маршруты один за другим.

Начнем с GET /dogs, который возвращает список всех собак:

export const getDogs = ({ response }: { response: any }) => {
    response.body = dogs
}



Вот как получить определенную собаку по имени:

export const getDog = ({
  params,
  response,
}: {
    params: {
        name: string
    },
    response: any
}) => {
    const dog = dogs.filter(dog => dog.name === params.name)
    if (dog.length) {
        response.status = 200
        response.body = dog[0]
        return
    }

    response.status = 400
    response.body = { msg: `Cannot find dog ${params.name}` }
}



Вот как добавить новую собаку в список:

export const addDog = async ({
    request,
    response,
}: {
    request: any
    response: any
}) => {
    const body = await request.body()
    const dog: Dog = await body.value
    dogs.push(dog)

    response.body = { msg: 'OK' }
    response.status = 200
}



Вот как обновить возраст собаки:

export const updateDog = async ({
    params,
    request,
    response,
}: {
    params: {
        name: string
    },
    request: any
    response: any
}) => {
    const temp = dogs.filter((existingDog) => existingDog.name === params.name)
    const body = await request.body()
    const { age }: { age: number } = await body.value

    if (temp.length) {
        temp[0].age = age
        response.status = 200
        response.body = { msg: 'OK' }
        return
    }

    response.status = 400
    response.body = { msg: `Cannot find dog ${params.name}` }
}



И вот как удалить собаку из списка:

export const removeDog = ({
    params,
    response,
}: {
    params: {
        name: string
    },
    response: any
}) => {
    const lengthBefore = dogs.length
    dogs = dogs.filter((dog) => dog.name !== params.name)

    if (dogs.length === lengthBefore) {
        response.status = 400
        response.body = { msg: `Cannot find dog ${params.name}` }
        return
    }

    response.body = { msg: 'OK' }
    response.status = 200
}



Полный код приложения:

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

const env = Deno.env.toObject()
const PORT = env.PORT || 4000
const HOST = env.HOST || '127.0.0.1'

interface Dog {
  name: string
  age: number
}

let dogs: Array<Dog> = [
  {
    name: 'Roger',
    age: 8,
  },
  {
    name: 'Syd',
    age: 7,
  },
]

export const getDogs = ({ response }: { response: any }) => {
  response.body = dogs
}

export const getDog = ({
  params,
  response,
}: {
  params: {
    name: string
  },
  response: any
}) => {
  const dog = dogs.filter(dog => dog.name === params.name)
  if (dog.length) {
    response.status = 200
    response.body = dog[0]
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

export const addDog = async ({
  request,
  response,
}: {
  request: any
  response: any
}) => {
  const body = await request.body()
  const { name, age }: { name: string; age: number } = await body.value
  dogs.push({
    name: name,
    age: age,
  })

  response.body = { msg: 'OK' }
  response.status = 200
}

export const updateDog = async ({
  params,
  request,
  response,
}: {
  params: {
    name: string
  },
  request: any
  response: any
}) => {
  const temp = dogs.filter((existingDog) => existingDog.name === params.name)
  const body = await request.body()
  const { age }: { age: number } = await body.value

  if (temp.length) {
    temp[0].age = age
    response.status = 200
    response.body = { msg: 'OK' }
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

export const removeDog = ({
  params,
  response,
}: {
  params: {
    name: string
  },
  response: any
}) => {
  const lengthBefore = dogs.length
  dogs = dogs.filter(dog => dog.name !== params.name)

  if (dogs.length === lengthBefore) {
    response.status = 400
    response.body = { msg: `Cannot find dog ${params.name}` }
    return
  }

  response.body = { msg: 'OK' }
  response.status = 200
}

const router = new Router()
router
  .get('/dogs', getDogs)
  .get('/dogs/:name', getDog)
  .post('/dogs', addDog)
  .put('/dogs/:name', updateDog)
  .delete('/dogs/:name', removeDog)

const app = new Application()

app.use(router.routes())
app.use(router.allowedMethods())

console.log(`Listening on port ${PORT}...`)

await app.listen(`${HOST}:${PORT}`)

Благодарю за внимание.
Теги:
Хабы:
Всего голосов 10: ↑7 и ↓3+4
Комментарии6

Публикации

Истории

Работа

Ближайшие события