create-better-t-stack 1.0.10 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +74 -60
  3. package/package.json +1 -3
  4. package/template/base/apps/server/package.json +1 -9
  5. package/template/base/apps/server/tsconfig.json +1 -1
  6. package/template/with-auth/apps/client/src/components/sign-in-form.tsx +1 -1
  7. package/template/with-auth/apps/client/src/components/sign-up-form.tsx +142 -142
  8. package/template/{with-prisma-sqlite/apps/server/src/with-auth-lib/context.ts → with-auth/apps/server/src/lib/with-elysia-context.ts} +4 -4
  9. package/template/{with-drizzle-sqlite/apps/server/src/with-auth-lib/context.ts → with-auth/apps/server/src/lib/with-hono-context.ts} +3 -3
  10. package/template/with-auth/apps/server/src/routers/index.ts +11 -13
  11. package/template/with-auth/apps/server/src/with-elysia-index.ts +38 -0
  12. package/template/with-auth/apps/server/src/with-hono-index.ts +39 -0
  13. package/template/with-elysia/apps/server/src/index.ts +27 -0
  14. package/template/with-elysia/apps/server/src/lib/context.ts +13 -0
  15. package/template/with-hono/apps/server/src/index.ts +33 -0
  16. package/template/{base → with-hono}/apps/server/src/lib/context.ts +2 -2
  17. package/template/base/apps/server/src/index.ts +0 -41
  18. package/template/with-auth/apps/server/src/index.ts +0 -46
  19. package/template/with-drizzle-postgres/apps/server/src/routers/todo.ts +0 -44
  20. package/template/with-drizzle-postgres/apps/server/src/with-auth-lib/context.ts +0 -18
  21. package/template/with-drizzle-postgres/apps/server/src/with-auth-lib/trpc.ts +0 -24
  22. package/template/with-drizzle-sqlite/apps/server/src/with-auth-lib/trpc.ts +0 -24
  23. package/template/with-prisma-postgres/apps/server/src/with-auth-lib/context.ts +0 -18
  24. package/template/with-prisma-postgres/apps/server/src/with-auth-lib/trpc.ts +0 -24
  25. package/template/with-prisma-sqlite/apps/server/src/with-auth-lib/trpc.ts +0 -24
  26. package/template/{with-drizzle-postgres/apps/server/src/with-auth-lib → with-auth/apps/server/src/with-drizzle-postgres-lib}/auth.ts +1 -1
  27. /package/template/{with-drizzle-sqlite/apps/server/src/with-auth-lib → with-auth/apps/server/src/with-drizzle-sqlite-lib}/auth.ts +0 -0
  28. /package/template/{with-prisma-postgres/apps/server/src/with-auth-lib → with-auth/apps/server/src/with-prisma-postgres-lib}/auth.ts +0 -0
  29. /package/template/{with-prisma-sqlite/apps/server/src/with-auth-lib → with-auth/apps/server/src/with-prisma-sqlite-lib}/auth.ts +0 -0
@@ -9,156 +9,156 @@ import { Input } from "./ui/input";
9
9
  import { Label } from "./ui/label";
10
10
 
