Tại sao tôi vẫn sử dụng Django cho các dự án SaaS của mình vào năm 2025?

0 0 0

Người đăng: Vinh Phạm

Theo Viblo Asia

Ngày nay, thật dễ để nghĩ rằng Django đã lỗi thời. Những công cụ và stack hợp thời như Next.js, Supabase, Astro, và T3 Stack đang xuất hiện khắp nơi. Chúng trông nhanh, hiện đại và đi kèm rất nhiều tính năng tích hợp sẵn. Bạn có thể triển khai một ứng dụng full-stack chỉ trong vài giờ và bắt đầu nhanh chóng — đúng với điều quan trọng nhất đối với một nhà xây dựng: tạo ra giá trị.

Tuy nhiên, khi tôi cần xây dựng một backend SaaS thực thụ — đòi hỏi sự kiểm soát, cấu trúc rõ ràng và khả năng bảo trì lâu dài — tôi vẫn chọn Django là lựa chọn hàng đầu.

Nó có thể không phải là xu hướng, nhưng lại đáng tin cậy. Và khi kết hợp với những công cụ phù hợp, Django vô cùng mạnh mẽ.

Những điều các công cụ hiện đại làm đúng

Phải thừa nhận rằng Next.js và các công cụ tương tự rất ấn tượng. Bạn có frontend và backend cùng chỗ, triển khai lên Vercel chỉ bằng một cú click, và các thư viện như Prisma hay Auth.js giúp bạn thiết lập rất nhanh chóng.

Nếu bạn đang xây dựng:

  • Một landing page
  • Một prototype
  • Một MVP SaaS có độ phức tạp thấp

...thì stack JavaScript là quá tuyệt. Tôi cũng từng dùng và nó giúp tôi triển khai nhanh chóng. Nhưng tôi không thích sự hỗn loạn xuất hiện khi ứng dụng bắt đầu phát triển. Và đó là điểm mà tôi cho rằng Django làm tốt hơn.

Điều Django vẫn làm tốt hơn

Nếu Django và các công cụ hiện đại được ví như trong câu chuyện “Rùa và Thỏ”, thì tôi nghĩ Django chính là chú rùa. Khi sản phẩm của bạn bắt đầu cần:

  • Mô hình dữ liệu phức tạp hơn
  • API thực thụ
  • Hệ thống phân quyền tinh vi
  • Kiểm soát lâu dài với backend

...thì các stack frontend-first hiện đại bắt đầu trở nên hạn chế.

Django thì ngược lại — nó buộc bạn phải rõ ràng. Bạn phải định nghĩa model, serializer, view, và route một cách tường minh. Nghe có vẻ nhiều việc hơn ban đầu, nhưng cấu trúc này thực sự giúp ích. Logic được giữ sạch sẽ, dễ debug, và khi có người mới tham gia dự án, họ không phải đoán xem logic nằm ở đâu.

Chưa kể, với các công cụ AI như Copilot, GPT, hay Cursor, bạn có thể tạo hầu hết đoạn code "boilerplate" trong vài giây:

“Viết một ViewSet trong Django REST Framework cho model này và đăng ký nó trong router.”

Điều từng là lặp đi lặp lại nay chỉ cần một dòng prompt. Cuối cùng, chú rùa đã trở thành ninja rùa.

Tuy nhiên, khi xây dựng API SaaS đầu tiên với Django, tôi gặp một vấn đề: phân phối API Key cho người dùng.

API Key với Django là một cơn ác mộng

Và không, bạn không thể dùng JWT cho API Key. Đó là quan điểm của tôi. Một sai lầm phổ biến trong API SaaS là cách các dev xử lý authentication. Nhiều tổ chức sử dụng JWT cho tất cả mọi thứ — ứng dụng, người dùng, máy chủ, dịch vụ nội bộ và đối tác.

Nhưng JWT được sinh ra để xác thực người dùng, không phải dùng làm API Key cho bên ngoài. Nghe hơi trừu tượng, nên tôi sẽ giải thích:

1. JWT là loại token tạm thời

JWT thường được phát sau khi xác thực thành công (login) và:

  • Chứa thông tin mã hoá như ID người dùng, scopes...
  • Có thời hạn ngắn (thường 15 phút – 1 giờ)
  • Được refresh thường xuyên bằng refresh token

