Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

ในโพสต์นี้ เราจะสร้างแอปพลิเคชันมือถือแบบไร้เซิร์ฟเวอร์ด้วย Flutter, Serverless Framework, Upstash และ Redis สำหรับการจัดเก็บข้อมูล

เกิดอะไรขึ้น ?

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

Upstash กำหนดค่าและจัดการฐานข้อมูลให้กับคุณ ซึ่งเป็นทางเลือกที่ดีสำหรับฐานข้อมูลอื่นๆ เช่น DynamoDB และ Fauna โดยมีข้อดีเช่น

  • เวลาแฝงต่ำ
  • ใช้งานง่าย เช่นเดียวกับ REDIS API

นี่คือเอกสารโดยละเอียดที่เปรียบเทียบ Upstash กับโซลูชันระบบคลาวด์ทางเลือก ซึ่งให้เหตุผลที่ชัดเจนว่าทำไมคุณจึงควรเลือกใช้สำหรับโครงการต่อไปของคุณ

คุณยังสามารถอ่านบทความนี้เพื่อเปรียบเทียบฐานข้อมูลแบบไร้เซิร์ฟเวอร์ที่มีอยู่ทั้งหมดได้ที่นั่น

ด้วย Upstash

  • คุณเริ่มฟรีและจ่ายเฉพาะสิ่งที่คุณใช้
  • มีการจัดเก็บที่รวดเร็วและทนทาน
  • คุณเข้าถึงฐานข้อมูลได้จากทุกที่ทั่วโลกโดยมีเวลาแฝงต่ำเนื่องจากฐานข้อมูลทั่วโลกและ Edge Caching

เริ่มต้นใช้งาน Upstash ฟรีวันนี้

ในการสร้างแอปพลิเคชันบน Upstash อย่างมีประสิทธิภาพ คุณต้องเข้าใจ Redis ดังนั้นเราจึงแนะนำ Redis สั้นๆ และดูว่าเราจะใช้งาน Redis อย่างไรภายในแอป Upstash

หากคุณต้องการบางสิ่งที่ละเอียดและลึกซึ้งกว่านี้ ฉันขอแนะนำเว็บไซต์ทางการของ Redis

Redis เป็นโอเพ่นซอร์ส (ลิขสิทธิ์ BSD) ที่จัดเก็บโครงสร้างข้อมูลในหน่วยความจำ ใช้เป็นฐานข้อมูล แคช และนายหน้าข้อความ

รองรับโครงสร้างข้อมูลมากมาย เช่น

  • สตริง
  • แฮช
  • รายการ
  • ชุด
  • จัดเรียงชุดด้วยการสืบค้นช่วง
  • บิตแมป
  • ไฮเปอร์ล็อกล็อก
  • ดัชนีภูมิสารสนเทศ

คุณโต้ตอบกับฐานข้อมูล Redis โดยใช้คำสั่งและบันทึกข้อมูลในรูปแบบค่าคีย์ โดยที่คีย์สามารถเป็นสตริงและค่า โครงสร้างข้อมูลใดๆ ก็ตามที่ Redis รองรับ

ตัวอย่างเช่น ฉันสามารถใช้คำสั่ง Redis SET เพื่อเก็บค่านามสกุลของฉันไว้อย่างนั้น

SET surname Rosius

โดยที่ surname คือกุญแจสำคัญ และโรเซียสคือคุณค่า

สิ่งสำคัญอย่างหนึ่งที่ควรทราบด้วย Redis คือ จัดเก็บข้อมูลของคุณในแบบที่คุณสามารถเรียกค้นข้อมูลได้อย่างง่ายดาย

ไม่มีวิธีโดยตรงในการค้นหาคีย์ตามค่าใน Redis

ข้อมูลใน Redis ถูกเก็บไว้อย่างถาวร จึงสามารถดึงข้อมูลที่เก็บไว้ที่คีย์ surname เช่นนั้น

GET surname

ผลการค้นหา 'โรเซียส'

นอกจากนี้เรายังสามารถลบค่าที่เก็บไว้ที่คีย์ surname เช่นนั้น

DEL surname

สมมติว่าเราต้องการเพิ่มไลค์ของโพสต์ เราสามารถทำได้ง่ายๆ โดยใช้ INCR คำสั่งซึ่งเป็นปรมาณู

SET likes 10
INCR likes => 11
INCR likes => 12
INCR likes => 13

