Computer >> Máy Tính >  >> Lập trình >> Redis

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Trong bài đăng này, chúng tôi sẽ xây dựng một ứng dụng di động không có máy chủ với Flutter, Serverless Framework, Upstash và Redis để lưu trữ dữ liệu.

Upstash là gì?

Upstash là một Cơ sở dữ liệu không máy chủ cho Redis. Với Upstash, bạn trả tiền theo yêu cầu. Điều này có nghĩa là bạn không bị tính phí khi cơ sở dữ liệu không được sử dụng.

Upstash định cấu hình và quản lý cơ sở dữ liệu cho bạn. Đây là một giải pháp thay thế mạnh mẽ cho các cơ sở dữ liệu khác như DynamoDB và Fauna, với các ưu điểm như

  • độ trễ thấp
  • Dễ sử dụng, giống như API REDIS.

Đây là tài liệu chi tiết so sánh Upstash với các giải pháp dựa trên đám mây thay thế, cung cấp cho bạn lý do rõ ràng về lý do tại sao bạn nên chọn nó cho dự án tiếp theo của mình.

Bạn cũng có thể xem bài viết này để so sánh tất cả các cơ sở dữ liệu serverless hiện có

Với Upstash,

  • Bạn bắt đầu miễn phí và chỉ trả tiền cho những gì bạn sử dụng
  • Nó có bộ nhớ nhanh, bền
  • Bạn có thể truy cập cơ sở dữ liệu của mình từ mọi nơi trên toàn cầu với độ trễ thấp do Cơ sở dữ liệu toàn cầu và Edge Caching.

Bắt đầu Upstash miễn phí ngay hôm nay

Để xây dựng hiệu quả các ứng dụng trên Upstash, bạn phải hiểu về Redis.

Nếu bạn thích thứ gì đó chi tiết và chuyên sâu hơn, tôi khuyên bạn nên sử dụng Trang web chính thức của Redis

Redis là một kho lưu trữ cấu trúc dữ liệu trong bộ nhớ, mã nguồn mở (được cấp phép BSD), được sử dụng làm cơ sở dữ liệu, bộ nhớ cache và môi giới tin nhắn.

Nó hỗ trợ rất nhiều cấu trúc dữ liệu như

  • chuỗi
  • băm
  • danh sách
  • bộ
  • tập hợp được sắp xếp với các truy vấn phạm vi
  • ảnh bitmap
  • siêu nhật ký
  • chỉ mục không gian địa lý

Bạn tương tác với cơ sở dữ liệu Redis bằng cách sử dụng các lệnh và lưu dữ liệu ở định dạng giá trị khóa, trong đó khóa có thể là một chuỗi và giá trị, bất kỳ cấu trúc dữ liệu nào được Redis hỗ trợ.

Ví dụ:tôi có thể sử dụng lệnh Redis SET để lưu trữ giá trị họ của tôi như vậy

SET surname Rosius

nơi surname là khóa và Rosius là giá trị.

Một điều rất quan trọng cần lưu ý với Redis là luôn lưu trữ dữ liệu của bạn theo cách mà bạn có thể dễ dàng truy xuất.

Không có cách trực tiếp nào để tìm kiếm khóa theo giá trị trong Redis.

Dữ liệu trong Redis được lưu trữ vĩnh viễn. Vì vậy, tôi có thể truy xuất dữ liệu được lưu trữ tại khóa surname như vậy

GET surname

Kết quả đến 'Rosius'

Chúng tôi cũng có thể xóa giá trị được lưu trữ tại khóa surname như vậy

DEL surname

Giả sử chúng tôi muốn tăng lượt thích của một bài đăng. Đây là cách chúng tôi có thể dễ dàng làm điều đó, sử dụng INCR lệnh nguyên tử.

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

Đầu tiên, chúng tôi đặt giá trị ban đầu của lượt thích là 10, sau đó chúng tôi tăng nguyên tử giá trị của lượt thích. Bây giờ, bạn có thể nghĩ rằng cũng có thể tăng likes theo cách này.

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

Điều này hoàn toàn ổn, miễn là bạn là người duy nhất sử dụng ứng dụng của mình.

Khi có> 2 người tăng lên giống như vậy, quá trình trên (GET, Increment, SET) không còn là nguyên tử nữa. Vì vậy,

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

Từ đoạn mã trên, người dùng 1 nhận giá trị của lượt thích là 10 và lưu trữ nó trong một biến x, đồng thời, người dùng 2 nhận cùng giá trị lượt thích đó, cũng là 10 và lưu trữ nó trong một biến y.

