Skip to main content
0
Software Development

เข้าใจ Query / Mutation ใน GraphQL

สวัสดีครับสำหรับวันนี้เรามาอยู่กับบทความในหัวข้อที่เกี่ยวกับ “GraphQL” กันบ้างครับ เชื่อว่าคนที่ทำ API (Application Programming Interface) อยู่ต้องเจอคำว่า “GraphQL” กันอยู่บ้าง แล้วพอลองใช้งานจะเจอกับสองคำศัพท์หลัก ๆ นั่นก็คือ Query กับ Mutation เป็นพื้นฐานแรก ๆ เลยที่มือใหม่หัดใช้ GraphQL จะต้องรู้ สองคำนี้คืออะไรมาหาคำตอบกันเลย!

ก่อนจะพูดถึง Query และ Mutation เรามาทำความรู้จักกับ GraphQL กันแบบไว ๆ กันก่อนดีกว่าครับ โดย GraphQL (Graph Query Language) คือภาษาที่ใช้ในการดึงและจัดการข้อมูลระหว่าง Client และ Server การใช้งานเราจะต้องมี “GraphQL Server” ที่ทำหน้าที่ประมวลผล Request จากฝั่ง Client แล้วส่งข้อมูลกลับไปตามรูปแบบที่ Client ระบุ ซึ่งข้อดีหลัก ๆ ของ GraphQL คือมันทำให้เราสามารถเรียกข้อมูลมาเฉพาะเท่าที่ต้องการ ตามคอนเซปที่ถูกเรียกว่า request exactly what you need, nothing more, nothing less

“Client เป็นคนกำหนดเองว่าจะดึงข้อมูลอะไรบ้างจาก Server” แทนที่จะให้ Server กำหนดตายตัวเหมือนกับ REST API หากใครต้องการเริ่มต้นใช้งาน GraphQL ใน Back-End ครั้งแรก แอดมินแนะนำบทความจากคุณ “sorajit.a” ได้ที่ลิงก์นี้ได้เลย www.borntodev.com/2024/04/10/การใช้งาน-graphql-ใน-back-end/

Query ใน GraphQL

โดย Query จะเป็นคำสั่งฝั่ง “ดึงข้อมูล” ออกมาจาก GraphQL Server โดยเราสามารถเขียนโครงสร้างของ Query เพื่อให้ได้ข้อมูลตามที่ต้องการ หน้าตาจะเป็นแบบนี้

query {
  getUser(id: 1) {
    name
    email
  }
}
JavaScript

จากตัวอย่างนี้เป็นการดีงข้อมูล user ที่มี id เท่ากับ 1 เอามาเฉพาะฟิลด์ที่เป็น name และ email เท่านั้น อย่างอื่นไม่ต้องเอามา เราสามารถตัวอย่างนี้ เป็นการดึงข้อมูลของผู้ใช้ที่มี id เท่ากับ 1 แล้วให้ส่งเฉพาะฟิลด์ name กับ email กลับมา ไม่เอาอย่างอื่น

จะเห็นได้ว่าตรง id: 1 คือเราสามารถใส่ parameter เข้าไปได้ เพื่อกำหนดเงื่อนไขในการดึงข้อมูลมาสำหรับข้อมูลที่เฉพาะเจาะจง

Mutation ใน GraphQL

มาในฝั่ง Mutation กันบ้าง สำหรับฝั่งนี้จะเป็นการแก้ไข เพิ่ม หรือลบข้อมูลในระบบ โดยการทำงานนั้นหากเราต้องการเพิ่มข้อมูลก็เขียนเป็น mutation { … } ถ้าอยากแก้ไขข้อมูล หรือลบก็ทำผ่าน Mutation ได้เช่นกัน ตัวอย่างหน้าตาของ mutation

mutation {
  createUser(
    name: "Aef BorntoDev",
    email: "aef@borntodev.com"
  ) {
    id
    name
    email
  }
}
JavaScript

สรุปความเหมือน/แตกต่างระหว่าง Query vs. Mutation

ตัวอย่างการทำงาน

เพื่อให้เห็นภาพมากขึ้นเดี๋ยวผมจะพาไปสร้าง GraphQL ต่อกับ MySQL โดยใช้ Node.js พร้อมกับ Apollo Server โดย Apollo Server เป็น เซิร์ฟเวอร์ GraphQL

