Open5
Tanstack Start , full-stack メモ

概要
- フルスタックの Tanstack Start 入門メモになります。
- beta版みたいでした。
- vinxi 採用してるみたい。
- File based Route
[ 公開 2025/02/14 ]
構成
- Tanstack Start
- node 20
- vite
- vinxi
関連
install
-
quick-start
-
https://tanstack.com/start/latest/docs/framework/react/quick-start
-
build-from-scratch
-
https://tanstack.com/start/latest/docs/framework/react/build-from-scratch
-
build-from-scratch 、ファイル数少なめ でした。
- dev-start
npm run dev
Vercel + Tanstack Start Deploy
-
hosting 対応, Verce Netlify など、設置できそう。
-
https://tanstack.com/start/latest/docs/framework/react/hosting
vercel
-
app.config.ts , server 部分を追加
-
github 経由で、vercel にデプロイできました。
// app.config.ts
import { defineConfig } from '@tanstack/start/config'
export default defineConfig({
server: {
preset: 'vercel',
},
})
- routes/index.tsx
- サンプルの、ファイル書込み処理を削除して。フロントのみに修正
// app/routes/index.tsx
import {useState} from 'react';
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: Home,
})
function Home() {
const [count, setCount] = useState(0);
return (
<>
<h1>index.tsx</h1>
<hr />
<button
type="button"
onClick={() => {
setCount(count + 1);
}}
>
Add 1 to 0
</button>
count = {count}
</>
)
}

API POST , GET 通信
- バックエンド API通信
- GET 通信
- app/routes/api/helloget.ts
import { json } from '@tanstack/start'
import { createAPIFileRoute } from '@tanstack/start/api'
export const APIRoute = createAPIFileRoute('/api/helloget')({
GET: ({ request, params }) => {
console.log("#GET /api/helloget")
return json({ message: 'Hello , /api/helloget' })
},
})
- POST 通信
- app/routes/api/hello.ts
- body: await request.json で受信
- レスポンスに、body値を返す。
import { json } from '@tanstack/start'
import { createAPIFileRoute } from '@tanstack/start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
POST: async ({ request, params }) => {
const body = await request.json();
return json({
message: 'Hello ,/api/hello POST-DATA' , data: body
})
},
})
- front
- app/routes/ApiTest.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/ApiTest')({
component: RouteComponent,
})
function RouteComponent() {
const testProc = async function(){
try{
const response = await fetch(`/api/helloget`);
if (!response.ok) throw new Error("error, /api/helloget");
const data = await response.json();
console.log(data);
const postData = {id: 1, name: "name-post1"}
const resPost = await fetch(`/api/hello`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postData),
}
);
if (!resPost.ok) throw new Error("error, /api/hello");
const dataPost = await resPost.json();
console.log(dataPost);
}catch(e){console.error(e)}
}
return (
<>
<div className="text-3xl">ApiTest !</div>
<hr className="my-2" />
<button
className="btn"
onClick={()=>testProc()}>Test</button>
</>
)
}
画面LOG

sqlite3 + drizzle , Tanstack Start
- sqlite3 + drizzle 使う例です。
- CRUDサンプル的な例です。
書いたコード
API
- sqlite_1/app/routes/api/todos/list.ts
import { json } from '@tanstack/start'
import { createAPIFileRoute } from '@tanstack/start/api'
import {db} from '../../../../src/index';
import {todos} from '../../../../src/db/schema';
export const APIRoute = createAPIFileRoute('/api/todos/list')({
GET: async ({ request, params }) => {
const allTodos = await db.select().from(todos);
return json({
message: 'Hello ,/api/hello POST-DATA',
data: allTodos,
})
},
})
- sqlite_1/app/routes/api/todos/create.ts
import { json } from '@tanstack/start'
import { createAPIFileRoute } from '@tanstack/start/api'
import {db} from '../../../../src/index';
import {todos} from '../../../../src/db/schema';
export const APIRoute = createAPIFileRoute('/api/todos/create')({
POST: async({ request, params }) => {
try{
const body = await request.json();
console.log(body);
const title = body.title;
if (!title) {
return res.status(400).json({ message: 'Title is required' });
}
const newTodo = await db.insert(todos).values({ title }).returning();
return json({ message: 'Hello "/api/todos/create"!' })
}catch(e){
console.error(e);
return json({ message: '' })
};
},
})
見た目

ログイン認証 , Tanstack Start + D1 database
- ログイン認証などのメモになります。
- D1 に、ユーザーデータを保存
構成
- Tanstack Start
- D1
- CF-workers
関連
- Git の prismaの認証参考 , start-basic-auth
- https://github.com/TanStack/router/tree/main/examples/react/start-basic-auth
D1 + workers 連携 認証、書いたコード
- Login
- workers_auth/app/routes/login.tsx
- signup
- workers_auth/app/routes/signup.tsx
- 関連のファイル
- __root.tsx : 認証データの読込
- workers_auth/app/routes/__root.tsx
const fetchUser = createServerFn({ method: 'GET' }).handler(async () => {
const session = await useAppSession()
if (!session.data.userEmail) {
return null
}
return {
email: session.data.userEmail,
}
})
- _authed.tsx : 認証データの判定
- workers_auth/app/routes/_authed.tsx
- createFileRoute, throw redirect に変更しました。
- throw 部分、コメントに修正
import { createFileRoute, redirect } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/start'
import { Login } from '~/components/Login'
import { useAppSession } from '~/utils/session'
export const loginFn = createServerFn()
.validator((d) => d as { email: string; password: string })
.handler(async ({ data }) => {
console.log("#loginFn");
console.log(data);
const user = {email: data.email}
// Create a session
const session = await useAppSession()
await session.update({
userEmail: user.email,
})
})
export const Route = createFileRoute('/_authed')({
beforeLoad: ({ context }) => {
console.log("#_authed.beforeLoad");
console.log(context);
if (!context.user) {
//throw new Error('Not authenticated')
throw redirect({
to: '/login',
});
}
},
errorComponent: ({ error }) => {
if (error.message === 'Not authenticated') {
return <Login />
}
throw error
},
})
***

タスク管理 , Tanstack Start
- タスク管理 の作成メモになります。
- D1 に、データを保存
- CF-workers + D1に連携します。
構成
- Tanstack Start
- D1
- workers
- React 19
- vinxi
- vite
- zod
- tailwindcss
書いたコード
- dev-start
npm run dev
- .env
- API_URL , API_KEY 設定
VITE_API_URL=https://localhost
VITE_API_KEY="123"
VITE_USER_USERNAME="u1@example.com"
VITE_USER_PASSWORD="1234"
見た目
- task 一覧
- ガントチャート Excel 出力