11
11
  export default function SignUpForm({
12
- onSwitchToSignIn,
12
+ onSwitchToSignIn,
13
13
  }: {
14
- onSwitchToSignIn: () => void;
14
+ onSwitchToSignIn: () => void;
15
15
  }) {
16
- const navigate = useNavigate({
17
- from: "/",
18
- });
19
- const { isPending } = authClient.useSession();
16
+ const navigate = useNavigate({
17
+ from: "/",
18
+ });
19
+ const { isPending } = authClient.useSession();
20
20
 
21
- const form = useForm({
22
- defaultValues: {
23
- email: "",
24
- password: "",
25
- name: "",
26
- },
27
- onSubmit: async ({ value }) => {
28
- await authClient.signUp.email(
29
- {
30
- email: value.email,
31
- password: value.password,
32
- name: value.name,
33
- },
34
- {
35
- onSuccess: () => {
36
- toast.success("Sign up successful");
37
- navigate({
38
- to: "/dashboard",
39
- });
40
- },
41
- onError: (error) => {
42
- toast.error(error.error.message);
43
- },
44
- },
45
- );
46
- },
47
- validators: {
48
- onSubmit: z.object({
49
- name: z.string().min(2, "Name must be at least 2 characters"),
50
- email: z.string().email("Invalid email address"),
51
- password: z.string().min(6, "Password must be at least 6 characters"),
52
- }),
53
- },
54
- });
21
+ const form = useForm({
22
+ defaultValues: {
23
+ email: "",
24
+ password: "",
25
+ name: "",
26
+ },
27
+ onSubmit: async ({ value }) => {
28
+ await authClient.signUp.email(
29
+ {
30
+ email: value.email,
31
+ password: value.password,
32
+ name: value.name,
33
+ },
34
+ {
35
+ onSuccess: () => {
36
+ navigate({
37
+ to: "/dashboard",
38
+ });
39
+ toast.success("Sign up successful");
40
+ },
41
+ onError: (error) => {
42
+ toast.error(error.error.message);
43
+ },
44
+ },
45
+ );
46
+ },
47
+ validators: {
48
+ onSubmit: z.object({
49
+ name: z.string().min(2, "Name must be at least 2 characters"),
50
+ email: z.string().email("Invalid email address"),
51
+ password: z.string().min(6, "Password must be at least 6 characters"),
52
+ }),
53
+ },
54
+ });
55
55
 
56
- if (isPending) {
57
- return <Loader />;
58
- }
56
+ if (isPending) {
57
+ return <Loader />;
58
+ }
59
59
 
60
- return (
61
- <div className="mx-auto mt-10 max-w-md p-6">
62
- <h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
60
+ return (
61
+ <div className="mx-auto mt-10 max-w-md p-6">
62
+ <h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
63
63
 
64
- <form
65
- onSubmit={(e) => {
66
- e.preventDefault();
67
- e.stopPropagation();
68
- void form.handleSubmit();
69
- }}
70
- className="space-y-4"
71
- >
72
- <div>
73
- <form.Field name="name">
74
- {(field) => (
75
- <div className="space-y-2">
76
- <Label htmlFor={field.name}>Name</Label>
77
- <Input
78
- id={field.name}
79
- name={field.name}
80
- value={field.state.value}
81
- onBlur={field.handleBlur}
82
- onChange={(e) => field.handleChange(e.target.value)}
83
- />
84
- {field.state.meta.errors.map((error) => (
85
- <p key={error?.message} className="text-red-500">
86
- {error?.message}
87
- </p>
88
- ))}
89
- </div>
90
- )}
91
- </form.Field>
92
- </div>
64
+ <form
65
+ onSubmit={(e) => {
66
+ e.preventDefault();
67
+ e.stopPropagation();
68
+ void form.handleSubmit();
69
+ }}
70
+ className="space-y-4"
71
+ >
72
+ <div>
73
+ <form.Field name="name">
74
+ {(field) => (
75
+ <div className="space-y-2">
76
+ <Label htmlFor={field.name}>Name</Label>
77
+ <Input
78
+ id={field.name}
79
+ name={field.name}
80
+ value={field.state.value}
81
+ onBlur={field.handleBlur}
82
+ onChange={(e) => field.handleChange(e.target.value)}
83
+ />
84
+ {field.state.meta.errors.map((error) => (
85
+ <p key={error?.message} className="text-red-500">
86
+ {error?.message}
87
+ </p>
88
+ ))}
89
+ </div>
90
+ )}
91
+ </form.Field>
92
+ </div>
93
93
 
94
- <div>
95
- <form.Field name="email">
96
- {(field) => (
97
- <div className="space-y-2">
98
- <Label htmlFor={field.name}>Email</Label>
99
- <Input
100
- id={field.name}
101
- name={field.name}
102
- type="email"
103
- value={field.state.value}
104
- onBlur={field.handleBlur}
105
- onChange={(e) => field.handleChange(e.target.value)}
106
- />
107
- {field.state.meta.errors.map((error) => (
108
- <p key={error?.message} className="text-red-500">
109
- {error?.message}
110
- </p>
111
- ))}
112
- </div>
113
- )}
114
- </form.Field>
115
- </div>
94
+ <div>
95
+ <form.Field name="email">
96
+ {(field) => (
97
+ <div className="space-y-2">
98
+ <Label htmlFor={field.name}>Email</Label>
99
+ <Input
100
+ id={field.name}
101
+ name={field.name}
102
+ type="email"
103
+ value={field.state.value}
104
+ onBlur={field.handleBlur}
105
+ onChange={(e) => field.handleChange(e.target.value)}
106
+ />
107
+ {field.state.meta.errors.map((error) => (
108
+ <p key={error?.message} className="text-red-500">
109
+ {error?.message}
110
+ </p>
111
+ ))}
112
+ </div>
113
+ )}
114
+ </form.Field>
115
+ </div>
116
116
 
117
- <div>
118
- <form.Field name="password">
119
- {(field) => (
120
- <div className="space-y-2">
121
- <Label htmlFor={field.name}>Password</Label>
122
- <Input
123
- id={field.name}
124
- name={field.name}
125
- type="password"
126
- value={field.state.value}
127
- onBlur={field.handleBlur}
128
- onChange={(e) => field.handleChange(e.target.value)}
129
- />
130
- {field.state.meta.errors.map((error) => (
131
- <p key={error?.message} className="text-red-500">
132
- {error?.message}
133
- </p>
134
- ))}
135
- </div>
136
- )}
137
- </form.Field>
138
- </div>
117
+ <div>
118
+ <form.Field name="password">
119
+ {(field) => (
120
+ <div className="space-y-2">
121
+ <Label htmlFor={field.name}>Password</Label>
122
+ <Input
123
+ id={field.name}
124
+ name={field.name}
125
+ type="password"
126
+ value={field.state.value}
127
+ onBlur={field.handleBlur}
128
+ onChange={(e) => field.handleChange(e.target.value)}
129
+ />
130
+ {field.state.meta.errors.map((error) => (
131
+ <p key={error?.message} className="text-red-500">
132
+ {error?.message}
133
+ </p>
134
+ ))}
135
+ </div>
136
+ )}
137
+ </form.Field>
138
+ </div>
139
139
 
140
- <form.Subscribe>
141
- {(state) => (
142
- <Button
143
- type="submit"
144
- className="w-full"
145
- disabled={!state.canSubmit || state.isSubmitting}
146
- >
147
- {state.isSubmitting ? "Submitting..." : "Sign Up"}
148
- </Button>
149
- )}
150
- </form.Subscribe>
151
- </form>
140
+ <form.Subscribe>
141
+ {(state) => (
142
+ <Button
143
+ type="submit"
144
+ className="w-full"
145
+ disabled={!state.canSubmit || state.isSubmitting}
146
+ >
147
+ {state.isSubmitting ? "Submitting..." : "Sign Up"}
148
+ </Button>
149
+ )}
150
+ </form.Subscribe>
151
+ </form>
152
152
 
153
- <div className="mt-4 text-center">
154
- <Button
155
- variant="link"
156
- onClick={onSwitchToSignIn}
157
- className="text-indigo-600 hover:text-indigo-800"
158
- >
159
- Already have an account? Sign In
160
- </Button>
161
- </div>
162
- </div>
163
- );
153
+ <div className="mt-4 text-center">
154
+ <Button
155
+ variant="link"
156
+ onClick={onSwitchToSignIn}
157
+ className="text-indigo-600 hover:text-indigo-800"
158
+ >
159
+ Already have an account? Sign In
160
+ </Button>
161
+ </div>
162
+ </div>
163
+ );
164
164
  }
