Chuyển tới nội dung chính

Bước 3 - Giao tiếp với Server qua RESTful API

Sử dụng RESTful API để giao tiếp với Server là một nhu cầu phổ biến của hầu hết các ứng dụng, không chỉ riêng Zalo Mini App. Trong trường hợp của Coffee Shop, bạn có thể muốn gửi đơn hàng của người dùng lên sau khi họ đã chọn món trên giao diện, hoặc đơn giản hơn là lấy danh sách các sản phẩm từ cơ sở dữ liệu.

1. Đọc dữ liệu từ Server

Chúng ta sẽ sử dụng Fetch API để gửi các HTTP requests đến Server. Kết hợp với async selector của Recoilcơ chế Suspense mới của React, bạn có thể xử lý trạng thái loading cũng như caching dữ liệu dễ dàng hơn:

Mẹo

Do nhu cầu về state management là không thể thiếu đối với các ứng dụng React, một project Zalo Mini App được khởi tạo sẵn với Recoil. Vì vậy, bạn không cần phải thiết lập thêm.

// src/types/product.ts
interface Product {
id: number;
name: string;
price: number;
}

// src/state.ts
export const keywordState = atom({
key: "keyword",
default: ""
});

export const searchResultState = selector<Product[]>({
key: "searchResult",
get: async ({ get }) => {
const keyword = get(keywordState);
if (keyword.trim().length > 0) {
const params = new URLSearchParams({ keyword }).toString();
const response = await fetch(`https://dummyjson.com/products?${params}`);
const data = await response.json();
return data.products;
}
return [];
}
});

// src/pages/search.tsx
export const SearchPage: FC = () => {
const [keyword, setKeyword] = useRecoilState(keywordState);
return (
<Page>
<Input value={keyword} onChange={(e) => setKeyword(e.target.value)} />
<Suspense fallback={<div>Đang lấy danh sách sản phẩm...</div>}>
<SearchResult />
</Suspense>
</Page>
);
};

const SearchResult: FC = () => {
const result = useRecoilValue(searchResultState);
return (
<Box>
<Text.Title>Kết quả ({result.length})</Text.Title>
{result.map((product) => (
<ProductPicker key={product.id} product={product}>
{({ open }) => (
<div onClick={open}>
<b>Sản phẩm: {product.name}</b>
<u>Giá: {product.price}</u>
</div>
)}
</ProductPicker>
))}
</Box>
);
};

Điều tuyệt vời ở Recoil selector là đoạn code trên đảm bảo ứng vỗi mỗi keyword khác nhau chỉ có duy nhất một HTTP request được gửi đi. Các state management như Redux thường cần phải có thêm 1 lớp logic caching cũng nhưng xử lý state loading nữa.

CORS

Một trong những vấn đề thường gặp nhất khi giao tiếp tới Server của doanh nghiệp từ Mini App đó là vấn đề về cross origins.

Đây là cơ chế mặc định của các trình duyệt web để đảm bảo chỉ có những domain mà Server cho phép mới có thể gửi dữ liệu đến Server. Các domain được cho phép được khai báo trong header Access-Control-Allow-Origin mà Server trả về trong response và nó phải bao gồm ít nhất các domain sau:

Code mẫu bạn có thể tham khảo ở đây: Call API.

2. Lưu dữ liệu lên Server

Tương tự như việc đọc dữ liệu từ Server, việc lưu dữ liệu cũng có thể được thực hiện tương tự với fetch và các methods POST, PUT, DELETE. Tuy nhiên các thao tác này thường sẽ cần phải định danh người dùng, ví dụ như bạn sẽ muốn lưu order theo ID người dùng, hoặc xác thực xem người dùng này có quyền chỉnh sửa thông tin nào đó hay không.

Định danh người dùng bằng Zalo access token

ZMP SDK cung cấp nhiều API để đối tác Zalo Mini App có thể giao tiếp với người dùng Zalo như mở cửa sổ chat, theo dõi OA, chia sẻ thông tin, xin số điện thoại,... Trong trường hợp này chúng ta sẽ sử dụng API getAccessToken và đính kèm access token vào header của request.

import { BASE_URL } from "configs";
import { getAccessToken } from "zmp-sdk";

export const placeOrder = async (cart: Cart) => {
const accessToken = await getAccessToken();
return fetch(`${BASE_URL}/order`, {
method: "POST",
body: JSON.stringify(cart),
headers: {
"zalo-access-token": `Bearer ${accessToken}`
}
});
};

Khi Server nhận được request có chứa zalo-access-token trong header, Server có thể gọi qua https://graph.zalo.me để lấy định danh người dùng. Code mẫu đối với Node.js:

export async function getUserFromAccessToken(access_token: string) {
const response = await fetch(
`https://graph.zalo.me/v2.0/me?fields=id%2Cname%2Cpicture`,
{
headers: {
access_token
}
}
);
const user = await response.json();
return user as User;
}

export interface User {
name: string;
id: string;
error: number;
message: string;
picture: Picture;
}

export interface Picture {
data: Data;
}

export interface Data {
url: string;
}

Từ đó bạn có thể lưu giỏ hàng theo người dùng Zalo. Người dùng của bạn không cần phải đăng nhập hay đăng ký gì trên Zalo Mini App cả. Code mẫu nếu Server của bạn sử dụng Express:

const app = express();

app.post("/order", async (req, res) => {
const cart = req.body.cart;
const access_token = req.headers["zalo-access-token"].split(" ")[1];
const { id } = await getUserFromAccessToken(access_token);

await businessLogic({
user_id: id,
cart: cart,
date: new Date()
});
res.sendStatus(200);
});