ประการแรก เราตั้งค่าเริ่มต้นของการชอบเป็น 10 จากนั้นเราจึงเพิ่มมูลค่าการชอบแบบอะตอม ตอนนี้ คุณอาจจะคิดว่ามันเป็นไปได้ที่จะเพิ่ม likes ทางนี้

x = GET likes
x = x + 1
SET likes x

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

เมื่อมี>2 คนเพิ่มขึ้นแบบเดียวกัน กระบวนการด้านบน (GET,Increment,SET) จะไม่เป็นอะตอมอีกต่อไป ดังนั้น

x = GET likes (yields 10)
y = GET likes (yields 10)
x = x + 1 (x is now 11)
y = y + 1 (y is now 11)
SET likes x (likes is now 11)
SET likes y (likes is now 11

จากโค้ดด้านบน ผู้ใช้ 1 รับค่าของไลค์ที่ 10 และเก็บไว้ในตัวแปร x และในขณะเดียวกัน ผู้ใช้ 2 จะได้รับค่าไลค์ที่เท่ากัน ซึ่งก็คือ 10 และเก็บไว้ในตัวแปร ญ.

ผู้ใช้ 1 เพิ่ม 1 ให้กับค่าของ likes(x) และตั้งค่าใหม่ ซึ่งตอนนี้คือ 11

ผู้ใช้ 2 ทำเช่นเดียวกัน

ดังนั้นมูลค่าของไลค์คือ 11

แต่มันถูกต้องจริงหรือ? อย่าลืมว่าจำนวนไลค์เพิ่มขึ้นสองครั้งโดยผู้ใช้ 2 คน

มูลค่าของการชอบต้องเป็น 12 ไม่ใช่ 11 นั่นคือเหตุผลที่ Redis ให้ INCR คำสั่งซึ่งเป็นปรมาณูและแก้ปัญหาดังกล่าวได้

ประเภทข้อมูลแฮช

แฮช Redis เทียบเท่ากับแฮชของภาษาการเขียนโปรแกรมอื่นๆ โดยพื้นฐานแล้ว แฮชเหล่านี้ประกอบด้วยคอลเล็กชันของฟิลด์ที่เกี่ยวข้องกับค่าต่างๆ:

ตัวอย่างเช่น นี่คือวิธีที่ฉันจะจัดเก็บข้อมูลโปรไฟล์ผู้ใช้ในแฮช

HMSET userProfile:100034  "userId" 100034 "username" "Rosius Ndimofor"
            "firstName" "Rosius" "lastName" "Ndimofor" "profilePic" "rosius.jpeg"

ประการแรก กุญแจสำหรับ Hash ของเราคือ userProfile:100034 เราก็มี key value คู่กันตลอด ตัวอย่างเช่น "userId" เป็นกุญแจสำคัญ และ 100034' is the value. We can retrieve specific user profile information such as first name by using the HGETcommand and userId` ชอบครับ

userId = 100034
HGET userProfile:{userId} firstName

หรือเราสามารถดึงข้อมูลโปรไฟล์ผู้ใช้ทั้งหมดสำหรับผู้ใช้เฉพาะโดยใช้ GETALL คำสั่งเช่นนั้น

userId = 100034
HGETALL userProfile:{userId}

รายการประเภทข้อมูล

ก่อนหน้านี้ ฉันบอกว่าใน Redis การบันทึกข้อมูลในแบบที่คุณวางแผนจะดึงข้อมูลเป็นสิ่งสำคัญมาก

จะเป็นอย่างไรถ้าเราต้องการรับผู้ใช้ทั้งหมดบนแพลตฟอร์มของเรา

เราเพิ่งใช้โครงสร้างข้อมูลแฮชเพื่อบันทึกข้อมูลโปรไฟล์ผู้ใช้ ตอนนี้เราจำเป็นต้องรับผู้ใช้ทั้งหมดในระบบของเรา

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการบันทึก userId ของผู้ใช้ทุกคนในรายการโดยใช้คำสั่ง LPUSH (ย่อมาจาก Left Push) เช่นนั้น

LPUSH "users" userId

รับผู้ใช้ทั้งหมดจากรายการของเรา เราใช้คำสั่ง LRANGE เช่นนั้น

LRANGE  "users" 0  -1

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

แล้วแอปพลิเคชันฟูลสแตกของเราควรทำงานอย่างไร

ฉันดีใจที่คุณถาม ดังนั้นวันนี้ เราจะเริ่มต้นด้วยการสร้าง CRUD API นี่คือกรณีการใช้งาน

เรามี 2 หน่วยงาน Users และ Posts .

พวกเขาแบ่งปันความสัมพันธ์แบบหนึ่งต่อหลายต่อหลายคน ดังนั้นผู้ใช้หนึ่งรายสามารถมีได้หลายโพสต์ และหนึ่งโพสต์สามารถเป็นของผู้ใช้ได้เพียงคนเดียวเท่านั้น

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

อนุญาตให้ผู้ใช้

  • สร้างบัญชี ไม่มีการรับรองความถูกต้อง คุณสามารถเพิ่มการรับรองความถูกต้องด้วย AWS Cognito หรือ Auth0
  • อัปเดตบัญชีของพวกเขา
  • รับบัญชีของพวกเขา
  • สร้างโพสต์
  • อัปเดตโพสต์
  • ถูกใจโพสต์
  • รับรายการโพสต์ทั้งหมด

นี่คือมุมมองทางสถาปัตยกรรมของโซลูชัน

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1 มาเริ่มกันเลย

สร้างบัญชี Upstash

โปรดสร้างบัญชี Upstash ฟรีที่นี่ เข้าสู่ระบบ Upstash

หลังจากสร้างบัญชีแล้ว ให้สร้างฐานข้อมูล Upstash

คุณได้รับอนุญาตให้สร้างฐานข้อมูลได้เพียงฐานข้อมูลเดียวในระดับฟรี

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1 จดบันทึกปลายทางฐานข้อมูล Redis ของคุณ ควรมีลักษณะเช่นนี้rediss://:2c9bb162c2444bf7ab689640bb2ead23@gusc1-smashing-bee-30249.upstash.io:00049

การสร้างโปรเจ็กต์แบบไร้เซิร์ฟเวอร์

ข้อกำหนดเบื้องต้น

โปรดติดตั้งการพึ่งพาเหล่านี้ก่อนดำเนินการต่อ

  • AWS CLI

  • NodeJS

  • CLI แบบไร้เซิร์ฟเวอร์

    สร้างโปรเจ็กต์แบบไร้เซิร์ฟเวอร์ใหม่โดยใช้คำสั่งด้านล่างและปฏิบัติตามคำแนะนำ

serverless

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

เลือก nodejs HTTP API ตั้งชื่อโปรเจ็กต์ของคุณ แล้วกด enter .

นี่คือโครงสร้างเริ่มต้นของโครงการแบบไร้เซิร์ฟเวอร์ของฉัน

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

ภายในโฟลเดอร์นั้น ให้เริ่มต้นโปรเจ็กต์โหนดใหม่ด้วยคำสั่ง

npm init

ถัดไป ติดตั้งไคลเอนต์ redis ด้วย

npm install ioredis

นอกจากนี้ ให้ติดตั้งการพึ่งพา Identifier(uuid) ที่ไม่ซ้ำกันในระดับสากล เราจะนำไปใช้อย่างกว้างขวางในโครงการนี้

npm install uuid

ตอนนี้ เพิ่ม Redis Database Endpoint เป็นตัวแปรสภาพแวดล้อมใน serverless.yml ไฟล์แบบนั้น

provider:
  name: aws
  region: us-east-1
  stage: dev
  runtime: nodejs12.x
  lambdaHashingVersion: "20201221"
  environment:
    REDIS_CLIENT: "rediss://:2c9bb162c2444bf7ab689640bb2ead23@gusc1-smashing-wasp-30249.upstash.io:30249"

ต่อไป สร้าง 2 โฟลเดอร์ภายในโปรเจ็กต์ของคุณชื่อ users และ Posts . โฟลเดอร์ทั้งสองนี้จะมีฟังก์ชันแลมบ์ดาสำหรับกรณีการใช้งานที่เกี่ยวข้อง

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

มาเริ่มสร้างเอนพอยต์ API ของเรากันเถอะ

สร้างผู้ใช้

เราต้องการให้ผู้ใช้สามารถสร้างบัญชีได้เอง

ไม่มีการรับรองความถูกต้อง ทั้งหมดที่พวกเขาต้องทำคือส่ง

  • ชื่อผู้ใช้
  • ชื่อ
  • นามสกุล
  • รูปโปรไฟล์

สร้างไฟล์ใน user โฟลเดอร์ชื่อ create.js .

ที่ด้านบนของไฟล์ นำเข้าและสร้างอินสแตนซ์ไคลเอ็นต์ redis โดยใช้ URL ฐานข้อมูล redis ที่เราบันทึกเป็นตัวแปรสภาพแวดล้อมใน serverless.yml ไฟล์.

"use strict";
const uuid = require("uuid");
var Redis = require("ioredis");
if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

นอกจากนี้เรายังนำเข้าการพึ่งพา uuid เนื่องจากเราจะใช้เพื่อสร้าง ID ที่ไม่ซ้ำสำหรับผู้ใช้

ประการแรก เราต้องการโครงสร้างข้อมูลเพื่อบันทึก user profile ข้อมูลและโครงสร้างข้อมูลอื่นเพื่อบันทึก userId's ของผู้ใช้ทั้งหมดในแอปพลิเคชัน

จำประเภทข้อมูลแฮชและรายการได้หรือไม่

แฮชประเภทข้อมูลเพื่อบันทึกข้อมูลโปรไฟล์ผู้ใช้ จดคีย์ประเภทข้อมูล userItem:${userId}

await client.hmset(
  `userItem:${userId}`,
  "userId",
  userId,
  username,
  data.username,
  firstName,
  data.firstName,
  lastName,
  data.lastName,
  profilePic,
  data.profilePic,
  "timestamp",
  timestamp
);

จากนั้นเราบันทึกuserId .ที่สร้างขึ้น ไปยังรายการชื่อ Users

await client.lpush("users", userId);

หากคุณสังเกตเห็น เราต้องส่งการผ่าตัด 2 ครั้ง เป็นไปได้ที่จะส่งทีละรายการ แต่นั่นไม่เหมาะสม

Upstash รองรับการดำเนินการแบบกลุ่มผ่านคุณสมบัติที่เรียกว่า pipelining .

ดังนั้น แทนที่จะส่งคำสั่งเดียวและรอการตอบกลับ เราสามารถส่งหลายคำสั่งและการตอบกลับกลับมาในลักษณะเดียวกับที่เราส่งคำสั่ง

นี่คือลักษณะการทำงานของเราหลังจากใช้ไปป์ไลน์

client.pipeline(
  await client.hmset(
    `userItem:${userId}`,
    "userId",
    userId,
    username,
    data.username,
    firstName,
    data.firstName,
    lastName,
    data.lastName,
    profilePic,
    data.profilePic,
    "timestamp",
    timestamp
  ),
  await client.lpush("users", userId)
);

จากนั้นเราจะสามารถรับข้อมูลโปรไฟล์ที่บันทึกของผู้ใช้ทั้งหมดและส่งคืนเป็นการตอบกลับผ่าน api-gateway .

//get and display saved user item
const userItem = await client.hgetall(`userItem:${userId}`);

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

อย่าลืมอัปเดต serverless.yml เพื่อสะท้อนจุดสิ้นสุดนี้

functions:
  createUser:
    handler: user/create.createUser
    events:
      - http:
          path: /user
          method: post

ชื่อของฟังก์ชันของเราคือ createUser และอยู่ในไฟล์ชื่อ create.js ซึ่งอยู่ในโฟลเดอร์ชื่อ user . ดังนั้นตัวจัดการ user/create.createUser .

จดเส้นทาง /user

createUser ฟังก์ชั่นใช้ Posts วิธี http

นี่คือรหัสที่สมบูรณ์สำหรับ users/create.js ไฟล์

"use strict";
const uuid = require("uuid");
var Redis = require("ioredis");
const username = "username";
const firstName = "firstName";
const lastName = "lastName";
const profilePic = "profilePic";

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

/**
 *
 * @param {username,firstName,lastName,profilePic} event
 * @returns
 */
module.exports.createUser = async (event) => {
  const timestamp = new Date().getTime();

  const data = JSON.parse(event.body);
  if (data == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't create the user item",
        },
        null,
        2
      ),
    };
  }

  const userId = uuid.v1();
  console.log(`userId is ${userId}`);
  // here, we use a pipeline to perform multiple requests
  // Firstly, we save the user details to a hash dataset with key (`userItem:${userId}`)
  //
  client.pipeline(
    await client.hmset(
      `userItem:${userId}`,
      "userId",
      userId,
      username,
      data.username,
      firstName,
      data.firstName,
      lastName,
      data.lastName,
      profilePic,
      data.profilePic,
      "timestamp",
      timestamp
    ),
    await client.lpush("users", userId)
  );

  //get and display saved user item
  const userItem = await client.hgetall(`userItem:${userId}`);

  console.log(userItem);

  return {
    statusCode: 200,
    body: JSON.stringify(userItem),
  };
};

