Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/cute-falcons-wear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
"@tanstack/solid-db": minor
---

Update solid-db to enable suspense support.
You can now run do

```tsx
// Use Suspense boundaries
const todosQuery = useLiveQuery((q) => q.from({ todos: todoCollection }))

return (
<>
{/* Status and other getters don't trigger Suspense */}
<div>Status {todosQuery.status}</div>
<div>Loading {todosQuery.isLoading ? "yes" : "no"}</div>

<Suspense fallback={<div>Loading...</div>}>
<For each={todosQuery()}>
{(todo) => <li key={todo.id}>{todo.text}</li>}
</For>
</Suspense>
</>
)
```

All values returned from useLiveQuery are now getters, so no longer need to be called as functions. This is a breaking change. This is to match how createResource works, and everything still stays reactive.

```tsx
const todos = useLiveQuery(() => existingCollection)

const handleToggle = (id) => {
// Can now access collection directly
todos.collection.update(id, (draft) => {
draft.completed = !draft.completed
})
}

return (
<>
{/* Status and other getters don't trigger Suspense */}
<div>Status {todos.status}</div>
<div>Loading {todos.isLoading ? "yes" : "no"}</div>
<div>Ready {todos.isReady ? "yes" : "no"}</div>
<div>Idle {todos.isIdle ? "yes" : "no"}</div>
<div>Error {todos.isError ? "yes" : "no"}</div>
</>
)
```
34 changes: 12 additions & 22 deletions examples/solid/todo/src/db/validation.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,24 @@
import { createInsertSchema, createSelectSchema } from 'drizzle-zod'
import { z } from 'zod'
import { config, todos } from './schema'
import type { z } from 'zod'

// Date transformation schema - handles Date objects, ISO strings, and parseable date strings
const dateStringToDate = z
.union([
z.date(), // Already a Date object
z
.string()
.datetime()
.transform((str) => new Date(str)), // ISO datetime string
z.string().transform((str) => new Date(str)), // Any parseable date string
])
.optional()

