借助 Firebase SQL Connect 客户端 SDK,您可以直接从 Firebase 应用调用服务器端查询和变更。在设计要部署到 SQL Connect 服务的架构、查询和变更时,您可以同时生成自定义客户端 SDK。然后,您将此 SDK 中的方法集成到客户端逻辑中。
正如我们在其他地方提到的,请务必注意,SQL Connect 查询和变更不是由客户端代码提交并在服务器上执行的。相反,在部署时,SQL Connect 操作会像 Cloud Functions 函数一样存储在服务器上。这意味着您需要部署相应的客户端更改,以免影响现有用户(例如,使用旧版应用的用户)。
因此,SQL Connect 为您提供了开发者环境和工具,让您可以对服务器部署的架构、查询和变更进行原型设计。它还会在您进行原型设计时自动生成客户端 SDK。
当您对服务和客户端应用进行迭代更新后,服务器端和客户端更新都已准备好进行部署。
客户端开发工作流是什么?
如果您已阅读使用入门,那么您已经了解了 SQL Connect 的整体开发流程。在本指南中,您将详细了解如何根据架构生成 Web SDK,以及如何使用客户端查询和变更。
总而言之,如需在客户端应用中使用生成的 Web SDK,您需要按照以下前提步骤操作:
- 将 Firebase 添加到您的 Web 应用。
然后:
- 开发应用架构。
- 使用 JavaScript SDK 初始化客户端代码。
- 设置 SDK 生成:
- 使用 SQL Connect VS Code 扩展程序中的将 SDK 添加到应用按钮。
- 更新 JavaScript SDK 的
connector.yaml。
- 使用 JavaScript SDK 导入库和生成的代码。
- 使用 JavaScript SDK 实现对查询和变更的调用。
- 测试:使用 JavaScript SDK 设置 SQL Connect 模拟器。
使用 Firebase JavaScript SDK 实现客户端代码
本部分介绍了如何使用 Firebase JavaScript SDK 实现客户端。
如果您使用的是 React 或 Angular,请参阅替代设置说明以及有关为框架生成 SQL Connect SDK 的其他文档的链接。
初始化您的应用
首先,使用标准 Firebase 序列初始化您的应用。
initializeApp({...});
安装生成的 JavaScript SDK
使用 Firebase CLI 在应用中设置 SQL Connect 生成的 SDK。init 命令应检测当前文件夹中的所有应用,并自动安装生成的 SDK。
firebase init dataconnect:sdk
将您的应用连接到 SQL Connect 服务。
import { connectDataConnectEmulator } from 'firebase/data-connect';
import { connectorConfig } from '@dataconnect/generated';
const dataConnect = getDataConnect(connectorConfig);
// [Optionally] Configure the SDK to use Data Connect local emulator.
connectDataConnectEmulator(dataConnect, 'localhost', 9399);
在原型设计期间更新 SDK
如果您安装了 SQL Connect VS Code 扩展程序,它会始终保持生成的 SDK 为最新状态。
如果您不使用 SQL Connect VS Code 扩展程序,则可以使用 Firebase CLI 使生成的 SDK 保持最新状态。
firebase dataconnect:sdk:generate --watch在构建流水线中生成 SDK
您可以使用 Firebase CLI 在 CI/CD 构建流程中生成 SQL Connect SDK。
firebase dataconnect:sdk:generate导入库
初始化客户端代码需要两组导入:常规 SQL Connect 导入和特定的生成 SDK 导入。
请注意常规导入中包含的 ConnectorConfig 对象。
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// generated queries and mutations from SDK
import { listMovies, ListMoviesResponse, createMovie, connectorConfig } from '@dataconnect/generated';
使用 JavaScript SDK 中的查询
生成的代码将已包含预定义的查询引用。您只需导入并调用 execute 即可。
import { executeQuery } from 'firebase/data-connect';
import { listMoviesRef } from '@dataconnect/generated';
const ref = listMoviesRef();
const { data } = await executeQuery(ref);
console.log(data.movies);
调用 SDK 查询方法
以下示例展示了如何使用这些操作快捷函数:
import { listMovies } from '@dataconnect/generated';
function onBtnClick() {
// This will call the generated JS from the CLI and then make an HTTP request out
// to the server.
listMovies().then(data => showInUI(data)); // == executeQuery(listMoviesRef);
}
订阅变更
处理对枚举字段的更改
应用的架构可以包含枚举,这些枚举可由 GraphQL 查询访问。
随着应用设计的变化,您可能会添加新的枚举支持的值。例如,假设您决定在应用生命周期的后期向 AspectRatio 枚举添加 FULLSCREEN 值。
在 SQL Connect 工作流中,您可以使用本地开发者工具更新查询和 SDK。
但是,在您发布客户端的更新版本之前,旧版已部署客户端可能会损坏。
弹性实现示例
始终向枚举值上的 switch 语句添加 default 分支,或向与枚举值进行比较的 if/else if 块添加 else 分支。
JavaScript/TypeScript 语言不会强制执行此操作,但这是在添加新枚举值时使客户端代码保持稳健的方法。
const queryResult = await getOldestMovie();
if (queryResult.data) {
// we can use a switch statement's "default" case to check for unexpected values
const oldestMovieAspectRatio = queryResult.data.originalAspectRatio;
switch (oldestMovieAspectRatio) {
case AspectRatio.ACADEMY:
case AspectRatio.WIDESCREEN:
case AspectRatio.ANAMORPHIC:
console.log('This movie was filmed in Academy, widescreen or anamorphic aspect ratio!');
break;
default:
// the default case will catch FULLSCREEN, UNAVAILABLE or _UNKNOWN
// it will also catch unexpected values the SDK isn't aware of, such as CINEMASCOPE
console.log('This movie was was NOT filmed in Academy, widescreen or anamorphic.');
break;
}
// alternatively, we can check to see if the returned enum value is a known value
if (!Object.values(AspectRatio).includes(oldestMovieAspectRatio)) {
console.log(`Unrecognized aspect ratio: ${oldestAspectRatio}`);
}
} else {
console.log("no movies found!");
}
使用 JavaScript SDK 中的变更
变更的访问方式与查询相同。
import { executeMutation } from 'firebase/data-connect';
import { createMovieRef } from '@dataconnect/generated';
const { data } = await executeMutation(createMovieRef({ movie: 'Empire Strikes Back' }));
连接到 SQL Connect 模拟器
(可选)您可以通过调用 connectDataConnectEmulator,然后传入 SQL Connect 实例来连接到模拟器,如下所示:
import { connectDataConnectEmulator } from 'firebase/data-connect';
import { connectorConfig } from '@dataconnect/generated';
const dataConnect = getDataConnect(connectorConfig);
connectDataConnectEmulator(dataConnect, 'localhost', 9399);`
// Make calls from your app
如需切换到生产资源,请注释掉用于连接到模拟器的代码行。
启用客户端缓存
SQL Connect 具有可选的客户端缓存功能,您可以通过修改 connector.yaml 文件来启用该功能。启用此功能后,生成的客户端 SDK 将在本地缓存查询响应,这可以减少应用发出的数据库请求数量,并使应用中依赖数据库的部分在网络可用性中断时仍能正常运行。
如需启用客户端缓存,请将客户端缓存配置添加到连接器配置中:
generate:
javascriptSdk:
outputDir: ../web/
package: "@dataconnect/generated"
clientCache:
maxAge: 5s
storage: memory
此配置有两个参数,均为可选参数:
maxAge:在客户端 SDK 提取新值之前,缓存响应的最长存在时间。示例:“0”“30s”“1h30m”。maxAge的默认值为0,这意味着响应会被缓存,但客户端 SDK 始终会提取最新值。仅当CACHE_ONLY指定为executeQuery()且从subscribe()返回初始结果时,才会使用缓存的值。storage:客户端 SDK 可配置为在persistent存储空间或memory中缓存响应。缓存在persistent存储空间中的结果会在应用重启后保留。在 Web SDK 中,仅支持memory存储。
更新连接器的缓存配置后,请重新生成客户端 SDK 并重新构建应用。完成这些操作后,executeQuery() 和 subscribe() 将根据您配置的政策缓存响应并使用缓存的值。这通常会自动发生,您无需执行任何其他步骤;但请注意以下事项:
executeQuery()的默认行为如上所述:如果查询结果已缓存,且缓存值不早于maxAge,则使用缓存值。此默认行为称为PREFER_CACHE政策。您还可以指定
executeQuery()的各个调用,以仅提供缓存值 (CACHE_ONLY) 或无条件地从服务器提取新值 (SERVER_ONLY)。await executeQuery(queryRef, QueryFetchPolicy.CACHE_ONLY);await executeQuery(queryRef, QueryFetchPolicy.SERVER_ONLY);当您调用
subscribe()时,无论maxAge设置如何,它始终会立即返回缓存的内容(如果存在)。后续对executeQuery()的调用将根据配置的maxAge通知监听器。
SDK 中的数据类型
SQL Connect 服务器表示常见的 GraphQL 数据类型。这些在 SDK 中表示如下。
| SQL Connect 类型 | TypeScript |
|---|---|
| 时间戳 | 字符串 |
| 日期 | 字符串 |
| UUID | 字符串 |
| Int64 | 字符串 |
| 双精度 | 数字 |
| 浮点数 | 数字 |
使用 TanStack 生成 React 和 Angular SDK
Firebase SQL Connect 提供了一个生成的 SDK,其中包含适用于 React 和 Angular 的钩子,这些钩子使用了我们的合作伙伴 Invertase 提供的库 TanStack Query Firebase。
此库提供了一组钩子,可大大简化应用中 Firebase 异步任务的处理。
TanStack 附带了自己的客户端缓存和实时订阅实现,这些实现可以与 SQL Connect 的内置实时支持一起使用,但存在一些困难。我们建议您使用基于 TanStack 的绑定或 SQL Connect 的内置实时支持,但不要同时使用这两者。
请注意,与 TanStack 绑定相比,SQL Connect 自己的实时实现具有一些优势:
- 标准化缓存:SQL Connect 实现了标准化缓存,与查询级缓存相比,它提高了数据一致性以及内存和网络效率。借助标准化缓存,如果某个实体在应用的某个区域中更新,它也会在使用该实体的其他区域中更新。
- 远程失效:SQL Connect 可以远程使所有订阅设备上的缓存实体失效。
初始化您的应用
首先,与任何 Firebase Web 应用一样,使用标准 Firebase 序列初始化您的应用。
initializeApp({...});
安装 TanStack Query Firebase 软件包
在项目中安装 TanStack Query 的软件包。
回应
npm i --save @tanstack/react-query @tanstack-query-firebase/react
npm i --save firebase@latest # Note: React has a peer dependency on ^11.3.0
Angular
ng add @angular/fire
生成 React 或 Angular SDK
与标准 Web SDK 一样,如前文所述,Firebase 工具会根据您的架构和操作自动生成 SDK。
如果您刚刚向项目添加了 React 或 Angular,请重新运行 firebase init dataconnect:sdk,以重新配置生成的 SDK,使其包含额外的框架绑定。
导入库
初始化 React 或 Angular 客户端代码需要四组导入:常规 SQL Connect 导入、常规 TanStack 导入,以及针对 JS 和 React 生成的 SDK 的特定导入。
请注意常规导入中包含的 ConnectorConfig 类型。
回应
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// TanStack Query-related functions
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// generated queries and mutations from SDK
import { ListMoviesResponse, connectorConfig } from '@dataconnect/generated';
// generated React hooks from SDK
import { useListAllMovies, useCreateMovie } from "@dataconnect/generated/react";
Angular
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// TanStack Query-related functions
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental";
// generated queries and mutations from SDK
import { ListMoviesResponse, connectorConfig } from '@dataconnect/generated';
// generated React hooks from SDK
import { injectListAllMovies, injectCreateMovie } from "@dataconnect/generated/angular";
在 React 或 Angular 客户端中使用查询和变更
设置完成后,您就可以整合生成的 SDK 中的方法了。
在以下代码段中,请注意 React 的 use 前缀方法 useListAllMovies 和 Angular 的 inject 前缀方法 injectListAllMovies,两者均来自生成的 SDK。
回应
生成的 SDK 中的所有此类操作(包括查询和变更)都会调用 TanStackQuery 绑定:
- 查询会调用并返回 TanStack
useDataConnectQuery钩子 - 变更会调用并返回 TanStack
useDataConnectMutation钩子
import { useListAllMovies } from '@dataconnect/generated/react';
function MyComponent() {
const { isLoading, data, error } = useListAllMovies();
if(isLoading) {
return <div>Loading...</div>
}
if(error) {
return <div> An Error Occurred: {error} </div>
}
}
// App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import MyComponent from './my-component';
function App() {
const queryClient = new QueryClient();
return <QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
}
Angular
import { injectAllMovies, connectorConfig } from '@dataconnect/generated/angular';
import { provideDataConnect, getDataConnect } from '@angular/fire/data-connect';
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental";
const queryClient = new QueryClient();
...
providers: [
...
provideTanStackQuery(queryClient),
provideDataConnect(() => {
const dc = getDataConnect(connectorConfig);
return dc;
})
]
在 React 和 Angular 中使用自动重新加载查询
您可以将查询配置为在数据发生变化时自动重新加载。
回应
export class MovieListComponent {
movies = useListAllMovies();
}
export class AddPostComponent {
const mutation = useCreateMovie({ invalidate: [listAllMoviesRef()] });
addMovie() {
// The following will automatically cause TanStack to reload its listAllMovies query
mutation.mutate({ title: 'The Matrix });
}
}
Angular
// class
export class MovieListComponent {
movies = injectListAllMovies();
}
// template
@if (movies.isPending()) {
Loading...
}
@if (movies.error()) {
An error has occurred: {{ movies.error() }}
}
@if (movies.data(); as data) {
@for (movie of data.movies; track movie.id) {
<mat-card appearance="outlined">
<mat-card-content>{{movie.description}}</mat-card-content>
</mat-card>
} @empty {
<h2>No items!</h2>
}
}
连接到 SQL Connect 模拟器
(可选)您可以通过调用 connectDataConnectEmulator,然后将 SQL Connect 实例传递给生成的钩子来连接到模拟器,如下所示:
React
import { getDataConnect, connectDataConnectEmulator } from 'firebase/data-connect';
import { connectorConfig } from '@dataconnect/generated';
import { useListAllMovies } from '@dataconnect/generated/react';
const dc = getDataConnect(connectorConfig);
connectDataConnectEmulator(dc, 'localhost', 9399);
class AppComponent() {
...
const { isLoading, data, error } = useListAllMovies(dc);
...
}
Angular
// app.config.ts
import { provideDataConnect } from '@angular/fire/data-connect';
import { getDataConnect, connectDataConnectEmulator } from 'firebase/data-connect';
provideDataConnect(() => {
const dc = getDataConnect(connectorConfig);
connectDataConnectEmulator(dc, 'localhost', 9399);
return dc;
}),
如需切换到生产资源,请注释掉用于连接到模拟器的代码行。
在原型设计期间更新 SDK
如果您安装了 SQL Connect VS Code 扩展程序,它会始终保持生成的 SDK 为最新状态。
如果您不使用 SQL Connect VS Code 扩展程序,则可以使用 Firebase CLI 使生成的 SDK 保持最新状态。
firebase dataconnect:sdk:generate --watch在构建流水线中生成 SDK
您可以使用 Firebase CLI 在 CI/CD 构建流程中生成 SQL Connect SDK。
firebase dataconnect:sdk:generate