อัปเดตผู้ใช้

ผู้ใช้ชอบที่จะอัปเดตโปรไฟล์เป็นครั้งคราว ถูกต้องแล้วที่เรามีจุดสิ้นสุดการอัปเดตผู้ใช้

คำสั่งเดียวที่เราต้องการสำหรับการดำเนินการนี้คือ HMSET คำสั่งและ userId .

จากเอกสารของ Redis นี่คือวิธีที่ HMSET คำสั่งได้ผล

ตั้งค่าฟิลด์ที่ระบุเป็นค่าที่เกี่ยวข้องในแฮชที่เก็บไว้ที่คีย์ คำสั่งนี้จะเขียนทับฟิลด์ใดๆ ที่ระบุอยู่แล้วในแฮช

นี่คือลักษณะของโค้ด

"use strict";
const uuid = require("uuid");
var Redis = require("ioredis");
const username = "username";
const firstName = "firstName";
const lastName = "lastName";

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

/**
 *
 * @param {username,firstName,lastName,age,profilePic} event
 * @returns
 */
module.exports.updateUser = async (event) => {
  const timestamp = new Date().getTime();
  const userId = event.pathParameters.id;
  const data = JSON.parse(event.body);
  if (userId == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't update the user item",
        },
        null,
        2
      ),
    };
  }

  //get

  await client.hmset(
    `userItem:${userId}`,
    username,
    data.username,
    firstName,
    data.firstName,
    lastName,
    data.lastName
  );

  //get and display saved user item
  const userItem = await client.hgetall(`userItem:${userId}`);

  console.log(userItem);

  return {
    statusCode: 200,
    body: JSON.stringify(userItem),
  };
};