Bạn không thể thu hồi JWT một khi đã phát ra, trừ khi duy trì danh sách đen (blacklist) hoặc xoay vòng secret liên tục. Tính tự chứa của nó giúp xác thực nhanh, nhưng đồng nghĩa với việc một khi token được phát ra, nó sẽ hợp lệ cho đến khi hết hạn.

Mà API Key thì ngược lại — được phát một lần, dùng trong nhiều tuần/tháng. Để hỗ trợ thu hồi JWT, bạn phải lưu nó trong database — đi ngược lại với cách JWT được thiết kế.

2. JWT dẫn đến việc "nhồi nhét thông tin"

Tôi từng thấy các dev nhồi quá nhiều thứ vào JWT khi dùng làm API Key:

{ "sub": "org_abc", "role": "super", "scopes": ["read", "write"], "feature_flags": ["beta_search"], "tenant_id": "xyz"
}

Khi API Key không chỉ dùng cho quyền truy cập mà còn nhồi cả logic vào token, nó trở nên khó kiểm soát và nguy hiểm.

API Key nên là:

  • Dạng opaque (không chứa thông tin rõ ràng)
  • Đơn giản
  • Gắn với metadata trong ứng dụng (không phải logic phía client)

3. Không có chiến lược xoay vòng (rotation)

Nếu bạn thay đổi secret trong hệ thống JWT, tất cả token cũ đều vô hiệu hóa. Client sẽ bị lỗi ngay lập tức. Với JWT, bạn không thể hỗ trợ nhiều secret đồng thời một cách tự nhiên. Bạn phải:

  • Chấp nhận downtime và phát lại token, hoặc
  • Xây hệ thống quản lý key ID tùy chỉnh

Điều này làm phức tạp hóa việc bảo mật API Key. Trong khi đó, hệ thống API Key tốt cần có:

  • Hỗ trợ nhiều secret cùng lúc
  • Cho phép xoay vòng mượt mà
  • Tùy chỉnh scope hoặc rate limit

JWT không làm được điều này một cách dễ dàng.

Quay lại với Django, khi tôi xây dựng hệ thống thanh toán đầu tiên, tôi cần API Key authorization. May mắn thay, có gói: Django REST Framework API Key. Rất tiện, dễ triển khai, và hoạt động ổn.

Nhưng... tôi gặp vấn đề về hiệu năng.

Gói này tạo ra API Key, rồi lưu hash của key bằng framework mã hoá mật khẩu của Django — vốn được thiết kế an toàn chứ không nhanh. Khi có request, gói này phải hash key được gửi lên và tìm trong DB — chậm khi có nhiều permission logic chồng lên nhau.

Tôi đã tối ưu bằng cách chuyển sang dùng Argon2. Thời gian giảm từ 5 giây xuống còn khoảng 2.5 giây. Nhưng vẫn chưa đủ tốt.

Thêm nữa, gói này không hỗ trợ xoay vòng, và phải tự viết logging, tracking. Nó cũng không hoàn toàn theo pattern xác thực của Django, vốn dựa vào request.user.

Vậy là tôi quyết định tự xây gói: drf-simple-api-key

Tôi dùng Fernet (trong thư viện cryptography) để mã hóa API Key. Fernet hỗ trợ xoay vòng key một cách tự nhiên. Bạn có thể mã hóa bằng key hiện tại, rồi giải mã bằng key cũ nếu cần — hoàn hảo để xoay vòng mà không gây gián đoạn.

Tôi bắt đầu nhỏ, tập trung cải thiện hiệu năng và theo đúng mô hình permission của Django. Sau đó, tôi thêm:

  • Xác thực bằng API Key
  • Hỗ trợ xoay vòng
  • Theo dõi usage và thống kê

Ví dụ về cách dùng MultiFernet:

from cryptography.fernet import MultiFernet, Fernet # Your current and previous keys
current_key = Fernet(b'CURRENT_SECRET_KEY')
previous_key = Fernet(b'OLD_SECRET_KEY') # Use MultiFernet for seamless decryption
f = MultiFernet([current_key, previous_key]) # Encrypt new API keys using the current key
encrypted = f.encrypt(b"api-key:my-user-123") # Decrypt any key (old or new)
decrypted = f.decrypt(encrypted)