// Auto-generated schemas from Drizzle schema with date transformation
export const insertTodoSchema = createInsertSchema(todos, {
created_at: dateStringToDate,
updated_at: dateStringToDate,
// Auto-generated schemas from Drizzle schema (omit auto-generated fields)
export const insertTodoSchema = createInsertSchema(todos).omit({
id: true,
created_at: true,
updated_at: true,
})
export const selectTodoSchema = createSelectSchema(todos)

// Partial schema for updates
export const updateTodoSchema = insertTodoSchema.partial().strict()

// Config schemas with date transformation
export const insertConfigSchema = createInsertSchema(config, {
created_at: dateStringToDate,
updated_at: dateStringToDate,
}).strict()
// Config schemas (omit auto-generated fields)
export const insertConfigSchema = createInsertSchema(config).omit({
id: true,
created_at: true,
updated_at: true,
})
export const selectConfigSchema = createSelectSchema(config)
export const updateConfigSchema = insertConfigSchema.partial().strict()

Expand Down
11 changes: 0 additions & 11 deletions examples/solid/todo/src/main.tsx

This file was deleted.

170 changes: 85 additions & 85 deletions examples/solid/todo/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { createServerRootRoute } from '@tanstack/solid-start/server'

import { Route as rootRouteImport } from './routes/__root'
import { Route as TrailbaseRouteImport } from './routes/trailbase'
import { Route as QueryRouteImport } from './routes/query'
import { Route as ElectricRouteImport } from './routes/electric'
import { Route as IndexRouteImport } from './routes/index'
import { ServerRoute as ApiTodosServerRouteImport } from './routes/api/todos'
import { ServerRoute as ApiConfigServerRouteImport } from './routes/api/config'
import { ServerRoute as ApiTodosIdServerRouteImport } from './routes/api/todos.$id'
import { ServerRoute as ApiConfigIdServerRouteImport } from './routes/api/config.$id'

const rootServerRouteImport = createServerRootRoute()
import { Route as ApiTodosRouteImport } from './routes/api/todos'
import { Route as ApiConfigRouteImport } from './routes/api/config'
import { Route as ApiTodosIdRouteImport } from './routes/api/todos.$id'
import { Route as ApiConfigIdRouteImport } from './routes/api/config.$id'

const TrailbaseRoute = TrailbaseRouteImport.update({
id: '/trailbase',
Expand All @@ -42,95 +38,98 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const ApiTodosServerRoute = ApiTodosServerRouteImport.update({
const ApiTodosRoute = ApiTodosRouteImport.update({
id: '/api/todos',
path: '/api/todos',
getParentRoute: () => rootServerRouteImport,
getParentRoute: () => rootRouteImport,
} as any)
const ApiConfigServerRoute = ApiConfigServerRouteImport.update({
const ApiConfigRoute = ApiConfigRouteImport.update({
id: '/api/config',
path: '/api/config',
getParentRoute: () => rootServerRouteImport,
getParentRoute: () => rootRouteImport,
} as any)
const ApiTodosIdServerRoute = ApiTodosIdServerRouteImport.update({
const ApiTodosIdRoute = ApiTodosIdRouteImport.update({
id: '/$id',
path: '/$id',
getParentRoute: () => ApiTodosServerRoute,
getParentRoute: () => ApiTodosRoute,
} as any)
const ApiConfigIdServerRoute = ApiConfigIdServerRouteImport.update({
const ApiConfigIdRoute = ApiConfigIdRouteImport.update({
id: '/$id',
path: '/$id',
getParentRoute: () => ApiConfigServerRoute,
getParentRoute: () => ApiConfigRoute,
} as any)

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/electric': typeof ElectricRoute
'/query': typeof QueryRoute
'/trailbase': typeof TrailbaseRoute
'/api/config': typeof ApiConfigRouteWithChildren
'/api/todos': typeof ApiTodosRouteWithChildren
'/api/config/$id': typeof ApiConfigIdRoute
'/api/todos/$id': typeof ApiTodosIdRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/electric': typeof ElectricRoute
'/query': typeof QueryRoute
'/trailbase': typeof TrailbaseRoute
'/api/config': typeof ApiConfigRouteWithChildren
'/api/todos': typeof ApiTodosRouteWithChildren
'/api/config/$id': typeof ApiConfigIdRoute
'/api/todos/$id': typeof ApiTodosIdRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/electric': typeof ElectricRoute
'/query': typeof QueryRoute
'/trailbase': typeof TrailbaseRoute
'/api/config': typeof ApiConfigRouteWithChildren
'/api/todos': typeof ApiTodosRouteWithChildren
'/api/config/$id': typeof ApiConfigIdRoute
'/api/todos/$id': typeof ApiTodosIdRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/electric' | '/query' | '/trailbase'
fullPaths:
| '/'
| '/electric'
| '/query'
| '/trailbase'
| '/api/config'
| '/api/todos'
| '/api/config/$id'
| '/api/todos/$id'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/electric' | '/query' | '/trailbase'
id: '__root__' | '/' | '/electric' | '/query' | '/trailbase'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
ElectricRoute: typeof ElectricRoute
QueryRoute: typeof QueryRoute
TrailbaseRoute: typeof TrailbaseRoute
}
export interface FileServerRoutesByFullPath {
'/api/config': typeof ApiConfigServerRouteWithChildren
'/api/todos': typeof ApiTodosServerRouteWithChildren
'/api/config/$id': typeof ApiConfigIdServerRoute
'/api/todos/$id': typeof ApiTodosIdServerRoute
}
export interface FileServerRoutesByTo {
'/api/config': typeof ApiConfigServerRouteWithChildren
'/api/todos': typeof ApiTodosServerRouteWithChildren
'/api/config/$id': typeof ApiConfigIdServerRoute
'/api/todos/$id': typeof ApiTodosIdServerRoute
}
export interface FileServerRoutesById {
__root__: typeof rootServerRouteImport
'/api/config': typeof ApiConfigServerRouteWithChildren
'/api/todos': typeof ApiTodosServerRouteWithChildren
'/api/config/$id': typeof ApiConfigIdServerRoute
'/api/todos/$id': typeof ApiTodosIdServerRoute
}
export interface FileServerRouteTypes {
fileServerRoutesByFullPath: FileServerRoutesByFullPath
fullPaths: '/api/config' | '/api/todos' | '/api/config/$id' | '/api/todos/$id'
fileServerRoutesByTo: FileServerRoutesByTo
to: '/api/config' | '/api/todos' | '/api/config/$id' | '/api/todos/$id'
to:
| '/'
| '/electric'
| '/query'
| '/trailbase'
| '/api/config'
| '/api/todos'
| '/api/config/$id'
| '/api/todos/$id'
id:
| '__root__'
| '/'
| '/electric'
| '/query'
| '/trailbase'
| '/api/config'
| '/api/todos'
| '/api/config/$id'
| '/api/todos/$id'
fileServerRoutesById: FileServerRoutesById
fileRoutesById: FileRoutesById
}
export interface RootServerRouteChildren {
ApiConfigServerRoute: typeof ApiConfigServerRouteWithChildren
ApiTodosServerRoute: typeof ApiTodosServerRouteWithChildren
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
ElectricRoute: typeof ElectricRoute
QueryRoute: typeof QueryRoute
TrailbaseRoute: typeof TrailbaseRoute
ApiConfigRoute: typeof ApiConfigRouteWithChildren
ApiTodosRoute: typeof ApiTodosRouteWithChildren
}

declare module '@tanstack/solid-router' {
Expand Down Expand Up @@ -163,78 +162,79 @@ declare module '@tanstack/solid-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
}
}
declare module '@tanstack/solid-start/server' {
interface ServerFileRoutesByPath {
'/api/todos': {
id: '/api/todos'
path: '/api/todos'
fullPath: '/api/todos'
preLoaderRoute: typeof ApiTodosServerRouteImport
parentRoute: typeof rootServerRouteImport
preLoaderRoute: typeof ApiTodosRouteImport
parentRoute: typeof rootRouteImport
}
'/api/config': {
id: '/api/config'
path: '/api/config'
fullPath: '/api/config'
preLoaderRoute: typeof ApiConfigServerRouteImport
parentRoute: typeof rootServerRouteImport
preLoaderRoute: typeof ApiConfigRouteImport
parentRoute: typeof rootRouteImport
}
'/api/todos/$id': {
id: '/api/todos/$id'
path: '/$id'
fullPath: '/api/todos/$id'
preLoaderRoute: typeof ApiTodosIdServerRouteImport
parentRoute: typeof ApiTodosServerRoute
preLoaderRoute: typeof ApiTodosIdRouteImport
parentRoute: typeof ApiTodosRoute
}
'/api/config/$id': {
id: '/api/config/$id'
path: '/$id'
fullPath: '/api/config/$id'
preLoaderRoute: typeof ApiConfigIdServerRouteImport
parentRoute: typeof ApiConfigServerRoute
preLoaderRoute: typeof ApiConfigIdRouteImport
parentRoute: typeof ApiConfigRoute
}
}
}

interface ApiConfigServerRouteChildren {
ApiConfigIdServerRoute: typeof ApiConfigIdServerRoute
interface ApiConfigRouteChildren {
ApiConfigIdRoute: typeof ApiConfigIdRoute
}

const ApiConfigServerRouteChildren: ApiConfigServerRouteChildren = {
ApiConfigIdServerRoute: ApiConfigIdServerRoute,
const ApiConfigRouteChildren: ApiConfigRouteChildren = {
ApiConfigIdRoute: ApiConfigIdRoute,
}

const ApiConfigServerRouteWithChildren = ApiConfigServerRoute._addFileChildren(
ApiConfigServerRouteChildren,
const ApiConfigRouteWithChildren = ApiConfigRoute._addFileChildren(
ApiConfigRouteChildren,
)

interface ApiTodosServerRouteChildren {
ApiTodosIdServerRoute: typeof ApiTodosIdServerRoute
interface ApiTodosRouteChildren {
ApiTodosIdRoute: typeof ApiTodosIdRoute
}

const ApiTodosServerRouteChildren: ApiTodosServerRouteChildren = {
ApiTodosIdServerRoute: ApiTodosIdServerRoute,
const ApiTodosRouteChildren: ApiTodosRouteChildren = {
ApiTodosIdRoute: ApiTodosIdRoute,
}

const ApiTodosServerRouteWithChildren = ApiTodosServerRoute._addFileChildren(
ApiTodosServerRouteChildren,
const ApiTodosRouteWithChildren = ApiTodosRoute._addFileChildren(
ApiTodosRouteChildren,
)

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
ElectricRoute: ElectricRoute,
QueryRoute: QueryRoute,
TrailbaseRoute: TrailbaseRoute,
ApiConfigRoute: ApiConfigRouteWithChildren,
ApiTodosRoute: ApiTodosRouteWithChildren,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
const rootServerRouteChildren: RootServerRouteChildren = {
ApiConfigServerRoute: ApiConfigServerRouteWithChildren,
ApiTodosServerRoute: ApiTodosServerRouteWithChildren,

import type { getRouter } from './router.tsx'
import type { startInstance } from './start.tsx'
declare module '@tanstack/solid-start' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
config: Awaited<ReturnType<typeof startInstance.getOptions>>
}
}
export const serverRouteTree = rootServerRouteImport
._addFileChildren(rootServerRouteChildren)
._addFileTypes<FileServerRouteTypes>()
Loading