จากนั้นใน serverless.yml ไฟล์ ภายใต้ฟังก์ชัน เพิ่ม...

updateUser:
  handler: user/update.updateUser
  events:
    - http:
        path: /user/{id}
        method: put

เราส่งผ่าน userId เป็นพารามิเตอร์เส้นทาง /user/{id}

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

รับผู้ใช้

การใช้ HGETALL คำสั่งและ hash key ทำให้เราสามารถรับข้อมูลโปรไฟล์ผู้ใช้เฉพาะผู้ใช้ได้

จำไว้ว่าเรายังใช้ HGET . ได้ คำสั่งรับข้อมูลเฉพาะผู้ใช้ เช่น ชื่อหรือนามสกุล เป็นต้น

มาดูโค้ดกันเลย

สร้างไฟล์ใน user โฟลเดอร์ชื่อ get.js

"use strict";
var Redis = require("ioredis");

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

module.exports.getUserById = async (event) => {
  const userId = event.pathParameters.id;

  if (userId == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't get the user item",
        },
        null,
        2
      ),
    };
  }

  console.log(`userId is ${userId}`);

  //get and display saved user item
  const userItem = await client.hgetall(`userItem:${userId}`);

  console.log(userItem);

  return {
    statusCode: 200,
    body: JSON.stringify(userItem),
  };
};

