|
1 |
| -Welcome to your new TanStack app! |
| 1 | +# Realtime TanStack Start Demo |
2 | 2 |
|
3 |
| -# Getting Started |
| 3 | +This example shows how to combine TanStack Start with the OpenAI Agents SDK to create a realtime voice agent using TanStack Router for routing. |
4 | 4 |
|
5 |
| -To run this application: |
| 5 | +## Run the example |
6 | 6 |
|
7 |
| -```bash |
8 |
| -pnpm install |
9 |
| -pnpm start |
10 |
| -``` |
11 |
| - |
12 |
| -# Building For Production |
13 |
| - |
14 |
| -To build this application for production: |
15 |
| - |
16 |
| -```bash |
17 |
| -pnpm build |
18 |
| -``` |
19 |
| - |
20 |
| -## Testing |
21 |
| - |
22 |
| -This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with: |
23 |
| - |
24 |
| -```bash |
25 |
| -pnpm test |
26 |
| -``` |
27 |
| - |
28 |
| -## Styling |
29 |
| - |
30 |
| -This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. |
31 |
| - |
32 |
| -## Routing |
33 |
| - |
34 |
| -This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`. |
35 |
| - |
36 |
| -### Adding A Route |
37 |
| - |
38 |
| -To add a new route to your application just add another a new file in the `./src/routes` directory. |
39 |
| - |
40 |
| -TanStack will automatically generate the content of the route file for you. |
41 |
| - |
42 |
| -Now that you have two routes you can use a `Link` component to navigate between them. |
43 |
| - |
44 |
| -### Adding Links |
45 |
| - |
46 |
| -To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. |
47 |
| - |
48 |
| -```tsx |
49 |
| -import { Link } from '@tanstack/react-router'; |
50 |
| -``` |
51 |
| - |
52 |
| -Then anywhere in your JSX you can use it like so: |
53 |
| - |
54 |
| -```tsx |
55 |
| -<Link to="/about">About</Link> |
56 |
| -``` |
57 |
| - |
58 |
| -This will create a link that will navigate to the `/about` route. |
59 |
| - |
60 |
| -More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). |
61 |
| - |
62 |
| -### Using A Layout |
63 |
| - |
64 |
| -In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component. |
65 |
| - |
66 |
| -Here is an example layout that includes a header: |
67 |
| - |
68 |
| -```tsx |
69 |
| -import { Outlet, createRootRoute } from '@tanstack/react-router'; |
70 |
| -import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'; |
71 |
| - |
72 |
| -import { Link } from '@tanstack/react-router'; |
73 |
| - |
74 |
| -export const Route = createRootRoute({ |
75 |
| - component: () => ( |
76 |
| - <> |
77 |
| - <header> |
78 |
| - <nav> |
79 |
| - <Link to="/">Home</Link> |
80 |
| - <Link to="/about">About</Link> |
81 |
| - </nav> |
82 |
| - </header> |
83 |
| - <Outlet /> |
84 |
| - <TanStackRouterDevtools /> |
85 |
| - </> |
86 |
| - ), |
87 |
| -}); |
88 |
| -``` |
89 |
| - |
90 |
| -The `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout. |
91 |
| - |
92 |
| -More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). |
93 |
| - |
94 |
| -## Data Fetching |
95 |
| - |
96 |
| -There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. |
97 |
| - |
98 |
| -For example: |
99 |
| - |
100 |
| -```tsx |
101 |
| -const peopleRoute = createRoute({ |
102 |
| - getParentRoute: () => rootRoute, |
103 |
| - path: '/people', |
104 |
| - loader: async () => { |
105 |
| - const response = await fetch('https://swapi.dev/api/people'); |
106 |
| - return response.json() as Promise<{ |
107 |
| - results: { |
108 |
| - name: string; |
109 |
| - }[]; |
110 |
| - }>; |
111 |
| - }, |
112 |
| - component: () => { |
113 |
| - const data = peopleRoute.useLoaderData(); |
114 |
| - return ( |
115 |
| - <ul> |
116 |
| - {data.results.map((person) => ( |
117 |
| - <li key={person.name}>{person.name}</li> |
118 |
| - ))} |
119 |
| - </ul> |
120 |
| - ); |
121 |
| - }, |
122 |
| -}); |
123 |
| -``` |
124 |
| - |
125 |
| -Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters). |
126 |
| - |
127 |
| -### React-Query |
128 |
| - |
129 |
| -React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze. |
130 |
| - |
131 |
| -First add your dependencies: |
132 |
| - |
133 |
| -```bash |
134 |
| -pnpm add @tanstack/react-query @tanstack/react-query-devtools |
135 |
| -``` |
136 |
| - |
137 |
| -Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`. |
138 |
| - |
139 |
| -```tsx |
140 |
| -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; |
141 |
| - |
142 |
| -// ... |
143 |
| - |
144 |
| -const queryClient = new QueryClient(); |
145 |
| - |
146 |
| -// ... |
147 |
| - |
148 |
| -if (!rootElement.innerHTML) { |
149 |
| - const root = ReactDOM.createRoot(rootElement); |
150 |
| - |
151 |
| - root.render( |
152 |
| - <QueryClientProvider client={queryClient}> |
153 |
| - <RouterProvider router={router} /> |
154 |
| - </QueryClientProvider>, |
155 |
| - ); |
156 |
| -} |
157 |
| -``` |
158 |
| - |
159 |
| -You can also add TanStack Query Devtools to the root route (optional). |
160 |
| - |
161 |
| -```tsx |
162 |
| -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; |
163 |
| - |
164 |
| -const rootRoute = createRootRoute({ |
165 |
| - component: () => ( |
166 |
| - <> |
167 |
| - <Outlet /> |
168 |
| - <ReactQueryDevtools buttonPosition="top-right" /> |
169 |
| - <TanStackRouterDevtools /> |
170 |
| - </> |
171 |
| - ), |
172 |
| -}); |
173 |
| -``` |
174 |
| - |
175 |
| -Now you can use `useQuery` to fetch your data. |
176 |
| - |
177 |
| -```tsx |
178 |
| -import { useQuery } from '@tanstack/react-query'; |
179 |
| - |
180 |
| -import './App.css'; |
181 |
| - |
182 |
| -function App() { |
183 |
| - const { data } = useQuery({ |
184 |
| - queryKey: ['people'], |
185 |
| - queryFn: () => |
186 |
| - fetch('https://swapi.dev/api/people') |
187 |
| - .then((res) => res.json()) |
188 |
| - .then((data) => data.results as { name: string }[]), |
189 |
| - initialData: [], |
190 |
| - }); |
191 |
| - |
192 |
| - return ( |
193 |
| - <div> |
194 |
| - <ul> |
195 |
| - {data.map((person) => ( |
196 |
| - <li key={person.name}>{person.name}</li> |
197 |
| - ))} |
198 |
| - </ul> |
199 |
| - </div> |
200 |
| - ); |
201 |
| -} |
202 |
| - |
203 |
| -export default App; |
204 |
| -``` |
205 |
| - |
206 |
| -You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview). |
207 |
| - |
208 |
| -## State Management |
209 |
| - |
210 |
| -Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project. |
211 |
| - |
212 |
| -First you need to add TanStack Store as a dependency: |
| 7 | +Set the `OPENAI_API_KEY` environment variable and run: |
213 | 8 |
|
214 | 9 | ```bash
|
215 |
| -pnpm add @tanstack/store |
216 |
| -``` |
217 |
| - |
218 |
| -Now let's create a simple counter in the `src/App.tsx` file as a demonstration. |
219 |
| - |
220 |
| -```tsx |
221 |
| -import { useStore } from '@tanstack/react-store'; |
222 |
| -import { Store } from '@tanstack/store'; |
223 |
| -import './App.css'; |
224 |
| - |
225 |
| -const countStore = new Store(0); |
226 |
| - |
227 |
| -function App() { |
228 |
| - const count = useStore(countStore); |
229 |
| - return ( |
230 |
| - <div> |
231 |
| - <button onClick={() => countStore.setState((n) => n + 1)}> |
232 |
| - Increment - {count} |
233 |
| - </button> |
234 |
| - </div> |
235 |
| - ); |
236 |
| -} |
237 |
| - |
238 |
| -export default App; |
| 10 | +pnpm examples:realtime-tantsack-start |
239 | 11 | ```
|
240 | 12 |
|
241 |
| -One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates. |
242 |
| - |
243 |
| -Let's check this out by doubling the count using derived state. |
244 |
| - |
245 |
| -```tsx |
246 |
| -import { useStore } from '@tanstack/react-store'; |
247 |
| -import { Store, Derived } from '@tanstack/store'; |
248 |
| -import './App.css'; |
249 |
| - |
250 |
| -const countStore = new Store(0); |
251 |
| - |
252 |
| -const doubledStore = new Derived({ |
253 |
| - fn: () => countStore.state * 2, |
254 |
| - deps: [countStore], |
255 |
| -}); |
256 |
| -doubledStore.mount(); |
257 |
| - |
258 |
| -function App() { |
259 |
| - const count = useStore(countStore); |
260 |
| - const doubledCount = useStore(doubledStore); |
261 |
| - |
262 |
| - return ( |
263 |
| - <div> |
264 |
| - <button onClick={() => countStore.setState((n) => n + 1)}> |
265 |
| - Increment - {count} |
266 |
| - </button> |
267 |
| - <div>Doubled - {doubledCount}</div> |
268 |
| - </div> |
269 |
| - ); |
270 |
| -} |
271 |
| - |
272 |
| -export default App; |
273 |
| -``` |
274 |
| - |
275 |
| -We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating. |
276 |
| - |
277 |
| -Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook. |
278 |
| - |
279 |
| -You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest). |
280 |
| - |
281 |
| -# Demo files |
282 |
| - |
283 |
| -Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. |
| 13 | +Open [http://localhost:3000](http://localhost:3000) in your browser and start talking. |
284 | 14 |
|
285 |
| -# Learn More |
| 15 | +## Endpoints |
286 | 16 |
|
287 |
| -You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). |
| 17 | +- **`/`** – Realtime voice demo with agent handoffs, tools, and guardrails using the `RealtimeSession` class. Code in `src/routes/index.tsx`. |
0 commit comments