diff --git a/README.md b/README.md index a1ae7a3..2cf0354 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ I was aiming for a "_soydev_" stack, I think I've got it. - [TailwindCSS](https://tailwindcss.com/) - [TypeScript](https://www.typescriptlang.org/) - [Bun](https://bun.sh) +- [tRPC](https://trpc.io) (with [svelte-query](https://github.com/vishalbalaji/trpc-svelte-query-adapter)) # Building diff --git a/bun.lockb b/bun.lockb index 153cf64..cbd80b0 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 4bb9c69..12838e9 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,10 @@ "svelte-preprocess" ], "dependencies": { + "@tanstack/svelte-query": "^5.51.21", + "@tanstack/svelte-query-devtools": "^5.51.21", + "@trpc/client": "^10.45.2", + "@trpc/server": "^10.45.2", "bits-ui": "^0.21.13", "clsx": "^2.1.1", "drizzle-orm": "^0.33.0", @@ -50,6 +54,9 @@ "mode-watcher": "^0.4.1", "svelte-radix": "^1.1.0", "tailwind-merge": "^2.5.2", - "tailwind-variants": "^0.2.1" + "tailwind-variants": "^0.2.1", + "trpc-svelte-query-adapter": "^2.3.14", + "trpc-sveltekit": "^3.6.2", + "zod": "^3.23.8" } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..c0f1d88 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,6 @@ +import { createContext } from "$lib/trpc/context"; +import { router } from "$lib/trpc/router"; +import type { Handle } from "@sveltejs/kit"; +import { createTRPCHandle } from "trpc-sveltekit"; + +export const handle: Handle = createTRPCHandle({ router, createContext }); diff --git a/src/lib/components/suspense.svelte b/src/lib/components/suspense.svelte new file mode 100644 index 0000000..0132edb --- /dev/null +++ b/src/lib/components/suspense.svelte @@ -0,0 +1,28 @@ + + +{#if $query.isPending && onpending} + {@render onpending()} +{:else if $query.isError && onerror} + {@render onerror($query.error)} +{:else} + {@render ondone($query.data, $query.isPlaceholderData)} +{/if} \ No newline at end of file diff --git a/src/lib/trpc/client.ts b/src/lib/trpc/client.ts new file mode 100644 index 0000000..0f605b1 --- /dev/null +++ b/src/lib/trpc/client.ts @@ -0,0 +1,29 @@ +// type imported for TSDoc, ignore this. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import type { page } from "$app/stores"; +import type { Router } from "$lib/trpc/router"; + +import { createTRPCClient, type TRPCClientInit } from "trpc-sveltekit"; +import type { QueryClient } from "@tanstack/svelte-query"; +import { svelteQueryWrapper } from "trpc-svelte-query-adapter"; + +let browserClient: ReturnType>; + +/** + * allows access to a client-side tRPC client + * + * @param init it's easier to just set this to {@link page | `$page`} + * @returns a client-side client for tRPC with this server + */ +export const trpc = (init?: TRPCClientInit, queryClient?: QueryClient) => { + const isBrowser = typeof window !== "undefined"; + if (isBrowser && browserClient) return browserClient; + const client = svelteQueryWrapper({ + client: createTRPCClient({ init }), + queryClient, + }); + if (isBrowser) browserClient = client; + return client; +}; + +export type trpcClient = typeof browserClient; diff --git a/src/lib/trpc/context.ts b/src/lib/trpc/context.ts new file mode 100644 index 0000000..ecce27b --- /dev/null +++ b/src/lib/trpc/context.ts @@ -0,0 +1,9 @@ +import type { RequestEvent } from "@sveltejs/kit"; + +export const createContext = async (event: RequestEvent) => { + return { + event, + }; +}; + +export type Context = Awaited>; diff --git a/src/lib/trpc/router.ts b/src/lib/trpc/router.ts new file mode 100644 index 0000000..9430c6d --- /dev/null +++ b/src/lib/trpc/router.ts @@ -0,0 +1,23 @@ +import z from "zod"; + +import type { Context } from "$lib/trpc/context"; +import { initTRPC } from "@trpc/server"; + +export const t = initTRPC.context().create(); + +export const router = t.router({ + greeting: t.procedure + .input( + z.object({ + name: z.string().min(10).max(20).describe("The name to greet"), + }), + ) + .query(async (opts) => { + await new Promise((res) => setTimeout(res, 1000)); + return `Hello ${opts.input.name}`; + }), +}); + +export const createCaller = t.createCallerFactory(router); + +export type Router = typeof router; diff --git a/src/routes/(landing)/+page.svelte b/src/routes/(landing)/+page.svelte index 5834557..8d2b5e1 100644 --- a/src/routes/(landing)/+page.svelte +++ b/src/routes/(landing)/+page.svelte @@ -1,9 +1,31 @@
- - Go to /app - +
+

Jail Bird

+ + {#snippet onpending()} + Loading... + {/snippet} + + {#snippet onerror(error: (typeof $query)['error'])} + Error +
{error?.message}
+ {/snippet} + + {#snippet ondone(data: string | undefined, is_placeholder: boolean)} +
is_placeholder: {is_placeholder}
+ {data} + {/snippet} + + +
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index aa2f795..40623c4 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,14 +4,21 @@ import type { Snippet } from "svelte"; import { ModeWatcher } from "mode-watcher"; + import { QueryClientProvider } from "@tanstack/svelte-query"; + import { SvelteQueryDevtools } from "@tanstack/svelte-query-devtools" - interface Props { - children: Snippet; - } + import type { LayoutData } from "./$types"; - const { children }: Props = $props(); + const { queryClient, children }: LayoutData & { children: Snippet } = $props(); - + + Jail Bird + -{@render children()} + + + + {@render children()} + + diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..a661cb5 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,16 @@ +import { browser } from "$app/environment"; +import { QueryClient } from "@tanstack/svelte-query"; + +import type { LayoutLoad } from "./$types"; + +export const load = (async () => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + enabled: browser + } + } + }); + + return { queryClient }; +}) satisfies LayoutLoad; \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 3b231cc..4dcfa87 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -57,6 +57,14 @@ const config = { fontFamily: { sans: [...fontFamily.sans], }, + fontSize: { + hero: [ + "10em", + { + letterSpacing: "20px", + }, + ], + }, }, }, };