จากนั้นใน serverless.yml ไฟล์

getUser:
  handler: user/get.getUserById
  events:
    - http:
        path: /user/{id}
        method: get

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

แสดงรายการผู้ใช้

เมื่อเราเขียน handler สำหรับสร้างผู้ใช้ จำไว้ว่าเราทิ้ง (LPUSH ) userId เป็น users รายการ

ตอนนี้ เราต้องคว้า id ทั้งหมดโดยใช้คำสั่ง LRANGE .

หมายเหตุ:LRANGE จะไม่มีประสิทธิภาพมากนักหากรายการโพสต์เริ่มมีขนาดใหญ่มาก และเราต้องการเข้าถึงองค์ประกอบที่อยู่ตรงกลางของรายการ เนื่องจาก Redis Lists ได้รับการสนับสนุนโดยรายการที่เชื่อมโยง หากระบบได้รับการออกแบบสำหรับการแบ่งหน้าลึกของรายการนับล้าน ควรใช้ Sorted Sets แทน

หลังจากได้รับ userId ทั้งหมดแล้ว เราก็สามารถวนซ้ำแต่ละ id และรับข้อมูลโปรไฟล์ผู้ใช้โดยใช้ HGETALL .

มาดูกันว่าโค้ดจะออกมาเป็นอย่างไร

"use strict";
var Redis = require("ioredis");

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

