Найшвидший спосіб створити реальний додаток з використанням Zod та TypeScript

pic

Вступ

При створенні додатків реального часу, таких як чати або інформаційні панелі, ми зазвичай розглядаємо Node.js та його численні фреймворки. У цьому посібнику я покажу вам найпродуктивніший спосіб створювати додатки реального часу за допомогою Zod і TypeScript.

Більшість з вас, можливо, подумає, що я буду говорити про новий фреймворк JavaScript, але якщо ви достатньо довго були в спільноті JavaScript, ви могли вже почути про Meteor.js.

pic

У цьому посібнику я покажу вам новий погляд на створення додатків Meteor і чому він такий продуктивний. Наприкінці у вас буде робочий чат додаток реального часу, який використовує Zod для валідацій і повну підтримку TypeScript для ваших API.

Початок роботи

Переконайтеся, що у вас встановлений meteor на вашій машині.

Якщо у вас ще немає встановленого Meteor, ви можете виконати наступну команду:

npx meteor

Це встановить інструмент командного рядка Meteor на вашу машину.

Створення проекту

meteor create

Він запитає вас про назву вашого нового додатку та яку основу ви хочете використати. Я вибрав super-chat як назву нашого додатку і стартову основу TypeScript.

pic

Далі ми можемо зайти в каталог і почати встановлювати пакети, необхідні для нашого посібника:

cd super-chat &&  
meteor npm i meteor-rpc @tanstack/react-query zod react-router-dom@6

Пакети, які ми встановлюємо, є необхідними для роботи meteor-rpc:

Ми також встановлюємо react-router-dom для маршрутизації в нашому додатку.

Налаштування нашого React додатку

Перш ніж продовжити, нам потрібно налаштувати наш React додаток. Ви можете слідувати керівництвам react-query і react-router, або ви можете вставити цей фрагмент у ваш imports/ui/App.tsx:

import React, { Suspense } from "react";  
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";  
import { BrowserRouter, Routes, Route } from "react-router-dom";const queryClient = new QueryClient();  
export const App = () => (  




 Loading...}>  
 Hello from Super-Chat!  


 }  
 />  
 Chat!} />  



);

Далі ми видаляємо Hello.tsx та Info.tsx, оскільки вони нам не потрібні.

Запуск нашого додатку

Перше, що нам потрібно зробити, це видалити весь код з main.ts, а потім створити точку входу для сервера за допомогою createModule з meteor-rpc; на цьому етапі ваш файл main.ts повинен виглядати ось так:

import { createModule } from "meteor-rpc";  
const server = createModule().build();  
export type Server = typeof server;

Колекція чату

Нам спершу потрібно створити нашу ChatCollection для зберігання повідомлень та груп чатів. Для цього ми створимо в server/chat/model.ts нашу ChatCollection.
Код повинен виглядати ось так:

import { Mongo } from "meteor/mongo";  
export interface Message {  
 text: string;  
 who: string;  
 createdAt: Date;  
}  
export interface Chat {  
 _id?: string;  
 messages: Message[];  
 createdAt: Date;  
}  
export const ChatCollection = new Mongo.Collection("chat");

Наш додаток не буде дуже складним, і це буде наша єдина колекція. Ми будемо зберігати кожне повідомлення з кожного чату, яке містить who (хто надіслав повідомлення), text (текст повідомлення) і дату створення.

Модуль чату

Тепер нам потрібно подумати, які методи чи функції потрібні нашому чату. Кожен чат-додаток має ці функції:

  • можна створювати кімнати чату;
  • можна надсилати повідомлення в кімнату чату;
  • можна бачити всі кімнати чату в реальному часі;
  • можна бачити бесіду в реальному часі;

З урахуванням цього, використаємо createModule з meteor-rpc і module.addPublication для функцій у реальному часі, а також module.addMethod для віддалених викликів.

Я створю файл TypeScript в server/chat/module.ts, який ми будемо використовувати як точку входу для наших функцій.

import { createModule } from "meteor-rpc";  
import { ChatCollection } from "./model";  
import { z } from "zod";  

export const ChatModule =  
 createModule("chat")  
 .addMethod("createRoom", z.void(), async () => {  
 return ChatCollection.insertAsync({ createdAt: new Date(), messages: [] });  
 })  
 .addMethod(  
 "sendMessage",  
 z.object({ chatId: z.string(), message: z.string(), user: z.string() }),  
 async ({ chatId, message, user }) => {  
 return ChatCollection.updateAsync(  
 { _id: chatId },  
 {  
 $push: {  
 messages: { text: message, who: user, createdAt: new Date() },  
 },  
 }  
 );  
 }  
 )  
 .addPublication("room", z.string(), (chatId) => {  
 return ChatCollection.find({ _id: chatId });  
 })  
 .addPublication("rooms", z.void(), () => {  
 return ChatCollection.find();  
 })  
 .buildSubmodule(); // це дуже важливо, не забудьте викликати цей метод

Voilá! Ми майже все налаштували на сервері. Тепер ми повинні зареєструвати цей модуль у файлі server/main.ts, і після цього можна переходити до клієнтської частини.

У файлі server/main.ts буде виглядати так, коли ми зареєструємо наш чат-модуль:

import { createModule } from "meteor-rpc";  
import { ChatModule } from "./chat/module";  

const server = createModule().addSubmodule(ChatModule).build();  
export type Server = typeof server;

І з цим наш бекенд готовий!

Клієнтська частина

