使用生成的 Web SDK

借助 Firebase SQL Connect 客户端 SDK,您可以直接从 Firebase 应用调用服务器端查询和变更。在设计要部署到 SQL Connect 服务的架构、查询和变更时,您可以同时生成自定义客户端 SDK。然后,您将此 SDK 中的方法集成到客户端逻辑中。

正如我们在其他地方提到的,请务必注意,SQL Connect 查询和变更不是由客户端代码提交并在服务器上执行的。相反,在部署时,SQL Connect 操作会像 Cloud Functions 函数一样存储在服务器上。这意味着您需要部署相应的客户端更改,以免影响现有用户(例如,使用旧版应用的用户)。

因此,SQL Connect 为您提供了开发者环境和工具,让您可以对服务器部署的架构、查询和变更进行原型设计。它还会在您进行原型设计时自动生成客户端 SDK。

当您对服务和客户端应用进行迭代更新后,服务器端和客户端更新都已准备好进行部署。

客户端开发工作流是什么?

如果您已阅读使用入门,那么您已经了解了 SQL Connect 的整体开发流程。在本指南中,您将详细了解如何根据架构生成 Web SDK,以及如何使用客户端查询和变更。

总而言之,如需在客户端应用中使用生成的 Web SDK,您需要按照以下前提步骤操作:

  1. 将 Firebase 添加到您的 Web 应用。

然后:

  1. 开发应用架构。
  2. 使用 JavaScript SDK 初始化客户端代码。
  3. 设置 SDK 生成:
    • 使用 SQL Connect VS Code 扩展程序中的将 SDK 添加到应用按钮。
    • 更新 JavaScript SDK 的 connector.yaml。
  4. 使用 JavaScript SDK 导入库和生成的代码。
  5. 使用 JavaScript SDK 实现对查询和变更的调用。
  6. 测试:使用 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);
}

订阅变更

请参阅从 SQL Connect 获取实时更新。

处理对枚举字段的更改

应用的架构可以包含枚举,这些枚举可由 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 绑定:

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