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
3 changes: 3 additions & 0 deletions boilerplates/express/files/server/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createTodoHandler } from "@batijs/shared-server/server/create-todo-hand
import { telefuncHandler } from "@batijs/telefunc/server/telefunc-handler";
import { trpcHandler } from "@batijs/trpc/server/trpc-handler";
import { tsRestHandler } from "@batijs/ts-rest/server/ts-rest-handler";
import { getTodosHandler } from "@batijs/react-query/server/todo-handlers";
import { apply, serve } from "@photonjs/express";
import express from "express";

Expand Down Expand Up @@ -35,6 +36,8 @@ function startApp() {
//# BATI.has("ts-rest")
// ts-rest route. See https://ts-rest.com
tsRestHandler,
//# BATI.has("react-query")
getTodosHandler,
//# !BATI.has("telefunc") && !BATI.has("trpc") && !BATI.has("ts-rest")
createTodoHandler,
]);
Expand Down
3 changes: 3 additions & 0 deletions boilerplates/fastify/files/server/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createTodoHandler } from "@batijs/shared-server/server/create-todo-hand
import { telefuncHandler } from "@batijs/telefunc/server/telefunc-handler";
import { trpcHandler } from "@batijs/trpc/server/trpc-handler";
import { tsRestHandler } from "@batijs/ts-rest/server/ts-rest-handler";
import { getTodosHandler } from "@batijs/react-query/server/todo-handlers";
import { apply, serve } from "@photonjs/fastify";
import fastify from "fastify";
import rawBody from "fastify-raw-body";
Expand Down Expand Up @@ -42,6 +43,8 @@ async function startApp() {
//# BATI.has("ts-rest")
// ts-rest route. See https://ts-rest.com
tsRestHandler,
//# BATI.has("react-query")
getTodosHandler,
//# !BATI.has("telefunc") && !BATI.has("trpc") && !BATI.has("ts-rest")
createTodoHandler,
]);
Expand Down
3 changes: 3 additions & 0 deletions boilerplates/h3/files/server/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createTodoHandler } from "@batijs/shared-server/server/create-todo-hand
import { telefuncHandler } from "@batijs/telefunc/server/telefunc-handler";
import { trpcHandler } from "@batijs/trpc/server/trpc-handler";
import { tsRestHandler } from "@batijs/ts-rest/server/ts-rest-handler";
import { getTodosHandler } from "@batijs/react-query/server/todo-handlers";
import { apply, serve } from "@photonjs/h3";
import { createApp } from "h3";

Expand Down Expand Up @@ -35,6 +36,8 @@ function startApp() {
//# BATI.has("ts-rest")
// ts-rest route. See https://ts-rest.com
tsRestHandler,
//# BATI.has("react-query")
getTodosHandler,
//# !BATI.has("telefunc") && !BATI.has("trpc") && !BATI.has("ts-rest")
createTodoHandler,
]);
Expand Down
3 changes: 3 additions & 0 deletions boilerplates/hono/files/server/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createTodoHandler } from "@batijs/shared-server/server/create-todo-hand
import { telefuncHandler } from "@batijs/telefunc/server/telefunc-handler";
import { trpcHandler } from "@batijs/trpc/server/trpc-handler";
import { tsRestHandler } from "@batijs/ts-rest/server/ts-rest-handler";
import { getTodosHandler } from "@batijs/react-query/server/todo-handlers";
import { apply, serve } from "@photonjs/hono";
import { Hono } from "hono";
import { getMiddlewares } from "vike-photon/universal-middlewares";
Expand Down Expand Up @@ -43,6 +44,8 @@ function startApp() {
//# BATI.has("ts-rest")
// ts-rest route. See https://ts-rest.com
tsRestHandler,
//# BATI.has("react-query")
getTodosHandler,
//# !BATI.has("telefunc") && !BATI.has("trpc") && !BATI.has("ts-rest")
createTodoHandler,
]);
Expand Down
7 changes: 7 additions & 0 deletions boilerplates/react-query/bati.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from "@batijs/core/config";

export default defineConfig({
if(meta) {
return meta.BATI.has("react-query");
},
});
7 changes: 7 additions & 0 deletions boilerplates/react-query/files/$package.json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { loadPackageJson, type TransformerProps } from "@batijs/core";

export default async function getPackageJson(props: TransformerProps) {
const packageJson = await loadPackageJson(props, await import("../package.json").then((x) => x.default));

return packageJson.addDependencies(["@tanstack/react-query", "vike-react-query"]);
}
13 changes: 13 additions & 0 deletions boilerplates/react-query/files/pages/+config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Config } from "vike/types";
import vikePhoton from "vike-photon/config";
import vikeReact from "vike-react/config";
import vikeReactQuery from "vike-react-query/config";

