メインコンテンツまでスキップ

Pages

Next.js で Squid を使用する場合、application の server side と client side の両方で Squid client に access できます。server では initial payload の一部として data を query できます。client では query、mutation、real-time data update の stream を実行できます。

まず、pages/_app.tsx で Component を SquidContextProvider で wrap します。placeholder を Squid configuration option に置き換えます。これらの value は Squid Console または .env file で確認できます。.env file は Squid backend の作成時に自動生成され、backend directory にあります。

pages/_app.tsx
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import { SquidContextProvider } from '@squidcloud/react';

export default function App({ Component, pageProps }: AppProps) {
return (
<SquidContextProvider
options={{
appId: 'YOUR_APP_ID',
region: 'YOUR_REGION',
environmentId: 'dev',
squidDeveloperId: 'YOUR_SQUID_DEVELOPER_ID',
}}
>
<Component {...pageProps} />
</SquidContextProvider>
);
}

User を Query する​

app に users collection の client-side query を導入するには、pages/index.tsx を以下の code に置き換えます。

pages/index.tsx
import { useCollection, useQuery } from '@squidcloud/react';

type User = {
id: string;
};

export default function Home() {
const collection = useCollection<User>('users');

const { loading, data, error } = useQuery(collection.query().dereference());

if (loading) {
return <div className="flex flex-col items-center justify-center min-h-screen">Loading...</div>;
}

if (error) {
return <div className="flex flex-col items-center justify-center min-h-screen">{error.message}</div>;
}

return (
<div className="flex flex-col items-center justify-center min-h-screen">
<span>Users</span>
<ul>
{data.map((user) => (
<li key={user.id}>{user.id}</li>
))}
</ul>
</div>
);
}

Squid はすぐに使用できる built-in database を提供します。この example では Users client component の useQuery hook を使用し、database の users collection に query を実行します。hook は query と boolean を受け取ります。boolean は table の live update を subscribe するかを示します。query().dereference() を使用すると query の raw data が返されます。

web app には “Users” heading が表示されますが、user はいません。collection に user を insert する必要があります。

注記

“Loading…” または error message が表示される場合は、Squid backend を開始していることを確認してください。tutorial-backend に移動して squid start を実行します。Project の実行を参照してください。

User を Insert する​

database に user を insert する function を trigger する button component を追加します。pages/index.tsx に以下を追加します。

pages/index.tsx
import { useCollection, useQuery } from "@squidcloud/react";

...