Người dùng 1 thêm 1 vào giá trị của lượt thích (x) và đặt giá trị mới, hiện là 11.

Người dùng 2 cũng làm như vậy.

Vì vậy, giá trị của lượt thích là 11.

Nhưng điều đó có thực sự chính xác? Hãy nhớ rằng số lượt thích đã được tăng gấp đôi bởi 2 người dùng khác nhau.

Giá trị của lượt thích phải là 12 chứ không phải 11. Đó là lý do tại sao Redis cung cấp INCR lệnh nguyên tử và giải quyết các vấn đề như vậy.

Hash DataType

Các hàm băm của Redis tương đương với các hàm băm của các ngôn ngữ lập trình khác. Về cơ bản, chúng được tạo thành từ một tập hợp các trường được liên kết với các giá trị:

Ví dụ, đây là cách tôi sẽ lưu trữ thông tin hồ sơ người dùng trong một Hash.

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

Thứ nhất, khóa cho Hash của chúng tôi là userProfile:100034 , thì chúng ta có key value tất cả các cặp thông qua. Ví dụ:"userId" là khóa và 100034' is the value. We can retrieve specific user profile information such as first name by using the Lệnh HGET command and userId` như vậy.

userId = 100034
HGET userProfile:{userId} firstName

Hoặc Chúng tôi có thể truy xuất tất cả thông tin hồ sơ người dùng cho một người dùng cụ thể bằng cách sử dụng GETALL lệnh như vậy

userId = 100034
HGETALL userProfile:{userId}

Loại dữ liệu danh sách

Trước đó, tôi đã nói rằng, trong Redis, điều rất quan trọng là phải lưu dữ liệu theo cách bạn định truy xuất.

Điều gì sẽ xảy ra nếu chúng tôi muốn có được tất cả người dùng trên nền tảng của mình?

Chúng tôi vừa sử dụng cấu trúc dữ liệu Hash để lưu thông tin hồ sơ người dùng. Bây giờ chúng tôi cần có được tất cả người dùng trên hệ thống của chúng tôi.

Cách dễ nhất để thực hiện điều này là lưu userId của mọi người dùng trong một Danh sách bằng cách sử dụng lệnh LPUSH (viết tắt của Left Push) như vậy

LPUSH "users" userId

Lấy tất cả người dùng từ Danh sách của chúng tôi, chúng tôi sử dụng lệnh LRANGE như vậy

LRANGE  "users" 0  -1

Đây chỉ là một vài lệnh mà chúng tôi sẽ sử dụng trong ứng dụng của mình. Nhưng tôi khuyến khích bạn truy cập trang web chính thức của Redis để xem phần còn lại của các lệnh và tìm hiểu cách sử dụng chúng.

Vậy ứng dụng full-stack của chúng ta hoạt động như thế nào?

Tôi rất vui vì bạn đã hỏi. Vì vậy, hôm nay, chúng ta sẽ bắt đầu bằng cách tạo API CRUD. Đây là trường hợp sử dụng.

Chúng tôi có 2 thực thể. UsersPosts .

Họ chia sẻ một mối quan hệ một đến nhiều. Vì vậy, một người dùng có thể có nhiều bài đăng và một bài đăng chỉ có thể thuộc về một người dùng.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Người dùng được phép

  • Tạo một tài khoản. Không có chứng thực. Bạn có thể thêm xác thực bằng AWS Cognito hoặc Auth0.
  • Cập nhật Tài khoản của họ
  • Nhận tài khoản của họ
  • Tạo một bài đăng
  • Cập nhật bài đăng
  • Thích một bài đăng
  • Nhận danh sách tất cả các bài đăng

Đây là chế độ xem kiến ​​trúc giải pháp

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1 Vì vậy, hãy bắt đầu.

Tạo tài khoản Upstash

Vui lòng tạo một tài khoản Upstash miễn phí tại đây Đăng nhập Upstash.

Sau khi tạo tài khoản, hãy tạo cơ sở dữ liệu Upstash.

Bạn chỉ được phép tạo một cơ sở dữ liệu trong cấp miễn phí.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1 Lưu ý điểm cuối cơ sở dữ liệu Redis của bạn. Nó sẽ trông giống như thế này. rediss://:2c9bb162c2444bf7ab689640bb2ead23@gusc1-smashing-bee-30249.upstash.io:00049

Tạo dự án không máy chủ

Điều kiện tiên quyết

Vui lòng cài đặt các phần phụ thuộc này trước khi tiếp tục

  • AWS CLI

  • NodeJS

  • CLI không máy chủ

    Tạo một dự án không máy chủ mới bằng cách sử dụng lệnh bên dưới và làm theo lời nhắc.