module.exports.getAllUsers = async (event) => {
  let users = [];
  let response = await client.lrange("users", 0, -1);

  async function getAllUsers() {
    let users = [];

    await Promise.all(
      response.map(async (userId) => {
        const item = await client.hgetall(`userItem:${userId}`);
        users.push(item);
        console.log(users);
      })
    );

    return users;
  }
  users = await getAllUsers();

  return {
    statusCode: 200,
    body: JSON.stringify(users),
  };
};

จากนั้นสำหรับ serverless.yml

listUsers:
  handler: user/list.getAllUsers
  events:
    - http:
        path: /users
        method: get

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

และนั่นก็เพื่อ Users ปลายทาง คุณสามารถดำเนินการต่อและทำให้แอปใช้งานได้ หากคุณยังไม่ได้ดำเนินการ

sls deploy

โพสต์

หลังจากที่ผู้ใช้สร้างบัญชีแล้ว พวกเขาควรจะได้รับอนุญาตให้

  • สร้างโพสต์
  • รับโพสต์ตามรหัส
  • รับรายการโพสต์ของพวกเขา
  • ชอบหรือไม่ชอบโพสต์ (ตอบกลับโพสต์)

มาเริ่มกันเลย

สร้างโพสต์

พารามิเตอร์ที่จำเป็นในการสร้างโพสต์คือ

  • รหัสผู้ใช้
  • postId(สร้างอัตโนมัติโดย UUID)
  • ข้อความโพสต์
  • โพสต์อิมเมจ
  • createdOn

มี 3 ขั้นตอนที่เกี่ยวข้อง

  1. ใช้คำสั่ง HMSET เพื่อตั้งค่ารายละเอียดโพสต์เป็นแฮชคีย์ postItem:${postId} .
  2. ใช้คำสั่ง LPUSH เพื่อเพิ่ม postId ในรายการ posts .
  3. ใช้คำสั่ง LPUSH เพื่อเพิ่ม postId ในรายการ userPost:${userId} .

เราจะใช้ไปป์ไลน์เพื่อดำเนินการงานนี้ตามลำดับเวลา

สร้างไฟล์ใน Posts โฟลเดอร์ชื่อ create.js และเพิ่มโค้ดต่อไปนี้เข้าไป

"use strict";
const uuid = require("uuid");
var Redis = require("ioredis");

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

/**
 *
 * @param {userId,postId,postText,postImage,createdOn} event
 * @returns
 */
module.exports.createPost = async (event) => {
  const timestamp = new Date().getTime();
  const postId = uuid.v1();
  const data = JSON.parse(event.body);
  if (data == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't create the Post item",
        },
        null,
        2
      ),
    };
  }

  console.log(`postId is ${postId}`);

  client.pipeline(
    await client.hmset(
      `postItem:${postId}`,
      "id",
      postId,
      "userId",
      data.userId,
      "postText",
      data.postText,
      "postImage",
      data.postImage,
      "createdOn",
      timestamp
    ),
    await client.lpush("posts", postId),
    await client.lpush(`userPost:${data.userId}`, postId)
  );

  //get and display saved post item
  const postItem = await client.hgetall(`postItem:${postId}`);

  console.log(postItem);

  return {
    statusCode: 200,
    body: JSON.stringify(postItem),
  };
};

จากนั้น ภายใต้ฟังก์ชันใน serverless.yml , เพิ่ม

createPost:
  handler: post/create.createPost
  events:
    - http:
        path: /post
        method: post

ปรับใช้และทดสอบ

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

รับโพสต์ด้วยรหัส

เมื่อได้รับโพสต์โดย Id เราต้องการรับและแนบรายละเอียดของผู้ดูแลโพสต์พร้อมกับจำนวนการตอบรับ (ไลค์) ที่โพสต์มีจนถึงปัจจุบัน

ฉันรู้ว่าเรายังไม่ได้ดูการกดถูกใจหรือไม่ชอบโพสต์เลย แค่รอเดี๋ยวก็ถึงแล้ว สร้างไฟล์ชื่อ get.js ในโฟลเดอร์โพสต์และบันทึกสิ่งนี้

"use strict";
var Redis = require("ioredis");

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