export default function Home() {
...

const insertUser = async () => {
await collection.doc().insert({
id: crypto.randomUUID(),
});
}
...

return (
<div className="flex flex-col items-center justify-center min-h-screen">
<button onClick={insertUser}>Insert</button>
<span>Users</span>
<ul>
{data.map((user) => (
<li key={user.id}>{user.id}</li>
))}
</ul>
</div>
);

web app に Insert button が表示されます。button をクリックすると random ID を持つ user が insert されます。database query は live update を subscribe しているため、button をクリックすると user ID がすぐに user list に表示されます。collection.doc().insert(...) を実行すると user data が application の built-in database に persist されるため、page を refresh しても user list は保持されます。

Server で Query を実行する​

page を refresh すると、user list が表示される前に Loading… indicator が短時間表示されます。これは Users component が client component であり、client で user を query するのに少し時間がかかるためです。Next.js App Router では、この query を React Server Component 内で実行し、page load 時に server から client に data を渡せます。

pages/index.tsx で新しい static getServerSideProps function を作成します。この function は initial user data を Squid で query し、users list を Home component の initial rendering に渡します。

pages/index.tsx
import { useCollection, useQuery } from "@squidcloud/react";
import { Squid } from "@squidcloud/client";

...

export const getServerSideProps = (async () => {
const squid = Squid.getInstance({
appId: 'YOUR_APP_ID',
region: 'YOUR_REGION',
environmentId: 'dev',
squidDeveloperId: 'YOUR_SQUID_DEVELOPER_ID',
});
const users = await squid
.collection<User>("users")
.query()
.dereference()
.snapshot();
return { props: { users } };
}) satisfies GetServerSideProps<{
users: Array<User>;
}>;

export default function Home({
users,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
...
}
注記

app の initial setup 時には、client で SquidContextProvider を使用して Squid を初期化しました。React Server Component は React Context に access できないため、この Squid instance は Home page 内で access できません。代わりに、Squid React SDK の install 時に自動 install される @squidcloud/client package を使用して別の instance を作成する必要があります。code の repetition を減らすため、Squid option を取得する shared utility を作成することを推奨します。

utils という folder を作成し、squid.ts という file を追加します。新しい file に次の code を追加します。

utils/squid.ts
import { SquidOptions } from '@squidcloud/client';

export function getOptions(): SquidOptions {
return {
appId: 'YOUR_APP_ID',
region: 'YOUR_REGION',
environmentId: 'dev',
squidDeveloperId: 'YOUR_SQUID_DEVELOPER_ID',
};
}

これで任意の options={...} を options={getOptions()} に置き換えられます。 pages/_app.tsx で getOptions function を import し、SquidContextProvider の options として渡します。

pages/_app.tsx
import { getOptions } from "@/utils/squid";

...

export default function App({ Component, pageProps }: AppProps) {
return (
<SquidContextProvider options={getOptions()}>
<Component {...pageProps} />
</SquidContextProvider>
);

server で query した users に access できるようになったため、useQuery hook を update して initial value を受け取れるようにします。

pages/index.tsx
...

export default function Home({
users,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
...

const { loading, data, error } = useQuery(
collection.query().dereference(),
{ initialData: users }
);

...

if (loading && !data.length) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
Loading...
</div>
);
}

...
}

この code block では 2 つの change を行います。

  1. users list を initial data として useQuery hook に渡します。これにより、hook から返される initial data は空の array ではなく user list になります。
  2. Loading… condition を update して data が存在するかを確認します。default では、useQuery に initial value を渡しても、client で data の query が成功するまで loading value は true です。data の存在を確認することで、client で query がまだ loading 中でも server からの user list を render できます。

これらの change により、page を refresh しても Loading… indicator は表示されなくなります。代わりに、page load と同時に user list が表示されます。

Server で Data を処理する​

getServerSideProps 内で Squid を使用することに加えて、API route で Squid を使用できます!client ではなく API route から user を insert してみましょう。

  1. default では Next.js は pages/api/hello.ts file を生成します。この file を insert.ts に rename します。
  2. pages/api/insert.ts を次の code に置き換えます。
import type { NextApiRequest, NextApiResponse } from 'next';
import { getOptions } from '@/utils/squid';
import { Squid } from '@squidcloud/client';

type User = {
id: string;
};

export default async function handler(req: NextApiRequest, res: NextApiResponse<User>) {
const squid = Squid.getInstance(getOptions());
const user = {
id: crypto.randomUUID(),
};
await squid.collection<User>('users').doc().insert(user);
res.status(200).json(user);
}

この code は Home component の insertUser function と非常に似ていることに注目してください。どちらも built-in database に user を作成しますが、こちらは client ではなく server で実行されます。

client からこの function を call するには、insertUser function を以下に update します。

pages/index.tsx
export default function Home(...) {
...

const insertUser = async () => {
await fetch("api/insert", { method: "POST" });
};
...
}

“Insert” button をクリックして user を insert します。今回は server が insert を処理します。

Optimistic Update​

button の click と新しく insert された user が user list に表示される間に、小さな delay があることに注目してください。これは Squid が client で optimistic update を自動処理する仕組みによるものです。

client-side implementation の insertUser では、insert は client で直接実行されます。この場合、Squid は optimistic に insert を実行します。つまり、insert request がまだ in flight の間でも新しい user が instantaneously に表示されます。何らかの理由で insert が failure した場合、Squid は optimistic insert を rollback します。

ヒント

server から insert する場合、optimistic update の benefit は失われます。一般に、API route 内の Squid を使用して insert することはできますが、client から直接 insert・update するほうが user experience が向上することがよくあります。