Open5

Tanstack Start , full-stack メモ

knaka Tech-Blogknaka Tech-Blog

概要

  • フルスタックの Tanstack Start 入門メモになります。
  • beta版みたいでした。
  • vinxi 採用してるみたい。
  • File based Route

[ 公開 2025/02/14 ]


構成

  • Tanstack Start
  • node 20
  • vite
  • vinxi

関連


install


  • dev-start
npm run dev

Vercel + Tanstack Start Deploy


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}
    </>  
  )
}

knaka Tech-Blogknaka Tech-Blog

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

knaka Tech-Blogknaka Tech-Blog

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: '' })
    };
  },
})


見た目


knaka Tech-Blogknaka Tech-Blog

ログイン認証 , Tanstack Start + D1 database

  • ログイン認証などのメモになります。
  • D1 に、ユーザーデータを保存

構成

  • Tanstack Start
  • D1
  • CF-workers

関連


D1 + workers 連携 認証、書いたコード


  • Login
  • workers_auth/app/routes/login.tsx

https://github.com/kuc-arc-f/tanstack_start_3ex/blob/main/workers_auth/app/routes/login.tsx


  • signup
  • workers_auth/app/routes/signup.tsx

https://github.com/kuc-arc-f/tanstack_start_3ex/blob/main/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
  },
})

***
knaka Tech-Blogknaka Tech-Blog

タスク管理 , 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 出力