module.exports.getPostById = async (event) => {
  const postId = event.pathParameters.id;
  if (postId == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't get the post item",
        },
        null,
        2
      ),
    };
  }

  console.log(`postId is ${postId}`);

  //get and display saved post item
  const postItem = await client.hgetall(`postItem:${postId}`);
  const userItem = await client.hgetall(`userItem:${postItem.userId}`);
  const postReactionsCount = await client.get(`postReactionsCount:${postId}`);
  postItem["postAdmin"] = userItem;
  postItem["reactionCount"] =
    postReactionsCount == null ? 0 : postReactionsCount;
  console.log(postItem);
  console.log(userItem);

  return {
    statusCode: 200,
    body: JSON.stringify(postItem),
  };
};

จากโค้ดด้านบน อันดับแรก เราได้รับรายละเอียดโพสต์ทั้งหมดสำหรับโพสต์หนึ่งๆ โดยใช้ hgetall คำสั่งและคีย์ postItem:${postId} .

จากนั้น เนื่องจากวัตถุโพสต์มีรหัสผู้ใช้ของผู้ใช้ที่สร้างโพสต์ (ผู้ดูแลระบบโพสต์) เราจึงใช้ userItem:${postItem.userId} เพื่อรับรายละเอียดผู้ใช้สำหรับผู้ใช้รายนั้น

โปรดจำไว้ว่านี่คือคีย์เดียวกันกับที่เราใช้ในจุดสิ้นสุดการสร้างผู้ใช้ด้านบน

ประการที่สาม เราได้รับ postReactionCount โดยใช้ get คำสั่งและคีย์ postReactionsCount:${postId} ซึ่งเราจะใช้ภายหลังเพื่อบันทึก postReactionsCount

ปรับใช้และทดสอบ

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

ตอบกลับโพสต์

นี่คือจุดสิ้นสุดที่น่าสนใจที่สุดของแอปพลิเคชันทั้งหมด เขียนได้สนุกค่ะ

ผู้ใช้ได้รับอนุญาตให้ชอบหรือไม่ชอบโพสต์ ตอนนี้ เมื่อผู้ใช้คลิกปุ่มชอบ เราจะตรวจสอบก่อนว่าผู้ใช้รายนี้เคยชอบโพสต์นี้หรือไม่

ถ้าใช่ เราไม่เหมือนโพสต์ อย่างอื่นเราชอบโพสต์นี้

เข้าใจไหม

เช่นเดียวกับ Instagram หรือ Twitter

สร้างไฟล์ในโฟลเดอร์โพสต์ชื่อ react_to_post.js

ปลายทางใช้ userId และ postId เป็นพารามิเตอร์เส้นทาง

มาดูคำสั่งใหม่กัน ชุดเรียง. เราจะใช้ชุดที่จัดเรียงเพื่อเพิ่ม userId ของผู้ใช้ที่ถูกใจโพสต์ และเวลาประทับเมื่อพวกเขาชอบโพสต์

zadd(`postReactions:${postId}`, timestamp, data.userId)

กุญแจสำคัญคือ postReactions:${postId} .

ชุดที่จัดเรียงขึ้นต้นด้วย Z จากเอกสาร Redis zadd

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

