Разработка простого приложения для заметок на 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 запроса на создание заметки

Результат POST запроса на создание заметки

Взглянем на нашу таблицу:

Содержимое таблицы notes

Содержимое таблицы 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 запроса на получение всех заметок

Результат выполнения 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 запроса
Ошибочный результат выполнения 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..<tmpNotes.len:           notes[i] = tmpNotes[i]       else:         # Если размер списка изменился - полностью меняем список         notes.set(tmpNotes)     )   proc toggleNote(note: Note) {.async.} =   # Отправляем PATCH запрос   discard await fetch(fmt"{BASE}/note/{note.id}".cstring, newfetchOptions(     HttpPatch, $(%*{"completed": not note.completed})   ))   proc addNote(name: string) {.async.} =   # Отправляем POST запрос   discard await fetch(fmt"{BASE}/note".cstring, newfetchOptions(     HttpPost, $(%*{"name": name})   ))

Давайте получим список заметок при загрузке страницы:

# Сразу получаем список заметок 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..<notes.len:           tDiv(             class =               # Меняем класс в зависимости от выполненности заметки               if notes[i].completed:                 "rounded-full select-none px-6 py-2 cursor-pointer hover:scale-110 translation-all duration-300 bg-green-300"               else:                 "rounded-full select-none px-6 py-2 cursor-pointer hover:scale-110 translation-all duration-300 bg-red-300"           ):             # аналогично с эмоджи             if notes[i].completed:               "✅ "             else:               "❌ "             {notes[i].name}             @click:               # При нажатии шлем PATCH запрос и обновляем список заметок               discard toggleNote(notes[i]).then(() => (discard updateNotes()))

Наконец-то, теперь мы можем запустить?

Все верно! Самое время протестировать наше приложение! Запускаем бэкенд:

nim c -r server/src/main

И отдельно запускаем фронтенд:

cd client hpx dev --reload --port 8000
Итоговое приложение с запущенным бэкендом и фронтендом

Итоговое приложение с запущенным бэкендом и фронтендом

Вот и все! Мы написали простое веб-приложение, используя Nim и веб-фреймворк HappyX.

Полезные ссылки

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Насколько простым для понимания кажется вам HappyX?

0% Очень простой0
33.33% Простой2
33.33% Средний2
16.67% Сложный1
16.67% Невероятно сложный1

Проголосовали 6 пользователей. Воздержавшихся нет.

ссылка на оригинал статьи https://habr.com/ru/articles/825486/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *