Node Master
1.02K subscribers
24 photos
2 files
156 links
Group Chat: @nodemastergp
Admin: @napoleon_n1
Download Telegram
اگر درحال یادگیری #NestJS هستید و یا مدت زیادی هست با این framework محبوب کار میکنید به این نکته حتما توجه کنید.
1. به هیچ عنوان از Nest CLI برای ایجاد یک پروژه جدید استفاده نکنید.
2. حداقل اگر استفاده میکنید سعی کنید dep های اضافه رو پاک کنید.

درمورد نکته اول یک اتفاق فاجعه بار درصورت رعایت نکردن میتونه رخ بده. اگر با Nest CLI پروژه ایجاد کنید و بعد به tsconfig یک نگاهی بندازید همچین خطی رو میبینید.

{ "strictNullChecks": false }

همین یک خط config توانایی این رو داره که پروژه رو تبدیل کنه به میدان مین جنگی و گوشی شما یکهویی ساعت ۱ شب زنگ میخوره کارفرما به شما میگه رو production مشکل داریم و بعد از چک کردن log ها متوجه Null pointer های زیبا خواهید شد.
قبلا درمورد این موضوع یعنی Null References: The Billion Dollar Mistake در این پست مفصل صحبت کردیم.
https://t.iss.one/NodeMaster/84

این نکته خیلی میتونه ترسناک باشه به این دلیل که اگر کسی تازه داره روی کد onboard میشه اگر معمولا فرض بر این داره که #TypeScript کمک به Null safe بودن داره و نکته جذاب اینجا هست که ترکیب false بودن این flag با همچین تصور پیشفرضی به زیبایی میتونه جهنم باشه ( به قول خارجی ها road to disaster ). چندتا config دیگ هم مربوط به safety به صورت پیش فرض در این حالت false هستن که هیچ کدوم به اندازه این ترسناک نیستن.
در ادامه اگر روی یک code base بزرگ باشید و این flag مهم از قبل false بوده بعد از true کردن این flag پروژه شما در کسری از ثانیه به رنگ خون در میاد و فرایند refactor کردن در این سناریو فرایندی بسیار هزینه بر هست. پس بهتره از همون اول پروژه این رو درنظر بگیرید.

درمورد نکته دوم این که به صورت پیش فرض #NestJS یک سری Dep به پروژه از این روش اضافه میکنه که ممکنه اصلا نیازی بهش نداشته باشید و خب این یک bad practise محسوب میشه ( YAGNI Principle ). این حالت زمان build time هم الکی اضاف میکنه و در پروژه بزرگ میتونه آزار دهنده باشه. موضوع بعد در این مورد این که بعضی از package هایی که وجود دارن در package.json آپدیت نیستن و خیلی سریعتر از زمان معمول ممکنه نیاز به آپدیت کردن پروژه داشته باشید. با نصب کردن dep ها به صورت دستی حداقل از این موضوع میتونید اطمینان پیدا کنید که از جدیدترین ورژن پکیج ها دارید استفاده میکنید.

#Tip #NodeJS
👍11
Node Master
اگر درحال یادگیری #NestJS هستید و یا مدت زیادی هست با این framework محبوب کار میکنید به این نکته حتما توجه کنید. 1. به هیچ عنوان از Nest CLI برای ایجاد یک پروژه جدید استفاده نکنید. 2. حداقل اگر استفاده میکنید سعی کنید dep های اضافه رو پاک کنید. درمورد نکته…
ترسناک تر از Dynamic Type بودن #JavaScript در حقیقت پروژه #TypeScript هست که به تو توهم Type safe بودن بده.
هروقت پروژه #TypeScript دیدین اول بیاین این قسمت رو چک کنید که مثل عکس بالا strict باشه و هیچ کدوم از flag های زیرش false نباشه.
اگر در موقع کار با #TypeScript مجبور شدین اینجا فلگی رو false کنید این برداشت رو میتونید کنید که #TypeScript بلد نیستید!
👍19
هم اکنون با این ابزار جدید میتونید #TypeScript رو به #Lua تبدیل کنید.
اگر براتون سوال هست که کجا میتونه کاربرد داشته باشه این میتونه باشه که با #Lua میتونید به عنوان مثال برای #Redis اسکریپت بنویسید و حالا میتونید این کار رو بدون یاد گرفتن #Lua و با نوشتن #TypeScript و گرفتن خروجی #Lua انجام بدید. هرچند من شخصا طرفدار این موضوع نیستم ولی جالبه.