Tôi còn thêm setting để khởi động “cửa sổ xoay vòng”: hệ thống phát key mới bằng secret mới, nhưng vẫn chấp nhận key cũ. Khi kết thúc, có thể xoá secret cũ.

Kết quả: thời gian xử lý giảm từ 2.5 giây xuống dưới 100ms.

Vài suy nghĩ cuối cùng về Django và kiến trúc của nó

Điều tôi yêu thích ở Django là khả năng mở rộng hành vi thông qua hệ thống class của Python. Bạn không cần viết decorator hoặc middleware phức tạp — chỉ cần kế thừa class, override, và gọi super() đúng lúc.

Ví dụ:

class IsActiveEntity(BasePermission): message = "Entity is not active." def has_permission(self, request, view): return request.user.is_active class HasActiveSubscription(IsActiveEntity): message = "Entity is not active or does not have an active subscription." def has_permission(self, request, view): if not super().has_permission(request, view): return False return getattr(request.user, "has_active_subscription", False)

Bạn có thể xây logic thành từng lớp nhỏ. Một class validate key, một class theo dõi usage, một cái rate limit — và tất cả hoạt động cùng nhau thông qua method override.

Tổng kết

Tôi vẫn dùng các công cụ hiện đại như Next.js, T3 Stack, Laravel, NestJS, Fastify... tùy dự án.

Nhưng Django vẫn phù hợp với tôi hơn. Nó cho tôi kiểm soát, cấu trúc, và hỗ trợ lâu dài (LTS) — điều mà hệ sinh thái JavaScript hiếm khi cung cấp.

Tất nhiên, nếu bạn không quen với Django, không biết chọn package nào, nó có thể trở nên nặng nề và chậm chạp. Django đòi hỏi bạn phải biết mình đang làm gì.

Và nếu Django không phù hợp với dự án hiện tại của bạn, cũng không sao. Hãy dùng cái gì phù hợp: FastAPI, Node.js, NestJS, Ruby, hay bất cứ framework nào đáp ứng đúng mục tiêu.

Quan trọng nhất là bạn hiểu mình đang tối ưu cho điều gì: tốc độ ra mắt, khả năng bảo trì, hay kỹ năng của team — và đưa ra quyết định phù hợp.

Đó là những gì giúp tôi phát triển nhanh.

Cảm ơn các bạn đã theo dõi!

Bình luận

Bài viết tương tự

- vừa được xem lúc

Creating a Custom User Model in Django

Làm thế nào để thay thế username với email trong Django authentication. Cần chú ý rằng các làm này sẽ làm thay đổi rất nhiều đến schema của database vậy nên khuyến khích khi thực hiện một dự án mới.

0 0 35

- vừa được xem lúc

Câu chuyện kiểm soát truy cập trong Django.

Nếu bạn đang xây dựng một ứng dụng với Django, có thể bạn sẽ muốn kiểm soát quyền truy cập ứng với từng loại user. Những tính năng này sẽ phổ biến trên các trang web có quy mô lớn hơn một chút.

0 0 89

- vừa được xem lúc

Django: Handling CSV Data

Khi làm việc với các dự án Python hay Django thì phần xử lý CSV hầu như là không thể thiếu. Qua bài viết này, các bạn hãy cùng mình hiểu rõ hơn về CSV cũng như xử lý chúng ra sao nhé.

0 0 44

- vừa được xem lúc

Python Django - Xây dựng ứng dụng e-commerce (Part 1)

Giới thiệu Django - Khởi tạo project. Sơ qua về Django. . Django là một Framework lập trình web bậc cao được viết bằng ngôn ngữ lập trình Python.

0 0 963

- vừa được xem lúc

Deploy thủ công ứng dụng Django

Đối với một developer mà nói, mục đích cuối cùng của chúng ta khi phát triển một sản phẩm là có thể đưa sản phẩm ấy đến được với người dùng, và deploy là bước cuối cùng mà chúng ta cần thực hiện. Tron

0 0 282

- vừa được xem lúc

Dễ dàng có ngay ứng dụng e-commerce với Django (Part 2)

Xin chào tất cả mọi người. Tiếp nối bài viết trước về việc dùng Python Django để xây dựng 1 ứng dụng thương mại điện tử, sau đây mình xin phép được làm tiếp phần 2 để bổ sung thêm các chức năng, giúp

0 0 717