Express.js หลบไป เพราะข้าคือ “Adonis”

noomerZx
Stories of Sellsuki
4 min readJan 21, 2017

สวัสดีครับสำหรับทุกท่าน วันนี้เราจะพาไปรู้จักกับเจ้า Adonis.js ซึ่งเป็น Web Framework สุดแจ่มอีกตัวสำหรับฝั่ง JavaScript แต่ถ้าพูดถึง Web Framework สำหรับ JavaScript ทุกคนคงน่าจะคุ้นเคยกับ Express.js กันทุกคนอยู่แล้ว ?

Express.js คือ Web Framework ที่ทำให้เราสามารถพัฒนา Web Application หรือ Web Service ได้ด้วย JavaScript ค่อนข้างได้รับความนิยมมาก เพราะมีขนาดไม่ใหญ่ ใช้งานง่าย และไม่ซับซ้อน อยากติดตั้งอะไรเพิ่มเติมก็เลือกลงได้ตามสบายด้วย npm ทำให้มีความคล่องตัวสูง และเหมาะสำหรับผู้เริ่มต้นที่จะเขียนเว็บด้วย JavaScript

แต่ถ้าในระดับที่ใหญ่ขึ้นล่ะ ? ถ้าเราต้องการอะไรที่พร้อมใช้งานได้ทันที และเป็นระเบียบง่ายต่อการทำงานเป็นทีม ง่ายต่อการ Maintenance แต่ก็พร้อมที่สามารถพัฒนาเว็บที่ Scale ใหญ่ขึ้นได้อย่างรวดเร็ว แต่น แตน แต๊นนนน… วันนี้ผมจะพาทุกท่านไปพบกับ… Adonis.js

Adonis.js

ก่อนอื่นขออนุญาติพูดถึง Laravel Framework ของทางฝั่ง PHP กันก่อน ถ้าใครเคยเขียนหรือรู้จัก Laravel จะรู้ว่ามันเป็น Framework ที่ง่ายต่อการเรียนรู้ มีโครงสร้างโปรเจคที่ดี และเพียบพร้อมไปด้วย Features มากมายที่สามารถพัฒนาเว็บไซต์ได้ทุก scale ได้อย่างรวดเร็ว ..แต่ถามว่า แล้วผมไปพูดถึงมันทำไม ? ก็นั่นแหละครับท่านผู้ชม มันคือนิยามของ Adonis.js

“ A Laravel-style Framework for Node.js “

เรียกได้ว่าถอดความเทพทุกอย่างจาก Laravel ลงมาให้เราเขียนได้ด้วย JavaScript กันเลยละครับ แต่เอ๊ะและถ้าไม่รู้จัก ไม่เคยใช้ Laravel ล่ะจะรู้ได้ยังไงว่ามันเทพจริงป่าว หรือแค่โม้ งั้นเรามาดูกันครับว่ามันมีอะไรเด็ดๆบ้าง

1. Good & Clean Documents

แน่นอนว่าเรื่องนี้เป็นเรื่องสำคัญแทบจะเป็นอันดับต้นๆเลย เพราะเมื่อเราต้องการจะศึกษาข้อมูลอะไรซักอย่าง และถ้าข้อมูลจาก Official Source นั้นอ่านง่ายและครบถ้วน เราก็จะเรียนรู้ได้อย่างรวดเร็ว ซึ่งในเรื่องของ Documents นั้น Adonis ก็ทำได้เยี่ยมเพราะมันอ่านง่าย สะอาด และครบถ้วน ถ้าไม่เชื่อก็คลิกเดะ !

2. Project Structure (MVC)

โครงสร้างของ Adonis จะเป็นแบบ MVC ซึ่งจะแยกส่วนต่างๆไว้ชัดเจน ตั้งแต่ Config, Route, Controller, Middleware, Model, Database และ View ซึ่งมันจะทำให้เราบริหารจัดการระบบของเราได้ง่ายขึ้น

3. View — Template Engine (Nunjack)

