Разработка простого приложения для заметок на HappyX
Как создать веб-приложение, если вы пишите на Nim? Что такое HappyX и как можно создать на нем приложение для заметок? Обо всем этом вы узнаете в полной статье.
Обложка статьи
Здравствуйте! Меня зовут Никита, и сегодня я хочу рассказать вам о создании веб-приложений на языке программирования Nim.
Коротко о Nim
Начнем с простого. Nim — это язык программирования, который может компилироваться в C, C++, Objective C и в JavaScript. Благодаря этому мы можем писать как серверные приложения, так и клиентские.
Если у вас не установлен Nim, установите его, следуя инструкции на официальном сайте.
Что такое HappyX?
HappyX — это полнофункциональный веб-фреймворк, написанный на этом самом Nim. И исходя из вышесказанного, он позволяет разрабатывать серверные и клиентские веб-приложения. Для серверной части он компилируется в C, а для клиентской в JavaScript.
Хорошо, а дальше что?
Серверная часть
Разрабатывать мы ее будем все на том же HappyX с использованием примитивной sqlite, представленной нам библиотекой Nim.
Сперва необходимо поставить библиотеки happyx и db_connector. Делается это с помощью пакетного менеджера nimble:
nimble install happyx@#head
nimble install db_connector
После установки библиотек создаем проект с помощью happyx cli:
hpx create --name server --kind SSR --language Nim
Далее во время выполнения команды выбираем don’t use templates и создаем клиентский проект:
hpx create --name client --kind SPA --language Nim -u
Теперь переходим в /server/src/main.nim
и импортируем необходимые библиотеки:
import
happyx,
db_connector/db_sqlite
Затем создадим модели запроса для создания и редактирования наших заметок:
# Модель запроса
# Ниже мы будем обрабатывать ее в виде JSON
model CreateNote:
# единственное обязательное поле string
name: string
model EditNote:
completed: bool
Далее объявляем наше серверное приложение:
# Задаем хост и порт
serve "127.0.0.1", 5000:
# Преднастройка сервера в gcsafe (garbage collector safe) области
setup:
# Подключаем базу данных
var db = open("notes.db", "", "", "")
# Создаем таблицу, если она не существует
db.exec(sql"""
CREATE TABLE IF NOT EXISTS notes(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(50) NOT NULL,
completed INTEGER NOT NULL DEFAULT 0
);
""")
Пока что ничего сложного, верно?
Создадим POST
запрос на создание заметки:
serve "127.0.0.1", 5000:
setup:
...
# Объявляем POST запрос для создания новой заметки
post "/note[note:CreateNote]":
# Выведем название заметки
echo note.name
# Вставляем заметку в базу данных и получаем ее ID
let id = db.insertId(sql"INSERT INTO notes (name) VALUES (?)", note.name)
# Возвращаем ID заметки в ответе
return {"response": id}
И запускаем наше приложение:
nim c -r server/src/main
Пробуем сделать запрос:
Результат POST
запроса на создание заметки
Взглянем на нашу таблицу:
Содержимое таблицы notes
Отлично! Что дальше?
Теперь попробуем сделать GET
запрос на получение всех заметок:
post "/note[note:Note]":
...
# GET запрос для получения всех заметок
get "/notes":
# Список заметок:
var notes = %*[]
# Пробегаемся по всем строчкам:
for row in db.rows(sql"SELECT * FROM notes"):
# Добавляем новый элемент в список
notes.add %*{"id": row[0].parseInt, "name": row[1], "completed": row[2] != "0"}
return {"response": {
"items": notes,
"size": notes.len
}}
Взглянем на Postman:
Результат выполнения GET
запроса на получение всех заметок
Наконец, создадим PATCH
запрос:
get "/notes":
...
# PATCH запрос на изменение заметки по ее ID
patch "/note/{noteId:int}[note:EditNote]":
# Смотрим, есть ли такая заметка вообще
var row = db.getRow(sql"SELECT * FROM notes WHERE id = ?", noteId)
# заметка не найдена - возвращаем ошибку
if row[0] == "":
statusCode = 404
return {"error": "заметка с таким ID не найдена"}
# Обновляем нашу заметку
db.exec(sql"UPDATE notes SET completed = ? WHERE id = ?", note.completed, noteId)
# И возвращаем успешный статус
return {"response": "success"}
И снова смотрим Postman:
Успешный результат выполнения PATCH
запроса
Ошибочный результат выполнения PATCH
запроса
Круто! Что теперь?
На этом разработка серверной части окончена. Теперь мы перейдем к написанию клиентской части.
Клиентская часть
Здесь мы переходим в client/src/main.nim
и редактируем его. Для начала разберемся с импортом:
import
happyx,
std/strformat, # форматирование строк
std/jsfetch, # fetch
std/asyncjs, # async
std/sugar, # синтаксический сахар
std/httpcore, # HTTP методы
std/json # работа с JSON
Теперь зададим базовую ссылку на наше API:
# базовый URL для API
const BASE = "http://localhost:5000"
И объявим тип заметки для дальнейшей обработки:
# тип для заметки
type Note = object
id: cint
name: cstring
completed: bool
После чего объявим реактивные переменные:
var
# реактивный список заметок
notes = remember newSeq[Note]()
# реактивное название для новой заметки
newName = remember ""
Теперь вернемся к серверной части и вспомним, что мы там писали. Создадим три процедуры для взаимодействия с API:
proc updateNotes() {.async.} =
# Делаем запрос к серверу на получение всех заметок
await fetch(fmt"{BASE}/notes".cstring)
# Получаем JSON
.then((response: Response) => response.json())
.then(proc(data: JsObject) =
# Преобразуем JSON в список Note
var tmpNotes: seq[Note] = collect:
for i in data["response"]["items"]:
i.to(Note)
# Если размер списка не изменился - просто меняем параметры
if notes.len == tmpNotes.len:
for i in 0..
Давайте получим список заметок при загрузке страницы:
# Сразу получаем список заметок
discard updateNotes()
А теперь время верстки!
# Объявляем наше одностраничное приложение в элементе с ID app
appRoutes "app":
# Главный маршрут
"/":
tDiv(class = "flex flex-col gap-2 w-fit p-8"):
tDiv(class = "flex"):
# input для
tInput(id = "newNameChanger", class = "rounded-l-full px-6 py-2 border-2 outline-none", value = $newName):
@input:
# Меняем название заметки
newName.set($ev.target.InputElement.value)
tButton(class = "bg-green-400 hover:bg-green-500 active:bg-green-600 rounded-r-full px-4 py-2 transition-all duration-300"):
"Добавить"
@click:
# Добавляем новую заметку
discard addNote(newName).then(() => (discard updateNotes()))
newName.set("")
tDiv(class = "flex flex-col gap-2"):
# Пробегаемся по заметкам
for i in 0.. (discard updateNotes()))
Наконец-то, теперь мы можем запустить?
Все верно! Самое время протестировать наше приложение! Запускаем бэкенд:
nim c -r server/src/main
И отдельно запускаем фронтенд:
cd client
hpx dev --reload --port 8000
Итоговое приложение с запущенным бэкендом и фронтендом
Вот и все! Мы написали простое веб-приложение, используя Nim и веб-фреймворк HappyX.