Создание Небольшого Api На Deno

В этом посте я хотел бы рассказать и показать процесс создания небольшого API с помощью Deno. Deno — это новейшая среда выполнения Javascript и Typescript, разработанная создателем Node.js Райаном Далем.



Создание небольшого API на Deno

Наши цели :

  • Разработать API, который будет работать с пользовательскими данными
  • Предоставьте возможность использовать методы GET, POST, PUT и DELETE.
  • Сохраняйте и обновляйте пользовательские данные в локальный файл JSON.
  • Используйте фреймворк для ускорения разработки
Единственное, что нам нужно установить — это Deno. Deno поддерживает Typescript прямо из коробки.

Для этого примера я использовал версию Deno 0.22, и этот код может не работать в будущих версиях.

Версию установленного Deno можно узнать командой дено-версия в терминале.



Структура программы

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

handlers middlewares models services config.ts index.ts routing.ts

Как вы, возможно, заметили, это похоже на небольшое веб-приложение на Node.js.
  • обработчики - содержит обработчики маршрутов
  • промежуточное ПО — содержит функции, которые будут запускаться при каждом запросе
  • модели — содержит обозначение моделей, в нашем случае это только пользовательский интерфейс
  • услуги — содержит. услуги!
  • config.ts - файл конфигурации приложения
  • index.ts — точка входа для нашего приложения
  • маршрутизация.

    ts - содержит маршруты API



Выбор фреймворка

Существует множество отличных фреймворков для Node.js. Один из самых популярных – Выражать .

Существует также современная версия Экспресса — Коа .

К сожалению, Deno не поддерживает библиотеки Node.js и выбора гораздо меньше, но есть фреймворк для Deno, основанный на Koa — Дуб .

Мы будем использовать его для нашего небольшого проекта.

Если вы никогда не использовали Koa, не волнуйтесь, он во многом похож на Express.

Создание точки входа приложения

index.ts

import { Application } from " https://deno.land/x/oak/mod.ts "; import { APP_HOST, APP_PORT } from ".

/config.ts"; import router from ".

/routing.ts"; import notFound from ".

/handlers/notFound.ts"; import errorMiddleware from ".

/middlewares/error.ts"; const app = new Application(); app.use(errorMiddleware); app.use(router.routes()); app.use(router.allowedMethods()); app.use(notFound); console.log(`Listening on ${APP_PORT}.