สำหรับในส่วนของ View นั้นจากหัวข้อที่แล้วเราจะเห็นได้ว่ามันเป็นไฟล์ .njk มันคือไรฟร่ะ !! ไม่ต้องต๊กกะใจ๋ มันคือ Nunjack Template Engine อารมณ์ก็คล้ายๆ Jade อะไรแบบนั้นแหละครับ มาลองดู code กันซักหน่อย

master.njk & welcome.njk

ถ้าสังเกตในส่วนของ code block ที่เป็น {% some js code %} ส่วนนี้แหละคือ Nunjack Engine ซึ่งช่วยให้เราจัดการกับการทำ Server-Side Rendering ได้ง่ายขึ้น การส่งข้อมูลจาก Controller มาทำ Pesentation Logic ต่างๆจะทำได้สบายๆ

แต่ความเด็ดอีกอย่างนึงคือการแยก Template ออกเป็นส่วนๆ และเรียกใช้งาน ซึ่งจะทำให้เราสามารถ reuse html code ของเราได้

จากตัวอย่างจะมีไฟล์ 2 ไฟล์ คือ master.njk และ welcome.njk ภายใน welcome.njk จะมีการ extend master.njk เข้ามา และมี html code อยู่ภายใต้ block content เมื่อเราย้อนกลับไปดูที่ไฟล์ master.njk เราก็จะเห็นว่ามีส่วนของ block ต่างๆระบุไว้ 2 จุดได้แก่ block content และ block footer ซึ่งถ้าเราอยากจะให้ข้อมูลจาก welcome.njk มาแสดงในส่วนใดของ master เราก็ระบุ block ให้ถูกต้อง จากตัวอย่างนี้ html code ของ welcome.njk จะมาแสดงภายใต้ block content ของ master.njk ลองมาดูผลลัพธ์กันว่าเมื่อ Compile แล้วและส่งกลับมาที่ Client แล้วหน้าตาของ HTML เราจะเป็นยังไง

สังเกตจาก HTML Code ก็จะเห็นว่า มันทำงานได้ถูกต้องสมบูรณ์ และ code ของเราก็สะอาดสวยงามจัดการง่ายและยัง Reusable อีกด้วย

4. Database — Query Builder, Lucid, Migration, Seed & Factory

สำหรับในส่วนของ Database นั้นค่อนข้างจะมี Features เยอะพอสมควร และทำให้เราทำงานง่ายขึ้นมาก อีกทั้งยังมีตัว mock data ที่ใช้งานง่ายให้เราขึ้นโปรเจคได้อย่างรวดเร็ว มาครับมาลองไล่ดูกันไปทีละอันดีกว่า

4.1 Query Builder

ไม่ต้องพูดอะไรมาก มันคือตัวช่วยในการ Query ข้อมูลจาก Database นั่นเองแทนที่เราจะเขียน SQL Query กันโต้งๆ เราก็จะมาใช้เจ้าตัวนี้ช่วยจัดการถามว่ามันดียังไง บอกง่ายๆว่ามันอ่านง่ายสบายตากว่าเยอะครับ มาดูตัวอย่างง่ายๆกันหน่อย

yield Database.select('id', 'username').from('users')
yield Database.select('*').from('users')

หรือถ้าซับซ้อนขึ้นมาหน่อย

const subquery = Database
.from('accounts')
.where('account_name', 'somename')
.select('account_name')

const users = yield Database
.from('users')
.whereIn('id', subquery)

จะเห็นได้ว่ามันค่อนข้างสะอาด ลองมาดูเล่นกันว่าคำสั่งที่ 2 ถ้าเราเขียน SQL เองจะหน้าตาเป้นยังไง ?

select * from `users` where `id` in (select `account_name` from `accounts` where `account_name` = 'somename')

ครับผมซึ่งตัว Query Builder นี้ Framework ใหญ่ๆก็มักจะมีให้ใช้กันอยู่แล้ว ซึ่งจริงๆยังมี ฟังก์ชื่ออื่นๆให้ใช่อีกมากไม่ว่าจะเป็น Where, OrderBy, GroupBy และอื่นๆตามที่ SQL จะทำได้

4.2 Lucid