serverless

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Chọn API nodejs HTTP, đặt tên cho dự án của bạn và nhấn enter .

Đây là cấu trúc ban đầu của dự án không máy chủ của tôi.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Bên trong thư mục đó, khởi tạo một dự án nút mới bằng lệnh

npm init

Tiếp theo, cài đặt ứng dụng redis với

npm install ioredis

Ngoài ra, hãy cài đặt phụ thuộc Mã định danh duy nhất (uuid) phổ biến. Chúng tôi sẽ sử dụng nó rộng rãi trong dự án này.

npm install uuid

Bây giờ, hãy thêm Điểm cuối cơ sở dữ liệu Redis của bạn làm biến môi trường trong serverless.yml tệp như vậy.

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"

Tiếp theo, Tạo 2 thư mục bên trong dự án của bạn được gọi là usersposts . Cả hai thư mục này sẽ chứa các hàm lambda cho các trường hợp sử dụng tương ứng của chúng.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Hãy bắt đầu tạo các enpoint API của chúng ta

Tạo người dùng

Chúng tôi muốn người dùng có thể tạo một tài khoản cho chính họ.

Không có chứng thực. Tất cả những gì họ phải làm là gửi

  • tên người dùng
  • tên
  • họ
  • ảnh hồ sơ

Tạo tệp trong users thư mục có tên create.js .

Ở đầu tệp, nhập và khởi tạo ứng dụng khách redis bằng cách sử dụng URL cơ sở dữ liệu redis mà chúng tôi đã lưu dưới dạng biến môi trường trong serverless.yml tệp.

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

Chúng tôi cũng nhập phần phụ thuộc uuid, vì chúng tôi sẽ sử dụng nó để tạo ID duy nhất cho người dùng.

Đầu tiên, chúng ta cần một cấu trúc dữ liệu để lưu user profile thông tin và một cấu trúc dữ liệu khác để lưu userId's của tất cả người dùng trong ứng dụng.

Nhớ kiểu dữ liệu băm và danh sách?

Kiểu dữ liệu băm để lưu thông tin hồ sơ người dùng. Lưu ý về khóa kiểu dữ liệu userItem:${userId}

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

Sau đó, chúng tôi lưu userId đã tạo vào danh sách có tên users

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

Nếu bạn nhận thấy, chúng tôi phải gửi 2 hoạt động. Có thể gửi chúng lần lượt, nhưng điều đó không tối ưu.

Upstash hỗ trợ các hoạt động hàng loạt, thông qua một tính năng được gọi là pipelining .

Vì vậy, thay vì gửi các lệnh đơn lẻ và chờ phản hồi, chúng ta có thể gửi nhiều lệnh và phản hồi sẽ quay lại giống như cách chúng ta đã gửi các lệnh.

Vì vậy, đây là cách hoạt động của chúng tôi sẽ như thế nào sau khi sử dụng đường ống dẫn

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)
);

Sau đó, chúng tôi có thể lấy tất cả thông tin hồ sơ người dùng lưu và gửi lại dưới dạng phản hồi thông qua api-gateway .

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

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Đừng quên cập nhật serverless.yml để phản ánh điểm cuối này.

Các hàm
functions:
  createUser:
    handler: user/create.createUser
    events:
      - http:
          path: /user
          method: post

Tên hàm của chúng tôi là createUser và nó nằm trong một tệp có tên là create.js nằm trong thư mục có tên user .Do đó trình xử lý user/create.createUser .

Ghi lại đường dẫn /user

createUser hàm sử dụng post Phương thức http.

Đây là mã hoàn chỉnh cho users/create.js tệp

"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),
  };
};

Cập nhật người dùng

Người dùng muốn cập nhật hồ sơ của họ theo thời gian. Vì vậy, đúng là chúng tôi cung cấp một điểm cuối cập nhật người dùng.

Lệnh duy nhất chúng ta cần cho thao tác này là HMSETuserId .

Từ tài liệu Redis, đây chính xác là cách HMSET lệnh hoạt động.

Đặt các trường được chỉ định thành các giá trị tương ứng của chúng trong hàm băm được lưu trữ tại khóa. Lệnh này ghi đè bất kỳ trường cụ thể nào đã tồn tại trong hàm băm.

Đây là cách mã trông như thế nào.

"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),
  };
};

Sau đó, trong serverless.yml tệp, dưới các chức năng, thêm ...

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

Chúng tôi chuyển userId dưới dạng tham số đường dẫn /user/{id}

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Nhận người dùng