`); await app.listen(`${APP_HOST}:${APP_PORT}`);

В первой линии мы используем одну из основных возможностей Deno — импорт модулей напрямую из Интернета.

После этого нет ничего необычного: создаем приложение, добавляем промежуточное ПО, маршруты и, наконец, запускаем сервер.

Все то же самое, что и при использовании Express или Koa.

Создание конфигурации

config.ts

const env = Deno.env(); export const APP_HOST = env.APP_HOST || "127.0.0.1"; export const APP_PORT = env.APP_PORT || 4000; export const DB_PATH = env.DB_PATH || ".

/db/users.json";

Наш файл конфигурации.

Настройки переносятся из среды запуска, но мы также добавим значения по умолчанию.

Функция Дено.

env() является аналогом процесс.

env в Node.js.

Добавление модели пользователя

модели/user.ts

export interface User { id: string; name: string; role: string; jiraAdmin: boolean; added: Date; }



Создание файла с маршрутами

маршрутизация.

ts

import { Router } from " https://deno.land/x/oak/mod.ts "; import getUsers from ".

/handlers/getUsers.ts"; import getUserDetails from ".

/handlers/getUserDetails.ts"; import createUser from ".

/handlers/createUser.ts"; import updateUser from ".

/handlers/updateUser.ts"; import deleteUser from ".

/handlers/deleteUser.ts"; const router = new Router(); router .

get("/users", getUsers) .

get("/users/:id", getUserDetails) .

post("/users", createUser) .

put("/users/:id", updateUser) .

delete("/users/:id", deleteUser); export default router;

Опять же, ничего необычного.

Мы создали маршрутизатор и добавили к нему несколько маршрутов.

Похоже, вы скопировали код из приложения Express, не так ли?

Обработка событий для маршрутов

обработчики/getUsers.ts

import { getUsers } from ".

/services/users.ts"; export default async ({ response }) => { response.body = await getUsers(); };

Возвращает всех пользователей.

Если вы никогда не использовали Koa, позвольте мне объяснить.

Объект ответ является аналогом рез в Экспрессе.

Объект рез в Express есть несколько методов, таких как JSON или отправлять , которые используются для отправки ответа.

В Oak и Koa нам нужно установить значение, которое мы хотим вернуть свойству.

ответ.тело .

обработчики/getUserDetails.ts

import { getUser } from ".

/services/users.ts"; export default async ({ params, response }) => { const userId = params.id; if (!userId) { response.status = 400; response.body = { msg: "Invalid user id" }; return; } const foundUser = await getUser(userId); if (!foundUser) { response.status = 404; response.body = { msg: `User with ID ${userId} not found` }; return; } response.body = foundUser; };

Здесь тоже все легко.

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

обработчики/createUser.ts

import { createUser } from ".

/services/users.ts"; export default async ({ request, response }) => { if (!request.hasBody) { response.status = 400; response.body = { msg: "Invalid user data" }; return; } const { value: { name, role, jiraAdmin } } = await request.body(); if (!name || !role) { response.status = 422; response.body = { msg: "Incorrect user data. Name and role are required" }; return; } const userId = await createUser({ name, role, jiraAdmin }); response.body = { msg: "User created", userId }; };

Этот обработчик отвечает за создание пользователя.

обработчики/updateUser.ts

import { updateUser } from ".

/services/users.ts"; export default async ({ params, request, response }) => { const userId = params.id; if (!userId) { response.status = 400; response.body = { msg: "Invalid user id" }; return; } if (!request.hasBody) { response.status = 400; response.body = { msg: "Invalid user data" }; return; } const { value: { name, role, jiraAdmin } } = await request.body(); await updateUser(userId, { name, role, jiraAdmin }); response.body = { msg: "User updated" }; };

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

обработчики/deleteUser.ts

import { deleteUser, getUser } from ".

/services/users.ts"; export default async ({ params, response }) => { const userId = params.id; if (!userId) { response.status = 400; response.body = { msg: "Invalid user id" }; return; } const foundUser = await getUser(userId); if (!foundUser) { response.status = 404; response.body = { msg: `User with ID ${userId} not found` }; return; } await deleteUser(userId); response.body = { msg: "User deleted" }; };

Ответственный за удаление пользователя.

Также желательно обрабатывать запросы по несуществующим маршрутам и возвращать сообщение об ошибке.

обработчики/notFound.ts

export default ({ response }) => { response.status = 404; response.body = { msg: "Not Found" }; };



Добавление услуг

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

услуги/createId.ts

import { v4 as uuid } from " https://deno.land/std/uuid/mod.ts "; export default () => uuid.generate();

Каждый новый пользователь получит уникальный идентификатор.

Давайте воспользуемся модулем uuid из стандартной библиотеки Deno для генерации случайного числа.

услуги/db.ts

import { DB_PATH } from ".

/config.ts"; import { User } from ".

/models/user.ts"; export const fetchData = async (): Promise<User[]> => { const data = await Deno.readFile(DB_PATH); const decoder = new TextDecoder(); const decodedData = decoder.decode(data); return JSON.parse(decodedData); }; export const persistData = async (data): Promise<void> => { const encoder = new TextEncoder(); await Deno.writeFile(DB_PATH, encoder.encode(JSON.stringify(data))); };

Этот сервис поможет взаимодействовать с JSON-файлом, в котором будут храниться данные пользователя.

Чтобы получить всех пользователей, прочитайте содержимое файла.

Функция readFile возвращает объект типа Uint8Array , который необходимо преобразовать в тип перед вводом в файл JSON. Нить .

И, наконец, основной сервис для работы с пользовательскими данными.

услуги/users.ts

import { fetchData, persistData } from ".

/db.ts"; import { User } from ".

/models/user.ts"; import createId from ".

/services/createId.ts"; type UserData = Pick<User, "name" | "role" | "jiraAdmin">; export const getUsers = async (): Promise<User[]> => { const users = await fetchData(); // sort by name return users.sort((a, b) => a.name.localeCompare(b.name)); }; export const getUser = async (userId: string): Promise<User | undefined> => { const users = await fetchData(); return users.find(({ id }) => id === userId); }; export const createUser = async (userData: UserData): Promise<string> => { const users = await fetchData(); const newUser: User = { id: createId(), name: String(userData.name), role: String(userData.role), jiraAdmin: "jiraAdmin" in userData ? Boolean(userData.jiraAdmin) : false, added: new Date() }; await persistData([.

users, newUser]); return newUser.id; }; export const updateUser = async ( userId: string, userData: UserData ): Promise<void> => { const user = await getUser(userId); if (!user) { throw new Error("User not found"); } const updatedUser = { .

user, name: userData.name !== undefined ? String(userData.name) : user.name, role: userData.role !== undefined ? String(userData.role) : user.role, jiraAdmin: userData.jiraAdmin !== undefined ? Boolean(userData.jiraAdmin) : user.jiraAdmin }; const users = await fetchData(); const filteredUsers = users.filter(user => user.id !== userId); persistData([.

filteredUsers, updatedUser]); }; export const deleteUser = async (userId: string): Promise<void> => { const users = await getUsers(); const filteredUsers = users.filter(user => user.id !== userId); persistData(filteredUsers); };

Здесь много кода, но это чистый Typescript.

Обработка ошибок

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

Чтобы избежать этого сценария, вы можете использовать конструкцию try/catch в каждом обработчике.

Но есть более элегантное решение — добавлять промежуточное программное обеспечение перед каждым маршрутом и предотвращать все возможные непредвиденные ошибки.

промежуточное программное обеспечение/error.ts

export default async ({ response }, next) => { try { await next(); } catch (err) { response.status = 500; response.body = { msg: err.message }; } };

Прежде чем запустить саму программу, нам необходимо добавить данные запуска.

БД/users.json

[ { "id": "1", "name": "Daniel", "role": "Software Architect", "jiraAdmin": true, "added": "2017-10-15" }, { "id": "2", "name": "Markus", "role": "Frontend Engineer", "jiraAdmin": false, "added": "2018-09-01" } ]

Вот и все! Теперь мы можем попробовать запустить наше приложение:

deno -A index.ts

Флаг «А» указывает на то, что программе не требуется предоставлять какие-либо особые разрешения.

Имейте в виду, что этот флаг небезопасно использовать в производстве.

Скорее всего, вы увидите множество строк с загрузкой (Download) и компиляцией (Compile).

В итоге должна появиться заветная строка:

Listening on 4000



Давайте подведем итоги

Какие инструменты мы использовали?
  1. Глобальный объект Дено для чтения/записи файлов
  2. uuid из стандартной библиотеки Deno для создания уникального идентификатора
  3. дуб — сторонний фреймворк, вдохновленный Koa для Node.js.
  4. Объекты Pure Typescript, TextEncode или JSON, включенные в Javascript


В чем отличие от Node.js?

  • Нет необходимости устанавливать и настраивать компилятор Typescript или другие инструменты, такие как ts-node. Вы можете просто запустить программу командой дено index.ts
  • Включайте все сторонние модули непосредственно в код без необходимости предварительной установки.

  • Отсутствуют package.json и package-lock.json.
  • Отсутствие node_modules в корневом каталоге нашей программы.

    Все скачанные файлы находятся в глобальном кеше.

При необходимости вы можете найти исходный код по адресу связь .

Теги: #api #JavaScript #node.js #typescript #Deno

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.