@@ -1,13 +1,13 @@
1
- import type { Context as HonoContext } from "hono";
1
+ import type { Context as ElysiaContext } from "elysia";
2
2
  import { auth } from "./auth";
3
3
 
4
4
  export type CreateContextOptions = {
5
- hono: HonoContext;
5
+ context: ElysiaContext;
6
6
  };
7
7
 
8
- export async function createContext({ hono }: CreateContextOptions) {
8
+ export async function createContext({ context }: CreateContextOptions) {
9
9
  const session = await auth.api.getSession({
10
- headers: hono.req.raw.headers,
10
+ headers: context.request.headers,
11
11
  });
12
12
 
13
13
  return {
@@ -2,12 +2,12 @@ import type { Context as HonoContext } from "hono";
2
2
  import { auth } from "./auth";
3
3
 
4
4
  export type CreateContextOptions = {
5
- hono: HonoContext;
5
+ context: HonoContext;
6
6
  };
7
7
 
8
- export async function createContext({ hono }: CreateContextOptions) {
8
+ export async function createContext({ context }: CreateContextOptions) {
9
9
  const session = await auth.api.getSession({
10
- headers: hono.req.raw.headers,
10
+ headers: context.req.raw.headers,
11
11
  });
12
12
 
13
13
  return {
@@ -1,19 +1,17 @@
1
-
2
- import { router, publicProcedure, protectedProcedure } from "../lib/trpc";
1
+ import { protectedProcedure, publicProcedure, router } from "../lib/trpc";
3
2
  import { todoRouter } from "./todo";
4
3
 
5
-
6
4
  export const appRouter = router({
7
- healthCheck: publicProcedure.query(() => {
8
- return "OK";
9
- }),
10
- privateData: protectedProcedure.query(({ ctx }) => {
11
- return {
12
- message: "This is private",
13
- user: ctx.session.user,
14
- };
15
- }),
16
- todo: todoRouter,
5
+ healthCheck: publicProcedure.query(() => {
6
+ return "OK";
7
+ }),
8
+ privateData: protectedProcedure.query(({ ctx }) => {
9
+ return {
10
+ message: "This is private",
11
+ user: ctx.session.user,
12
+ };
13
+ }),
14
+ todo: todoRouter,
17
15
  });
18
16
 
19
17
  export type AppRouter = typeof appRouter;
@@ -0,0 +1,38 @@
1
+ import "dotenv/config";
2
+ import { Elysia } from "elysia";
3
+ import { cors } from "@elysiajs/cors";
4
+ import { auth } from "./lib/auth";
5
+ import { createContext } from "./lib/context";
6
+ import { appRouter } from "./routers/index";
7
+ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
8
+
9
+ const app = new Elysia()
10
+ .use(
11
+ cors({
12
+ origin: process.env.CORS_ORIGIN || "",
13
+ methods: ["GET", "POST", "OPTIONS"],
14
+ allowedHeaders: ["Content-Type", "Authorization"],
15
+ credentials: true,
16
+ }),
17
+ )
18
+ .all("/api/auth/*", async (context) => {
19
+ const { request } = context;
20
+ if (["POST", "GET"].includes(request.method)) {
21
+ return auth.handler(request);
22
+ } else {
23
+ context.error(405);
24
+ }
25
+ })
26
+ .all("/trpc/*", async (context) => {
27
+ const res = await fetchRequestHandler({
28
+ endpoint: "/trpc",
29
+ router: appRouter,
30
+ req: context.request,
31
+ createContext: () => createContext({ context }),
32
+ });
33
+ return res;
34
+ })
35
+ .get("/", () => "OK")
36
+ .listen(3000, () => {
37
+ console.log(`Server is running on http://localhost:3000`);
38
+ });
@@ -0,0 +1,39 @@
1
+
2
+ import { trpcServer } from "@hono/trpc-server";
3
+ import "dotenv/config";
4
+ import { Hono } from "hono";
5
+ import { cors } from "hono/cors";
6
+ import { logger } from "hono/logger";
7
+ import { auth } from "./lib/auth";
8
+ import { createContext } from "./lib/context";
9
+ import { appRouter } from "./routers/index";
10
+
11
+ const app = new Hono();
12
+
13
+ app.use(logger());
14
+
15
+ app.use(
16
+ "/*",
17
+ cors({
18
+ origin: process.env.CORS_ORIGIN || "",
19
+ allowMethods: ["GET", "POST", "OPTIONS"],
20
+ allowHeaders: ["Content-Type", "Authorization"],
21
+ credentials: true,
22
+ }),
23
+ );
24
+
25
+ app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
26
+
27
+ app.use(
28
+ "/trpc/*",
29
+ trpcServer({
30
+ router: appRouter,
31
+ createContext: (_opts, context) => {
32
+ return createContext({ context });
33
+ },
34
+ }),
35
+ );
36
+
37
+ app.get("/", (c) => {
38
+ return c.text("OK");
39
+ });
@@ -0,0 +1,27 @@
1
+ import "dotenv/config";
2
+ import { Elysia } from "elysia";
3
+ import { cors } from "@elysiajs/cors";
4
+ import { createContext } from "./lib/context";
5
+ import { appRouter } from "./routers/index";
6
+ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
7
+
8
+ const app = new Elysia()
9
+ .use(
10
+ cors({
11
+ origin: process.env.CORS_ORIGIN || "",
12
+ methods: ["GET", "POST", "OPTIONS"],
13
+ }),
14
+ )
15
+ .all("/trpc/*", async (context) => {
16
+ const res = await fetchRequestHandler({
17
+ endpoint: "/trpc",
18
+ router: appRouter,
19
+ req: context.request,
20
+ createContext: () => createContext({ context }),
21
+ });
22
+ return res;
23
+ })
24
+ .get("/", () => "OK")
25
+ .listen(3000, () => {
26
+ console.log(`Server is running on http://localhost:3000`);
27
+ });
@@ -0,0 +1,13 @@
1
+ import type { Context as ElysiaContext } from "elysia";
2
+
3
+ export type CreateContextOptions = {
4
+ context: ElysiaContext;
5
+ };
6
+
7
+ export async function createContext({ context }: CreateContextOptions) {
8
+ return {
9
+ session: null,
10
+ };
11
+ }
12
+
13
+ export type Context = Awaited<ReturnType<typeof createContext>>;
@@ -0,0 +1,33 @@
1
+ import { trpcServer } from "@hono/trpc-server";
2
+ import "dotenv/config";
3
+ import { Hono } from "hono";
4
+ import { cors } from "hono/cors";
5
+ import { logger } from "hono/logger";
6
+ import { createContext } from "./lib/context";
7
+ import { appRouter } from "./routers/index";
8
+
9
+ const app = new Hono();
10
+
11
+ app.use(logger());
12
+
13
+ app.use(
14
+ "/*",
15
+ cors({
16
+ origin: process.env.CORS_ORIGIN || "",
17
+ allowMethods: ["GET", "POST", "OPTIONS"],
18
+ }),
19
+ );
20
+
21
+ app.use(
22
+ "/trpc/*",
23
+ trpcServer({
24
+ router: appRouter,
25
+ createContext: (_opts, context) => {
26
+ return createContext({ context });
27
+ },
28
+ }),
29
+ );
30
+
31
+ app.get("/", (c) => {
32
+ return c.text("OK");
33
+ });
@@ -1,10 +1,10 @@
1
1
  import type { Context as HonoContext } from "hono";
2
2
 
3
3
  export type CreateContextOptions = {
4
- hono: HonoContext;
4
+ context: HonoContext;
5
5
  };
6
6
 
7
- export async function createContext({ hono }: CreateContextOptions) {
7
+ export async function createContext({ context }: CreateContextOptions) {
8
8
  return {
9
9
  session: null,
10
10
  };
@@ -1,41 +0,0 @@
1
- import { serve } from "@hono/node-server";
2
- import { trpcServer } from "@hono/trpc-server";
3
- import "dotenv/config";
4
- import { Hono } from "hono";
5
- import { cors } from "hono/cors";
6
- import { logger } from "hono/logger";
7
- import { createContext } from "./lib/context";
8
- import { appRouter } from "./routers/index";
9
-
10
- const app = new Hono();
11
-
12
- app.use(logger());
13
-
14
- app.use(
15
- "/*",
16
- cors({
17
- origin: process.env.CORS_ORIGIN || "",
18
- allowMethods: ["GET", "POST", "OPTIONS"],
19
- }),
20
- );
21
-
22
- app.use(
23
- "/trpc/*",
24
- trpcServer({
25
- router: appRouter,
26
- createContext: (_opts, hono) => {
27
- return createContext({ hono });
28
- },
29
- }),
30
- );
31
-
32
- app.get("/", (c) => {
33
- return c.text("OK");
34
- });
35
-
36
- serve({
37
- fetch: app.fetch,
38
- port: 3000,
39
- }, (info) => {
40
- console.log(`Server is running on http://localhost:${info.port}`)
41
- });
@@ -1,46 +0,0 @@
1
- import { serve } from "@hono/node-server";
2
- import { trpcServer } from "@hono/trpc-server";
3
- import "dotenv/config";
4
- import { Hono } from "hono";
5
- import { cors } from "hono/cors";
6
- import { logger } from "hono/logger";
7
- import { auth } from "./lib/auth";
8
- import { createContext } from "./lib/context";
9
- import { appRouter } from "./routers/index";
10
-
11
- const app = new Hono();
12
-
13
- app.use(logger());
14
-
15
- app.use(
16
- "/*",
17
- cors({
18
- origin: process.env.CORS_ORIGIN || "",
19
- allowMethods: ["GET", "POST", "OPTIONS"],
20
- allowHeaders: ["Content-Type", "Authorization"],
21
- credentials: true,
22
- }),
23
- );
24
-
25
- app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
26
-
27
- app.use(
28
- "/trpc/*",
29
- trpcServer({
30
- router: appRouter,
31
- createContext: (_opts, hono) => {
32
- return createContext({ hono });
33
- },
34
- }),
35
- );
36
-
37
- app.get("/", (c) => {
38
- return c.text("OK");
39
- });
40
-
41
- serve({
42
- fetch: app.fetch,
43
- port: 3000,
44
- }, (info) => {
45
- console.log(`Server is running on http://localhost:${info.port}`)
46
- });
@@ -1,44 +0,0 @@
1
- import { z } from "zod";
2
- import { router, publicProcedure } from "../lib/trpc";
3
- import { todo } from "../db/schema";
4
- import { eq } from "drizzle-orm";
5
- import { db } from "../db";
6
-
7
- export const todoRouter = router({
8
- getAll: publicProcedure.query(async () => {
9
- return await db.select().from(todo).all();
10
- }),
11
-
12
- create: publicProcedure
13
- .input(z.object({ text: z.string().min(1) }))
14
- .mutation(async ({ input }) => {
15
- return await db
16
- .insert(todo)
17
- .values({
18
- text: input.text,
19
- })
20
- .returning()
21
- .get();
22
- }),
23
-
24
- toggle: publicProcedure
25
- .input(z.object({ id: z.number(), completed: z.boolean() }))
26
- .mutation(async ({ input }) => {
27
- return await db
28
- .update(todo)
29
- .set({ completed: input.completed })
30
- .where(eq(todo.id, input.id))
31
- .returning()
32
- .get();
33
- }),
34
-
35
- delete: publicProcedure
36
- .input(z.object({ id: z.number() }))
37
- .mutation(async ({ input }) => {
38
- return await db
39
- .delete(todo)
40
- .where(eq(todo.id, input.id))
41
- .returning()
42
- .get();
43
- }),
44
- });