เริ่มจากทำการสร้างโปรเจกต์ใหม่

mkdir graphql-mysql-direct-sql
cd graphql-mysql-direct-sql
npm init -y
JavaScript

ติดตั้ง package ที่ใช้กับโปรเจกต์

npm install apollo-server graphql mysql2
JavaScript

apollo-server: เซิร์ฟเวอร์ GraphQL
graphql: ไลบรารีหลักของ GraphQL
mysql2: ไดรเวอร์สำหรับเชื่อมต่อกับ MySQL

สร้างไฟล์ db.js เพื่อกำหนดการเชื่อมต่อกับฐานข้อมูล MySQL โดยใช้ mysql2 (ปกติแล้วค่าในการเชื่อมต่อจะไม่เก็บในโค้ดนะ จะต้องแยกไฟล์ให้หรือเก็บไว้ใน .env ให้เรียบร้อย)

// db.js
const mysql = require('mysql2/promise');

// สร้างการเชื่อมต่อกับฐานข้อมูล MySQL
const pool = mysql.createPool({
  host: 'localhost',
  user: 'username',     // เปลี่ยนเป็นชื่อผู้ใช้ของคุณ
  password: 'password', // เปลี่ยนเป็นรหัสผ่านของคุณ
  database: 'demo_graphql_db', // เปลี่ยนเป็นชื่อฐานข้อมูลของคุณ
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

module.exports = pool;
JavaScript

หลังจากนั้นทำการกำหนด Schema สำหรับ GraphQL โดยการสร้างไฟล์ schema.js เพื่อกำหนดประเภทของข้อมูล (Type Definitions) และ Mutation ต่าง ๆ สำหรับ API ของเรา

// schema.js
const { gql } = require('apollo-server');

const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
  }

  type Query {
    users: [User]
    user(id: ID!): User
  }

  type Mutation {
    createUser(name: String!, email: String!): User
    updateUser(id: ID!, name: String, email: String): User
    deleteUser(id: ID!): Boolean
  }
`;

module.exports = typeDefs;
JavaScript

หลังจากนั้นกำหนด Resolvers สำหรับ Query และ Mutation
สร้างไฟล์ resolvers.js เพื่อกำหนดการทำงานของแต่ละ Query และ Mutation โดยใช้ SQL ตรงๆ ผ่าน mysql2

// resolvers.js
const pool = require('./db');

const resolvers = {
  Query: {
    // ดึงข้อมูลผู้ใช้ทั้งหมด
    users: async () => {
      const [rows] = await pool.query('SELECT id, name, email FROM users');
      return rows;
    },
    // ดึงข้อมูลผู้ใช้เฉพาะเจาะจงด้วย ID
    user: async (_, { id }) => {
      const [rows] = await pool.query('SELECT id, name, email FROM users WHERE id = ?', [id]);
      return rows[0] || null;
    },
  },
  Mutation: {
    // สร้างผู้ใช้ใหม่
    createUser: async (_, { name, email }) => {
      const [result] = await pool.query('INSERT INTO users (name, email) VALUES (?, ?)', [name, email]);
      const newUserId = result.insertId;
      const [rows] = await pool.query('SELECT id, name, email FROM users WHERE id = ?', [newUserId]);
      return rows[0];
    },
    // แก้ไขข้อมูลผู้ใช้
    updateUser: async (_, { id, name, email }) => {
      // ตรวจสอบว่าผู้ใช้มีอยู่จริงหรือไม่
      const [existingUser] = await pool.query('SELECT id, name, email FROM users WHERE id = ?', [id]);
      if (existingUser.length === 0) {
        throw new Error('User not found');
      }

      // กำหนดค่าที่จะอัพเดต
      const updatedName = name !== undefined ? name : existingUser[0].name;
      const updatedEmail = email !== undefined ? email : existingUser[0].email;

      // อัพเดตข้อมูลในฐานข้อมูล
      await pool.query('UPDATE users SET name = ?, email = ? WHERE id = ?', [updatedName, updatedEmail, id]);

      // ดึงข้อมูลผู้ใช้ที่อัพเดตแล้วมาแสดง
      const [rows] = await pool.query('SELECT id, name, email FROM users WHERE id = ?', [id]);
      return rows[0];
    },
    // ลบผู้ใช้
    deleteUser: async (_, { id }) => {
      const [result] = await pool.query('DELETE FROM users WHERE id = ?', [id]);
      return result.affectedRows > 0;
    },
  },
};

module.exports = resolvers;
JavaScript

การใช้ pool.query ใช้สำหรับรันคำสั่ง SQL โดยใช้การเชื่อมต่อแบบ Pooling เพื่อประสิทธิภาพที่ดีขึ้น ส่วน ? ใน SQL เป็นการใช้ parameterized queries เพื่อป้องกัน SQL Injection สุดท้ายเราก็จัดการผลลัพธ์ หลังจากทำ INSERT หรือ UPDATE จะดึงข้อมูลผู้ใช้ที่สร้างหรืออัพเดตแล้วมาแสดงผล หลังจากนั้น สร้างไฟล์ index.js เพื่อรันเซิร์ฟเวอร์ GraphQL

// index.js
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const pool = require('./db');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: {
    // คุณสามารถเพิ่ม context ต่าง ๆ ได้ที่นี่ เช่น authentication
  },
});

// รันเซิร์ฟเวอร์
server.listen({ port: 4000 }).then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});
JavaScript

ก่อนที่จะรันเซิร์ฟเวอร์ เราต้องแน่ใจก่อนว่าเราสร้างฐานข้อมูลใน MySQL และมีตาราง users ตามที่กำหนดใน Schema หากยังไม่มี สามารถสร้างได้ดังนี้

-- ตัวอยางการสรางฐานขอมูล
CREATE DATABASE database_name;

-- เลือกใชฐานขอมูล
USE database_name;

-- สรางตาราง users
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL UNIQUE
);
JavaScript

หมายเหตุ: เปลี่ยน ‘database_name’ ให้ตรงกับชื่อฐานข้อมูลที่คุณกำหนดในไฟล์ db.js

เมื่อพร้อมแล้วเราก็กลับไปที่ Terminal แล้วรัน

node index.js
JavaScript

ถ้าทุกอย่างถูกต้อง จะเห็นข้อความ

ตอนนี้เราสามารถเข้าถึง GraphQL Playground ได้ที่ http://localhost:4000/ เพื่อทดสอบ Query และ Mutation

แล้วเราลองเขียน Query ไป

จะเห็นได้ว่าตอนนี้ยังไม่มีข้อมูลในฐานข้อมูล เราก็ลองเป็นจาก Query เป็น Mutation สำหรับเพิ่มผู้ใช้ใหม่เข้าไปในระบบ

ลอง Query มาอีกครั้ง

Mutation: แก้ไขข้อมูลผู้ใช้

Mutation: ลบผู้ใช้

และนี่ก็จะเป็นตัวอย่างการใช้ Query และ Mutation ในการขอ หรือ เปลี่ยนแปลงข้อมูลนั่นเอง แอดหวังว่าบทความนี้จะมีประโยชน์สำหรับมือใหม่สาย GraphQL นะครับ 🧡

Sirasit Boonklang

Author Sirasit Boonklang

More posts by Sirasit Boonklang

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

    ประเภทของคุกกี้มีความจำเป็นสำหรับการทำงานของเว็บไซต์ เพื่อให้คุณสามารถใช้ได้อย่างเป็นปกติ และเข้าชมเว็บไซต์ คุณไม่สามารถปิดการทำงานของคุกกี้นี้ในระบบเว็บไซต์ของเราได้
    รายละเอียดคุกกี้

  • คุกกี้สำหรับการติดตามทางการตลาด

    ประเภทของคุกกี้ที่มีความจำเป็นในการใช้งานเพื่อการวิเคราะห์ และ นำเสนอโปรโมชัน สินค้า รวมถึงหลักสูตรฟรี และ สิทธิพิเศษต่าง ๆ คุณสามารถเลือกปิดคุกกี้ประเภทนี้ได้โดยไม่ส่งผลต่อการทำงานหลัก เว้นแต่การนำเสนอโปรโมชันที่อาจไม่ตรงกับความต้องการ
    รายละเอียดคุกกี้

บันทึกการตั้งค่า