tutorial

47 Membuat Mutation Login dan Signup

47 Membuat Mutation Login dan Signup

Proses autentikasi user merupakan fondasi dari hampir semua aplikasi modern. Kemanan dan efisiensi dalam mengelola identitas pengguna sangat krusial, baik untuk aplikasi sederhana hingga yang kompleks. Pada artikel ke-47 dari serial ini, saya ingin membahas secara mendalam tentang bagaimana membangun mutation untuk Login dan Signup menggunakan GraphQL pada Node.js, lengkap dengan kode contoh, simulasi, dan diagram alur agar lebih mudah dipahami.

Mengapa Mutation?

Mutation dalam GraphQL digunakan untuk memodifikasi data di server, cocok untuk operasi seperti create (Signup) dan update (Login—biasanya untuk otentikasi). Dibandingkan REST API, pendekatan GraphQL membuat endpoint lebih elegan dan eksplisit. Ini sangat berguna saat aplikasi berkembang dan requirements bertambah kompleks.


Arsitektur Kasar

Sebelum masuk ke kode, mari kita pahami arsitekturnya secara garis besar:

  • Frontend: Mengirim mutation login dan signup ke backend.
  • Backend: Menyediakan Schema dan Resolver untuk mutation tersebut.
  • Database: Menyimpan data user yang telah terenkripsi (password).

Mari kita ilustrasikan prosesnya dengan diagram alur berikut.

graph TD
A[User] -->|Signup| B[GraphQL Mutation - signup]
B --> C[Resolver]
C --> D[Hash Password]
D --> E[Store to Database]
A2[User] -->|Login| F[GraphQL Mutation - login]
F --> G[Resolver]
G --> H[Verify Password]
H --> I[Return JWT Token]

Desain Schema GraphQL

Mari mulai dengan mendesain schema dasar untuk mutation Login dan Signup.

type Mutation {
  signup(username: String!, password: String!): AuthPayload!
  login(username: String!, password: String!): AuthPayload!
}

type AuthPayload {
  token: String
  user: User
}

type User {
  id: ID!
  username: String!
}

Penjelasan:

  • signup dan login mengembalikan objek AuthPayload.
  • AuthPayload berisi JWT token dan data user.
  • Mutation menerima username dan password secara eksplisit.

Kode Implementasi pada Node.js

Tech Stack:

  • Node.js
  • Express.js
  • Apollo Server (GraphQL)
  • bcrypt (untuk hash password)
  • jsonwebtoken (untuk generate JWT)

Misal kita sudah punya connection ke database MongoDB.

1. Model User (MongoDB/Mongoose)

// models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  username: { type: String, unique: true, required: true },
  password: { type: String, required: true },
});

module.exports = mongoose.model('User', userSchema);

2. Implementasi Resolver

Mari lihat code logic untuk signup dan login.

// resolvers.js
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('./models/User');

const SECRET = 'MY_SUPER_SECRET_KEY';

const resolvers = {
  Mutation: {
    signup: async (_, { username, password }) => {
      // Cek user sudah ada
      const existingUser = await User.findOne({ username });
      if (existingUser) {
        throw new Error('Username has been taken');
      }

      // Hash password dengan bcrypt
      const hashedPassword = await bcrypt.hash(password, 10);

      // Create user baru
      const user = new User({ username, password: hashedPassword });
      await user.save();

      // Generate JWT token
      const token = jwt.sign(
        { userId: user.id, username: user.username },
        SECRET,
        { expiresIn: '7d' }
      );

      return {
        token,
        user
      };
    },

    login: async (_, { username, password }) => {
      const user = await User.findOne({ username });
      if (!user) {
        throw new Error('User not found');
      }

      const valid = await bcrypt.compare(password, user.password);
      if (!valid) {
        throw new Error('Invalid password');
      }

      const token = jwt.sign(
        { userId: user.id, username: user.username },
        SECRET,
        { expiresIn: '7d' }
      );

      return {
        token,
        user
      };
    }
  }
};

module.exports = resolvers;

Simulasi Request dan Response

Mari kita lihat contoh request dan response nyata pada mutation ini.

1. Signup

Query

mutation {
  signup(username: "johndoe", password: "supersecret") {
    token
    user {
      id
      username
    }
  }
}

Response

{
  "data": {
    "signup": {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "user": {
        "id": "649c22ebd58be255b9a38212",
        "username": "johndoe"
      }
    }
  }
}

2. Login

Query

mutation {
  login(username: "johndoe", password: "supersecret") {
    token
    user {
      id
      username
    }
  }
}

Response

{
  "data": {
    "login": {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "user": {
        "id": "649c22ebd58be255b9a38212",
        "username": "johndoe"
      }
    }
  }
}

Perbandingan Mutation vs REST

FiturGraphQL MutationREST API
EndpointSatu endpoint “/graphql”Banyak endpoint
Response DataSesuai permintaan clientFixed/terbatas
Evolusi APIMudah dikembangkanLebih rigid
Error HandlingKonsisten via response GraphQLTergantung implementasi

Best Practices

  • Hash Password: Selalu hash password dengan bcrypt sebelum disimpan di database.
  • JWT Expiry: Sertakan expired date pada JWT token untuk keamanan.
  • Error Message: Jangan tampilkan error message sensitif.
  • Validasi Input: Gunakan validasi di sisi backend untuk username dan password.
  • Rate-Limit & Lockout: Implementasikan rate limit pada endpoint login untuk mencegah brute-force.

Potensi Perluasan

Mutation ini adalah pondasi utama. Ada berbagai kemungkinan pengembangan:

  • Reset Password
  • Verifikasi Email
  • Social Login
  • Multi-Factor Authentication

Penutup

Membuat mutation login dan signup dengan GraphQL memang memberi banyak keunggulan: efisiensi, kemudahan ekspansi, dan kemanan lebih terstruktur. Kode yang bersih serta arsitektur yang jelas sangat mendukung skalabilitas aplikasi.

Jika Anda ingin memperkuat sistem autentikasi aplikasi, memahami dan mengimplementasikan dua mutation dasar ini adalah investasi yang sangat tepat. Selamat bereksperimen, jangan lupa tambahkan proteksi tambahan dan validasi pada langkah berikutnya!


Resource lebih lanjut:

comments powered by Disqus