const config: Config = {
extends: [vikeReact, vikeReactQuery, vikePhoton],
photon: {
server: "../server/entry.ts",
},
} satisfies Config;

export default config;
Empty file.
73 changes: 73 additions & 0 deletions boilerplates/react-query/files/pages/todo/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useMutation, useSuspenseQuery, useQueryClient } from "@tanstack/react-query";
import { type ChangeEvent, useState } from "react";
import { createTodo, fetchTodos, type TodoItem } from "../../services/api";

export function TodoList() {
const queryClient = useQueryClient();
const [newTodo, setNewTodo] = useState("");

const { data: todoItems = [] } = useSuspenseQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});

const createMutation = useMutation({
mutationFn: createTodo,
onMutate: async (text) => {
await queryClient.cancelQueries({ queryKey: ["todos"] });
const previousTodos = queryClient.getQueryData<TodoItem[]>(["todos"]);
queryClient.setQueryData<TodoItem[]>(["todos"], (old = []) => [...old, { text }]);
return { previousTodos };
},
onError: (_err, _text, context) => {
if (context?.previousTodos) {
queryClient.setQueryData(["todos"], context.previousTodos);
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});

return (
<>
<ul>
{todoItems.map((todoItem, index) => (
// biome-ignore lint: example
<li key={index}>{todoItem.text}</li>
))}
</ul>
<div>
<form
onSubmit={(ev) => {
ev.preventDefault();
if (newTodo.trim()) {
createMutation.mutate(newTodo);
setNewTodo("");
}
}}
>
<input
type="text"
onChange={(ev: ChangeEvent<HTMLInputElement>) => setNewTodo(ev.currentTarget.value)}
value={newTodo}
//# BATI.has("tailwindcss")
className={
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 w-full sm:w-auto p-2 mr-1 mb-1"
}
/>
<button
type="submit"
disabled={createMutation.isPending}
//# BATI.has("tailwindcss")
className={
"text-white bg-blue-700 hover:bg-blue-800 focus:ring-2 focus:outline-hidden focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto p-2 disabled:opacity-50"
}
>
{createMutation.isPending ? "Adding..." : "Add to-do"}
</button>
</form>
</div>
</>
);
}
50 changes: 50 additions & 0 deletions boilerplates/react-query/files/server/todo-handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as d1Queries from "@batijs/d1-sqlite/database/d1/queries/todos";
import type { dbD1, dbSqlite } from "@batijs/drizzle/database/drizzle/db";
import * as drizzleQueries from "@batijs/drizzle/database/drizzle/queries/todos";
import type { dbKysely, dbKyselyD1 } from "@batijs/kysely/database/kysely/db";
import * as kyselyQueries from "@batijs/kysely/database/kysely/queries/todos";
import type { db as sqliteDb } from "@batijs/sqlite/database/sqlite/db";
import * as sqliteQueries from "@batijs/sqlite/database/sqlite/queries/todos";
import { enhance, type UniversalHandler } from "@universal-middleware/core";

export const getTodosHandler: UniversalHandler<
Universal.Context &
BATI.If<{
'BATI.has("sqlite") && !BATI.hasD1': { db: ReturnType<typeof sqliteDb> };
'BATI.has("drizzle") && !BATI.hasD1': { db: ReturnType<typeof dbSqlite> };
'BATI.has("drizzle")': { db: ReturnType<typeof dbD1> };
'BATI.has("kysely") && !BATI.hasD1': { db: ReturnType<typeof dbKysely> };
'BATI.has("kysely")': { db: ReturnType<typeof dbKyselyD1> };
"BATI.hasD1": { db: D1Database };
_: object;
}>
> = enhance(
async (_request, _context, _runtime) => {
let todoItems: { text: string }[];

if (BATI.has("drizzle")) {
todoItems = await drizzleQueries.getAllTodos(_context.db);
} else if (BATI.has("sqlite") && !BATI.hasD1) {
todoItems = sqliteQueries.getAllTodos(_context.db);
} else if (BATI.has("kysely")) {
todoItems = await kyselyQueries.getAllTodos(_context.db);
} else if (BATI.hasD1) {
todoItems = await d1Queries.getAllTodos(_context.db);
} else {
todoItems = [{ text: "Buy milk" }, { text: "Buy strawberries" }];
}

return new Response(JSON.stringify(todoItems), {
status: 200,
headers: {
"content-type": "application/json",
},
});
},
{
name: "my-app:get-todos-handler",
path: `/api/todo`,
method: ["GET"],
immutable: false,
},
);
32 changes: 32 additions & 0 deletions boilerplates/react-query/files/services/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export type TodoItem = { text: string };

function getBaseUrl(): string {
if (typeof window !== "undefined") {
return "";
}
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
return `http://localhost:${port}`;
}

export async function fetchTodos(): Promise<TodoItem[]> {
const baseUrl = getBaseUrl();
const response = await fetch(`${baseUrl}/api/todo`);
if (!response.ok) {
throw new Error("Failed to fetch todos");
}
return response.json();
}

export async function createTodo(text: string): Promise<void> {
const baseUrl = getBaseUrl();
const response = await fetch(`${baseUrl}/api/todo/create`, {
method: "POST",
body: JSON.stringify({ text }),
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Failed to create todo");
}
}
68 changes: 68 additions & 0 deletions boilerplates/react-query/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "@batijs/react-query",
"private": true,
"version": "0.0.1",
"description": "",
"type": "module",
"scripts": {
"check-types": "tsc --noEmit",
"build": "bati-compile-boilerplate"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@batijs/compile": "workspace:*",
"@tanstack/react-query": "^5.90.16",
"@types/node": "^20.19.25",
"@types/react": "^19.2.7",
"@universal-middleware/core": "^0.4.14",
"react": "^19.2.1",
"vike": "^0.4.250",
"vike-react": "^0.6.18",
"vike-react-query": "^0.1.12",
"vite": "^7.3.0"
},
"dependencies": {
"@batijs/core": "workspace:*"
},
"files": [
"dist/"
],
"exports": {
"./pages/+config": {
"types": "./dist/types/pages/+config.d.ts"
},
"./pages/todo/!+data": {
"types": "./dist/types/pages/todo/!+data.d.ts"
},
"./services/api": {
"types": "./dist/types/services/api.d.ts"
},
"./pages/todo/TodoList": {
"types": "./dist/types/pages/todo/TodoList.d.ts"
},
"./server/todo-handlers": {
"types": "./dist/types/server/todo-handlers.d.ts"
}
},
"typesVersions": {
"*": {
"pages/+config": [
"./dist/types/pages/+config.d.ts"
],
"pages/todo/!+data": [
"./dist/types/pages/todo/!+data.d.ts"
],
"services/api": [
"./dist/types/services/api.d.ts"
],
"pages/todo/TodoList": [
"./dist/types/pages/todo/TodoList.d.ts"
],
"server/todo-handlers": [
"./dist/types/server/todo-handlers.d.ts"
]
}
}
}
10 changes: 10 additions & 0 deletions boilerplates/react-query/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": ["../tsconfig.base.json"],
"compilerOptions": {
"types": ["vite/client", "@types/node", "vike-react", "@batijs/core/types"],
"jsx": "react-jsx",
"jsxImportSource": "react",
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"baseUrl": "."
}
}
3 changes: 3 additions & 0 deletions packages/cli/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Choose one of them or remove selected Server`,
[RulesMessage.ERROR_SHADCN_R_REACT]: error(
`${inverse(bold("shadcn/ui"))} is only compatible with ${inverse(bold("React"))}`,
),
[RulesMessage.ERROR_VIKE_REACT_QUERY_R_REACT]: error(
`${inverse(bold("React"))} is required when using ${inverse(bold("React Query"))}`,
),
[RulesMessage.WARN_SHADCN_R_TAILWINDCSS]: warning(
`${inverse(bold("shadcn/ui"))} integration is tied to ${inverse(bold("TailwindCSS"))}. Using another CSS library with it may have unpredictable behaviour.`,
),
Expand Down
20 changes: 20 additions & 0 deletions packages/features/src/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,26 @@ export const features = [
},
],
},
{
category: "Data fetching",
label: "React Query",
flag: "react-query",
dependsOn: ["react"],
image: "https://tanstack.com/images/logos/logo-color-100.png",
url: "https://tanstack.com/query",
tagline: "Powerful asynchronous state management, server-state utilities and data fetching",
repo: "tanstack/query",
links: [
{
label: "Getting started",
href: "https://tanstack.com/query/latest/docs/react/overview",
},
{
label: "vike-react-query",
href: "https://github.com/vikejs/vike-react-query",
},
],
},

// Server
{
Expand Down
2 changes: 2 additions & 0 deletions packages/features/src/rules/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export enum RulesMessage {
ERROR_MANTINE_R_REACT,
// shadcn/ui is only compatible with React
ERROR_SHADCN_R_REACT,
// React is required when using React Query
ERROR_VIKE_REACT_QUERY_R_REACT,

// --- WARNING
// shadcn/ui integration is tailored for tailwind
Expand Down
7 changes: 7 additions & 0 deletions packages/features/src/rules/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export default [

return false;
}),
filter(RulesMessage.ERROR_VIKE_REACT_QUERY_R_REACT, (fts) => {
if (fts.has("react-query")) {
return !fts.has("react");
}

return false;
}),
filter(RulesMessage.WARN_SHADCN_R_TAILWINDCSS, (fts) => {
if (fts.has("shadcn-ui")) {
return fts.has("daisyui") || fts.has("compiled-css");
Expand Down
Loading
Loading