Lucid คืออะไรชื่อแปลกๆแฮะ พูดง่ายๆมันคือตัวจัดการข้อมูลจาก SQL ให้อยู่ในรูปของ Object ที่มี Relaion ระหว่างกันได้ หรือที่เขาเรียกๆกันว่า ORM นั่นแหละครับ ถ้าเห็นว่า Query Builder นั้นแจ่มแล้วเจอเจ้า Lucid ไปนี่จะบอกว่ามันเทพกว่ามาก ลองมาดูตัวอย่างกันหน่อยดีกว่า

Query Builder Style

yield Database.select('*').from('users')

Lucid Style

const User = use('App/Model/User')
const users = yield User.all()

จะเห็นได้ว่าการใช้ Lucid จะเริ่มมีการเกี่ยวข้องกับการทำ Model แล้ว ดูเหมือนจะยุ่งยากขึ้นมานิดหน่อย แล้วมันดียังไง ? คำตอบ คือ มันสามารถทำ Relation ระหว่างแต่ละ Model ได้ นั่นทำให้เราเขียน code ได้สะอาดและเข้าใจง่าย ลองมาดูตัวอย่าง User Model ง่ายๆกัน

'use strict'const Lucid = use('Lucid')class User extends Lucid {  profile () {
return this.hasOne('App/Model/Profile')
}
}

อันนี้คือ User Model ของเราที่ไปผูกไว้กับ Profile Model ซึ่งมันจะทำให้เราเขียน Query แบบนี้ได้ครับ

const user = yield User.find(1)
const userProfile = yield user.profile().fetch()

ในการทำงานจริง Table ใน Database ของเราก็มักจะมี Relation อยู่ ถ้าเราผูก Model ของเราเข้าด้วยกัน ก็จะทำให้ code ของเราเป็นระเบียบและจัดการได้ง่ายมากขึ้นไปอีก ซึ่งจริงๆแล้วเจ้า ORM นี้ก็ไม่ใช่เรื่องแปลกใหม่อะไรเพราะเช่นเดิมว่า Framework ใหญ่ๆก็มักจะมีกันอยู่แล้ว

4.3 Migration

เป็นส่วนของการบริหารจัดการ Database ของเราเช่นการสร้าง Table การแก้ไข Table เจ้า Adonis มันให้เราจัดการได้ด้วย JavaScript แทนที่จะเขียนด้วย SQL Command ผ่านการทำ Migration นี่แหละ ซึ่งมันจะมี Adonis Command line ง่ายๆให้ใช้ อ่ะลองมาดูการทำ Migration แบบคร่าวๆกันครับ

./ace make:migration users --create=users

เมื่อรันคำสั่งนี้จะได้ไฟล์ Migration สำหรับ Table users ซึ่งจะมีหน้าตาดังนี้

'use strict'

const Schema = use('Schema')

class UsersSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments()
table.timestamps()
})
}

down () {
this.drop('users')
}
}

module.exports = UsersSchema

โดยที่เราสามารถแก้ไขไฟล์นี้ได้เลย อยากจะเพิ่ม Attribute อะไรลงไปมี Type เป็นอะไร ก็สุดแล้วแต่ และเมื่อเราพอใจแล้วก็รันคำสั่งต่อไปนี้

./ace migration:run

เท่านี้ก็เรียบร้อย Table ก็จะปรากฏใน Database ของเราถ้าไม่พอใจหรืออยากจะแก้ไขก็สามารถสั่ง Rollback แล้ว Migrate ใหม่ก็ทำได้ครับผม ก็ถือว่าช่วยให้เราขึ้นโปรเจคได้ไวขึ้น และสามารถทำงานร่วมกับทีมได้ง่ายขึ้นครับ เพราะเมื่อเราทำ Migration Script เสร็จ ก็ให้ในทีมเรารัน Migrate ก็จบ Database ของทุกๆคนก็จะเหมือนกันหมดครับ

4.4 Seed & Factory