https://typescripttolua.github.io/

#NodeWeekly
👍4
به این خط ساده و کوچک دقت کنید، توانایی این رو داره هرپروژه #FrontEnd و #BackEnd که با #NodeJS توسعه داده شده رو تبدیل به جهنم کنه.
throw 1;

اگر در #TypeScript براتون سوال پیش اومده که چرا در try catch statement تایپ err نامشخص هست، دقیقا بخاطر این موضوع هست. به این دلیل که هر نوع Object رو میشه throw کرد (این خیلی ترسناکه) و چون #JavaScript مفهومی به اسم Compile Time Error نداره و با توجه به طبیعت Dynamic و البته مفسری که #JavaScript داره منطقی هست که شما وقتی try catch استفاده میکنی type برای error تا زمان runtime مشخص نباشه. با مثال پایین بیشتر به عمق مسئله پی میبرید.
try {
throw { hoo: "Pop up from stack , NodeMaster" };
} catch (err /* type => unknown */) {
console.log(err);
}

معمولا بعضیا با استفاده از type cast تایپ error رو تبدیل به Error میکنن مثل کد زیر که این به نوع خودش میتونه یک فاجعه دیگه باشه ( آخه نابغه اگر تایپ مشخص بود که Error هست #TypeScript همون اول خودش Error میزاشت و نیاز به Type Cast نبود دیگه)
try {
throw { hoo: "Pop up from stack , NodeMaster" };
} catch (err) {
console.log((err as Error).message); // WTFFFFFFFFF !!!!
}

خب حالا سناریو های بد رو دیدم چطور جلوگیری کنیم؟ در پروژه های #FontEnd تنها روش خیلی خوبی که وجود داره استفاده از instanceof هست در #NodeJS برای #BackEnd هم میشه از این روش استفاده کرد ولی یک تکنیک دیگه وجود داره که اون بیشتر برای سناریوهای خیلی خاص #NodeJS هم کاربردی هست.
try {
throw { hoo: "Pop up from stack , NodeMaster" };
} catch (err) {
if (err instanceof Error) {
console.error("it's valid error object", err);
} else {
console.error("WHAT THE F IS THIS ???", err);
}
}

برای پروژه های #NodeJS در std مربوط به Node یک فانکشن کمکی هست به اسم isNativeError که همین کار instanceof رو میکنه. حالا سوال پیش میاد که چرا باید از این استفاده کنیم؟ در حالت هایی به عنوان مثال استفاده از "node:vm" و ایجاد یک context جدید میتونه دردسر ساز بشه و instanceof نتونه تشخیص بده Error رو که نتایجش هم مشخصه. نکات دیگه ای هم وجود داره که پیشنهاد میکنم داکیومنت مربوط به این بخش رو بخونید کامل با مثال ساده توضیح داده.
import { types } from "node:util";
try {
throw { hoo: "Pop up from stack , NodeMaster" };
} catch (err) {
if (types.isNativeError(err)) {
console.error("it's valid error object", err);
} else {
console.error("WHAT THE F IS THIS ???", err);
}
}

حالا یک سوال دیگ شاید براتون پیش بیاد که چرا اینقدر بدبینانه به مسئله نگاه میکنیم؟
تو دنیایی که #NPM وجود داره اعتماد به پکیج های 3RD Party گاهی اوقات میتونه گرون تموم بشه. شاید اون پکیج که استفاده میکنید این داستان رو رعایت نکرده باشه. پس بهتره ما دقت کنیم تا از runtime ارور های نصف شب جلوگیری کنیم و با آسوده بخوابیم.
👍16
یک Pattern وجود داره که خیلی درموردش صحبت نمیشه در اکوسیستم #JavaScript چه پروژه های #FrontEnd و چه #Backend که با #NodeJS و بقیه runtime ها توسعه داده شده باشن. این پترن خیلی ساده هست. استفاده نکردن ( یا حداقل استفاده ) از Anonymous function ها میباشد.

حالا سوال پیش میاد چرا از این پترن باید استفاده کنیم؟
وقتی دارید برنامه توسعه میدین و به نوعی چه توسط خودتون یا Framework که استفاده میکنید با فرایند IOC درگیر هستید مثل #React یا حتی #Express و ... اگر سایز پروژه بزرگ باشه برای پیدا کردن Bug ها به هیچ عنوان تکنیک console.log راه بهینه ای نیست. و معمولا از ابزار ها و تکنیک های مختلف در کنار هم استفاده میشن تا این فرایند راحت تر باشه. در کنار این مفاهیم لاگ کردن Error ها و stack trace مربوط بهشون خیلی مهم هست و خیلی کمک میکنه تا scope مربوط به به باگ رو کوچک تر کنیم و سریعتر بتونیم باگ رو پیدا کنیم.

با استفاده زیاد از Anonymous function ها که معمولا بیشتر در هنگام arg برای پاس دادن به عنوان callback استفاده میشن، stack trace شکل خوبی نخواهد داشت و اگر زیاد استفاده بشه حتی stack trace میتونه کاملا بی مصرف بشه و با این کار یکی از عناصر خیلی مهم برای debug کردن رو از دست میدیم. به مثال زیر توجه کنید.
function iocContainer(cb) {
cb()
}
iocContainer(() => { throw new Error("ugly stack trace") })

    at /home/imanhpr/codes/main.js:6:28
at iocContainer (/home/imanhpr/codes/main.js:2:5)
at Object.<anonymous> (/home/imanhpr/codes/main.js:6:1)

اینجا سایز پروژه کوچک هست و راحت میشه Error رو پیدا کرد چون جزیات زیادی در stack وجود نداره و کد framework هم در نظر گرفته نشده. اما توجه داشته باشید که اون خط آخر یعنی Object.anonymous در شرایط فشار باگ روی production خیلی میتونه ترسناک باشه.یک نکته که حتی میتونه وحشتناک تر کنه داستان رو پروژه #TypeScript بدون source map و اگر به این ترکیب esbuild هم اضافه بشه جای صحبتی اصلا باقی نمیمونه.

حالا با یک مثال واقعی تر و خیلی ساده این موضوع رو برسی میکنیم
const express = require("express")

const app = express()

app.get("/", (req, res) => {
throw new Error("ugly error")
})

app.listen(3000)

Error: ugly error
at /home/imanhpr/codes/main.js:6:11
at Layer.handle [as handle_request] (/home/imanhpr/codes/node_modules/express/lib/router/layer.js:95:5)
at next (/home/imanhpr/codes/node_modules/express/lib/router/route.js:149:13)
at Route.dispatch (/home/imanhpr/codes/node_modules/express/lib/router/route.js:119:3)
at Layer.handle [as handle_request] (/home/imanhpr/codes/node_modules/express/lib/router/layer.js:95:5)
at /home/imanhpr/codes/node_modules/express/lib/router/index.js:284:15
at Function.process_params (/home/imanhpr/codes/node_modules/express/lib/router/index.js:346:12)
at next (/home/imanhpr/codes/node_modules/express/lib/router/index.js:280:10)
at expressInit (/home/imanhpr/codes/node_modules/express/lib/middleware/init.js:40:5)
at Layer.handle [as handle_request] (/home/imanhpr/codes/node_modules/express/lib/router/layer.js:95:5)

این مثال کیلومتر ها با پیچدگی پروژه های production فاصله داره اما همین کد ساده نشون میده چقدر توجه به این نکته میتونه ساعت ها از وقت شما نجات بده و کار کم استرس
تری هم تجربه کنید. مشکل دقیقا خط اول stack trace هست که هیچی ازش وجود نداره ( در این مثال خوش شانسیم که خطی که این اتفاق افتاده دقیقا اشاره داره به همون callback ما ولی همیشه اینطور نیست )

برای داشتن زندگی شیرین تر کافیه کمتر از Anonymous function ها چه به شکل arrow و چه با function keyword استفاده کنید. و برای function ها نام مناسب انتخاب کنید. مثل سناریو پایین که rootHandler رو داریم.

app.get("/", function rootHandler(req, res) {
throw new Error("ugly error")
})
// OR
const rootHandler = (req, res) => {
throw new Error("ugly error")

}
app.get("/", rootHandler)

Error: ugly error
at rootHandler (/home/imanhpr/codes/main.js:6:11)
at Layer.handle [as handle_request] (/home/imanhpr/codes/node_modules/express/lib/router/layer.js:95:5)

حالا چند نکته باید در نظر بگیرید.
- تفاوت arrow function و function keyword رو بدونید گاهی ممکنه دردسر ساز بشه.
- اگر از #TypeScript استفاده میکنید از اهمیت source map هرگز غافل نشید.

#Tip
👍15
نسخه 20.16 LTS مربوط به #NodeJS خیلی وقته اومده و خب چون مدتی فعال نبودیم. از دست ما در رفته نگاهی بهش بندازیم.
یک فانکشن کمکی به این نسخه اضافه شده و از این زاویه جالب هست که یک حرکت eco system رو نشون میده که برای ما developer ها در در طولانی مدت میتونه خوب باشه. این که در آینده کدهای بیشتری ببینیم که به runtime خاصی وابسته نباشن و فریم ورک های جدید تر روی هر runtime به صورت native اجرا بشه.

فانکشن کمکی جدید process.getBuiltinModule
این فانکشن به ما کمک میکنه که در runtime هر ماژولی رو از std مربوط به #NodeJS به راحتی import کنیم. حالا سوال پیش میاد که چرا از خود ES import استفاده نمیکنیم. به این دلیل که اگر یک کد داشته باشیم که به #Deno وابستگی داره حالا اگر بخواهیم اون کد رو با #NodeJS ران کنیم بره و از std مربوط به Node استفاده کنه و نه Deno. بزارید با مثال این رو ببینیم. نمونه ای خوب و ساده از Factory pattern و Polymorphism هم میشه دید. اگر تازه کارتر هستید و در حال یادگیری به این مثال بیشتر دقت کنید خیلی بهتون کمک خواهد کرد.
class DenoReader {
constructor() {
this.decoder = new TextDecoder();
}
readFile(fileName) {
const byteArray = Deno.readFileSync(fileName);
const result = this.decoder.decode(byteArray);
return result;
}
}
class NodeReader {
constructor() {
this.fs = globalThis.process.getBuiltinModule("fs");
}
readFile(fileName) {
const result = this.fs.readFileSync(fileName).toString("utf-8");
return result;
}
}

function getReader() {
switch (true) {
case globalThis.Deno !== undefined:
return new DenoReader();
case globalThis.process.getBuiltinModule !== undefined:
return new NodeReader();
default:
throw new Error("No Reader available");
}
}

const nodeReader = getReader();
const nodeResult = nodeReader.readFile("my-text.txt");
console.log(nodeResult);

هدف خوندن یک فایل از روی disk هست و البته که این کد برای runtime های #Deno و #NodeJS از Native API مربوط به هر runtime استفاده کنه. باتوجه به این که این runtime ها هرکدوم دنیای خودشون رو دارن و API های خاص خودشون. اول باید یک API مشترک برای این دوتا بسازیم که بتونیم به هدفمون برسیم. اینجا Factory pattern به کمک ما میاد که بهمون اجازه میده با استفاده از type switch تصمیم بگیریم که روی کدام runtime هستیم و با توجه به اون runtime کلاس reader مربوط بهش رو بهمون میده و البته با استفاده از Duck typing در #JavaScript یا با استفاده از interface ها در #TypeScript یک interface یکسان برای خواندن فایل با دو implemetion متفاوت باید ایجاد کنیم که در بالا معادل NodeReader و DenoReader هست که readFile میشه interface یکسان بین این دو.
نکته قابل توجه این که چون اینجا مشخص نیست کدوم runtime رو قراره استفاده کنیم، استفاده از top-level import به صورت کلی کنسل هست به این دلیل که اگر کد زیر بزاریم و برنامه با Deno ران کنیم کلا هدفمون میره تو دیوار.
import fs from "node:fs";

یک سوال دیگ که پیش میاد دقیقا همین ویژگی رو ما میتونیم با استفاده از dynamic import داشته باشیم چرا از اون استفاده نکنیم؟ در جواب این موضوع مشکل خیلی خاصی به وجود نمیاد. مزیت این روش نسبت به dynamic import این هست که این روش به صورت sync فرایند import رو انجام میده درصورتی که حاصل dynamic imprort یک Promise هست که اگر ESM باشید با top-level await میتونید تمیز حلش کنید. اما اگر روی CJS باشید کثیف کاری خواهید داشت در نتیجه در این سناریو این روش جدید منطقی تر به نظر میرسه.

قبلا هم درمورد Duck typing مفصل صحبت کرده بودیم که اینجا میتونید ببینید اگر دوست داشتید.
https://t.iss.one/NodeMaster/136
👍11
وقتی در حال توسعه با #NodeJS هستید یکی از مشکلاتی که در حال حاظر وجود داره استفاده از #TypeScript در هنگام توسعه میباشد. باتوجه به آپدیت های اخیر #NodeJS حرکت هایی جهت پشتیبانی راحت #TypeScript در #NodeJS با اضافه کردن فلگ های --experimental-transform-types و --experimental-strip-types تغییراتی داشته ولی همچنان تا Production ready بودن خیلی فاصله داره. گزینه های که میتونه بهتون در این مورد کمک کنه استفاده از ts-node و tsx هست.
در یک سال گذشته شخصا از هردو در پروژه های مختلف استفاده کردم. با توجه به آزمون و خطایی که من کردم در حال حاظر tsx خیلی خیلی بهتر از ts-node هست و کارتون رو به خوبی راه میندازه.
https://tsx.is/
👍10
یک بخش جدید به داکیومنت #NodeJS از ورژن 22 اضافه شده برای TypeScript.
خودش پیشنهاد میکنه از tsx استفاده کنیم برای NodeJS.

به صورت کلی فاصله زیادی داریم تا #NodeJS هم مثل #Bun و #Deno به صورت کامل و Stable از #TypeScript ساپورت کنه. داخل این داکیومنت در این مورد توضیحات مربوطه داده شده.
درکل برای پروژه Production درکل tsx یا ts-node گزینه منطقی تری هست.
https://nodejs.org/docs/latest-v22.x/api/typescript.html
#Update
👍11
درود دوستان ارادتتتت.
خیلی وقت بود میخواستم این ویدیو رو بگیرم. روزی که من #Python گذاشتم کنار و حرفه ای شروع به کد زدن #JavaScript و #NodeJS کردم همیشه هروقت باکسی بحث برنامه نویسی میشد من این رو میگفتم که جای یک چیزی مثل Context Manager مثل پایتون در اکوسیستم #JavaScript واقعا خالی هست. وقتی #TypeScript ورژن 5.2 منتشر شد و این syntax رو برای بار اول دیدم واقعا خوشحال شدم
async function main() {
using resource1 = getResource()
await using resource2 = await getResource()
}

در این ویدیو به Explicit Resource Management در زبان های برنامه نویسی #python , #golang و #cpp میکنیم. با یک پترن خیلی قدیمی به اسم RAII پترن آشنا میشیم و در نهایت میرسیم به ارتباط RAII پترن در C++ در #Typescript .
این ویدیو قرار هست در دو پارت منتشر بشه و پارت دوم رو کامل درمورد مثال های عملی #Typescript گذاشتیم و تمرکز ویدیو اول هم بیشتر نگاه به زبان های دیگر و برسی این موضوع که چرا به "using" نیاز داریم.

https://youtu.be/0CG_WA6Yu9o?si=QYunevpiEyWp6gWW
👍13
درود دوستان.
قبلا که #python کد میزدم یک پکیج داشت به اسم isort که import ها رو خیلی تر تمیز sort میکرد. همیشه دنبال یک پکیج خوب بودم برای #typescript که همچین کاری رو انجام بده تا بلاخره دیروز یک پکیح خوب پیدا کردم که میتونید به عنوان config prettier استفاده کنید.
البته package های دیگ هم هستن ولی این خیلی برای من راحت تر بود. فکر کنم eslint هم همچین چیزی داشته باشه ولی فرصت نشده تست کنم.
https://www.npmjs.com/package/prettier-plugin-organize-imports
👍7