Sử dụng HGETALL và khóa băm, chúng tôi có thể lấy thông tin hồ sơ người dùng cho một người dùng cụ thể.

Hãy nhớ rằng chúng ta cũng có thể sử dụng HGET lệnh để lấy thông tin người dùng cụ thể như firstName hoặc lastName, v.v.

Hãy xem mã

Tạo tệp trong users thư mục có tên 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),
  };
};

Sau đó, trong serverless.yml tệp,

Các sự kiện
getUser:
  handler: user/get.getUserById
  events:
    - http:
        path: /user/{id}
        method: get

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Liệt kê Người dùng

Khi chúng tôi viết trình xử lý để tạo người dùng, hãy nhớ rằng chúng tôi đã đẩy (LPUSH ) userId thành một users danh sách.

Bây giờ, chúng ta phải lấy tất cả các id đó bằng lệnh LRANGE .

Lưu ý:LRANGE không hiệu quả lắm nếu danh sách các bài đăng bắt đầu quá lớn và chúng tôi muốn truy cập các phần tử ở giữa danh sách, vì Redis Lists được hỗ trợ bởi các danh sách được liên kết. Nếu một hệ thống được thiết kế để phân loại sâu hàng triệu mục, tốt hơn nên sử dụng các Bộ được sắp xếp để thay thế.

Sau khi nhận được tất cả userId, chúng ta có thể lặp lại từng id và nhận thông tin hồ sơ người dùng bằng HGETALL .

Hãy xem điều này trông như thế nào trong mã.

"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),
  };
};

Sau đó, đối với serverless.yml

Các sự kiện
listUsers:
  handler: user/list.getAllUsers
  events:
    - http:
        path: /users
        method: get

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Và đó là nó dành cho người dùng users điểm cuối. Bạn có thể tiếp tục và triển khai ứng dụng của mình, nếu bạn chưa làm điều đó.

sls deploy

Đăng

Sau khi người dùng tạo tài khoản, họ sẽ được phép

  • Tạo một bài đăng
  • Nhận một bài đăng theo Id
  • Nhận danh sách các bài đăng của họ
  • Thích hoặc không thích một bài đăng (Phản ứng lại bài đăng)

Hãy bắt đầu.

Tạo bài đăng

Các thông số cần thiết để tạo một bài đăng là

  • userId
  • postId (Tự động tạo bởi UUID)
  • postText
  • postImage
  • createdOn

Có 3 bước.

  1. Sử dụng lệnh HMSET để đặt chi tiết bài đăng thành Khóa băm postItem:${postId} .
  2. Sử dụng lệnh LPUSH để thêm postId vào danh sách posts .
  3. Sử dụng lệnh LPUSH để thêm postId vào danh sách userPost:${userId} .

Chúng tôi sẽ sử dụng một đường dẫn để thực hiện nhiệm vụ này theo thứ tự thời gian.

Tạo một tệp trong post thư mục có tên create.js và thêm mã sau vào nó.

"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),
  };
};

Sau đó, dưới các hàm trong serverless.yml , thêm

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

Triển khai và thử nghiệm.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Nhận bài đăng theo Id

Khi nhận được bài đăng bằng Id, chúng tôi muốn lấy và đính kèm thông tin chi tiết của quản trị viên bài đăng, cùng với số lượt phản ứng (thích) bài đăng đã có cho đến nay.

Tôi biết rằng chúng tôi chưa xem xét việc thích hay không thích một bài đăng nào, chỉ cần đợi ở đó, chúng tôi sẽ hoàn thành nó. Tạo một tệp có tên get.js trong thư mục bài đăng và lưu cái này

"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),
  };
};

Từ đoạn mã trên, trước tiên, chúng tôi nhận được tất cả các chi tiết về bài đăng cho một bài đăng cụ thể bằng cách sử dụng hgetall và khóa postItem:${postId} .

Sau đó, vì đối tượng bài đăng có userId của người dùng tạo bài đăng (quản trị viên bài đăng), chúng tôi sử dụng userItem:${postItem.userId} để lấy chi tiết người dùng cho người dùng đó.

Hãy nhớ rằng đây là khóa chính xác mà chúng tôi đã sử dụng trong điểm cuối tạo người dùng ở trên.

Thứ ba, chúng tôi nhận được postReactionCount bằng cách sử dụng get lệnh và một khóa postReactionsCount:${postId} mà chúng tôi sẽ sử dụng sau này để lưu postReactionsCount.

Triển khai và thử nghiệm

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Phản hồi bài đăng

Đây là điểm cuối thú vị nhất của toàn bộ ứng dụng. Thật là vui khi viết.