ส่วนสุดท้ายที่จัดว่าแจ่มแมว และผมชอบมากๆครับ คือเมื่อเรา Migrate แล้วได้ Database อย่างที่เราต้องการแล้ว แต่เราก็จะยังทดสอบระบบของเราไมไ่ด้ ถ้ายังขาดซึ่งข้อมูลต่างๆที่ควรจะอยู่ในตารางนั้นๆ

ดังนั้นเราจะมาพูดถึง Features ที่มีชื่อว่า Seed & Factory โดยจริงๆแล้วการ Seed นั้นก็คือการเขียน Script เพื่อเพิ่มข้อมูลลงไปใน Database แต่โดยปกติแล้วข้อมูลที่จเพิ่มลงไปเราจะต้อง Mock ขึ้นมาเองหรือพูดง่ายๆก็มั่วขึ้นมาเองนั่นแหละครับ ทำให้ค่อนข้างเสียเวลาพอสมควร แต่ Adonis แก้ปัญหานี้ด้วยเจ้า Factory อ่ะ ลองมาดู code กันนิดนึงดีกว่า จะได้เข้าใจมากขึ้น

const Factory = use('Factory')

Factory.blueprint('App/Model/User', (fake) => {
return {
username: fake.username(),
email: fake.email(),
password: fake.password(),
firstName: fake.first(),
lastName: fake.last()
}
})

อธิบายง่ายๆ Factory ก็คือโรงงานที่จะสร้างข้อมูลจาก Blueprint ของเราซึ่งนั่นก็คือ Model นั่นเอง จาก code จะเห็นว่ามีการ return object ซึ่งจะเป็น object ที่มีหน้าตาเหมือนกับที่ Model นั้นๆที่ระบุไว้

แต่จุดสังเกตคือ “fake” มันคืออะไรกัน fake.username() แล้วฟังก์ชั่น username มาจากไหนกัน ผมก็ได้ไล่อ่านดูจาก documents ปรากฏว่า fake นั้นเป็น instance ของ chance.js ซึ่งเป็น library สำหรับ generate random data ครับ จากนั้นผมก็เลยเข้าไปส่องดู ปรากฏว่ามันค่อนข้างเทพทีเดียว มีหมวดหมู่ในการเลือก generate ด้วยเช่น ชื่อคน ที่อยู่ จังหวัด ประเทศ และอีกมากมายครับ

มาต่อกันดีกว่า เมื่อเราสร้าง Model มาผูกกับ Factory ของเราแล้ว เราก็มาทำในส่วนของ Seed กันต่อ ซึ่งจะมีหน้าตาประมาณนี้

'use strict'

const Factory = use('Factory')

class DatabaseSeeder {
* run () {
yield Factory.model('App/Model/User').create(5)
}
}

module.exports = DatabaseSeeder

จะเห็นได้ว่าเราก็จะเรียกใช้ Factory และระบุว่าต้องการใช้ blurprint ของ Model ไหน และต้องการ Create ข้อมูลกี่ชุด เมื่อเรา setup ทุกอย่างเรียบร้อยก็รัน

./ace db:seed

บูม !! กลายเป็น โกโก้ ครันช์ เห้ย..ไม่ใช่ เท่านี้ข้อมูลก็จะปรากฏใน Database ของเราแล้วครับผม !!!! เป็นไงล่ะง่ายไหมล่ะครับ

สรุป

จบแล้วครับสำหรับการแนะนำเจ้า Adonis.js JavaScript MVC Framework อีกตัวที่น่าสนใจมากๆ ตอนแรกก็ว่าจะเขียนแนะนำสั้นๆ ให้ไปลองกันเอง แต่เขียนไปเขียนมาลากซะยาวเลย ก็สรุปได้ว่าถ้าใครชอบ Laravel ผมว่าจะต้องรัก Adonis แน่นอนครับ แต่ถ้าใครไม่เคยใช้ Laravel มาก่อนผมว่าเป็นอีกทางเลือกที่น่าลอง มันจะช่วยให้คุณขึ้นโปรเจคได้ไวมาก และยังสะอาดเป็นระเบียบอีกด้วย ผมการันตีแรงๆไป 2 ที เพี๊ยะๆ

--

--