Наш додаток наразі має лише imports/ui/App.tsx і не має взаємодії з сервером. Щоб змінити це, ми повинні створити точку входу для нашого API, щоб імпортувати серверні виклики API. У моєму додатку я називатиму цю точку входу client.ts, і вона буде знаходитися в imports/api/client.ts. Також ми повинні очистити нашу папку API, видаливши файл links.ts. Ось як виглядає мій server.ts:

import { createClient } from "meteor-rpc";  
import type { Server } from "/server/main";  

export const client = createClient();

Важливо імпортувати лише тип Server з сервера; якщо ви спробуєте імпортувати будь-що інше, наша збірка не вдасться, тому що Meteor захищає нас від імпорту серверних файлів і, ймовірно, витоку конфіденційних файлів.
Типи підходять, оскільки вони видаляються на етапі збірки.

Ця змінна server в межах клієнтської області — це те, що ми будемо використовувати для взаємодії з нашим сервером.

Головна сторінка

Перше, що ми повинні створити на нашій клієнтській частині, — це головну сторінку, на якій повинна бути кнопка для створення нової кімнати чату та посилання для всіх доступних кімнат чату; з урахуванням цих вимог ми повинні створити файл Main.tsx у нашій директорії imports/ui, цей файл має виглядати ось так:

import React from "react";  
import { client } from "../api/client";  
import { useNavigate } from "react-router-dom";  
export const Main = () => {  
 const navigate = useNavigate();  
 const { data: rooms } = client.chat.rooms.usePublication();  
 const createChatRoom = async () => {  
 const room = await client.chat.createTheRoom();  
 navigate(`/chat/${room}`);  
 };  

 return (  

Ласкаво просимо до чату!  
    Натисніть, щоб створити нову кімнату чату або виберіть чат із списку нижче  
Чати:  
    {rooms.length === 0 && (    У нас ще немає кімнат, чому б не створити одну?    )}    
    {rooms?.map((room) => (    
Чат кімната {room._id}    
    ))}    
    );  
};  

Тепер ми повинні оновити наш App.tsx, щоб включити сторінку Main.tsx:

 // ....                
                Loading...}>                
                }    
    />   
 // .... 

Наш додаток має виглядати ось так:

pic

Однак наша сторінка чату все ще виглядає порожньою, коли ми натискаємо, щоб приєднатися до кімнати або створити нову. Це потрібно виправити!

Сторінка чату

По-перше, ми повинні створити файл Chat.tsx в imports/ui; в цьому компоненті ми повинні використати наш параметр chatId, щоб передати його на сервер, щоб він знав, в якій кімнаті потрібно додавати повідомлення, а також у якій кімнаті ми очікуємо оновлення.

Також ми повинні створити та перевірити наші поля введення.
В кінці ми повинні мати файл, який виглядатиме ось так:

import React, { useReducer } from "react";  
import { client } from "../api/client";  
import { useNavigate, useParams } from "react-router-dom";  
export const Chat = () => {  
 let { chatId } = useParams();  
 const navigate = useNavigate();  
 const {  
 data: [chatRoom],  
 } = client.chat.room.usePublication(chatId as string);  
 const [state, dispatch] = useReducer(  
 (  
 state: { who: string; message: string },  
 action: { type: string; value: string }  
 ) => {  
 switch (action.type) {  
 case "who":  
 return { ...state, who: action.value };  
 case "message":  
 return { ...state, message: action.value };  
 default:  
 return state;  
 }  
 },  
 { who: "", message: "" }  
 );  
 const sendMessage = async () => {  
 if (state.who === "" || state.message === "") {  
 alert("Будь ласка, заповніть обидва поля");  
 return;  
 }  
 await client.chat.sendMessage({  
 chatId: chatId as string,  
 message: state.message,  
 user: state.who,  
 });  
 dispatch({ type: "message", value: "" });  
 };  
 return (  

 navigate("/")}>Повернутися на головну сторінку    
Чат в кімнаті: {chatId}
Хто:     dispatch({ type: "who", value: e.target.value })}    value={state.who}    />    
Повідомлення:     dispatch({ type: "message", value: e.target.value })}    value={state.message}    />    
Надіслати повідомлення    
Повідомлення:
    {chatRoom.messages.length === 0 && Немає повідомлень}    
    {chatRoom.messages.map((message, i) => (    
{message.who}: {message.text}    
    ))}    
    );  
};  

Це виглядає великим, але терпіти! У цьому компоненті ми слухаємо кожне повідомлення в рамках розмови за її chatId. Ми можемо викликати метод sendMessage, визначений на сервері. Також у нас є валідація для перевірки перед відправкою повідомлень.

Не забувайте додати цей компонент до нашого маршрутизатора у App.tsx.

Подивимося, як це працює на практиці:

pic

Розгортання

Тепер, коли наш додаток готовий, ми можемо його розгорнути. Все, що потрібно зробити, це виконати цю команду, і ваш додаток буде розгорнуто в Galaxy Cloud безкоштовно з включеною MongoDB:

meteor deploy .meteorapp.com - free - mongo

Висновок

Одна з найкращих функцій, яку meteor-rpc додає до наших Meteor додатків, — це структурований об'єкт для виклику; іншими словами, кожен метод, що визначений і має IntelliSense; якщо ви ще не тестували це самі, ви можете побачити це в цьому відео:

pic

Ви можете переглянути повний вихідний код тут, а якщо у вас виникли проблеми з пакетом meteor-rpc, будь ласка, дайте нам знати або в репозиторії пакету, або зв'яжіться зі мною через мій X акаунт.

Перекладено з: The fastest way of creating a real-time app with Zod and TypeScript

Leave a Reply

Your email address will not be published. Required fields are marked *