Người dùng được phép thích hoặc không thích một bài đăng. Bây giờ, khi người dùng nhấp vào nút thích, trước tiên, chúng tôi kiểm tra xem người dùng này đã thích bài đăng trước đó chưa.

Nếu có, chúng tôi, không giống như bài đăng. Khác, chúng tôi thích bài đăng.

Bạn có hiểu không?

Cũng giống như Instagram hoặc twitter.

Tạo một tệp trong thư mục bài đăng có tên react_to_post.js

Điểm cuối lấy userIdpostId dưới dạng tham số đường dẫn.

Hãy xem một lệnh mới. Tập hợp đã được sắp xếp. Chúng tôi sẽ sử dụng một tập hợp được sắp xếp để thêm userId của người dùng thích bài đăng và dấu thời gian khi họ thích bài đăng.

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

Chìa khóa là postReactions:${postId} .

Các nhóm được sắp xếp bắt đầu bằng Z. Từ tài liệu Redis, zadd

Thêm tất cả các thành viên được chỉ định với điểm số được chỉ định vào tập hợp đã sắp xếp được lưu trữ tại khóa. Có thể chỉ định nhiều cặp điểm / thành viên. Nếu một thành viên được chỉ định đã là thành viên của tập hợp được sắp xếp, điểm số sẽ được cập nhật và phần tử được lắp lại vào đúng vị trí để đảm bảo thứ tự chính xác.

Bước tiếp theo là tăng postReactionsCount:${postId} using the lệnh incr`.

Hãy nhớ rằng chúng tôi đã sử dụng khóa ở trên trong điểm cuối nhận Bài đăng theo Id để nhận số lượng phản ứng sau.

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

Cuối cùng, chúng tôi lưu chi tiết phản ứng bài đăng của người dùng vào Hash

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

Dựa trên tất cả những điều này, đây là các mẫu truy cập có sẵn.

  • Chúng tôi có thể nhận được số lượng phản ứng bài đăng.
  • Chúng tôi có thể thu hút tất cả người dùng đã phản ứng với một bài đăng, theo thứ tự tăng dần hoặc giảm dần
  • Chúng tôi có thể xem tất cả các bài đăng mà người dùng đã phản hồi.

Chúng tôi đã đề cập trước đó rằng, chúng tôi cần thực hiện kiểm tra xem liệu trước đó người dùng có thích một bài đăng hay không. Nếu có, chúng tôi không giống như bài đăng đó. Khác, chúng tôi thích bài đăng

Chúng tôi sẽ sử dụng zscore lệnh cho việc này.

Trả về điểm của thành viên trong tập hợp đã sắp xếp tại khóa. Nếu thành viên không tồn tại trong tập hợp đã sắp xếp hoặc khóa không tồn tại, thì trả về nil.

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

if zscore là null, thì người dùng không thích nó. Khác, người dùng đã thích nó.

Đây là giao diện của mã hoàn chỉnh

"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),
    }),
  };
};

Sau đó, trong serverless.yml

Các sự kiện
reactToPosts:
  handler: post/react_to_post.reactToPost
  events:
    - http:
        path: /post/{id}/react
        method: post

Triển khai và thử nghiệm.

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 1

Nhấn nút gửi trên trình kiểm tra API của bạn (tôi sử dụng PostMan) nhiều lần và xem cách "postReactionCount" chuyển đổi giữa 0 và 1.

Thay đổi UserId và kiểm tra lại.

Có rất nhiều mẫu truy cập và các bản sửa lỗi khác mà bạn có thể thêm vào API này.

Còn bạn thì sao, hãy chấp nhận thử thách để mở rộng điều này và tìm hiểu thêm.

Đây là liên kết đến mã nguồn hoàn chỉnh

Tôi hoàn toàn yêu thích đường cong học tập không quá dốc của Redis và thực tế là nó cho phép tôi lưu dữ liệu theo cách tôi truy xuất.

Luôn biết các kiểu truy cập của bạn trước khi bạn bắt đầu phát triển ứng dụng.

Tôi rất vui khi viết bài này, hy vọng bạn đã học được một hoặc hai điều.

Tôi đã làm sai ở đâu đó? Một siêu Sayan như bạn sẽ không ngần ngại cho tôi biết.

Trong bài viết tiếp theo, chúng tôi sẽ xây dựng một ứng dụng Flutter để sử dụng API này. Hãy theo dõi.

Chúc mừng đồng chí mã hóa✌🏿

Tham chiếu

  • Upstash Docs
  • Redis
  • Rung động
  • Tìm nạp dữ liệu từ internet