Node.js đang dần trở nên phổ biến với những ứng dụng dạng microservice hay REST Api bới hiệu năng cực nhanh và tính bất đồng bộ của chúng. Đặc biệt là rất gọn nhẹ và xử lý đồng thời nhiều request với thời gian phản hồi siêu nhanh.
Nhưng dù bất kể ngôn ngữ nào chúng ta cũng cần xây dựng một khung sườn (skeleton) vững chắc để có thể bám theo nó để dễ dàng maintain và phát triển nó. Vậy nên hôm nay chúng ta cùng nhau chia sẻ và xây dựng một skeleton riêng cho mình nhé.
Construct
Cũng giống như các ngôn ngữ REST API khác chúng ta cũng có thể xây dựng skeleton theo dạng MVC pattern. Nhưng ở bài viết này chúng ta chỉ xây dựng đến API chỉ cần sử dụng đến controller và models kết hợp với middleware để kiếm soát các request và response. Các bạn có thể tham khảo dưới đây:
Cấu trúc thư mục gồm:
- common: các functions, contants chung cho toàn dự án.
- controllers: các action điều hướng xử lý các request và response.
- database: chứa các file config kết nối mongosee, mysql, vv…
- middleware: chứa các rules để kiếm soát các request và response .
- models: chứa các model và function xử lý dữ liệu.
- routes: chứa các function thiếu lập điều hướng với các route.
Init skeleton
Trước khi xây dựng server REST API với Express, chúng ta cần cài đặt một số thư viện cơ bản như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token string">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"bcrypt"</span><span class="token operator">:</span> <span class="token string">"^5.0.0"</span><span class="token punctuation">,</span> <span class="token string">"body-parser"</span><span class="token operator">:</span> <span class="token string">"^1.18.2"</span><span class="token punctuation">,</span> <span class="token string">"express"</span><span class="token operator">:</span> <span class="token string">"^4.16.2"</span><span class="token punctuation">,</span> <span class="token string">"jquery"</span><span class="token operator">:</span> <span class="token string">"^3.5.1"</span><span class="token punctuation">,</span> <span class="token string">"jsonwebtoken"</span><span class="token operator">:</span> <span class="token string">"^8.1.0"</span><span class="token punctuation">,</span> <span class="token string">"mongoose"</span><span class="token operator">:</span> <span class="token string">"^5.9.25"</span><span class="token punctuation">,</span> <span class="token string">"nodemon"</span><span class="token operator">:</span> <span class="token string">"^2.0.4"</span><span class="token punctuation">,</span> <span class="token string">"request"</span><span class="token operator">:</span> <span class="token string">"^2.83.0"</span><span class="token punctuation">,</span> <span class="token string">"request-promise"</span><span class="token operator">:</span> <span class="token string">"^4.2.2"</span><span class="token punctuation">,</span> <span class="token string">"validator"</span><span class="token operator">:</span> <span class="token string">"^9.1.1"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"devDependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"env-cmd"</span><span class="token operator">:</span> <span class="token string">"^10.1.0"</span> <span class="token punctuation">}</span> |
Vì database chúng ta sẽ sử dụng là mongo nên sẽ cần sử dụng mongoose để truy xuất database và sẽ cần env-cmd để load file .evn (các biến môi trường).
Database
mongoose.js
chứa thông tin cấu hình kết nối mongodb.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">let</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> mongoose<span class="token punctuation">.</span>Promise <span class="token operator">=</span> global<span class="token punctuation">.</span>Promise<span class="token punctuation">;</span> mongoose<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">MONGODB_URL</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> useNewUrlParser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> useCreateIndex<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">;</span> |
Models
use.model.js
chứa thông tin khởi tạo Schema kèm theo đó là các functions. Chúng ta hoàn toàn có thể tách schema ra thành một thư mục riêng để code có thể clear và rõ ràng hơn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> bcrypt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'bcrypt'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> UserSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> username<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> lowercase<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token string">'can't be blank'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> match<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token regex">/^[a-zA-Z0-9]+$/</span><span class="token punctuation">,</span> <span class="token string">'is invalid'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> index<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> lowercase<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token string">'can't be blank'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> match<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token regex">/<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>S+.S+/</span><span class="token punctuation">,</span> <span class="token string">'is invalid'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> index<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> password<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> minLength<span class="token operator">:</span> <span class="token number">7</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> tokens<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> token<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>timestamps<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">}</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> UserSchema<span class="token punctuation">.</span><span class="token function">pre</span><span class="token punctuation">(</span><span class="token string">'save'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">next</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token function">isModified</span><span class="token punctuation">(</span><span class="token string">'password'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> user<span class="token punctuation">.</span>password <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">hash</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>password<span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> UserSchema<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function-variable function">toAuthJSON</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> username<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>username<span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>email<span class="token punctuation">,</span> token<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">generateAuthToken</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> UserSchema<span class="token punctuation">.</span>methods<span class="token punctuation">.</span><span class="token function-variable function">generateAuthToken</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> token <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span> <span class="token punctuation">{</span> _id<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_id<span class="token punctuation">,</span> username<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>username <span class="token punctuation">}</span><span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">JWT_KEY</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>tokens <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>tokens<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span><span class="token punctuation">{</span>token<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> token<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> UserSchema<span class="token punctuation">.</span>statics<span class="token punctuation">.</span><span class="token function-variable function">findByCredentials</span> <span class="token operator">=</span> <span class="token keyword">async</span><span class="token punctuation">(</span><span class="token parameter">email<span class="token punctuation">,</span> password</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span>email<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token punctuation">{</span>error<span class="token operator">:</span> <span class="token string">'Invalid login credentials'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> isPasswordMatch <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">compare</span><span class="token punctuation">(</span>password<span class="token punctuation">,</span> user<span class="token punctuation">.</span>password<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isPasswordMatch<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token punctuation">{</span>error<span class="token operator">:</span> <span class="token string">'Invalid login credentials'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> user<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> User <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">'User'</span><span class="token punctuation">,</span> UserSchema<span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> User<span class="token punctuation">;</span> |
Middleware
auth.middleware.js để bảo mật chúng ta cần verify lại request trước khi cho phép cấp quyền tiếp tục tương tác bằng cách kiểm tra token với header Authorization.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">const</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../models/user.model'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">auth</span> <span class="token operator">=</span> <span class="token keyword">async</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> token <span class="token operator">=</span> req<span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token string">'Authorization'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">'Bearer '</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> data <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">JWT_KEY</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span>_id<span class="token operator">:</span> data<span class="token punctuation">.</span>_id<span class="token punctuation">,</span> <span class="token string">'tokens.token'</span><span class="token operator">:</span> token<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> req<span class="token punctuation">.</span>user <span class="token operator">=</span> user<span class="token punctuation">;</span> req<span class="token punctuation">.</span>token <span class="token operator">=</span> token<span class="token punctuation">;</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">401</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span>error<span class="token operator">:</span> <span class="token string">'Not authorized to access this resource'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> auth<span class="token punctuation">;</span> |
Routes
index.js
sẽ đóng vài trò load toàn bộ cấu hình route
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> authRoute <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./auth.routes'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> router <span class="token operator">=</span> express<span class="token punctuation">.</span><span class="token function">Router</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> authRoute<span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> router<span class="token punctuation">;</span> |
auth.route.js
sẽ thiết lập các phương thức xử lý và điều hướng chúng tới các controller – action. Chúng ta sẽ chia ra làm 2 loại route là public và private bằng cách thiết lập middleware cho chúng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">let</span> router <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Router</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> AuthController <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../controllers/auth.controller'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../middleware/auth.middleware'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//public</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/login'</span><span class="token punctuation">,</span> AuthController<span class="token punctuation">.</span>login<span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/signup'</span><span class="token punctuation">,</span> UserController<span class="token punctuation">.</span>signup<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//private</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/logout'</span><span class="token punctuation">,</span> auth<span class="token punctuation">,</span> AuthController<span class="token punctuation">.</span>logout<span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/logout-all'</span><span class="token punctuation">,</span> auth<span class="token punctuation">,</span> AuthController<span class="token punctuation">.</span>logoutAll<span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> router<span class="token punctuation">;</span> |
Controllers
Ở đây, chúng ta cần tạo ra các function tương ứng với các action như đã khai báo tại auth.route.js.
Các action đóng vài trò điều hướng và xử lý dữ liệu trả về trước khi response trở lại.
auth.controller.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <span class="token keyword">const</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../models/user.model'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">AuthController</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">signup</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> user <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> user<span class="token punctuation">.</span>username <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>username<span class="token punctuation">;</span> user<span class="token punctuation">.</span>email <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>email<span class="token punctuation">;</span> user<span class="token punctuation">.</span>password <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">;</span> user<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span>user<span class="token operator">:</span> user<span class="token punctuation">.</span><span class="token function">toAuthJSON</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">login</span> <span class="token operator">=</span> <span class="token keyword">async</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>email<span class="token punctuation">,</span> password<span class="token punctuation">}</span> <span class="token operator">=</span> req<span class="token punctuation">.</span>body<span class="token punctuation">;</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findByCredentials</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">401</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span>error<span class="token operator">:</span> <span class="token string">'Login failed! Check authentication credentials'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span>user<span class="token operator">:</span> user<span class="token punctuation">.</span><span class="token function">toAuthJSON</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span>error<span class="token operator">:</span> <span class="token string">'Login failed'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">logout</span> <span class="token operator">=</span> <span class="token keyword">async</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span>tokens <span class="token operator">=</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span>tokens<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">token</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> token<span class="token punctuation">.</span>token <span class="token operator">!=</span> req<span class="token punctuation">.</span>token<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">await</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string">'message'</span><span class="token operator">:</span> <span class="token string">'Logout Done!'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">logoutAll</span> <span class="token operator">=</span> <span class="token keyword">async</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span>tokens<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span>tokens<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">await</span> req<span class="token punctuation">.</span>user<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> res<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token function">AuthController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Create server run app
Tạo app.js
với nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> port <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span><span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> route <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./routes/index'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> bodyParser <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'body-parser'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./database/mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span> bodyParser<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span> express<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> route<span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>port<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Server listening on '</span> <span class="token operator">+</span> port<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Vì chúng ta cần xử lý dữ liệu dưới dạng json vậy nên sẽ cần parser json với bodyParser. Tiếp đó load toàn bộ route với require('./routes/index');
Trước khi chạy thử chúng ta cần config lại package.json để ứng dụng có thể load được các biến môi trường với:
1 2 3 4 5 | <span class="token string">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"start"</span><span class="token operator">:</span> <span class="token string">"env-cmd -f ./.env node src/app.js"</span><span class="token punctuation">,</span> <span class="token string">"dev"</span><span class="token operator">:</span> <span class="token string">"env-cmd -f ./.env nodemon --exec "node src/app.js""</span> <span class="token punctuation">}</span> |
nodemon sẽ được sử dụng như một công cụ tự động reload lại app sau khi save thành công.
Tiếp theo, chúng ta thử chạy thử skeleton nhé. http://localhost:3000/
Signup API
Login API
Kết luận
Như vậy, chúng ta đã có thể tạo ra một bộ skeleton đơn giản với đầy đủ những tính năng cơ bản như login, signup, logout. ngoài ra chúng ta có thể tự bổ sung cho mình một bộ skeleton đầy đủ hơn với nhiều những tính năng hay ho khác. Hy vọng bài viết sẽ hữu ích với các ae Newbie ngôn ngữ này. Mọi đóng góp sẽ giúp tác giả hoàn thiện hơn trong những bài viết tiếp theo.