ขั้นตอนต่อไปคือการเพิ่ม postReactionsCount:${postId} using the incr` คำสั่ง

จำไว้ว่าเราใช้คีย์ด้านบนในจุดรับ Post by Id เพื่อรับจำนวนหลังปฏิกิริยา

incr(`postReactionsCount:${postId}`),

สุดท้ายนี้ เราบันทึกรายละเอียดการโต้ตอบของผู้ใช้ลงในแฮช

hmset(`userPostReactions:${data.userId}`,
                "postId", postId,
                "userId", data.userId,
                "timestamp", timestamp
            ),

จากทั้งหมดนี้ นี่คือรูปแบบการเข้าถึงที่มีให้

  • เราสามารถนับจำนวนปฏิกิริยาหลังได้
  • เราสามารถรับผู้ใช้ทั้งหมดที่ตอบสนองต่อโพสต์ โดยเรียงลำดับจากน้อยไปมากหรือมากไปหาน้อย
  • เรารับโพสต์ทั้งหมดที่ผู้ใช้ตอบกลับได้

เราได้กล่าวไว้ก่อนหน้านี้ว่า เราต้องทำการตรวจสอบเพื่อดูว่าผู้ใช้เคยชอบโพสต์หรือไม่ ถ้าใช่ เราไม่ชอบโพสต์นั้น มิฉะนั้นเราชอบโพสต์

เราจะใช้ zscore คำสั่งนี้

ส่งคืนคะแนนของสมาชิกในชุดที่จัดเรียงที่คีย์ หากไม่มีสมาชิกในชุดที่จัดเรียง หรือไม่มีคีย์ ระบบจะส่งคืนค่าศูนย์

zscore(`postReactions:${postId}`, data.userId);

ถ้า zscore เป็นโมฆะ แสดงว่าผู้ใช้ไม่ชอบมัน มิฉะนั้น ผู้ใช้ชอบมัน

นี่คือลักษณะโค้ดที่สมบูรณ์

"use strict";
const uuid = require("uuid");
var Redis = require("ioredis");

if (typeof client === "undefined") {
  var client = new Redis(process.env.REDIS_CLIENT);
}

/**
 *
 * @param {userId,postId,postText,postImage,createdOn} event
 * @returns
 */
module.exports.reactToPost = async (event) => {
  const timestamp = new Date().getTime();

  const data = JSON.parse(event.body);
  const postId = event.pathParameters.id;

  if (data == null || postId == null) {
    return {
      statusCode: 400,
      body: JSON.stringify(
        {
          message: "Couldn't react to the Post",
        },
        null,
        2
      ),
    };
  }

  console.log(`postId is ${postId}`);
  console.log(`userId is ${data.userId}`);
  // first check if user has already liked the post
  const hasUserReacted = await client.zscore(
    `postReactions:${postId}`,
    data.userId
  );
  if (hasUserReacted == null) {
    //user hasn't reacted.
    client.pipeline(
      await client.incr(`postReactionsCount:${postId}`),
      await client.zadd(`postReactions:${postId}`, timestamp, data.userId),
      await client.hmset(
        `userPostReactions:${data.userId}`,
        "postId",
        postId,
        "userId",
        data.userId,
        "timestamp",
        timestamp
      )
    );
  } else {
    //user already reacted, so unreact
    client.pipeline(
      await client.decr(`postReactionsCount:${postId}`),
      await client.zrem(`postReactions:${postId}`, data.userId),
      await client.hdel(
        `userPostReactions:${data.userId}`,
        "postId",
        postId,
        "userId",
        data.userId,
        "timestamp",
        timestamp
      )
    );
  }

  //return the post reaction count
  const postReactionsCount = await client.get(`postReactionsCount:${postId}`);

  console.log(postReactionsCount);

  return {
    statusCode: 200,
    body: JSON.stringify({
      postReactionCount: parseInt(postReactionsCount),
    }),
  };
};

จากนั้นใน serverless.yml

reactToPosts:
  handler: post/react_to_post.reactToPost
  events:
    - http:
        path: /post/{id}/react
        method: post

ปรับใช้และทดสอบ

แอพ Fullstack Serverless พร้อม Flutter, Serverless Framework และ Upstash (REDIS) - PART 1

กดปุ่มส่งบนตัวทดสอบ API ของคุณ (ฉันใช้ PostMan) หลายครั้งและดูว่า "postReactionCount" สลับไปมาระหว่าง 0 ถึง 1 ได้อย่างไร

เปลี่ยน UserId และทำการตรวจสอบอีกครั้ง

มีรูปแบบการเข้าถึงและการแก้ไขอื่นๆ มากมายที่คุณสามารถเพิ่มลงใน API นี้ได้

ลองใช้ความท้าทายเพื่อขยายและเรียนรู้เพิ่มเติม

นี่คือลิงค์ไปยังซอร์สโค้ดที่สมบูรณ์

ฉันชอบเส้นโค้งการเรียนรู้ที่ไม่ชันมากของ Redis และความจริงที่ว่ามันช่วยให้ฉันสามารถบันทึกข้อมูลในแบบที่ฉันจะดึงมันกลับมา

รู้รูปแบบการเข้าถึงของคุณเสมอก่อนเริ่มพัฒนาแอป

ฉันสนุกกับการเขียนงานชิ้นนี้มาก หวังว่าคุณจะได้เรียนรู้สิ่งหนึ่งหรือสองสิ่ง

ฉันทำผิดพลาดที่ไหนสักแห่ง? สุดยอดสายัณห์อย่างคุณไม่ลังเลที่จะแจ้งให้เราทราบ

ในบทความถัดไป เราจะสร้างแอป Flutter เพื่อใช้ API นี้ คอยติดตาม

สุขสันต์ โค้ดดิ้ง สหาย✌🏿

ข้อมูลอ้างอิง

  • เอกสารสำรอง
  • เรดิส
  • กระพือปีก
  • ดึงข้อมูลจากอินเทอร์เน็ต