<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="https://publishpress.com/"
	>

<channel>
	<title>Chưa phân loại Archives - Tomoshare</title>
	<atom:link href="https://blog.tomosia.com.vn/danh-muc/khong-phan-loai/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.tomosia.com.vn/danh-muc/khong-phan-loai/</link>
	<description>Kênh chia sẻ kiến thức Tomosia Việt Nam</description>
	<lastBuildDate>Fri, 22 May 2026 07:26:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://blog.tomosia.com.vn/wp-content/uploads/2023/09/cropped-icon-32x32.png</url>
	<title>Chưa phân loại Archives - Tomoshare</title>
	<link>https://blog.tomosia.com.vn/danh-muc/khong-phan-loai/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Laravel Macros: Nghệ thuật tái sử dụng code cho Query Builder và Collection</title>
		<link>https://blog.tomosia.com.vn/laravel-macros-nghe-thuat-tai-su-dung-code-cho-query-builder-va-collection/</link>
					<comments>https://blog.tomosia.com.vn/laravel-macros-nghe-thuat-tai-su-dung-code-cho-query-builder-va-collection/#comments</comments>
		
		<dc:creator><![CDATA[Khánh Phạm]]></dc:creator>
		<pubDate>Fri, 22 May 2026 07:26:33 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3957</guid>

					<description><![CDATA[<p>Trong quá trình phát triển các dự án Laravel, chắc hẳn bạn đã không ít lần gặp phải&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/laravel-macros-nghe-thuat-tai-su-dung-code-cho-query-builder-va-collection/">Laravel Macros: Nghệ thuật tái sử dụng code cho Query Builder và Collection</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Trong quá trình phát triển các dự án Laravel, chắc hẳn bạn đã không ít lần gặp phải tình trạng: <strong>Viết đi viết lại cùng một logic xử lý dữ liệu.</strong> Một trong những tình huống điển hình nhất là tính năng <strong>Tìm kiếm (Search)</strong>. Khi hệ thống lớn dần, việc quản lý hàng chục Model với các câu lệnh truy vấn trùng lặp không chỉ làm file code dài ra mà còn tiềm ẩn những lỗi logic cực kỳ khó chịu. Hôm nay, chúng ta sẽ cùng tìm hiểu về <strong>Macroable</strong> — một tính năng mạnh mẽ giúp bạn &#8220;định nghĩa lại&#8221; cuộc chơi.</p>



<h2 id="1-van-de-su-cong-kenh-cua-nhung-cau-query-thuan" class="wp-block-heading">1. Vấn đề: Sự cồng kềnh của những câu Query thuần</h2>



<p class="wp-block-paragraph">Thông thường, khi cần tìm kiếm một từ khóa trên nhiều cột (ví dụ: <code>name</code>, <code>email</code>, <code>address</code>), chúng ta thường viết như sau:</p>



<pre class="wp-block-code" style="font-size:12px"><code>$searchTerm = $request-&gt;input('q');

$users = User::where('name', 'LIKE', "%{$searchTerm}%")
             -&gt;orWhere('email', 'LIKE', "%{$searchTerm}%")
             -&gt;orWhere('address', 'LIKE', "%{$searchTerm}%")
             -&gt;get();
</code></pre>



<h4 id="nhung-rui-ro-tiem-an" class="wp-block-heading">Những rủi ro tiềm ẩn:</h4>



<ol start="1" class="wp-block-list">
<li><strong>Lỗi logic AND/OR:</strong> Nếu bạn thêm một điều kiện <code>where('status', 'active')</code> vào sau cụm trên, SQL sẽ hiểu nhầm ý định của bạn. Thay vì tìm (A hoặc B hoặc C) VÀ phải Active, nó sẽ hiểu là (A) hoặc (B) hoặc (C và Active). Điều này dẫn đến kết quả tìm kiếm sai lệch hoàn toàn.</li>



<li><strong>Vi phạm nguyên tắc DRY (Don&#8217;t Repeat Yourself):</strong> Nếu Model <code>Post</code>, <code>Product</code>, hay <code>Order</code> cũng cần tìm kiếm tương tự, bạn sẽ phải copy-paste đoạn code đó. Khi cần đổi <code>LIKE</code> thành <code>Full-text search</code>, bạn phải đi sửa ở&#8230; 20 file khác nhau.</li>



<li><strong>Khó đọc:</strong> Controller của bạn sẽ bị lấp đầy bởi những chi tiết triển khai (implementation details) thay vì tập trung vào luồng nghiệp vụ chính.</li>
</ol>



<h2 id="2-giai-phap-tai-sao-khong-dung-scope-ma-lai-dung-macro" class="wp-block-heading">2. Giải pháp: Tại sao không dùng Scope mà lại dùng Macro?</h2>



<p class="wp-block-paragraph">Nhiều người sẽ nghĩ đến <strong>Local Scope</strong>. Tuy nhiên, Scope có một giới hạn về kiến trúc:</p>



<ul class="wp-block-list">
<li><strong>Scope:</strong> Được khai báo bên trong Model. Nó mang tính chất <strong>nghiệp vụ</strong>. Ví dụ: <code>Order::isPaid()</code> là logic chỉ dành cho Đơn hàng.</li>



<li><strong>Macro:</strong> Được khai báo toàn cục (Global). Nó mang tính chất <strong>tiện ích</strong>. Một hàm như <code>whereLike()</code> (tìm kiếm gần đúng) là nhu cầu chung của mọi bảng trong cơ sở dữ liệu.</li>
</ul>



<p class="wp-block-paragraph">Thay vì ép mọi Model phải kế thừa một Trait hoặc viết Scope riêng, chúng ta sử dụng Macro để &#8220;bơm&#8221; trực tiếp phương thức này vào <strong>Query Builder</strong> của Laravel.</p>



<h2 id="3-trien-khai-chi-tiet-macro-wherelike-ho-tro-ca-relationship" class="wp-block-heading">3. Triển khai chi tiết Macro <code>whereLike</code> (Hỗ trợ cả Relationship)</h2>



<p class="wp-block-paragraph">Chúng ta sẽ tạo một <code>MacroServiceProvider</code> để quản lý. Macro này không chỉ giải quyết bài toán tìm kiếm cơ bản mà còn xử lý được cả các bảng liên quan (Relation) bằng dấu chấm (<code>.</code>).</p>



<pre class="wp-block-code has-d-4-d-4-d-4-color has-text-color" style="font-size:11px"><code>namespace App\Providers;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class MacroServiceProvider extends ServiceProvider
{
    public function boot()
    {
        /**
         * Macro tìm kiếm đa cột và đa bảng
         * @param string|array $attributes Danh sách cột cần tìm (VD: 'name' hoặc &#91;'name', 'category.title'])
         * @param string $searchTerm Từ khóa tìm kiếm
         */
        Builder::macro('whereLike', function ($attributes, string $searchTerm) {
            // Bao bọc toàn bộ logic trong một closure where() để cô lập điều kiện OR
            // Điều này tạo ra SQL dạng: WHERE ... AND (column1 LIKE %?% OR column2 LIKE %?%)
            this->where(function (Builder $query) use ($attributes, $searchTerm) {
                foreach (Arr::wrap($attributes) as $attribute) {
                    $query->orWhere(function (Builder $query) use ($attribute, $searchTerm) {
                        
                        // Kiểm tra nếu attribute có chứa dấu chấm (Tìm kiếm trên quan hệ)
                        if (str_contains($attribute, '.')) {
                            &#91;$relationName, $relationAttribute] = explode('.', $attribute);

                            // Sử dụng orWhereHas để truy vấn sang bảng liên quan
                            $query->orWhereHas($relationName, function (Builder $query) use ($relationAttribute, $searchTerm) {
                                $query->where($relationAttribute, 'LIKE', "%{$searchTerm}%");
                            });
                        } else {
                            // Tìm kiếm trên bảng hiện tại
                            $query->where($attribute, 'LIKE', "%{$searchTerm}%");
                        }
                    });
                }
            });

            return $this; // Trả về Builder để tiếp tục chaining (ví dụ: ->orderBy())
        });
    }
}
</code></pre>



<h2 id="4-trai-nghiem-thuc-te-sau-khi-ap-dung" class="wp-block-heading">4. Trải nghiệm thực tế sau khi áp dụng</h2>



<p class="wp-block-paragraph">Hãy xem cách code của bạn &#8220;lột xác&#8221; trong Controller. Giả sử bạn cần tìm kiếm bài viết theo <code>tiêu đề</code>, <code>nội dung</code> và cả <code>tên tác giả</code> (nằm ở bảng Users).</p>



<p class="wp-block-paragraph"><strong>Cách cũ:</strong></p>



<pre class="wp-block-code" style="font-size:12px"><code>$posts = Post::where('title', 'like', "%$q%")
    ->orWhere('content', 'like', "%$q%")
    ->orWhereHas('author', function($query) use ($q) {
        $query->where('name', 'like', "%$q%");
    })
    ->where('is_published', true) // Dễ bị sai logic
    ->get();
</code></pre>



<p class="wp-block-paragraph"><strong>Cách mới với Macro (Sạch hơn):</strong></p>



<pre class="wp-block-code" style="font-size:12px"><code>$posts = Post::whereLike(&#91;'title', 'content', 'author.name'], $q)
    -&gt;where('is_published', true)
    -&gt;latest()
    -&gt;paginate(15);
</code></pre>



<h4 id="tai-sao-cach-nay-toi-uu-hon" class="wp-block-heading">Tại sao cách này tối ưu hơn?</h4>



<ul class="wp-block-list">
<li><strong>Tính đóng gói:</strong> Logic xử lý dấu chấm (relation) hay nhóm ngoặc đơn SQL đã được giấu kín bên trong Macro.</li>



<li><strong>Tính nhất quán:</strong> Mọi lập trình viên trong dự án đều dùng chung một cách gọi hàm, giúp việc bảo trì trở nên cực kỳ dễ dàng.</li>



<li><strong>Khả năng mở rộng:</strong> Nếu sau này bạn muốn đổi sang một bộ máy tìm kiếm khác (như Elasticsearch), bạn chỉ cần thay đổi logic tại một nơi duy nhất: <code>MacroServiceProvider</code>.</li>
</ul>



<h2 id="5-mo-rong-cho-collection" class="wp-block-heading">5. Mở rộng cho Collection</h2>



<p class="wp-block-paragraph">Không chỉ Query Builder, <strong>Collection</strong> cũng cực kỳ mạnh mẽ khi kết hợp với Macro. Ví dụ, bạn có một mảng dữ liệu phức tạp và muốn trích xuất nhanh để làm dữ liệu cho dropdown:</p>



<pre class="wp-block-code" style="font-size:12px"><code>use Illuminate\Support\Collection;

Collection::macro('toOptions', function ($label = 'name', $value = 'id') {
    return $this-&gt;map(fn($item) =&gt; &#91;
        'label' =&gt; data_get($item, $label),
        'value' =&gt; data_get($item, $value),
    ])-&gt;values();
});

// Sử dụng:
$userOptions = User::all()-&gt;toOptions('full_name', 'uuid');
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="loi-ket" class="wp-block-heading">Lời kết</h2>



<p class="wp-block-paragraph">Macroable là một &#8220;vũ khí bí mật&#8221; giúp bạn viết code Laravel một cách tinh tế hơn. Nó biến những thao tác lặp đi lặp lại thành những công cụ mạnh mẽ, giúp codebase của bạn luôn sạch sẽ và dễ hiểu.</p>



<p class="wp-block-paragraph">Tuy nhiên, có một lưu ý nhỏ: Vì Macro là các phương thức được thêm vào khi chương trình đang chạy, IDE như VS Code hay PHPStorm sẽ không tự nhận diện được để gợi ý code. Bạn nên cài thêm package <code>barryvdh/laravel-ide-helper</code> để hỗ trợ việc viết code mượt mà hơn.</p>



<p class="wp-block-paragraph"><strong>Bạn đã sẵn sàng để &#8220;Macro hóa&#8221; dự án của mình chưa? Hãy bắt đầu với những logic mà bạn thấy mình đang copy-paste nhiều nhất nhé!</strong></p>



<p class="wp-block-paragraph"><em>Hy vọng bài viết này giúp ích cho lộ trình phát triển kỹ năng của bạn. Nếu có bất kỳ thắc mắc nào về cách triển khai nâng cao, đừng ngần ngại để lại bình luận!</em></p>
<p>The post <a href="https://blog.tomosia.com.vn/laravel-macros-nghe-thuat-tai-su-dung-code-cho-query-builder-va-collection/">Laravel Macros: Nghệ thuật tái sử dụng code cho Query Builder và Collection</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/laravel-macros-nghe-thuat-tai-su-dung-code-cho-query-builder-va-collection/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn viết Cursor Rule cho người mới bắt đầu</title>
		<link>https://blog.tomosia.com.vn/huong-dan-viet-cursor-rule-cho-nguoi-moi-bat-dau/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-viet-cursor-rule-cho-nguoi-moi-bat-dau/#comments</comments>
		
		<dc:creator><![CDATA[manh nguyen]]></dc:creator>
		<pubDate>Mon, 08 Dec 2025 02:51:54 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3707</guid>

					<description><![CDATA[<p>Hướng dẫn chi tiết cách viết Cursor Rule cho người mới bắt đầu, giúp bạn hiểu rõ vì&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-viet-cursor-rule-cho-nguoi-moi-bat-dau/">Hướng dẫn viết Cursor Rule cho người mới bắt đầu</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Hướng dẫn chi tiết cách viết Cursor Rule cho người mới bắt đầu, giúp bạn hiểu rõ vì sao phải dùng rule, cách cấu trúc đúng, cách tránh các lỗi AI hay mắc, và cách tối ưu workflow trong Cursor Editor. Bài viết giải thích từng bước dựa trên logic kỹ thuật, kèm lý do tại sao phải làm như vậy, giúp bạn tạo ra bộ rule ổn định, hạn chế AI phá project, giữ code đồng nhất và phù hợp với version của dự án. Đây là tài liệu nền tảng dành cho người mới dùng Cursor AI nhưng cần độ chính xác cao.<br></p>



<h2 id="1-vi-sao-phai-viet-cursor-rule" class="wp-block-heading"><strong>1. Vì sao phải viết <em>Cursor Rule</em>?</strong></h2>



<h3 id="1-1-de-ai-hieu-dung-cach-ban-muon-no-lam-viec" class="wp-block-heading"><strong>1.1. Để AI hiểu đúng cách bạn muốn nó làm việc</strong></h3>



<p class="wp-block-paragraph">Khi không có rule, AI đoán phong cách, chất lượng và mục tiêu theo thói quen chung → dễ sai, mơ hồ, hoặc không phù hợp với dự án.<br>Rule giúp cố định cách AI hành động, không bị lệch dù prompt thay đổi.</p>



<h3 id="1-2-de-giu-chat-luong-dau-ra-on-dinh" class="wp-block-heading"><strong>1.2. Để giữ chất lượng đầu ra ổn định</strong></h3>



<p class="wp-block-paragraph">Khi làm team, nhiều người dùng AI cho chung một dự án. Không có rule → mỗi người cho ra một kiểu.<br>Có rule → kết quả ổn định, “giống một người viết”.</p>



<h3 id="1-3-de-tiet-kiem-thoi-gian" class="wp-block-heading"><strong>1.3. Để tiết kiệm thời gian</strong></h3>



<p class="wp-block-paragraph">Không phải lặp lại các yêu cầu cố định như coding style, cấu trúc file, cấm/tắt tính năng…<br>Rule giúp giảm 50–80% thời gian bạn cần để “định hướng” AI.</p>



<h3 id="1-4-de-chong-loi-va-giam-rui-ro" class="wp-block-heading"><strong>1.4. Để chống lỗi và giảm rủi ro</strong></h3>



<p class="wp-block-paragraph">AI không tự biết giới hạn của dự án.<br>Rule giúp chặn:</p>



<ul class="wp-block-list">
<li>Sai kiến trúc</li>



<li>Viết sai chuẩn coding</li>



<li>Phát sinh thư mục rác</li>



<li>Tự ý tạo file không có trong dự án</li>



<li>Sử dụng công nghệ dự án không hỗ trợ</li>
</ul>



<h2 id="2-cau-truc-can-co-cua-mot-cursor-rule" class="wp-block-heading"><strong>2. Cấu trúc cần có của một <em>Cursor Rule</em></strong></h2>



<h3 id="2-1-muc-alwaysapply-de-rule-luon-duoc-kich-hoat" class="wp-block-heading"><strong>2.1. Mục “alwaysApply”: để rule luôn được kích hoạt</strong></h3>



<pre class="wp-block-code"><code>alwaysApply: true</code></pre>



<p class="wp-block-paragraph">Nếu không bật, rule chỉ hoạt động khi bạn chọn thủ công → vô dụng. Bật lên để toàn bộ dự án luôn tuân theo một chuẩn.</p>



<h3 id="2-2-muc-mo-ta-description-de-ghi-ro-muc-dich" class="wp-block-heading"><strong>2.2. Mục mô tả (description): để ghi rõ mục đích</strong></h3>



<pre class="wp-block-code"><code>description: |
  Quy tắc dùng khi làm việc với dự án X.</code></pre>



<p class="wp-block-paragraph">Đây là phần giúp AI hiểu bối cảnh tổng quát. Nếu không có, AI dễ “đi lạc” sang phong cách khác.</p>



<h3 id="2-3-danh-sach-rules-rules-de-kiem-soat-tung-hanh-vi" class="wp-block-heading"><strong>2.3. Danh sách rules (rules[]): để kiểm soát từng hành vi</strong></h3>



<p class="wp-block-paragraph">Một rule có các thành phần cơ bản:</p>



<pre class="wp-block-code"><code>rules:
  - name: "Quy tắc đặt tên file"
    pattern: "*.js"
    description: |
      Tất cả file phải dùng camelCase.
    examples:
      good: &#91;"userService.js"]
      bad: &#91;"User_service.JS"]</code></pre>



<p class="wp-block-paragraph">Giải thích từng phần:</p>



<figure class="wp-block-table alignleft"><table class="has-fixed-layout"><tbody><tr><td class="has-text-align-left" data-align="left">name</td><td>Để quản lý, tìm kiếm, debug khi nhiều rule.</td></tr><tr><td class="has-text-align-left" data-align="left">pattern</td><td>Giúp rule áp dụng đúng file, không ảnh hưởng toàn dự án. Tránh “tác dụng phụ”.</td></tr><tr><td class="has-text-align-left" data-align="left">description </td><td>Giải thích ý nghĩa để AI hiểu đúng logic, không chỉ làm máy móc.</td></tr><tr><td class="has-text-align-left" data-align="left">examples</td><td>Cách nhanh nhất để training AI. AI bắt chước ví dụ mạnh hơn bất cứ mô tả nào.</td></tr></tbody></table></figure>



<h3 id="2-4-muc-cam-han-che-do-dont" class="wp-block-heading"><strong>2.4. Mục cấm / hạn chế (do/dont)</strong></h3>



<p class="wp-block-paragraph">Ví dụ:</p>



<pre class="wp-block-code"><code>intent:
  - "Không tự tạo file mới nếu người dùng không yêu cầu."</code></pre>



<p class="wp-block-paragraph">AI hay tự sinh file → gây rác → ảnh hưởng cấu trúc dự án → không kiểm soát được.<br>Rule này khóa hành vi đó.</p>



<h3 id="2-5-dinh-nghia-style-coding-coding-conventions" class="wp-block-heading"><strong>2.5. Định nghĩa style coding (coding conventions)</strong></h3>



<pre class="wp-block-code"><code>  - name: "Code style"
    description: |
      Viết code theo chuẩn PSR-12.</code></pre>



<p class="wp-block-paragraph">Nếu không chốt style → mỗi lần AI tạo code là mỗi lần thay đổi format → khó review, khó merge.</p>



<h2 id="3-quy-trinh-viet-mot-cursor-rule-chuan" class="wp-block-heading"><strong>3. Quy trình viết một <em>Cursor Rule</em> chuẩn</strong></h2>



<h3 id="buoc-1-xac-dinh-muc-tieu-du-an" class="wp-block-heading"><strong>Bước 1 — Xác định mục tiêu dự án</strong></h3>



<p class="wp-block-paragraph">Bạn phải trả lời: “Tôi muốn AI hỗ trợ cái gì?”</p>



<p class="wp-block-paragraph">Không biết mục tiêu → rule lan man, dài, không hiệu quả → AI rối.</p>



<h3 id="buoc-2-ghi-ro-gioi-han-du-an" class="wp-block-heading"><strong>Bước 2 — Ghi rõ giới hạn dự án</strong></h3>



<p class="wp-block-paragraph">Ví dụ:</p>



<ul class="wp-block-list">
<li>PHP version: 7.4</li>



<li>Framework: Laravel 8</li>



<li>Không dùng TypeScript</li>



<li>Không tạo file migration mới</li>
</ul>



<p class="wp-block-paragraph">Giới hạn càng rõ → AI càng ít sai.<br>Không có giới hạn → AI rất hay dùng công nghệ mới, sai version, sai API.</p>



<h3 id="buoc-3-xac-dinh-nhung-hanh-vi-ai-hay-sai-nhat" class="wp-block-heading"><strong>Bước 3 — Xác định những hành vi AI <em>hay sai nhất</em></strong></h3>



<p class="wp-block-paragraph">Ví dụ:</p>



<ul class="wp-block-list">
<li>Tự sinh file linh tinh</li>



<li>Viết import sai</li>



<li>Sử dụng syntax của version mới</li>



<li>Tạo code không phù hợp folder structure</li>
</ul>



<p class="wp-block-paragraph">Đây là những điểm tạo ra rule quan trọng nhất. Tập trung 20% rule để sửa 80% lỗi.</p>



<h3 id="buoc-4-viet-rule-bang-ngon-ngu-don-gian-khong-mo-ho" class="wp-block-heading"><strong>Bước 4 — Viết rule bằng ngôn ngữ đơn giản, không mơ hồ</strong></h3>



<p class="wp-block-paragraph">Ví dụ sai: “Viết code sạch” → AI không hiểu.<br>Ví dụ đúng: “Không dùng biến một ký tự như <code>i, j, k</code>.”</p>



<p class="wp-block-paragraph">AI không hiểu từ trừu tượng.<br>Muốn AI làm đúng → phải mô tả hành vi cụ thể.</p>



<h3 id="buoc-5-bo-sung-vi-du-tot-xau" class="wp-block-heading"><strong>Bước 5 — Bổ sung ví dụ tốt/xấu</strong></h3>



<p class="wp-block-paragraph">Đây là phần cực quan trọng nhưng rất nhiều người bỏ qua.</p>



<p class="wp-block-paragraph">AI học theo ví dụ mạnh hơn theo mô tả.<br>Chỉ 2–3 ví dụ tốt/xấu đã giúp giảm lỗi 60–70%.</p>



<h3 id="buoc-6-test-rule-bang-mot-tac-vu-that" class="wp-block-heading"><strong>Bước 6 — Test rule bằng một tác vụ thật</strong></h3>



<p class="wp-block-paragraph">Ví dụ: “Tạo controller UserController.”</p>



<p class="wp-block-paragraph">Rule chỉ có giá trị khi được test trong thực tế.<br>Không test → rule chỉ là lý thuyết.</p>



<h2 id="4-mau-cursor-rule-don-gian-cho-nguoi-moi" class="wp-block-heading"><strong>4. Mẫu <em>Cursor Rule</em> đơn giản cho người mới</strong></h2>



<pre class="wp-block-code"><code>alwaysApply: true

description: |
  Quy tắc hỗ trợ viết mã PHP, Laravel 8 cho dự án cũ.

rules:
  - name: "Không dùng syntax Laravel mới"
    description: |
      Laravel trong dự án là bản 8. Không dùng các API của Laravel 9+.
    examples:
      bad: &#91;"Route::controller(...)"]
      good: &#91;"Route::get(...)", "Route::post(...)"]

  - name: "Không tự tạo file"
    description: |
      Chỉ tạo file khi người dùng yêu cầu.

  - name: "Style code"
    description: |
      Tất cả code phải tuân theo PSR-12.</code></pre>



<h2 id="ket-luan" class="wp-block-heading"><strong>Kết luận</strong></h2>



<p class="wp-block-paragraph">Viết <em>Cursor Rule</em> không phải để “làm màu”, mà để:</p>



<ul class="wp-block-list">
<li>Giữ AI chạy đúng đường.</li>



<li>Giảm lỗi.</li>



<li>Tăng tốc độ phát triển.</li>



<li>Chuẩn hóa output trong team.</li>



<li>Tạo ra môi trường làm việc ổn định.</li>
</ul>



<p class="wp-block-paragraph">Muốn viết rule tốt → phải hiểu vấn đề, hiểu dự án, và hiểu AI dễ sai ở đâu.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-viet-cursor-rule-cho-nguoi-moi-bat-dau/">Hướng dẫn viết Cursor Rule cho người mới bắt đầu</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-viet-cursor-rule-cho-nguoi-moi-bat-dau/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Hành Trình Debug Lỗi Redis OOM Trong Laravel: Tìm Ra Hơn 2.8 Triệu Keys Và Cách Khắc Phục Triệt Để</title>
		<link>https://blog.tomosia.com.vn/hanh-trinh-debug-loi-redis-oom-trong-laravel-tim-ra-hon-2-8-trieu-keys-va-cach-khac-phuc-triet-de/</link>
					<comments>https://blog.tomosia.com.vn/hanh-trinh-debug-loi-redis-oom-trong-laravel-tim-ra-hon-2-8-trieu-keys-va-cach-khac-phuc-triet-de/#comments</comments>
		
		<dc:creator><![CDATA[hoa nguyen]]></dc:creator>
		<pubDate>Sun, 07 Dec 2025 16:10:14 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3737</guid>

					<description><![CDATA[<p>Có những ngày làm kỹ thuật trôi qua rất yên bình, nhưng cũng có những ngày bắt đầu&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/hanh-trinh-debug-loi-redis-oom-trong-laravel-tim-ra-hon-2-8-trieu-keys-va-cach-khac-phuc-triet-de/">Hành Trình Debug Lỗi Redis OOM Trong Laravel: Tìm Ra Hơn 2.8 Triệu Keys Và Cách Khắc Phục Triệt Để</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Có những ngày làm kỹ thuật trôi qua rất yên bình, nhưng cũng có những ngày bắt đầu bằng một tiếng ping Slack “<strong>Queue đứng rồi anh ơi!</strong>”, &#8220;<strong>Server không vào được nữa anh ơi!</strong>&#8220;<br>Và đây là một trong những ca sự cố mà tôi nhớ rất rõ — bởi vì nó dẫn tôi đến việc tìm thấy <strong>hơn 2.8 triệu Redis keys</strong> chỉ trong môi trường phát triển.</p>



<p class="wp-block-paragraph">Đó là lúc tôi buộc phải lôi toàn bộ “đồ nghề” ra: từ metrics, redis-cli, bash script, đến việc ngồi soi từng pattern key một. Và những gì tôi học được trong hành trình đó, tôi muốn chia sẻ lại ở đây — để nếu bạn cũng dùng Redis giống tôi, bạn sẽ tránh được một ngày làm việc dở khóc dở cười.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="1-dau-hieu-ban-dau-khi-slack-bao-dong-khong-ngung" class="wp-block-heading"><strong>1. Dấu hiệu ban đầu – khi slack  báo động</strong> <strong>không ngừng</strong></h1>



<p class="wp-block-paragraph">Sự cố được phát hiện qua lỗi sau trên slack:</p>



<pre class="wp-block-code"><code>OOM command not allowed when used memory > 'maxmemory'.
RedisException: OOM command not allowed when used memory > 'maxmemory'. in /var/www/app/vendor/laravel/framework/src/Illuminate/Redis/Connections/PhpRedisConnection.php:405
</code></pre>



<p class="wp-block-paragraph">Nếu bạn đã từng gặp lỗi này, bạn biết nó nguy hiểm cỡ nào:</p>



<ul class="wp-block-list">
<li>Redis <strong>từ chối mọi lệnh ghi</strong></li>



<li>Queue dừng</li>



<li>Horizon đứng hình</li>



<li>Laravel không thể push thêm job</li>



<li>Toàn bộ request liên quan Redis gần như fail đồng loạt</li>
</ul>



<p class="wp-block-paragraph">Điều đầu tiên tôi làm là mở AWS ElastiCache lên. Và cảnh tượng trước mắt:</p>



<ul class="wp-block-list">
<li>Memory đã <strong>100%</strong></li>



<li>Biểu đồ tăng đều, gần như hoàn hảo theo dạng <em>diagonal slope</em></li>



<li>Không hề có eviction (vì đang dùng <code>volatile-lru</code> — mặc định của AWS ElastiCache)</li>
</ul>



<p class="wp-block-paragraph">Đây là dấu hiệu kinh điển của <strong>memory leak</strong> trong Redis.</p>



<p class="wp-block-paragraph">Giờ thì không còn đường lui nữa: phải lội vào trong Redis để xem nó đang chứa cái gì.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="2-bat-dau-dieu-tra-mo-mam-tung-dau-vet-trong-redis" class="wp-block-heading"><strong>2. Bắt đầu điều tra – mò mẫm từng dấu vết trong Redis</strong></h1>



<p class="wp-block-paragraph">Tôi ssh vào ECS container chạy Laravel (Alpine), cài nhanh redis-cli:</p>



<pre class="wp-block-code"><code>apk add redis vim</code></pre>



<p class="wp-block-paragraph">Rồi truy cập Redis database mà Laravel đang dùng:</p>



<pre class="wp-block-code"><code>redis-cli -h &lt;redis-host> -n 1 --scan</code></pre>



<p class="wp-block-paragraph">Mục tiêu của tôi:</p>



<p class="wp-block-paragraph"><strong>Tìm ra nhóm keys nào đã ăn hết RAM của Redis.</strong></p>



<p class="wp-block-paragraph">Bởi Redis mà đầy RAM thì chỉ có 1 trong 2 nguyên nhân:</p>



<ol class="wp-block-list">
<li><strong>Key quá to</strong></li>



<li><strong>Too many keys</strong></li>
</ol>



<p class="wp-block-paragraph">Và theo trực giác, tôi nghiêng về <strong>too many keys</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="3-nhan-dien-pattern-la-cuoc-dieu-tra-bat-dau-co-hy-vong" class="wp-block-heading"><strong>3. Nhận diện pattern lạ – cuộc điều tra bắt đầu có hy vọng</strong></h1>



<p class="wp-block-paragraph">Trong dự án <code>laravel_dev</code>, tôi lướt qua các module có khả năng sinh nhiều keys.<br>Sau một hồi đọc log và grep source, tôi để ý đến pattern:</p>



<pre class="wp-block-code"><code>laravel_dev_cache_:App\\\\Jobs*</code></pre>



<p class="wp-block-paragraph">Đây là nhóm keys do một service lưu metadata job, kiểu dạng log hoặc tracking.</p>



<p class="wp-block-paragraph">Tôi quyết định scan thử:</p>



<pre class="wp-block-code"><code>redis-cli -h &lt;redis-host> -n 1 --scan --pattern "laravel_dev_cache_:App\\\\Jobs*" | wc -l</code></pre>



<p class="wp-block-paragraph">Redis trả về:</p>



<p class="wp-block-paragraph"><strong>≈ 2.800.000 keys</strong></p>



<p class="wp-block-paragraph">Tôi ngồi im vài giây.</p>



<p class="wp-block-paragraph">Đây chính là thủ phạm.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="4-dao-sau-hon-khong-chi-nhieu-keys-ma-con-bat-tu" class="wp-block-heading"><strong>4. Đào sâu hơn – không chỉ nhiều keys mà còn “bất tử”</strong></h1>



<p class="wp-block-paragraph">Tôi kiểm tra TTL của một vài key:</p>



<pre class="wp-block-code"><code>redis-cli -h &lt;redis-host> -n 1 TTL "laravel_dev_cache_:App\\Jobs\\JobName:1234567890"</code></pre>



<p class="wp-block-paragraph">Kết quả:</p>



<pre class="wp-block-code"><code>-1</code></pre>



<p class="wp-block-paragraph">TTL = -1 nghĩa là:</p>



<p class="wp-block-paragraph"><strong>Key không bao giờ tự hết hạn.</strong></p>



<p class="wp-block-paragraph">Vậy là rõ:<br>Code đang sinh keys mỗi ngày, mỗi giờ, mỗi phút → và không bao giờ xoá.<br>Một kiểu memory leak rất phổ biến khi dùng Redis sai mục đích.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="5-van-de-kho-nhan-xoa-2-8-trieu-keys-sao-cho-khong-lam-redis-chet-tiep" class="wp-block-heading"><strong>5. Vấn đề khó nhằn: Xoá 2.8 triệu keys sao cho không làm Redis chết tiếp</strong></h1>



<p class="wp-block-paragraph">Không thể dùng <code>KEYS</code> — vì:</p>



<ul class="wp-block-list">
<li>Nó block Redis</li>



<li>Khi memory đang căng → khả năng treo Redis rất cao</li>



<li>AWS ElastiCache có thể tự động failover</li>
</ul>



<p class="wp-block-paragraph">Chỉ có một cách an toàn: <strong>SCAN + DEL dần dần</strong>.</p>



<p class="wp-block-paragraph">Tôi viết một đoạn bash loop:</p>



<pre class="wp-block-code"><code>while IFS= read -r key; do
  echo "Deleting key: '$key'"
  redis-cli -h &lt;redis-host> -n 1 DEL "$key"
done &lt; &lt;(redis-cli -h &lt;redis-host> -n 1 --scan --pattern "laravel_dev_cache_:App\\\\Jobs*")</code></pre>



<p class="wp-block-paragraph">Mất khoảng 30–40 phút, lượng keys giảm rõ rệt.<br>Biểu đồ memory từ <strong>100%</strong> rớt xuống <strong>10%</strong>.<br>Horizon hồi sinh. Queue bắt đầu đẩy job trở lại. Cache tiếp tục hoạt động</p>



<p class="wp-block-paragraph">Nếu muốn chạy background mà không cần giữ terminal:</p>



<pre class="wp-block-code"><code>nohup sh -c '
while IFS= read -r key; do
  echo "Deleting key: '\''$key'\''"
  redis-cli -h &lt;redis-host> -n 1 DEL "$key"
done &lt; &lt;(redis-cli -h &lt;redis-host> -n 1 --scan --pattern "laravel_dev_cache_:App\\\\Jobs*")
' > /tmp/redis_delete.log 2>&amp;1 &amp;</code></pre>



<p class="wp-block-paragraph">Một cảm giác rất “đã” — giống như nới được một cái van áp lực.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="6-tim-hieu-nguyen-nhan-trong-code-thu-pham-thuc-su-nam-o-logic" class="wp-block-heading"><strong>6. Tìm hiểu nguyên nhân trong code – thủ phạm thực sự nằm ở logic</strong></h1>



<p class="wp-block-paragraph">Tôi trace lại luồng xử lý và phát hiện:</p>



<ul class="wp-block-list">
<li>Mỗi job đều sinh ra 1 metadata key khi kết hợp với Laravel Scheduler chưa đúng cách/</li>



<li>Key lưu dạng JSON với prefix <code>laravel_dev_cache_:App\\Jobs:...</code></li>



<li>Không có TTL</li>



<li>Không có cơ chế cleanup</li>



<li>Job chạy nhiều hơn dự kiến → số lượng keys tăng <em>âm thầm</em> một cách đều đặn <em>hàng phút.</em></li>
</ul>



<p class="wp-block-paragraph">Đây là kiểu bug không gây lỗi ngay lập tức, nhưng sẽ phá hủy hệ thống sau vài tuần hoặc vài tháng — nên rất khó phát hiện nếu không quan sát metrics.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="7-fix-triet-de-de-su-co-nay-khong-bao-gio-quay-lai" class="wp-block-heading"><strong>7. Fix triệt để – để sự cố này không bao giờ quay lại</strong></h1>



<p class="wp-block-paragraph">Tôi áp dụng 3 thay đổi lớn.</p>



<h2 id="7-1-bat-buoc-gan-ttl-cho-moi-key" class="wp-block-heading"><strong>7.1 Bắt buộc gán TTL cho mọi key</strong></h2>



<p class="wp-block-paragraph">Trong Laravel:</p>



<pre class="wp-block-code"><code>Cache::put($key, $value, now()-&gt;addMinutes(60));
</code></pre>



<p class="wp-block-paragraph">Trong Redis thuần:</p>



<pre class="wp-block-code"><code>Redis::setex($key, 3600, $value);</code></pre>



<p class="wp-block-paragraph">Nếu bạn để key tồn tại mãi mãi → bạn đang đặt bom hẹn giờ trong hệ thống.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="7-2-gom-tat-ca-vao-hash-thay-vi-tao-hang-trieu-keys-roi-rac" class="wp-block-heading"><strong>7.2 Gom tất cả vào Hash thay vì tạo hàng triệu keys rời rạc</strong></h2>



<p class="wp-block-paragraph">Từ:</p>



<pre class="wp-block-code"><code>laravel_dev_cache_:App\\Jobs:&lt;jobId></code></pre>



<p class="wp-block-paragraph">Chuyển sang:</p>



<pre class="wp-block-code"><code>HSET laravel_dev:jobs:&lt;date> &lt;jobId> &lt;data>
EXPIRE laravel_dev:jobs:&lt;date> 86400</code></pre>



<p class="wp-block-paragraph">Thay vì 1.8M keys → chỉ còn &lt; 30 keys/ngày.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="7-3-gioi-han-dung-luong-log" class="wp-block-heading"><strong>7.3 Giới hạn dung lượng log</strong></h2>



<p class="wp-block-paragraph">Giữ 200 job gần nhất chẳng hạn:</p>



<pre class="wp-block-code"><code>Redis::ltrim('laravel_dev:jobs_log', -200, -1);</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="8-bai-hoc-sau-su-co-nhung-thu-toi-uoc-gi-minh-biet-som-hon" class="wp-block-heading"><strong>8. Bài học sau sự cố – những thứ tôi ước gì mình biết sớm hơn</strong></h1>



<p class="wp-block-paragraph">Sau ca này, tôi rút ra những kinh nghiệm rất thực chiến:</p>



<h3 id="8-1-redis-khong-phai-database" class="wp-block-heading"><strong>8.1. Redis không phải database.</strong></h3>



<p class="wp-block-paragraph">Dùng Redis để lưu dữ liệu dạng “log” là tự sát.</p>



<h3 id="8-2-moi-key-phai-co-ttl-luon-luon" class="wp-block-heading"><strong>8.2. Mọi key phải có TTL — luôn luôn.</strong></h3>



<p class="wp-block-paragraph">Không trừ trường hợp đặc biệt.</p>



<h3 id="8-3-so-luong-keys-cung-la-chi-so-quan-trong-nhu-memory" class="wp-block-heading"><strong>8.3. Số lượng keys cũng là chỉ số quan trọng như memory.</strong></h3>



<p class="wp-block-paragraph">Rất ít đội monitor key-count, nhưng nên làm.</p>



<h3 id="8-4-can-co-co-che-alert-som" class="wp-block-heading"><strong>8.4. Cần có cơ chế alert sớm.</strong></h3>



<ul class="wp-block-list">
<li>Memory > 80%</li>



<li>Key-count tăng nhanh</li>



<li>Horizon queue lag</li>
</ul>



<h3 id="8-5-khi-redis-day-dung-hoang-scan-va-xoa-dung-cach-la-cuu-duoc" class="wp-block-heading"><strong>8.5. Khi Redis đầy, đừng hoảng — scan và xoá đúng cách là cứu được.</strong></h3>



<p class="wp-block-paragraph">Không được dùng <code>KEYS</code>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="9-ket-luan-mot-ca-su-co-dang-nho-va-bai-hoc-cho-nhung-lan-sau" class="wp-block-heading"><strong>9. Kết luận – Một ca sự cố đáng nhớ, và bài học cho những lần sau</strong></h1>



<p class="wp-block-paragraph">Sự cố Redis OOM trong dự án <code>laravel_dev</code> không chỉ là một lỗi kỹ thuật.<br>Nó nhắc tôi rằng:</p>



<ul class="wp-block-list">
<li>Hệ thống có thể chạy rất mượt trước khi đột ngột “bung lụa”.</li>



<li>Metrics là thứ không bao giờ được xem nhẹ.</li>



<li>Redis mạnh, nhưng dùng sai cách thì nguy hiểm như bất kỳ database nào.</li>
</ul>



<p class="wp-block-paragraph">Hy vọng hành trình này giúp bạn tránh được một ngày làm việc “chữa cháy” như tôi.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/hanh-trinh-debug-loi-redis-oom-trong-laravel-tim-ra-hon-2-8-trieu-keys-va-cach-khac-phuc-triet-de/">Hành Trình Debug Lỗi Redis OOM Trong Laravel: Tìm Ra Hơn 2.8 Triệu Keys Và Cách Khắc Phục Triệt Để</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/hanh-trinh-debug-loi-redis-oom-trong-laravel-tim-ra-hon-2-8-trieu-keys-va-cach-khac-phuc-triet-de/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Làm thế nào để nói &#8220;Không&#8221; hiệu quả</title>
		<link>https://blog.tomosia.com.vn/lam-the-nao-de-noi-khong-hieu-qua/</link>
					<comments>https://blog.tomosia.com.vn/lam-the-nao-de-noi-khong-hieu-qua/#comments</comments>
		
		<dc:creator><![CDATA[ngt]]></dc:creator>
		<pubDate>Fri, 29 Dec 2023 08:45:31 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2959</guid>

					<description><![CDATA[<p>Xin chào, tiếp nối bài Sức mạnh của sự từ chối của chị Minh Ngô, hôm nay tôi&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/lam-the-nao-de-noi-khong-hieu-qua/">Làm thế nào để nói &#8220;Không&#8221; hiệu quả</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="685" data-id="2960" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-1024x685.png" alt="" class="wp-image-2960" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-1024x685.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-300x201.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-768x514.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-380x254.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-800x535.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280-1160x776.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/say-no-3218695_1280.png 1280w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p class="wp-block-paragraph"><em>Xin chào, tiếp nối bài <a href="http://blog.tomosia.com.vn/suc-manh-cua-su-tu-choi/"><strong>Sức mạnh của sự từ chối</strong></a> của chị Minh Ngô, hôm nay tôi sẽ giới thiệu cho các bạn một vài cách để nói &#8220;Không&#8221; ở bài viết này. Now let&#8217;s go!</em></p>



<p class="has-medium-font-size wp-block-paragraph"><strong>Nói &#8220;Không&#8221; – kỹ năng khó nhưng cần thiết để bảo vệ giới hạn và tôn trọng nhu cầu bản thân</strong></p>



<p class="wp-block-paragraph">Trong cuộc sống, chúng ta thường xuyên phải đối mặt với những yêu cầu từ người khác. Đôi khi, chúng ta không muốn hoặc không thể đáp ứng những yêu cầu đó. Lúc này, biết cách nói &#8220;Không&#8221; là một kỹ năng quan trọng giúp chúng ta bảo vệ thời gian, năng lượng và sức khỏe của bản thân.</p>



<p class="wp-block-paragraph">Đương nhiên, nói &#8220;Không&#8221; không phải lúc nào cũng dễ dàng. Chúng ta có thể cảm thấy ngại ngùng, xấu hổ hoặc lo lắng rằng người khác sẽ nghĩ gì về mình. Tuy nhiên, việc khéo léo từ chối một cách lịch sự có thể giúp xoa dịu tình hình và vẫn duy trì mối quan hệ tốt đẹp.<br>Điều quan trọng nhất là bạn phải kiên định với lập trường và tôn trọng giá trị của chính mình. Bạn cũng không nhất thiết phải có một lý do cụ thể để nói &#8220;Không&#8221;.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph">Vầy nhé, thử tưởng tượng bạn đang mắc kẹt trong một cuộc họp mặt ồn ào kéo dài đến tận khuya. Bạn cảm thấy mệt mỏi và lạc lõng, lúc này bạn hoàn toàn có thể nói &#8220;không&#8221; với việc tham gia tiếp, bởi sức khỏe và thời gian cá nhân của bạn cũng cần được cân nhắc.<br>Hay như trường hợp một người đồng nghiệp nhờ bạn giải quyết vấn đề cá nhân của họ. Bạn có thể lịch sự từ chối và đề xuất họ tìm sự giúp đỡ khác phù hợp hơn.</p>
</div></div>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" width="716" height="360" data-id="2966" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/360_F_176089605_fzC0ZcS0urFDcU04hOqC3JVGWzr8uWfQ.jpeg" alt="" class="wp-image-2966" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/360_F_176089605_fzC0ZcS0urFDcU04hOqC3JVGWzr8uWfQ.jpeg 716w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/360_F_176089605_fzC0ZcS0urFDcU04hOqC3JVGWzr8uWfQ-300x151.jpeg 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/360_F_176089605_fzC0ZcS0urFDcU04hOqC3JVGWzr8uWfQ-380x191.jpeg 380w" sizes="(max-width: 716px) 100vw, 716px" /></figure>
</figure>



<p class="wp-block-paragraph"><strong>Để từ chối hiệu quả, bạn cần tuân thủ các nguyên tắc sau:</strong></p>



<ul class="wp-block-list">
<li><strong>Tự tin và thẳng thắn:</strong> Đừng vòng vo hoặc đưa ra những lý do không thuyết phục. Hãy nói rõ rằng bạn không muốn làm điều đó.</li>



<li><strong>Có lý do chính đáng và đề nghị phương án khác nếu có thể</strong>:&nbsp;Nếu có thể, hãy đưa ra lý do chính đáng cho việc từ chối của bạn. Điều này sẽ giúp bạn tự tin hơn và cũng giúp người khác hiểu được quyết định của bạn. Ngoài ra, bạn có thể gợi ý phương án khác để cho thấy bạn tôn trọng người khác và sẵn sàng tìm giải pháp chung.</li>



<li><strong>Tôn trọng người khác:</strong> Ngay cả khi bạn đang từ chối, hãy vẫn tôn trọng người khác. Hãy nhớ giữ thái độ lịch sự và nhã nhặn trong cách bạn từ chối.</li>



<li><strong>Luyện tập thường xuyên:</strong> Luyện tập nói &#8220;Không&#8221; thường xuyên sẽ giúp bạn trở nên tự tin hơn và từ chối hiệu quả hơn. Hãy bắt đầu bằng những việc nhỏ, chẳng hạn như từ chối lời mời tham dự một cuộc hẹn, một bữa tiệc mà bạn không muốn đi. Khi bạn đã cảm thấy thoải mái hơn với việc từ chối, bạn có thể bắt đầu từ chối những yêu cầu lớn hơn, chẳng hạn như từ chối một công việc mà bạn không muốn làm.</li>
</ul>



<p class="has-text-align-left wp-block-paragraph">Dưới đây là một số ví dụ về cách nói &#8220;Không&#8221; hiệu quả:</p>



<ul class="wp-block-list">
<li>&#8220;Cảm ơn vì lời mời, nhưng tôi không thể tham dự vì đã có lịch từ trước.&#8221;</li>



<li>&#8220;Tôi xin lỗi, hôm nay công việc của tôi đang rất bận và tôi không có thời gian để giúp bạn.&#8221;</li>



<li>&#8220;Tôi biết bạn đang đề nghị một cơ hội tuyệt vời, nhưng có lẽ không phù hợp với tôi.&#8221;</li>
</ul>



<p class="wp-block-paragraph">Bạn cũng có thể tham khảo một số lời &#8220;từ chối&#8221; khéo léo hơn như sau:</p>



<ul class="wp-block-list">
<li>&#8220;Cảm ơn bạn đã nghĩ đến tôi. Tôi sẽ xem xét và cho bạn biết sau.&#8221;</li>



<li>&#8220;Tôi không chắc mình có thể làm được không, nhưng chúng ta có thể thử cách khác.&#8221;</li>



<li>&#8220;Tôi không thể tham dự cuộc họp hôm nay, nhưng tôi có thể gửi cho bạn tài liệu cần thiết.&#8221;</li>
</ul>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-full"><img decoding="async" width="720" height="720" data-id="2967" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1.jpeg" alt="" class="wp-image-2967" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1.jpeg 720w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1-300x300.jpeg 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1-150x150.jpeg 150w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1-80x80.jpeg 80w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/comrawpixel412516-1-380x380.jpeg 380w" sizes="(max-width: 720px) 100vw, 720px" /></figure>
</figure>



<p class="wp-block-paragraph"><strong>Một số lưu ý khi nói &#8220;Không&#8221;</strong></p>



<ul class="wp-block-list">
<li><strong>Đừng cảm thấy tội lỗi hoặc xấu hổ khi nói &#8220;Không&#8221;.</strong> Bạn có quyền từ chối bất cứ điều gì bạn không muốn làm khi điều đó không phù hợp với kỹ năng, chuyên môn hoặc sở thích của bản thân hoặc ảnh hưởng đến sức khỏe tâm lý, thể chất của bạn.</li>



<li><strong>Đừng sợ làm phật lòng người khác.</strong> Nếu ai đó không hài lòng vì bạn từ chối họ, đó không phải là lỗi của bạn.</li>



<li><strong>Hãy mạnh dạn nói &#8220;Không&#8221; với những yêu cầu quá đáng hoặc không hợp lý, đi ngược giá trị, niềm tin của bạn.</strong> Đừng để người khác lợi dụng bạn.</li>
</ul>



<p class="has-medium-font-size wp-block-paragraph"><strong>Tạm kết</strong></p>



<p class="wp-block-paragraph">Nói &#8220;Không&#8221; không phải là hành động ích kỷ, mà là một cách khẳng định bản thân và bảo vệ những điều quan trọng với bạn. Đôi khi, &#8220;Không&#8221; chính là câu trả lời duy nhất để bạn thoát khỏi những gánh nặng không cần thiết và ưu tiên hạnh phúc của mình.<br><br>Đây là nghệ thuật giao tiếp hết sức cần thiết trong cuộc sống và công việc hàng ngày, là một kỹ năng quan trọng cần mài giũa. Bằng cách luyện tập thường xuyên và áp dụng những nguyên tắc trên, biết nói “Không” một cách khéo léo và tinh tế sẽ giúp bạn tránh được phiền toái, tránh lãng phí thời gian và tập trung cho việc nâng cao giá trị của bản thân và sống cuộc sống như bản thân mong muốn.</p>



<p class="wp-block-paragraph"><em>P/s: Được rồi, cảm ơn các bạn đã đọc tới đây. 2023 sắp hết rồi, bạn nào chưa thực hành kỹ năng này thì hãy thử trong 2024 nhé. Chúc các bạn thành công ^^</em></p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/lam-the-nao-de-noi-khong-hieu-qua/">Làm thế nào để nói &#8220;Không&#8221; hiệu quả</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/lam-the-nao-de-noi-khong-hieu-qua/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Khởi tạo dự án với Apollo và NextJS SSR</title>
		<link>https://blog.tomosia.com.vn/khoi-tao-du-an-voi-apollo-va-nextjs-ssr/</link>
					<comments>https://blog.tomosia.com.vn/khoi-tao-du-an-voi-apollo-va-nextjs-ssr/#comments</comments>
		
		<dc:creator><![CDATA[Hoang Long La]]></dc:creator>
		<pubDate>Thu, 28 Dec 2023 09:26:30 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2849</guid>

					<description><![CDATA[<p>Gần đây mình có làm việc với 1 dự án sử dụng NextJS và Apollo, mọi việc đang&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/khoi-tao-du-an-voi-apollo-va-nextjs-ssr/">Khởi tạo dự án với Apollo và NextJS SSR</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Gần đây mình có làm việc với 1 dự án sử dụng NextJS và Apollo, mọi việc đang rất yên bình cho đến một ngày khách hàng yêu cầu chuyển đổi từ CSR sang SSR. Kiếp nạn này mình đã trải qua rồi nên cũng tự tin chút cho đến khi nhận ra Apollo (GraphQL) khác với REST API mình đã từng làm.</p>



<blockquote class="wp-block-quote is-style-default has-black-color has-text-color has-link-color has-medium-font-size wp-elements-005da075de4e7366837004f5909fc375 is-layout-flow wp-block-quote-is-layout-flow" style="font-style:normal;font-weight:400">
<p class="wp-block-paragraph">Apollo:</p>
<cite>Theo tài liệu Apollo cung cấp, phương thức getDataFromTree sẽ tìm kiếm trong page tree của mình các component có sử dụng hook <em>useQuery</em>  sau đó thực hiện truy vấn tất cả các query đó ở server </cite></blockquote>



<p class="wp-block-paragraph">Nghe có vẻ ổn phết nhưng đội ngũ NextJS nói không. <code>getDataFromTree</code> được đánh giá là anti-pattern đối với NextJS vì <em><code>getDataFromTree</code></em> không được tích hợp vào các phương thức mà NextJS đã cung cấp cho việc fetch dữ liệu như <code>getStaticProps</code> và <code>getServerSideProps</code>. Việc <code>getDataFromTree</code> duyệt page tree một lần nữa để tìm kiếm các useQuery cũng ảnh hưởng đến hiệu suất render trang. Và cuối cùng là mình gặp lỗi tích hợp <code>getDataFromTree</code> vào version của NextJS hiện tại của dự án : D.</p>



<p class="wp-block-paragraph">Thật may thì cuối cùng NextJS cũng đưa ra một phương án khác đối với Apollo vẫn tận dụng được các phương thức SSR của NextJS đó là tận dụng sức mạnh của Apollo cache.</p>



<h2 id="apollo-cache" class="wp-block-heading">Apollo cache</h2>



<p class="wp-block-paragraph">Apollo Client sử dụng một hệ thống cache để tối ưu hóa việc quản lý và truy cập dữ liệu trong ứng dụng của bạn. Cache trong Apollo Client là nơi lưu trữ tạm thời kết quả của các truy vấn GraphQL, cho phép ứng dụng của bạn tái sử dụng dữ liệu này mà không cần phải gửi lại truy vấn đến server. Nó giúp giảm thiểu số lượng yêu cầu mạng, giảm độ trễ và cải thiện hiệu suất ứng dụng.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="461" height="301" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Untitled-1.png" alt="" class="wp-image-2854" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Untitled-1.png 461w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Untitled-1-300x196.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Untitled-1-380x248.png 380w" sizes="auto, (max-width: 461px) 100vw, 461px" /></figure>
</div>


<p class="wp-block-paragraph">Ý tưởng của mình sẽ là fetch data thông qua getServerSideProps. Sau khi đã fetch tất cả các dữ liệu FE server sẽ gen ra một file html đầy đủ và trả về thêm dữ liệu từ Apollo thông qua props cho client. Việc trả về thêm Apollo data giúp chúng ta tận dụng được các thuộc tính data, loading và các phương thức như refetch của Apollo client và đồng bộ dữ liệu giữa client và FE server lúc khởi tạo trang thông qua Apollo cache</p>



<h2 id="bat-dau-thoi" class="wp-block-heading">Bắt đầu thôi</h2>



<ul class="wp-block-list">
<li>Khởi đầu với tạo dự án NextJS</li>
</ul>



<pre class="wp-block-code"><code>npx create-next-app</code></pre>



<ul class="wp-block-list">
<li>Cài các package liên quan đến Apollo client. Ở đây mình cài thêm graphql-codegen để tự động gen các hook React từ các document và schema</li>
</ul>



<pre class="wp-block-code"><code>npm i graphql @apollo/client @graphql-codegen/typescript-react-apollo @graphql-codegen/cli</code></pre>



<ul class="wp-block-list">
<li>Cài vài package dùng để xử lí logic và tạo proxy ẩn danh</li>
</ul>



<pre class="wp-block-code"><code>npm i http-proxy deepmerge lodash.isequal @types/lodash.isequal @types/http-proxy</code></pre>



<ul class="wp-block-list">
<li>Tạo file .env để chứ API_URL</li>
</ul>



<pre class="wp-block-code"><code>NEXT_PUBLIC_API_URL=https://beta.pokeapi.co/graphql/v1beta</code></pre>



<ul class="wp-block-list">
<li>Tạo file index.ts trong thư mục src/api để tạo 1 http proxy</li>
</ul>



<pre class="wp-block-code"><code>import httpProxy from 'http-proxy';
import type { NextApiRequest, NextApiResponse } from 'next';

// You can export a config variable from any API route in Next.js.
// We'll use this to disable the bodyParser, otherwise Next.js
// would read and parse the entire request body before we
// can forward the request to the API. By skipping the bodyParser,
// we can just stream all requests through to the actual API.
export const config = {
  api: {
    bodyParser: false,
  },
};

const proxy = httpProxy.createProxyServer();

export default async function handler(req: NextApiRequest, res: NextApiResponse&lt;unknown&gt;) {
  // Return a Promise to let Next.js know when we're done
  // processing the request:
  return new Promise(async (resolve, reject) =&gt; {
    // Rewrite the URL: strip out the leading '/api'.
    // For example, '/api/login' would become '/login'.
    // ️You might want to adjust this depending
    // on the base path of your API.
    req.url = req?.url?.replace(/^\/api/, '');

    // // Handle error
    proxy.once('error', () =&gt; {
      reject();
    });

    proxy.once('proxyRes', () =&gt; {
      resolve(true);
    });

    // Forward the request to the API
    proxy.web(req, res, {
      target: process.env.NEXT_PUBLIC_API_URL,
      autoRewrite: false,
      changeOrigin: true,
      selfHandleResponse: false,
    });
  });
}</code></pre>



<ul class="wp-block-list">
<li>Config file codegen bằng lệnh <code>npx graphql-code-generator init</code> hoặc tạo file codegen.ts ở ngoài cùng thư mục dự án và copy paste đoạn config dưới này</li>
</ul>



<pre class="wp-block-code"><code>import type { CodegenConfig } from '@graphql-codegen/cli';
import { loadEnvConfig } from '@next/env';

loadEnvConfig(process.cwd());

const config: CodegenConfig = {
  overwrite: true,
  schema: process.env.NEXT_PUBLIC_API_URL,
  documents: &#91;'./src/graphql/**/*.{gql,graphql}'],
  ignoreNoDocuments: true, // for better experience with the watcher
  generates: {
    'src/__generated__/graphql.tsx': {
      plugins: &#91;'typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
  },
};

export default config;</code></pre>



<ul class="wp-block-list">
<li>Định nghĩa một đối tượng Apollo client và hàm addApolloState ở file src/lib/apolloClient.ts</li>
</ul>



<pre class="wp-block-code"><code>import {
  ApolloClient,
  InMemoryCache,
  type NormalizedCacheObject,
} from '@apollo/client';
import merge from 'deepmerge';
import isEqual from 'lodash.isequal';


const COUNTRIES_API = 'https://countries.trevorblades.com';

export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__';

let apolloClient: ApolloClient&lt;NormalizedCacheObject&gt; | null;

function createApolloClient() {   
  return new ApolloClient({
    ssrMode: typeof window === 'undefined', 
    uri: typeof window === 'undefined' ? `${process.env.NEXT_PUBLIC_API_URL}` : '/api',
    cache: new InMemoryCache(),
  });
}

export function initApolloClient(initialState?: any) {
  const _apolloClient = apolloClient ?? createApolloClient();

  if (initialState) {
    const existingCache = _apolloClient.cache.extract();

    const data = merge(initialState, existingCache, {
      arrayMerge: (destinationArray, sourceArray) =&gt; &#91;
        ...sourceArray,
        ...destinationArray.filter((d) =&gt;
          sourceArray.every((s) =&gt; !isEqual(d, s))
        ),
      ],
    });
    _apolloClient.cache.restore(data);
  }

  if (typeof window === 'undefined') {
    return _apolloClient;
  }

  if (!apolloClient) {
    apolloClient = _apolloClient;
  }

  return _apolloClient;
}

export function addApolloState(
  client: ApolloClient&lt;NormalizedCacheObject&gt;,
  pageProps: any
) {
  if (pageProps?.props) {
    pageProps.props&#91;APOLLO_STATE_PROP_NAME] = client.cache.extract();
  }

  return pageProps;
}</code></pre>



<p class="wp-block-paragraph">Hàm <code>createApolloClient</code> trả về một đối tượng Apollo client: tham số uri định nghĩa API_URL. Khi ở server side call API sẽ gọi trực tiếp đến API_URL của BE, còn phía client side sẽ gọi thông qua proxy mình đã tạo ở trên để tăng tính bảo mật cho API_URL </p>



<p class="wp-block-paragraph">Hàm <code>initApolloClient</code> đảm bảo trong suốt phiên làm việc của web chỉ có duy nhất 1 đối tượng Apollo client được khởi tạo</p>



<p class="wp-block-paragraph">Hàm <code>addApolloState</code> sẽ đẩy data đã được fetch từ server side vào 1 prop có tên là __APOLLO_STATE__ sau đó sẽ được gửi đến client side thông qua pageProps là giá trị mặc định trả về của hàm <code>getServerSideProps</code></p>



<ul class="wp-block-list">
<li>Tiếp theo ta sẽ tạo một hook useApollo ở file src/hooks/useApollo.ts. Hook này sẽ theo dõi sự thay đổi của prop __APOLLO_STATE__ và sẽ quyết định tạo mới một đối tượng Apollo client nếu chưa có hoặc ngược lại sẽ trả về đối tượng Apollo client đã tồn tại</li>
</ul>



<pre class="wp-block-code"><code>import { APOLLO_STATE_PROP_NAME, initApolloClient } from '@/lib/apolloClient';
import { useMemo } from 'react';

function useApollo(pageProps: any) {
  const state = pageProps&#91;APOLLO_STATE_PROP_NAME];
  const client = useMemo(() =&gt; initApolloClient(state), &#91;state]);

  return client;
}

export default useApollo;
</code></pre>



<p class="wp-block-paragraph">Đến đây mọi thứ đã gần như hoàn thiện rồi việc cuối cùng là tạo một query ví dụ và kiểm tra xem SSR có hoạt động đúng như mong đợi của chúng ta hay không thôi</p>



<h2 id="thu-nghiem" class="wp-block-heading">Thử nghiệm</h2>



<ul class="wp-block-list">
<li>Tạo một query document ở file src/graphql/queries/getPokemons.query.gql. Ở đây mình muốn lấy danh sách tất cả tên các Pokemon nên là câu query sẽ dạng như thế này:</li>
</ul>



<pre class="wp-block-code"><code>query getPokemons {
  pokemon_v2_pokemonspecies {
    id
    name
  }
}</code></pre>



<ul class="wp-block-list">
<li>Chạy lệnh <code>npm run codegen</code> để graphql-codegen đọc file schema và các file document để tạo ra các đối tượng document và hook và NextJS có thể đọc được nhé</li>



<li>File src/pages/index.tsx mình sửa lại để call hook <code>useGetPokemonsQuery</code> và render data nhé.</li>
</ul>



<pre class="wp-block-code"><code>import { GetPokemonsDocument, useGetPokemonsQuery } from '@/__generated__/graphql'
import { addApolloState, initApolloClient } from '@/lib/apolloClient'

export default function Home() {
  const { data } = useGetPokemonsQuery()

  return (
    &lt;main
      className={`flex min-h-screen flex-col items-center justify-between p-24`}
    &gt;
      &lt;div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm flex flex-col"&gt;
        {data?.pokemon_v2_pokemonspecies.map((pokemon) =&gt; (
          &lt;div key={pokemon.id}&gt;{pokemon.name}&lt;/div&gt;
        ))}
      &lt;/div&gt;
    &lt;/main&gt;
  )
}</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="578" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-1024x578.png" alt="" class="wp-image-2860" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-1024x578.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-300x169.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-768x434.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-1536x867.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-2048x1156.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-380x215.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-800x452.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM-1160x655.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.50.38-PM.png 2880w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="580" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-1024x580.png" alt="" class="wp-image-2861" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-1024x580.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-300x170.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-768x435.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-1536x869.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-2048x1159.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-380x215.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-800x453.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM-1160x657.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.51.09-PM.png 2880w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Code thì đã chạy ổn đã fetch được data từ Graphql nhưng vẫn chưa đúng với mục tiêu của blog đưa ra là trang này phải chạy ở mode SSR. Vậy thì thêm tiếp dòng code dưới ở src/pages/index.tsx nhé.</p>



<pre class="wp-block-code"><code>export const getServerSideProps = async () =&gt; {
  const client = initApolloClient();
  await client.query({ query: GetPokemonsDocument});

  return addApolloState(client, {
    props: {},
  });
};</code></pre>



<p class="wp-block-paragraph">Đây là kết quả cuối cùng. Ở tab network ta sẽ không còn thấy request /api với payload getPokemons nữa và khi ta bật tab view page source nên sẽ thấy file html gen đầy đủ các thẻ div chứa các tên pokemon</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="581" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-1024x581.png" alt="" class="wp-image-2862" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-1024x581.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-300x170.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-768x436.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-1536x871.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-2048x1162.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-380x216.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-800x454.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM-1160x658.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-24-at-6.56.52-PM.png 2880w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Khi mọi người đọc code sẽ thấy query được gọi 2 lần. </p>



<p class="wp-block-paragraph">1 lần ở getServerSideProps: <code>await client.query({ query: GetPokemonsDocument}); </code><br>Và 1 lần nữa ở function component: <code>const { data } = useGetPokemonsQuery()</code>. Sẽ tự hỏi liệu việc gọi lại này có phải sẽ tạo 2 request đến BE không thì câu trả lời là không nhé. Vì gọi lại này nhằm mục đích tận dụng các thuộc tính data, các phương thức refresh do Apollo cung cấp, dễ quản lý dữ liệu ở function component hơn và data lấy từ function component là data được trả về thông qua pageProps mình đã giải thích ở trên nên không ảnh hưởng đến hiệu suất của trang web.<br></p>



<p class="wp-block-paragraph">Mn có thể tham khảo code đầy đủ hơn ở: <a href="https://github.com/long-la-tomosia/nextjs-ssr-apollo/tree/main">https://github.com/long-la-tomosia/nextjs-ssr-apollo/tree/main</a></p>



<p class="wp-block-paragraph">Bài viết dựa trên việc nghiên cứu của 1 cá nhân nên có thể sẽ có 1 vài sai sót mong mọi người góp ý và chia sẻ để hoàn thiện hơn. Cảm ơn mọi người đã theo dõi bài viết : D</p>
<p>The post <a href="https://blog.tomosia.com.vn/khoi-tao-du-an-voi-apollo-va-nextjs-ssr/">Khởi tạo dự án với Apollo và NextJS SSR</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/khoi-tao-du-an-voi-apollo-va-nextjs-ssr/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Rails console command made easier</title>
		<link>https://blog.tomosia.com.vn/rails-console-command-made-easier/</link>
					<comments>https://blog.tomosia.com.vn/rails-console-command-made-easier/#comments</comments>
		
		<dc:creator><![CDATA[Minh Tang]]></dc:creator>
		<pubDate>Tue, 12 Dec 2023 02:20:26 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2546</guid>

					<description><![CDATA[<p>Command trong rails thực sự rất là nhiều và củng không nhớ hết được syntax. Đâu đó thì&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/rails-console-command-made-easier/">Rails console command made easier</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Command trong rails thực sự rất là nhiều và củng không nhớ hết được syntax. Đâu đó thì củng chỉ nhớ được 1 vài task hay dùng, thay vì mỗi lần như vậy mình phải gõ cmd <code>bin/rails -T</code> để xem các command line có sẵn</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="rails db:create # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_...
rails db:drop # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_EN...
rails db:encryption:init  # Generate a set of keys for configuring Active Record encryption in a given environment
rails db:environment:set  # Set the environment value for the database
rails db:fixtures:load  # Loads fixtures into the current environment's database
rails db:migrate  # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rails db:migrate:down # Runs the &quot;down&quot; for a given migration VERSION
rails db:migrate:redo # Rolls back the database one migration and re-migrates up (options: STEP=x, VERSION=x)
rails db:migrate:status # Display status of migrations
rails db:migrate:up # Runs the &quot;up&quot; for a given migration VERSION
rails db:prepare # Runs setup if database does not exist, or runs migrations if it does
rails db:reset # Drops and recreates all databases from their schema for the current environment and ...
rails db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rails db:schema:cache:clear # Clears a db/schema_cache.yml file
rails db:schema:cache:dump  # Creates a db/schema_cache.yml file
rails db:schema:dump  # Creates a database schema file (either db/schema.rb or db/structure.sql, depending on...
rails db:schema:load  # Loads a database schema file (either db/schema.rb or db/structure.sql, depending on ...
rails db:seed # Loads the seed data from db/seeds.rb
rails db:seed:replant # Truncates tables of each database for current environment and loads the seeds
rails db:setup  # Creates all databases, loads all schemas, and initializes with the seed data (use db...
rails db:version  # Retrieves the current schema version number
rails test:db # Run tests quickly, but also reset db" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">create </span><span style="color: #7B7F8B"># Creates the database from DATABASE_URL or config/database.yml for the current RAILS_...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">drop </span><span style="color: #7B7F8B"># Drops the database from DATABASE_URL or config/database.yml for the current RAILS_EN...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">encryption</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">init  </span><span style="color: #7B7F8B"># Generate a set of keys for configuring Active Record encryption in a given environment</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">environment</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">set  </span><span style="color: #7B7F8B"># Set the environment value for the database</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">fixtures</span><span style="color: #F286C4">:</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Loads fixtures into the current environment&#39;s database</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">migrate  </span><span style="color: #7B7F8B"># Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">migrate</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">down </span><span style="color: #7B7F8B"># Runs the &quot;down&quot; for a given migration VERSION</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">migrate</span><span style="color: #F286C4">:redo</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B"># Rolls back the database one migration and re-migrates up (options: STEP=x, VERSION=x)</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">migrate</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">status </span><span style="color: #7B7F8B"># Display status of migrations</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">migrate</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">up </span><span style="color: #7B7F8B"># Runs the &quot;up&quot; for a given migration VERSION</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">prepare </span><span style="color: #7B7F8B"># Runs setup if database does not exist, or runs migrations if it does</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">reset </span><span style="color: #7B7F8B"># Drops and recreates all databases from their schema for the current environment and ...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">rollback </span><span style="color: #7B7F8B"># Rolls the schema back to the previous version (specify steps w/ STEP=n)</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">schema</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">cache</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">clear </span><span style="color: #7B7F8B"># Clears a db/schema_cache.yml file</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">schema</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">cache</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">dump  </span><span style="color: #7B7F8B"># Creates a db/schema_cache.yml file</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">schema</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">dump  </span><span style="color: #7B7F8B"># Creates a database schema file (either db/schema.rb or db/structure.sql, depending on...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">schema</span><span style="color: #F286C4">:</span><span style="color: #97E1F1">load</span><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Loads a database schema file (either db/schema.rb or db/structure.sql, depending on ...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">seed </span><span style="color: #7B7F8B"># Loads the seed data from db/seeds.rb</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">seed</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">replant </span><span style="color: #7B7F8B"># Truncates tables of each database for current environment and loads the seeds</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">setup  </span><span style="color: #7B7F8B"># Creates all databases, loads all schemas, and initializes with the seed data (use db...</span></span>
<span class="line"><span style="color: #F6F6F4">rails db</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">version  </span><span style="color: #7B7F8B"># Retrieves the current schema version number</span></span>
<span class="line"><span style="color: #F6F6F4">rails test</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4">db </span><span style="color: #7B7F8B"># Run tests quickly, but also reset db</span></span></code></pre></div>



<p class="wp-block-paragraph">Thay vì phải nhớ hết như vậy thì ta nên thiết lập alias cho đơn giản.</p>



<p class="wp-block-paragraph">Thiết lập alias tại: <code>vi ~/.bashrc</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="alias br='bin/rails'
alias be='bundle exec'
alias brdm='bin/rails db:migrate RAILS_ENV=development &amp;&amp; bin/rails db:migrate RAILS_ENV=test'
alias brdrol='bin/rails db:rollback STEP=1 &amp;&amp; bin/rails db:rollback STEP=1 RAILS_ENV=test'
alias bi='bundle install'
alias ees=&quot;export EDITOR='subl -w'&quot;
alias bd=&quot;./bin/dev&quot;
alias brdcm=&quot;br db:drop db:create db:migrate&quot;
alias brdcms=&quot;br db:drop db:create db:migrate db:seed&quot;
alias rcr=&quot;bin/rails cypress:run&quot;
alias rco=&quot;bin/rails cypress:open&quot;
alias resetdbtest='bin/rails db:drop db:create db:migrate RAILS_ENV=test'
alias resetdbdev='bin/rails db:drop db:create db:migrate RAILS_ENV=development'
alias brg='bin/rails generate'
alias brt='bin/rails test'
alias fd=&quot;foreman start -f Procfile.dev&quot;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> br</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> be</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bundle exec</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brdm</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails db:migrate RAILS_ENV=development &amp;&amp; bin/rails db:migrate RAILS_ENV=test</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brdrol</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails db:rollback STEP=1 &amp;&amp; bin/rails db:rollback STEP=1 RAILS_ENV=test</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> bi</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bundle install</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> ees</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">export EDITOR=&#39;subl -w&#39;</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> bd</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">./bin/dev</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brdcm</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">br db:drop db:create db:migrate</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brdcms</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">br db:drop db:create db:migrate db:seed</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> rcr</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">bin/rails cypress:run</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> rco</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">bin/rails cypress:open</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> resetdbtest</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails db:drop db:create db:migrate RAILS_ENV=test</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> resetdbdev</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails db:drop db:create db:migrate RAILS_ENV=development</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brg</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails generate</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> brt</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bin/rails test</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">alias</span><span style="color: #F6F6F4"> fd</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">foreman start -f Procfile.dev</span><span style="color: #DEE492">&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">Happy Coding!</p>
<p>The post <a href="https://blog.tomosia.com.vn/rails-console-command-made-easier/">Rails console command made easier</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/rails-console-command-made-easier/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Ước tính RAM / CPU cần sử dụng cho một website</title>
		<link>https://blog.tomosia.com.vn/uoc-tinh-ram-cpu-can-su-dung-cho-mot-website/</link>
					<comments>https://blog.tomosia.com.vn/uoc-tinh-ram-cpu-can-su-dung-cho-mot-website/#comments</comments>
		
		<dc:creator><![CDATA[Anh Le]]></dc:creator>
		<pubDate>Fri, 08 Dec 2023 10:08:27 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<category><![CDATA[Kinh nghiệm]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2333</guid>

					<description><![CDATA[<p>Ước lượng tài nguyên (RAM và CPU) cần thiết cho một website là một quá trình phức tạp&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/uoc-tinh-ram-cpu-can-su-dung-cho-mot-website/">Ước tính RAM / CPU cần sử dụng cho một website</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ước lượng tài nguyên (RAM và CPU) cần thiết cho một website là một quá trình phức tạp và phụ thuộc vào nhiều yếu tố khác nhau. Dưới đây là một số bước và yếu tố mà bạn có thể xem xét khi ước lượng tài nguyên:</p>



<ul class="wp-block-list">
<li><strong>Lượng Truy Cập (Traffic)</strong>: Ước lượng số lượt truy cập trang web hàng ngày, hàng giờ, hoặc trong khoảng thời gian nào đó.</li>



<li><strong>Loại Website</strong>: Loại website của bạn ảnh hưởng đến lượng tài nguyên cần thiết. Ví dụ, một trang web dạng blog có thể yêu cầu ít tài nguyên hơn so với một trang web thương mại điện tử phức tạp.</li>



<li><strong>Nội Dung Động</strong>: Nếu trang web của bạn chứa nhiều nội dung động, ví dụ như video, hình ảnh động, hay tính năng tương tác cao, sẽ đòi hỏi nhiều tài nguyên hơn so với trang web tĩnh.</li>



<li><strong>Cơ Sở Dữ Liệu</strong>: Kích thước cơ sở dữ liệu và số lượng truy vấn đối với cơ sở dữ liệu cũng quan trọng. Một trang web sử dụng cơ sở dữ liệu lớn và phức tạp sẽ đòi hỏi nhiều tài nguyên hơn.</li>



<li><strong>Cache và Tối Ưu Hóa</strong>: Sử dụng kỹ thuật tối ưu hóa và cache có thể giảm áp lực lên tài nguyên server.</li>



<li><strong>Dịch Vụ Bên Ngoài</strong>: Nếu bạn sử dụng các dịch vụ bên ngoài như CDN (Content Delivery Network) hoặc các dịch vụ quản lý nội dung, có thể giảm áp lực lên server của bạn.</li>



<li><strong>Đánh Giá Hiệu Năng Hệ Thống</strong>: Đánh giá hiệu năng của server hiện tại để xem xét việc nâng cấp.</li>



<li><strong>Dự Đoán Mở Rộng</strong>:Nếu bạn dự định mở rộng website trong tương lai, hãy xem xét các yếu tố mở rộng và dự đoán nhu cầu tài nguyên tương lai.</li>



<li><strong>Thử Nghiệm và Đo Lường</strong>: Thử nghiệm tải và đo lường hiệu suất có thể cung cấp thông tin quan trọng về cách website của bạn phản ứng dưới áp lực.</li>



<li><strong>Chính Sách và Yêu Cầu Kỹ Thuật</strong>: Xác định các chính sách và yêu cầu kỹ thuật cụ thể của bạn, chẳng hạn như thời gian phản hồi tối đa.</li>



<li>&#8230;</li>
</ul>



<h5 id="vay-cau-hinh-phan-cung-bao-nhieu-la-phu-hop" class="wp-block-heading">Vậy cấu hình phần cứng bao nhiêu là phù hợp?</h5>



<p class="wp-block-paragraph">Trước tiên cần xác định số lượng người truy cập trong một ngày cũng như số lượng người online đồng thời trung bình. Bạn có thể sử dụng Google Analytics để thống kê và phân tích dữ liệu người dùng khi truy cập website của bạn.<br>Đối với các loại website load nhẹ chỉ có bài viết, tin tức, hình ảnh vừa phải và có traffic dưới 2000 visit mỗi ngày hoặc website load nặng nhiều bài viết, hình ảnh và có traffic dưới 1000 visit mỗi ngày thì bạn có thể cấu hình tầm 512MB RAM và 1core CPU.<br>Dưới đây là bảng cấu hình tham khảo dành cho bạn dựa theo lượt truy cập.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>Visits/Day</td><td>RAM</td><td>CPU</td></tr><tr><td>1000 &#8211; 3000</td><td>1 GB</td><td>1 core</td></tr><tr><td>3000-6000</td><td>1-2 GB</td><td>1-2 core</td></tr><tr><td>6000-8000</td><td>2-4 GB</td><td>1-2 core</td></tr><tr><td>&gt;8000</td><td>&gt; 4 GB</td><td>&gt;= 1 core<br></td></tr></tbody></table></figure>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph">Nên chọn CPU có nhiều nhân (core) cho máy chủ. Số lượng nhân CPU càng nhiều thì nó sẽ càng có lợi cho người sử dụng. CPU nhanh hơn thì máy chủ nhanh hơn, nó giúp cho quá trình sử lý dữ liệu diễn ra nhanh gấp nhiều lần. Hãy luôn ưu tiên CPU nhiều nhân, một CPU có ít nhân nhưng có xung nhịp cao sẽ không tốt bằng một CPU có nhiều nhân với xung nhịp thấp hơn.<br>Đầu tư tối đa RAM cho cấu hình máy chủ. Cấu hình server với bộ nhớ RAM cao giúp sử lý được nhiều dữ liệu cùng một lúc, tránh tình trạng máy tính bị đơ hay treo máy.</p>



<p class="wp-block-paragraph">Đối với những website phát triển nhanh, số lượng dữ liệu lưu trữ ngày càng nhiều dẫn đến database ngày càng tăng. Bạn cần thường xuyên theo dõi mức độ sử dụng tài nguyên RAM, và có kế hoạch nâng cấp khi lượng RAM gần hết vì database càng lớn thì nhu cầu sử dụng RAM càng nhiều.</p>



<h5 id="tong-ket" class="wp-block-heading">Tổng kết</h5>



<p class="wp-block-paragraph">Một máy chủ có thể có cấu hình tối ưu với khoảng 4-8 GB RAM và 2-4 CPU core cho một website với lượng truy cập trung bình. Tuy nhiên, điều này chỉ là một ước lượng tổng quát và bạn nên thử nghiệm trên môi trường thực tế, và dự theo sự thay đổi của các yếu tố mình đã chia sẽ ở trên mà cập nhật lại để đảm bảo website đáp ứng đúng với yêu cầu cụ thể của bạn.</p>



<p class="wp-block-paragraph">Cảm ơn các bạn đã đọc bài viết của mình, chúc mọi người có thêm nhiều kiến thức hay để phục vụ cho dự án!</p>
<p>The post <a href="https://blog.tomosia.com.vn/uoc-tinh-ram-cpu-can-su-dung-cho-mot-website/">Ước tính RAM / CPU cần sử dụng cho một website</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/uoc-tinh-ram-cpu-can-su-dung-cho-mot-website/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>MOVE ON</title>
		<link>https://blog.tomosia.com.vn/move-on/</link>
					<comments>https://blog.tomosia.com.vn/move-on/#comments</comments>
		
		<dc:creator><![CDATA[trang chu]]></dc:creator>
		<pubDate>Fri, 08 Dec 2023 09:36:17 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2355</guid>

					<description><![CDATA[<p>Trong cuộc sống thường ngày đang có rất nhiều người bị mắc kẹt trong những nỗi đau từ&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/move-on/">MOVE ON</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><kbd>Trong cuộc sống thường ngày đang có rất nhiều người bị mắc kẹt trong những nỗi đau từ quá khứ, gia đình,&nbsp;bạn bè, yêu đương đến quan hệ công việc hay ngoài xã hội. Move on là 1 trong rất nhiều bài của NPV Podcast, 1 cách nhìn nhận về những sự thật mà ai cũng cần phải chấp nhận và 1 góc nhìn giúp chúng ta lớn lên và bao dung với chính nỗi đau, sự tổn thương của bản thân.</kbd></p>



<p class="wp-block-paragraph"><code>Đây là 1 bài post được nghe và viết lại, hy vọng sẽ là 1 bài post hữu ích cho nhiều người và chúc cho những ai đang tự giam cầm, đắm chìm bản thân vào những nỗi đau, thì có thể mở lòng, bỏ qua chuyện cũ và bước tiếp về tương lai. </code>&nbsp;</p>



<p class="wp-block-paragraph"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color"><strong>                                        MOVE ON &#8211; Khép lại chuyện cũ và bước tiếp </strong></mark></p>



<p class="wp-block-paragraph"><code>Một điều rất rõ ràng, việc bỏ qua những chuyện cũ, những tổn thương và nỗi đau đến từ nhiều khía cạnh khác nhau trong cuộc sống để bước tiếp về tương lai là điều không hề dễ dàng. Bất kì nỗi đau nào đã đến với mình, đều là những vết thương sưng tấy và rỉ máu bất chấp thời gian. Nó làm cho mình cảm thấy tổn thương và buồn bã mỗi khi nghĩ tới, đó là những nỗi đau không tên và có thể đi theo suốt cuộc đời của mình, khiến cho mình mắc kẹt ngay tại đó và không cách nào thoát ra được để có thể tiếp tục bước tiếp về phía trước. </code></p>



<p class="wp-block-paragraph"><code>Trong cuộc đời của 1 con người chắc chắn sẽ có rất nhiều vết thương như vậy, và cũng có rất nhiều người đau quá mà gục ngã không thể đứng lên, vì giận quá mà kẹt luôn ở trong đó không thoát ra được, chán nản và tuyệt vọng quá mà đầu hàng, bỏ cuộc với tương lai. Thực tế trong cuộc sống hiện đại ngày nay đã có những trường hợp vì quá chán nản tuyệt vọng mà đi đến những cái kết không mong muốn.&nbsp;</code></p>



<figure class="wp-block-image"><img decoding="async" src="https://llv.edu.vn/media/2017/11/Untitled_Artwork-8-1024x429.png" alt="Thuần thục “tần tần tật” 4 dạng câu hỏi trong tiếng Anh chỉ bằng 1 click! –  Language Link Academic"/></figure>



<p class="wp-block-paragraph"><code>Câu hỏi cần hỏi ở đây là: “Mình có cần phải như vậy hay không?”, “Có cần phải hy sinh cả cuộc đời còn lại để trả giá cho 1 lần vấp ngã hay tổn thương không? “, “Nó có phải là 1 cái giá quá đắt nếu làm như vậy không?" và “Có đáng phải quằn quại trong trong nỗi đau đó hay không?" trong khi người khác, dù họ vô tình hay cố ý gây ra tổn thương cho bạn, có khi vẫn đang vui vẻ, nhởn nhơ và</code> <code>cười trên nỗi đau của bạn ấy chứ. Vậy có đáng không khi bạn đang tự gánh chịu nỗi đau 1 mình, vật lộn mỗi ngày với nó</code>?</p>



<p class="wp-block-paragraph"><code>Có 1 số sự thật mà bản thân bạn cần phải chấp nhận để có thể move on, và việc chấp nhận được sự thật là 1 điểm tựa để mà bạn có thể bỏ lại quá khứ và hướng về 1 tương lai tốt hơn.&nbsp;</code></p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="225" height="225" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-13.png" alt="" class="wp-image-2362" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-13.png 225w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-13-150x150.png 150w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-13-80x80.png 80w" sizes="auto, (max-width: 225px) 100vw, 225px" /></figure>
</div>


<ol class="wp-block-list">
<li><strong>Bạn đau cỡ nào không ai quan tâm</strong></li>
</ol>



<ul class="wp-block-list">
<li><code>Đừng nghĩ rằng bạn đau, bạn nói về nó, la làng lên cho tất cả mọi người biết thì sẽ có nhiều người quan tâm. Sự thật là <strong>KHÔNG</strong>. Người ta chỉ là hỏi thăm vậy thôi và rồi cuối cùng chỉ có 1 mình bản thân bạn, ở đây để đối mặt với mọi thứ. </code></li>



<li><code>Bạn nghĩ chuyện xảy ra thật khủng khiếp và quá sức, khiến bạn gục ngã và sống quằn quại trong đó. Nhưng sự thật là người gây tổn thương cho bạn, họ không quan tâm, đôi khi họ còn không biết rằng đã gây ra sự tổn thương đó, họ không hối hận và không 1 lần nhìn lại xem bạn tổn thương như thế nào. Họ có cuộc đời riêng với nhiều vấn đề khác nhau và họ bận bịu với nó. Thế nên họ không nhớ đến câu chuyện của bạn đâu. </code></li>



<li><code>Bản chất của mỗi con người luôn tồn tại sự ích kỉ, người ta chỉ quan tâm chuyện của bản thân và không có mấy ai đi lo chuyện bao đồng, quan tâm đến người khác đâu. Cho nên bạn cũng không cần diễn lại để có được sự tội nghiệp, chú ý hay sự quan tâm của người ta. </code></li>



<li><code>Và cũng đừng chờ đợi hay mong mỏi có ai đó hiện ra như ông bụt và xoa dịu nỗi đau giúp bạn. Điều đó chỉ có trong chuyện có tích thôi, nó không có ở đời thực. Ông bụt chính là Nội lực, Động lực và là Bản lĩnh của chính bạn. Khi hiểu ra được, đời này chỉ có bạn cứu được chính mình, thì bạn sẽ tự đứng lên, dũng cảm bước ra và bắt đầu 1 hành trình mới.</code></li>
</ul>



<figure class="wp-block-image"><img decoding="async" src="https://i.ytimg.com/vi/XVpTRphZoEA/maxresdefault.jpg" alt="Zoe Wees - Don't Give Up (Lyrics) - YouTube"/></figure>



<p class="wp-block-paragraph">2. <strong>Bỏ cuộc là điều họ muốn</strong> </p>



<ul class="wp-block-list">
<li><code>Có người đã nói thế này “Cái người hả hê nhất khi bạn đau đớn, giận dữ, gục ngã chính là kẻ gây ra nỗi đau đó cho bạn vì người ta biết rằng điều người ta nói, làm và hành xử sẽ khiến bạn đau đớn." Vậy nên khi họ tổn thương bạn, người hả hê nhất khi thấy bạn bị kẹt trong đó không cách nào thoát ra được chính là họ</code>. <code>Thế thì tại sao bạn lại để họ điều khiển bản thân nhỉ? Tại sao bạn lại phải khổ sở đúng như họ mong muốn? Đây là 1 sự thật rất chua chát</code> <code>nhưng bạn phải chấp nhận sự thực rằng cảm xúc của bạn đang bị điều khiển bởi người khác. </code></li>



<li><code>Khi bạn tuyên bố bỏ cuộc, bạn nghĩ thế giới sẽ xúm vào đau lòng ư? Hoang tưởng!! =))) Người muốn bạn bỏ cuộc đang vui vẻ vì điều đó. Người quen bạn sẽ buồn bã lắc đầu và rồi cũng sẽ bỏ đi để lo chuyện của họ. Đám đông còn lại, họ cần được giải trí, cho nên chuyện của bạn càng nghiệt ngã thì họ càng thích thú,</code> <code>càng drama thì họ càng hào hứng vì có nhiều đề tài để bàn tán và tranh cãi. Như vậy nghĩa là bạn đang tự mang nỗi đau của mình ra cho đời giải trí. Không ai quan tâm đến bạn, họ chỉ muốn giải trí trên câu chuyện của bạn. Người ta sẽ xem bạn diễn, sau đó cười cợt cho đã và bỏ đi, không 1 chút bận tâm. Vậy thì bạn lăn ra đó đau khổ để làm gì?</code></li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRPxKYQQA3Sr2fi-osOJTykNIoedHJz57gsEw&amp;usqp=CAU" alt="Move on trong tình yêu là gì? Có ý nghĩa đặc biệt hay không?"/></figure>
</div>


<p class="wp-block-paragraph">3.<strong> Lớn lên &amp; bao dung với nỗi đau cũ</strong></p>



<ul class="wp-block-list">
<li><code>Bạn phải sống cuộc đời của bạn, bởi vì đây chính là cuộc đời, niềm vui, hạnh phúc của cá nhân bạn, và nó không hề liên quan tới bất kì ai cả. Nếu bạn không tự đứng dậy, không tự thoát ra để bước tiếp thì sẽ không có ai khác làm chuyện đó cho bạn cả.&nbsp;</code></li>
</ul>



<ul class="wp-block-list">
<li><code>”Người ta đối xử như vậy thì mình phải trả thù” :D Có cần thiết như vậy không? Khi bạn lớn lên, lớn hơn cả vấn đề và nỗi đau, bao dung được với cả những con người như vậy thì cuộc đời của bạn sẽ tích cực hơn, vui vẻ hơn, thành công hơn, đầy màu sắc lung linh hơn. Như vậy sẽ hay hơn so với việc suốt ngày nghĩ cách trả thù và loay hoay trong mớ lòng bong suốt đời, phải không? </code></li>



<li><code>Khi bạn bao dung được cả nỗi đau, thì sẽ không có ai đau đớn ở đây cả, ngược lại chính nỗi đau đó là điểm tựa để bạn lớn hơn và hạnh phúc hơn. Và rồi chuyện cũ sẽ thành chuyện nhỏ, có khi bạn sẽ thấy thương người đó, vì sự hẹp hòi và nhỏ nhen của họ. Có khi người ta tổn thương bạn chỉ vì chính họ cũng đang bị tổn thương</code>. <code> Như vậy thì đó chính là vấn đề của người ta, nó không phải vấn đề của bạn. Thế nên bạn không việc gì phải lăn ra đau khổ vì sự tổn thương của một ai khác cả.</code></li>



<li><code>Và rồi bạn nhận ra chuyện tự lăn ra đau khổ thật là vô lí và rất phí thời gian, năng lượng, tuổi thanh xuân và cơ hội của bạn. Thế nên bạn đứng lên, khép lại quá khứ và move on để tiếp tục hành trình, 1 hành trình hay ho và đầy hào hứng. </code></li>
</ul>



<p class="wp-block-paragraph"><code>Cuối cùng</code>, <code>mong cho những ai đang bị mắc kẹt, không thể bước tiếp, vì bất kì lí do &nbsp;nào, sau khi biết đến những chia sẻ này thì có thể nhìn nhận lại các vấn đề, chấp nhận sự thật và tìm được động lực để move on và tận hưởng 1 hành trình mới vui vẻ hơn trong tương l</code>ai.</p>
<p>The post <a href="https://blog.tomosia.com.vn/move-on/">MOVE ON</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/move-on/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Exception Filters là gì? Xử lý Unhandled Error trong NestJS</title>
		<link>https://blog.tomosia.com.vn/exception-filters-la-gi-xu-ly-unhandled-error-trong-nestjs/</link>
					<comments>https://blog.tomosia.com.vn/exception-filters-la-gi-xu-ly-unhandled-error-trong-nestjs/#comments</comments>
		
		<dc:creator><![CDATA[Hoang Nguyen]]></dc:creator>
		<pubDate>Wed, 06 Dec 2023 01:26:04 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<category><![CDATA[javascript]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2337</guid>

					<description><![CDATA[<p>Exception Filters Trong quá trình coding thì chúng ta sẽ không thể tránh khỏi những trường hợp lỗi&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/exception-filters-la-gi-xu-ly-unhandled-error-trong-nestjs/">Exception Filters là gì? Xử lý Unhandled Error trong NestJS</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="exception-filters" class="wp-block-heading">Exception Filters</h2>



<p class="wp-block-paragraph">Trong quá trình coding thì chúng ta sẽ không thể tránh khỏi những trường hợp lỗi không mong muốn và nếu như chúng ta throw ra lỗi đó qua phía client thì có thể dẫn đến lỗi bảo mật hay trải nghiệm người dùng không tốt.</p>



<p class="wp-block-paragraph">Để khắc phục tình trạng này thì NestJS có một <strong>Exception Layer</strong>. Layer này sẽ catch tất cả unhandled error và xử lý nó trước khi trả về Controller. Và tất nhiên chúng ta cũng có thể custom response hay handle lại error này trước khi nó được trả về.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="833" height="474" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11.png" alt="" class="wp-image-2338" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11.png 833w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11-300x171.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11-768x437.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11-380x216.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-11-800x455.png 800w" sizes="auto, (max-width: 833px) 100vw, 833px" /></figure>



<h2 id="default-response" class="wp-block-heading">Default Response</h2>



<pre class="wp-block-code"><code>{
  "statusCode": 500,
  "message": "Internal server error"
}</code></pre>



<h2 id="xu-ly-error-voi-exception-filters" class="wp-block-heading">Xử lý Error với Exception Filters</h2>



<p class="wp-block-paragraph">Để có thể handle sao cho phù hợp với từng loại Error thì chúng ta có thể <strong>Catch</strong> những Error mà ta mong muốn</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="@Catch(HttpException) // Http Error

@Catch(PrismaException) // Database Error

@Catch(CustomErrorInstance) // Custom Error" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">@</span><span style="color: #62E884">Catch</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">HttpException</span><span style="color: #F6F6F4">) </span><span style="color: #7B7F8B">// Http Error</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #62E884">Catch</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">PrismaException</span><span style="color: #F6F6F4">) </span><span style="color: #7B7F8B">// Database Error</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">@</span><span style="color: #62E884">Catch</span><span style="color: #F6F6F4">(</span><span style="color: #62E884; font-style: italic">CustomErrorInstance</span><span style="color: #F6F6F4">) </span><span style="color: #7B7F8B">// Custom Error</span></span></code></pre></div>



<p class="wp-block-paragraph">Sau khi đã <strong>Catch</strong> được Error mà mình mong muốn thì chúng ta sẽ phải implement lại class <strong>ExceptionFilter</strong> của NestJS và handle Error.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="@Catch()
export class InternalExceptionFilter implements ExceptionFilter {
  async catch(exception: HttpException, context: GqlExecutionContext) {
    if (exception instanceof HttpException) {
      if (exception.getStatus() !== 500) return exception;
    }
    //Handle Error on production environment
    if (process.env.NODE_ENV !== &quot;development&quot;) {
      const ctx = GqlExecutionContext.create(context).getContext();
      const req: Request &amp; { user: Partial&lt;User&gt; | null } = ctx.req;
      const { query, operationName, variables } = req.body;
      const authorization = {
        laboId: req.header(&quot;labo-id&quot;),
        userId: req.user?.databaseId,
      };

      const errorParams: ErrorTemplateParams = {
        hostname: req.hostname,
        query,
        payload: JSON.stringify({ operationName, variables }, null, 2),
        stack: exception.stack,
        errorName: exception.name,
        authorization: JSON.stringify(authorization, null, 2),
      };

      const slack = new SlackService();
      slack.sendMessage(errorParams);
      // Return default error response
      return new InternalServerErrorException(&quot;Internal_Error&quot;);
    }
    // return error + stacktrace
    return exception;
  }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">@</span><span style="color: #62E884">Catch</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">InternalExceptionFilter</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ExceptionFilter</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">async</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">catch</span><span style="color: #F6F6F4">(</span><span style="color: #FFB86C; font-style: italic">exception</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">HttpException</span><span style="color: #F6F6F4">, </span><span style="color: #FFB86C; font-style: italic">context</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">GqlExecutionContext</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (exception </span><span style="color: #F286C4">instanceof</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">HttpException</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (exception.</span><span style="color: #62E884">getStatus</span><span style="color: #F6F6F4">() </span><span style="color: #F286C4">!==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">500</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> exception;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">//Handle Error on production environment</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (process.env.</span><span style="color: #BF9EEE">NODE_ENV</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">!==</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">development</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> ctx </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> GqlExecutionContext.</span><span style="color: #62E884">create</span><span style="color: #F6F6F4">(context).</span><span style="color: #62E884">getContext</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> req</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Request</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">&amp;</span><span style="color: #F6F6F4"> { user</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Partial</span><span style="color: #F6F6F4">&lt;</span><span style="color: #FFB86C; font-style: italic">User</span><span style="color: #F6F6F4">&gt; </span><span style="color: #F286C4">|</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">null</span><span style="color: #F6F6F4"> } </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> ctx.req;</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> { query, operationName, variables } </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> req.body;</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> authorization </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        laboId</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> req.</span><span style="color: #62E884">header</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">labo-id</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">),</span></span>
<span class="line"><span style="color: #F6F6F4">        userId</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> req.user?.databaseId,</span></span>
<span class="line"><span style="color: #F6F6F4">      };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> errorParams</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ErrorTemplateParams</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">        hostname</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> req.hostname,</span></span>
<span class="line"><span style="color: #F6F6F4">        query,</span></span>
<span class="line"><span style="color: #F6F6F4">        payload</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">JSON</span><span style="color: #F6F6F4">.</span><span style="color: #62E884">stringify</span><span style="color: #F6F6F4">({ operationName, variables }, </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">),</span></span>
<span class="line"><span style="color: #F6F6F4">        stack</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> exception.stack,</span></span>
<span class="line"><span style="color: #F6F6F4">        errorName</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> exception.name,</span></span>
<span class="line"><span style="color: #F6F6F4">        authorization</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">JSON</span><span style="color: #F6F6F4">.</span><span style="color: #62E884">stringify</span><span style="color: #F6F6F4">(authorization, </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">2</span><span style="color: #F6F6F4">),</span></span>
<span class="line"><span style="color: #F6F6F4">      };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> slack </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">SlackService</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">      slack.</span><span style="color: #62E884">sendMessage</span><span style="color: #F6F6F4">(errorParams);</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #7B7F8B">// Return default error response</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">InternalServerErrorException</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Internal_Error</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// return error + stacktrace</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> exception;</span></span>
<span class="line"><span style="color: #F6F6F4">  }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">Thì ở đây mình đã <strong>Catch</strong> tất cả Error bao gồm <strong>Http Error</strong> và <strong>Database Error</strong> chỉ ngoại trừ những lỗi 4xx đã throw ra.</p>



<p class="wp-block-paragraph">Ở đây chúng ta có 2 tham số đó làm <strong>exception</strong> và <strong>context</strong> thì nó là cái gì?</p>



<p class="wp-block-paragraph"><strong>exception</strong> ở đây nó là lỗi nguyên bản nó thuộc về instance của <strong>Error</strong>. Thì trong exception nó sẽ bao gồm: stacktrace, message, errorName và error.</p>



<p class="wp-block-paragraph"><strong>context</strong> này nó được coi là host, chúng ta có thể sử dụng context này để có thể tham chiếu đến <strong>Request</strong> và <strong>Response</strong> và có thể custom lại Error chi tiết hơn để có thể tracking bug dễ dàng hơn.</p>



<h2 id="custom-response" class="wp-block-heading">Custom Response</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="409" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-1024x409.png" alt="" class="wp-image-2340" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-1024x409.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-300x120.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-768x307.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-1536x614.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-380x152.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-800x320.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12-1160x464.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/image-12.png 1584w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Và cuối cùng để NestJS có thể nhận được Exception Filter này thì t cần phải binding nó vào để có thể sử dụng như một global Filter.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">main.ts</span><span role="button" tabindex="0" data-code="// Handle Internal Error
  if (process.env.NODE_ENV !== &quot;development&quot;) {
    app.useGlobalFilters(new InternalExceptionFilter());
  }" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">// Handle Internal Error</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> (process.env.</span><span style="color: #BF9EEE">NODE_ENV</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">!==</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">development</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    app.</span><span style="color: #62E884">useGlobalFilters</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4; font-weight: bold">new</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">InternalExceptionFilter</span><span style="color: #F6F6F4">());</span></span>
<span class="line"><span style="color: #F6F6F4">  }</span></span></code></pre></div>



<h2 id="ket-bai" class="wp-block-heading">Kết bài:</h2>



<p class="wp-block-paragraph">NestJS là framework hỗ trợ mạnh mẽ với nhiều tính năng và Exception Filters là một trong số đó. Thì ở bài này chúng ta đã tìm hiểu qua ExceptionFilter là gì và Handle những Unhandled Error như thế nào. </p>



<p class="wp-block-paragraph">Hẹn gặp lại mọi người trong bài viết tìm hiểu về NestJS khác.</p>
<p>The post <a href="https://blog.tomosia.com.vn/exception-filters-la-gi-xu-ly-unhandled-error-trong-nestjs/">Exception Filters là gì? Xử lý Unhandled Error trong NestJS</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/exception-filters-la-gi-xu-ly-unhandled-error-trong-nestjs/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Cấu hình domain provider về Route53 (AWS)</title>
		<link>https://blog.tomosia.com.vn/cau-hinh-domain-provider-ve-route53-aws/</link>
					<comments>https://blog.tomosia.com.vn/cau-hinh-domain-provider-ve-route53-aws/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Truong]]></dc:creator>
		<pubDate>Fri, 01 Dec 2023 02:37:22 +0000</pubDate>
				<category><![CDATA[Chưa phân loại]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=404</guid>

					<description><![CDATA[<p>Mình đã đăng ký một domain từ tenten.vn, nhưng vì không muốn quản lý domain này ở console&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/cau-hinh-domain-provider-ve-route53-aws/">Cấu hình domain provider về Route53 (AWS)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Mình đã đăng ký một domain từ tenten.vn, nhưng vì không muốn quản lý domain này ở console của tenten và muốn làm việc đó thông qua Amazon Route 53. Mình đã setting chuyển về theo các bước bên dưới đây.</p>



<h2 id="tao-hosted-zone" class="wp-block-heading">Tạo Hosted Zone</h2>



<p class="wp-block-paragraph">Đầu tiên khi mình đã mua được domain thì cần login vào AWS và tìm kiếm ‘Route 53’. Chọn vào mục Host zone ở side menu. Mình khởi tạo hosted zones bằng cách click vào Create Host Zone.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="357" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-1024x357.png" alt="" class="wp-image-1604" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-1024x357.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-300x105.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-768x268.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-1536x535.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-380x132.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-800x279.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60-1160x404.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-60.png 2000w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Sau đó mình nhập domain đã mua ở tenten, để trống mục comment và chọn Public Host Zone cho mục Type. Mình click Create hosted zone và đợi một vài giây để Host Zone mới được tạo ra.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="783" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61.png" alt="" class="wp-image-1605" style="aspect-ratio:0.9833971902937421;width:404px;height:auto" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61.png 770w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61-295x300.png 295w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61-768x781.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61-80x80.png 80w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-61-380x386.png 380w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>



<p class="wp-block-paragraph">Sau khi tạo thành công cùng với đó sẽ có một tập các Name Server (NS) record được tạo ra. Các NS server này sẽ được sử dụng để trỏ domain từ tenten về Route53(AWS).</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="418" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-1024x418.png" alt="" class="wp-image-1606" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-1024x418.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-300x122.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-768x313.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-1536x627.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-380x155.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-800x326.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62-1160x473.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-62.png 2000w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="cau-hinh-ns-ve-route53aws" class="wp-block-heading">Cấu Hình NS về Route53(AWS)</h2>



<p class="wp-block-paragraph">Đăng nhập vào quản lý domain của tenten, mình chọn mục ‘Quản lý dịch vụ tên miền’.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="326" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-1024x326.png" alt="" class="wp-image-1607" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-1024x326.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-300x95.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-768x244.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-1536x488.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-380x121.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-800x254.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63-1160x369.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-63.png 2000w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Mình chọn vào ‘thao tác’ chọn mục ‘cài đặt NS’ domain mình muốn chuyển về Route53 (AWS), cập nhập và tạo các record NS dựa vào các giá trị record được tạo ra khi tạo mới hosted zone Route53 (bước ở trên).</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="860" height="751" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64.png" alt="" class="wp-image-1608" style="aspect-ratio:1.1451398135818909;width:382px;height:auto" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64.png 860w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64-300x262.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64-768x671.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64-380x332.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-64-800x699.png 800w" sizes="auto, (max-width: 860px) 100vw, 860px" /></figure>



<p class="wp-block-paragraph">Sau khi cập nhập chúng ta sẽ phải đợi một thời gian để tenten cấu hình trỏ về Route53.</p>



<h2 id="kiem-tra-domain-da-duoc-cau-hinh-thanh-cong-chua" class="wp-block-heading">Kiểm tra domain đã được cấu hình thành công chưa</h2>



<p class="wp-block-paragraph">Ta chỉ cần mở terminal và gỏ lệnh dưới để kiểm tra, trong đó <em>domain.com</em> là domain của mình.</p>



<pre class="wp-block-code"><code>nslookup -type=ns domain.com</code></pre>



<p class="wp-block-paragraph">Nếu cấu hình thành công, kết quả sẽ hiển thị ra theo dạng như sau:</p>



<pre class="wp-block-code"><code>erver:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
domain.com	nameserver = ns-xxxx.awsdns-09.org.
domain.com	nameserver = ns-xxxx.awsdns-09.org.
domain.com	nameserver = ns-xxxx.awsdns-09.org.
domain.com	nameserver = ns-xxxx.awsdns-09.org.</code></pre>
<p>The post <a href="https://blog.tomosia.com.vn/cau-hinh-domain-provider-ve-route53-aws/">Cấu hình domain provider về Route53 (AWS)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/cau-hinh-domain-provider-ve-route53-aws/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
	</channel>
</rss>
