start.server-funcs.tsx
1 import fs from 'node:fs' 2 import { useCallback, useState } from 'react' 3 import { createFileRoute, useRouter } from '@tanstack/react-router' 4 import { createServerFn } from '@tanstack/react-start' 5 6 /* 7 const loggingMiddleware = createMiddleware().server( 8 async ({ next, request }) => { 9 console.log("Request:", request.url); 10 return next(); 11 } 12 ); 13 const loggedServerFunction = createServerFn({ method: "GET" }).middleware([ 14 loggingMiddleware, 15 ]); 16 */ 17 18 const TODOS_FILE = 'todos.json' 19 20 async function readTodos() { 21 return JSON.parse( 22 await fs.promises.readFile(TODOS_FILE, 'utf-8').catch(() => 23 JSON.stringify( 24 [ 25 { id: 1, name: 'Get groceries' }, 26 { id: 2, name: 'Buy a new phone' }, 27 ], 28 null, 29 2, 30 ), 31 ), 32 ) 33 } 34 35 const getTodos = createServerFn({ 36 method: 'GET', 37 }).handler(async () => await readTodos()) 38 39 const addTodo = createServerFn({ method: 'POST' }) 40 .inputValidator((d: string) => d) 41 .handler(async ({ data }) => { 42 const todos = await readTodos() 43 todos.push({ id: todos.length + 1, name: data }) 44 await fs.promises.writeFile(TODOS_FILE, JSON.stringify(todos, null, 2)) 45 return todos 46 }) 47 48 export const Route = createFileRoute('/demo/start/server-funcs')({ 49 component: Home, 50 loader: async () => await getTodos(), 51 }) 52 53 function Home() { 54 const router = useRouter() 55 let todos = Route.useLoaderData() 56 57 const [todo, setTodo] = useState('') 58 59 const submitTodo = useCallback(async () => { 60 todos = await addTodo({ data: todo }) 61 setTodo('') 62 router.invalidate() 63 }, [addTodo, todo]) 64 65 return ( 66 <div 67 className="flex items-center justify-center min-h-screen bg-gradient-to-br from-zinc-800 to-black p-4 text-white" 68 style={{ 69 backgroundImage: 70 'radial-gradient(50% 50% at 20% 60%, #23272a 0%, #18181b 50%, #000000 100%)', 71 }} 72 > 73 <div className="w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10"> 74 <h1 className="text-2xl mb-4">Start Server Functions - Todo Example</h1> 75 <ul className="mb-4 space-y-2"> 76 {todos?.map((t) => ( 77 <li 78 key={t.id} 79 className="bg-white/10 border border-white/20 rounded-lg p-3 backdrop-blur-sm shadow-md" 80 > 81 <span className="text-lg text-white">{t.name}</span> 82 </li> 83 ))} 84 </ul> 85 <div className="flex flex-col gap-2"> 86 <input 87 type="text" 88 value={todo} 89 onChange={(e) => setTodo(e.target.value)} 90 onKeyDown={(e) => { 91 if (e.key === 'Enter') { 92 submitTodo() 93 } 94 }} 95 placeholder="Enter a new todo..." 96 className="w-full px-4 py-3 rounded-lg border border-white/20 bg-white/10 backdrop-blur-sm text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent" 97 /> 98 <button 99 disabled={todo.trim().length === 0} 100 onClick={submitTodo} 101 className="bg-blue-500 hover:bg-blue-600 disabled:bg-blue-500/50 disabled:cursor-not-allowed text-white font-bold py-3 px-4 rounded-lg transition-colors" 102 > 103 Add todo 104 </button> 105 </div> 106 </div> 107 </div> 108 ) 109 }