create-better-t-stack 2.37.0 → 2.38.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 (111) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +11 -5
  3. package/dist/index.js +1 -1
  4. package/dist/{src-CGVrE3la.js → src-BR5HLmM2.js} +266 -104
  5. package/package.json +1 -1
  6. package/templates/addons/ruler/.ruler/bts.md.hbs +2 -2
  7. package/templates/api/orpc/native/utils/orpc.ts.hbs +2 -2
  8. package/templates/api/orpc/server/base/src/lib/context.ts.hbs +10 -10
  9. package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +1 -1
  10. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +2 -2
  11. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  12. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +11 -1
  13. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  14. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  15. package/templates/api/trpc/native/utils/trpc.ts.hbs +2 -2
  16. package/templates/api/trpc/server/base/src/lib/context.ts.hbs +10 -10
  17. package/templates/api/trpc/server/base/src/lib/trpc.ts.hbs +1 -1
  18. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +2 -2
  19. package/templates/auth/clerk/convex/backend/convex/auth.config.ts.hbs +12 -0
  20. package/templates/auth/clerk/convex/backend/convex/privateData.ts.hbs +16 -0
  21. package/templates/auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs +12 -0
  22. package/templates/auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs +67 -0
  23. package/templates/auth/clerk/convex/native/base/app/(auth)/sign-out.tsx.hbs +110 -0
  24. package/templates/auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs +27 -0
  25. package/templates/auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs +29 -0
  26. package/templates/auth/clerk/convex/web/react/next/src/middleware.ts.hbs +12 -0
  27. package/templates/auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs +32 -0
  28. package/templates/auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +37 -0
  29. package/templates/auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +37 -0
  30. package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +18 -0
  31. package/templates/backend/convex/packages/backend/package.json.hbs +1 -0
  32. package/templates/backend/server/elysia/src/index.ts.hbs +3 -3
  33. package/templates/backend/server/express/src/index.ts.hbs +6 -6
  34. package/templates/backend/server/fastify/src/index.ts.hbs +4 -4
  35. package/templates/backend/server/hono/src/index.ts.hbs +4 -4
  36. package/templates/backend/server/server-base/src/routers/index.ts.hbs +4 -4
  37. package/templates/deploy/alchemy/alchemy.run.ts.hbs +2 -2
  38. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +35 -3
  39. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +28 -0
  40. package/templates/frontend/native/nativewind/package.json.hbs +1 -0
  41. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +32 -0
  42. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +35 -0
  43. package/templates/frontend/native/unistyles/package.json.hbs +1 -0
  44. package/templates/frontend/nuxt/app/components/Header.vue.hbs +3 -3
  45. package/templates/frontend/react/next/src/app/layout.tsx.hbs +23 -15
  46. package/templates/frontend/react/next/src/components/providers.tsx.hbs +12 -0
  47. package/templates/frontend/react/react-router/src/root.tsx.hbs +28 -1
  48. package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +19 -1
  49. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +8 -4
  50. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +52 -5
  51. package/templates/frontend/react/web-base/src/components/header.tsx.hbs +3 -3
  52. package/templates/frontend/solid/src/components/header.tsx.hbs +3 -3
  53. package/templates/frontend/svelte/src/components/Header.svelte.hbs +3 -3
  54. /package/templates/auth/{native → better-auth/native}/native-base/lib/auth-client.ts.hbs +0 -0
  55. /package/templates/auth/{native → better-auth/native}/nativewind/app/(drawer)/index.tsx.hbs +0 -0
  56. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-in.tsx.hbs +0 -0
  57. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-up.tsx.hbs +0 -0
  58. /package/templates/auth/{native → better-auth/native}/unistyles/app/(drawer)/index.tsx.hbs +0 -0
  59. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-in.tsx.hbs +0 -0
  60. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-up.tsx.hbs +0 -0
  61. /package/templates/auth/{server → better-auth/server}/base/src/lib/auth.ts.hbs +0 -0
  62. /package/templates/auth/{server → better-auth/server}/db/drizzle/mysql/src/db/schema/auth.ts +0 -0
  63. /package/templates/auth/{server → better-auth/server}/db/drizzle/postgres/src/db/schema/auth.ts +0 -0
  64. /package/templates/auth/{server → better-auth/server}/db/drizzle/sqlite/src/db/schema/auth.ts +0 -0
  65. /package/templates/auth/{server → better-auth/server}/db/mongoose/mongodb/src/db/models/auth.model.ts +0 -0
  66. /package/templates/auth/{server → better-auth/server}/db/prisma/mongodb/prisma/schema/auth.prisma +0 -0
  67. /package/templates/auth/{server → better-auth/server}/db/prisma/mysql/prisma/schema/auth.prisma +0 -0
  68. /package/templates/auth/{server → better-auth/server}/db/prisma/postgres/prisma/schema/auth.prisma +0 -0
  69. /package/templates/auth/{server → better-auth/server}/db/prisma/sqlite/prisma/schema/auth.prisma +0 -0
  70. /package/templates/auth/{server → better-auth/server}/next/src/app/api/auth/[...all]/route.ts +0 -0
  71. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignInForm.vue +0 -0
  72. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignUpForm.vue +0 -0
  73. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/UserMenu.vue +0 -0
  74. /package/templates/auth/{web → better-auth/web}/nuxt/app/middleware/auth.ts +0 -0
  75. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/dashboard.vue.hbs +0 -0
  76. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/login.vue +0 -0
  77. /package/templates/auth/{web → better-auth/web}/nuxt/app/plugins/auth-client.ts +0 -0
  78. /package/templates/auth/{web → better-auth/web}/react/base/src/lib/auth-client.ts.hbs +0 -0
  79. /package/templates/auth/{web → better-auth/web}/react/next/src/app/dashboard/page.tsx.hbs +0 -0
  80. /package/templates/auth/{web → better-auth/web}/react/next/src/app/login/page.tsx +0 -0
  81. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-in-form.tsx +0 -0
  82. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-up-form.tsx +0 -0
  83. /package/templates/auth/{web → better-auth/web}/react/next/src/components/theme-provider.tsx +0 -0
  84. /package/templates/auth/{web → better-auth/web}/react/next/src/components/user-menu.tsx +0 -0
  85. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-in-form.tsx +0 -0
  86. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-up-form.tsx +0 -0
  87. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/user-menu.tsx +0 -0
  88. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/dashboard.tsx.hbs +0 -0
  89. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/login.tsx +0 -0
  90. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-in-form.tsx +0 -0
  91. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-up-form.tsx +0 -0
  92. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/user-menu.tsx +0 -0
  93. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -0
  94. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/login.tsx +0 -0
  95. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-in-form.tsx +0 -0
  96. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-up-form.tsx +0 -0
  97. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/user-menu.tsx +0 -0
  98. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -0
  99. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/login.tsx +0 -0
  100. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-in-form.tsx +0 -0
  101. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-up-form.tsx +0 -0
  102. /package/templates/auth/{web → better-auth/web}/solid/src/components/user-menu.tsx.hbs +0 -0
  103. /package/templates/auth/{web → better-auth/web}/solid/src/lib/auth-client.ts +0 -0
  104. /package/templates/auth/{web → better-auth/web}/solid/src/routes/dashboard.tsx.hbs +0 -0
  105. /package/templates/auth/{web → better-auth/web}/solid/src/routes/login.tsx +0 -0
  106. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignInForm.svelte +0 -0
  107. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignUpForm.svelte +0 -0
  108. /package/templates/auth/{web → better-auth/web}/svelte/src/components/UserMenu.svelte +0 -0
  109. /package/templates/auth/{web → better-auth/web}/svelte/src/lib/auth-client.ts +0 -0
  110. /package/templates/auth/{web → better-auth/web}/svelte/src/routes/dashboard/+page.svelte.hbs +0 -0
  111. /package/templates/auth/{web → better-auth/web}/svelte/src/routes/login/+page.svelte +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.37.0",
3
+ "version": "2.38.0",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -77,7 +77,7 @@ Database models are located in `apps/server/src/db/models/`
77
77
  {{/if}}
78
78
  {{/if}}
79
79
 
80
- {{#if auth}}
80
+ {{#if (eq auth "better-auth")}}
81
81
  ## Authentication
82
82
 
83
83
  Authentication is enabled in this project:
@@ -129,4 +129,4 @@ This project includes a `bts.jsonc` configuration file that stores your Better-T
129
129
  - Turborepo handles build caching and parallel execution
130
130
  {{/if}}
131
131
  - Use `{{#if (eq packageManager "bun")}}bunx{{else if (eq packageManager "pnpm")}}pnpx{{else}}npx{{/if}}
132
- create-better-t-stack add` to add more features later
132
+ create-better-t-stack add` to add more features later
@@ -3,7 +3,7 @@ import { RPCLink } from "@orpc/client/fetch";
3
3
  import { createTanstackQueryUtils } from "@orpc/tanstack-query";
4
4
  import { QueryCache, QueryClient } from "@tanstack/react-query";
5
5
  import type { AppRouterClient } from "../../server/src/routers";
6
- {{#if auth}}
6
+ {{#if (eq auth "better-auth")}}
7
7
  import { authClient } from "@/lib/auth-client";
8
8
  {{/if}}
9
9
 
@@ -17,7 +17,7 @@ export const queryClient = new QueryClient({
17
17
 
18
18
  export const link = new RPCLink({
19
19
  url: `${process.env.EXPO_PUBLIC_SERVER_URL}/rpc`,
20
- {{#if auth}}
20
+ {{#if (eq auth "better-auth")}}
21
21
  headers() {
22
22
  const headers = new Map<string, string>();
23
23
  const cookies = authClient.getCookie();
@@ -1,11 +1,11 @@
1
1
  {{#if (eq backend 'next')}}
2
2
  import type { NextRequest } from "next/server";
3
- {{#if auth}}
3
+ {{#if (eq auth "better-auth")}}
4
4
  import { auth } from "./auth";
5
5
  {{/if}}
6
6
 
7
7
  export async function createContext(req: NextRequest) {
8
- {{#if auth}}
8
+ {{#if (eq auth "better-auth")}}
9
9
  const session = await auth.api.getSession({
10
10
  headers: req.headers,
11
11
  });
@@ -19,7 +19,7 @@ export async function createContext(req: NextRequest) {
19
19
 
20
20
  {{else if (eq backend 'hono')}}
21
21
  import type { Context as HonoContext } from "hono";
22
- {{#if auth}}
22
+ {{#if (eq auth "better-auth")}}
23
23
  import { auth } from "./auth";
24
24
  {{/if}}
25
25
 
@@ -28,7 +28,7 @@ export type CreateContextOptions = {
28
28
  };
29
29
 
30
30
  export async function createContext({ context }: CreateContextOptions) {
31
- {{#if auth}}
31
+ {{#if (eq auth "better-auth")}}
32
32
  const session = await auth.api.getSession({
33
33
  headers: context.req.raw.headers,
34
34
  });
@@ -45,7 +45,7 @@ export async function createContext({ context }: CreateContextOptions) {
45
45
 
46
46
  {{else if (eq backend 'elysia')}}
47
47
  import type { Context as ElysiaContext } from "elysia";
48
- {{#if auth}}
48
+ {{#if (eq auth "better-auth")}}
49
49
  import { auth } from "./auth";
50
50
  {{/if}}
51
51
 
@@ -54,7 +54,7 @@ export type CreateContextOptions = {
54
54
  };
55
55
 
56
56
  export async function createContext({ context }: CreateContextOptions) {
57
- {{#if auth}}
57
+ {{#if (eq auth "better-auth")}}
58
58
  const session = await auth.api.getSession({
59
59
  headers: context.request.headers,
60
60
  });
@@ -70,13 +70,13 @@ export async function createContext({ context }: CreateContextOptions) {
70
70
  }
71
71
 
72
72
  {{else if (eq backend 'express')}}
73
- {{#if auth}}
73
+ {{#if (eq auth "better-auth")}}
74
74
  import { fromNodeHeaders } from "better-auth/node";
75
75
  import { auth } from "./auth";
76
76
  {{/if}}
77
77
 
78
78
  export async function createContext(opts: any) {
79
- {{#if auth}}
79
+ {{#if (eq auth "better-auth")}}
80
80
  const session = await auth.api.getSession({
81
81
  headers: fromNodeHeaders(opts.req.headers),
82
82
  });
@@ -93,13 +93,13 @@ export async function createContext(opts: any) {
93
93
 
94
94
  {{else if (eq backend 'fastify')}}
95
95
  import type { IncomingHttpHeaders } from "node:http";
96
- {{#if auth}}
96
+ {{#if (eq auth "better-auth")}}
97
97
  import { fromNodeHeaders } from "better-auth/node";
98
98
  import { auth } from "./auth";
99
99
  {{/if}}
100
100
 
101
101
  export async function createContext(req: IncomingHttpHeaders) {
102
- {{#if auth}}
102
+ {{#if (eq auth "better-auth")}}
103
103
  const session = await auth.api.getSession({
104
104
  headers: fromNodeHeaders(req),
105
105
  });
@@ -5,7 +5,7 @@ export const o = os.$context<Context>();
5
5
 
6
6
  export const publicProcedure = o;
7
7
 
8
- {{#if auth}}
8
+ {{#if (eq auth "better-auth")}}
9
9
  const requireAuth = o.middleware(async ({ context, next }) => {
10
10
  if (!context.session?.user) {
11
11
  throw new ORPCError("UNAUTHORIZED");
@@ -1,4 +1,4 @@
1
- {{#if auth}}
1
+ {{#if (eq auth "better-auth")}}
2
2
  import { createContext } from '@/lib/context'
3
3
  {{/if}}
4
4
  import { appRouter } from '@/routers'
@@ -10,7 +10,7 @@ const handler = new RPCHandler(appRouter)
10
10
  async function handleRequest(req: NextRequest) {
11
11
  const { response } = await handler.handle(req, {
12
12
  prefix: '/rpc',
13
- context: {{#if auth}}await createContext(req){{else}}{}{{/if}},
13
+ context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
14
14
  })
15
15
 
16
16
  return response ?? new Response('Not found', { status: 404 })
@@ -12,7 +12,7 @@ export default defineNuxtPlugin(() => {
12
12
 
13
13
  const rpcLink = new RPCLink({
14
14
  url: rpcUrl,
15
- {{#if auth}}
15
+ {{#if (eq auth "better-auth")}}
16
16
  fetch(url, options) {
17
17
  return fetch(url, {
18
18
  ...options,
@@ -26,13 +26,23 @@ export const link = new RPCLink({
26
26
  {{else}}
27
27
  url: `${import.meta.env.VITE_SERVER_URL}/rpc`,
28
28
  {{/if}}
29
- {{#if auth}}
29
+ {{#if (eq auth "better-auth")}}
30
30
  fetch(url, options) {
31
31
  return fetch(url, {
32
32
  ...options,
33
33
  credentials: "include",
34
34
  });
35
35
  },
36
+ {{#if (includes frontend "next")}}
37
+ headers: async () => {
38
+ if (typeof window !== "undefined") {
39
+ return {}
40
+ }
41
+
42
+ const { headers } = await import("next/headers")
43
+ return Object.fromEntries(await headers())
44
+ },
45
+ {{/if}}
36
46
  {{/if}}
37
47
  });
38
48
 
@@ -14,7 +14,7 @@ export const queryClient = new QueryClient({
14
14
 
15
15
  export const link = new RPCLink({
16
16
  url: `${import.meta.env.VITE_SERVER_URL}/rpc`,
17
- {{#if auth}}
17
+ {{#if (eq auth "better-auth")}}
18
18
  fetch(url, options) {
19
19
  return fetch(url, {
20
20
  ...options,
@@ -15,7 +15,7 @@ export const queryClient = new QueryClient({
15
15
 
16
16
  export const link = new RPCLink({
17
17
  url: `${PUBLIC_SERVER_URL}/rpc`,
18
- {{#if auth}}
18
+ {{#if (eq auth "better-auth")}}
19
19
  fetch(url, options) {
20
20
  return fetch(url, {
21
21
  ...options,
@@ -1,4 +1,4 @@
1
- {{#if auth}}
1
+ {{#if (eq auth "better-auth")}}
2
2
  import { authClient } from "@/lib/auth-client";
3
3
  {{/if}}
4
4
  import { QueryClient } from "@tanstack/react-query";
@@ -12,7 +12,7 @@ const trpcClient = createTRPCClient<AppRouter>({
12
12
  links: [
13
13
  httpBatchLink({
14
14
  url: `${process.env.EXPO_PUBLIC_SERVER_URL}/trpc`,
15
- {{#if auth}}
15
+ {{#if (eq auth "better-auth")}}
16
16
  headers() {
17
17
  const headers = new Map<string, string>();
18
18
  const cookies = authClient.getCookie();
@@ -1,11 +1,11 @@
1
1
  {{#if (eq backend 'next')}}
2
2
  import type { NextRequest } from "next/server";
3
- {{#if auth}}
3
+ {{#if (eq auth "better-auth")}}
4
4
  import { auth } from "./auth";
5
5
  {{/if}}
6
6
 
7
7
  export async function createContext(req: NextRequest) {
8
- {{#if auth}}
8
+ {{#if (eq auth "better-auth")}}
9
9
  const session = await auth.api.getSession({
10
10
  headers: req.headers,
11
11
  });
@@ -22,7 +22,7 @@ export async function createContext(req: NextRequest) {
22
22
 
23
23
  {{else if (eq backend 'hono')}}
24
24
  import type { Context as HonoContext } from "hono";
25
- {{#if auth}}
25
+ {{#if (eq auth "better-auth")}}
26
26
  import { auth } from "./auth";
27
27
  {{/if}}
28
28
 
@@ -31,7 +31,7 @@ export type CreateContextOptions = {
31
31
  };
32
32
 
33
33
  export async function createContext({ context }: CreateContextOptions) {
34
- {{#if auth}}
34
+ {{#if (eq auth "better-auth")}}
35
35
  const session = await auth.api.getSession({
36
36
  headers: context.req.raw.headers,
37
37
  });
@@ -48,7 +48,7 @@ export async function createContext({ context }: CreateContextOptions) {
48
48
 
49
49
  {{else if (eq backend 'elysia')}}
50
50
  import type { Context as ElysiaContext } from "elysia";
51
- {{#if auth}}
51
+ {{#if (eq auth "better-auth")}}
52
52
  import { auth } from "./auth";
53
53
  {{/if}}
54
54
 
@@ -57,7 +57,7 @@ export type CreateContextOptions = {
57
57
  };
58
58
 
59
59
  export async function createContext({ context }: CreateContextOptions) {
60
- {{#if auth}}
60
+ {{#if (eq auth "better-auth")}}
61
61
  const session = await auth.api.getSession({
62
62
  headers: context.request.headers,
63
63
  });
@@ -74,13 +74,13 @@ export async function createContext({ context }: CreateContextOptions) {
74
74
 
75
75
  {{else if (eq backend 'express')}}
76
76
  import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
77
- {{#if auth}}
77
+ {{#if (eq auth "better-auth")}}
78
78
  import { fromNodeHeaders } from "better-auth/node";
79
79
  import { auth } from "./auth";
80
80
  {{/if}}
81
81
 
82
82
  export async function createContext(opts: CreateExpressContextOptions) {
83
- {{#if auth}}
83
+ {{#if (eq auth "better-auth")}}
84
84
  const session = await auth.api.getSession({
85
85
  headers: fromNodeHeaders(opts.req.headers),
86
86
  });
@@ -97,13 +97,13 @@ export async function createContext(opts: CreateExpressContextOptions) {
97
97
 
98
98
  {{else if (eq backend 'fastify')}}
99
99
  import type { CreateFastifyContextOptions } from "@trpc/server/adapters/fastify";
100
- {{#if auth}}
100
+ {{#if (eq auth "better-auth")}}
101
101
  import { fromNodeHeaders } from "better-auth/node";
102
102
  import { auth } from "./auth";
103
103
  {{/if}}
104
104
 
105
105
  export async function createContext({ req, res }: CreateFastifyContextOptions) {
106
- {{#if auth}}
106
+ {{#if (eq auth "better-auth")}}
107
107
  const session = await auth.api.getSession({
108
108
  headers: fromNodeHeaders(req.headers),
109
109
  });
@@ -7,7 +7,7 @@ export const router = t.router;
7
7
 
8
8
  export const publicProcedure = t.procedure;
9
9
 
10
- {{#if auth}}
10
+ {{#if (eq auth "better-auth")}}
11
11
  export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
12
12
  if (!ctx.session) {
13
13
  throw new TRPCError({
@@ -28,7 +28,7 @@ const trpcClient = createTRPCClient<AppRouter>({
28
28
  {{else}}
29
29
  url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
30
30
  {{/if}}
31
- {{#if auth}}
31
+ {{#if (eq auth "better-auth")}}
32
32
  fetch(url, options) {
33
33
  return fetch(url, {
34
34
  ...options,
@@ -78,7 +78,7 @@ export const trpcClient = createTRPCClient<AppRouter>({
78
78
  links: [
79
79
  httpBatchLink({
80
80
  url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
81
- {{#if auth}}
81
+ {{#if (eq auth "better-auth")}}
82
82
  fetch(url, options) {
83
83
  return fetch(url, {
84
84
  ...options,
@@ -0,0 +1,12 @@
1
+ export default {
2
+ providers: [
3
+ {
4
+ // Replace with your own Clerk Issuer URL from your "convex" JWT template
5
+ // or with `process.env.CLERK_JWT_ISSUER_DOMAIN`
6
+ // and configure CLERK_JWT_ISSUER_DOMAIN on the Convex Dashboard
7
+ // See https://docs.convex.dev/auth/clerk#configuring-dev-and-prod-instances
8
+ domain: process.env.CLERK_JWT_ISSUER_DOMAIN,
9
+ applicationID: "convex",
10
+ },
11
+ ],
12
+ };
@@ -0,0 +1,16 @@
1
+ import { query } from "./_generated/server";
2
+
3
+ export const get = query({
4
+ args: {},
5
+ handler: async (ctx) => {
6
+ const identity = await ctx.auth.getUserIdentity();
7
+ if (identity === null) {
8
+ return {
9
+ message: "Not authenticated",
10
+ };
11
+ }
12
+ return {
13
+ message: "This is private",
14
+ };
15
+ },
16
+ });
@@ -0,0 +1,12 @@
1
+ import { Redirect, Stack } from "expo-router";
2
+ import { useAuth } from "@clerk/clerk-expo";
3
+
4
+ export default function AuthRoutesLayout() {
5
+ const { isSignedIn } = useAuth();
6
+
7
+ if (isSignedIn) {
8
+ return <Redirect href={"/"} />;
9
+ }
10
+
11
+ return <Stack />;
12
+ }
@@ -0,0 +1,67 @@
1
+ import { useSignIn } from "@clerk/clerk-expo";
2
+ import { Link, useRouter } from "expo-router";
3
+ import { Text, TextInput, TouchableOpacity, View } from "react-native";
4
+ import React from "react";
5
+
6
+ export default function Page() {
7
+ const { signIn, setActive, isLoaded } = useSignIn();
8
+ const router = useRouter();
9
+
10
+ const [emailAddress, setEmailAddress] = React.useState("");
11
+ const [password, setPassword] = React.useState("");
12
+
13
+ // Handle the submission of the sign-in form
14
+ const onSignInPress = async () => {
15
+ if (!isLoaded) return;
16
+
17
+ // Start the sign-in process using the email and password provided
18
+ try {
19
+ const signInAttempt = await signIn.create({
20
+ identifier: emailAddress,
21
+ password,
22
+ });
23
+
24
+ // If sign-in process is complete, set the created session as active
25
+ // and redirect the user
26
+ if (signInAttempt.status === "complete") {
27
+ await setActive({ session: signInAttempt.createdSessionId });
28
+ router.replace("/");
29
+ } else {
30
+ // If the status isn't complete, check why. User might need to
31
+ // complete further steps.
32
+ console.error(JSON.stringify(signInAttempt, null, 2));
33
+ }
34
+ } catch (err) {
35
+ // See https://clerk.com/docs/custom-flows/error-handling
36
+ // for more info on error handling
37
+ console.error(JSON.stringify(err, null, 2));
38
+ }
39
+ };
40
+
41
+ return (
42
+ <View>
43
+ <Text>Sign in</Text>
44
+ <TextInput
45
+ autoCapitalize="none"
46
+ value={emailAddress}
47
+ placeholder="Enter email"
48
+ onChangeText={(emailAddress) => setEmailAddress(emailAddress)}
49
+ />
50
+ <TextInput
51
+ value={password}
52
+ placeholder="Enter password"
53
+ secureTextEntry={true}
54
+ onChangeText={(password) => setPassword(password)}
55
+ />
56
+ <TouchableOpacity onPress={onSignInPress}>
57
+ <Text>Continue</Text>
58
+ </TouchableOpacity>
59
+ <View style=\{{ display: "flex", flexDirection: "row", gap: 3 }}>
60
+ <Text>Don't have an account?</Text>
61
+ <Link href="/sign-up">
62
+ <Text>Sign up</Text>
63
+ </Link>
64
+ </View>
65
+ </View>
66
+ );
67
+ }
@@ -0,0 +1,110 @@
1
+ import * as React from "react";
2
+ import { Text, TextInput, TouchableOpacity, View } from "react-native";
3
+ import { useSignUp } from "@clerk/clerk-expo";
4
+ import { Link, useRouter } from "expo-router";
5
+
6
+ export default function SignUpScreen() {
7
+ const { isLoaded, signUp, setActive } = useSignUp();
8
+ const router = useRouter();
9
+
10
+ const [emailAddress, setEmailAddress] = React.useState("");
11
+ const [password, setPassword] = React.useState("");
12
+ const [pendingVerification, setPendingVerification] = React.useState(false);
13
+ const [code, setCode] = React.useState("");
14
+
15
+ // Handle submission of sign-up form
16
+ const onSignUpPress = async () => {
17
+ if (!isLoaded) return;
18
+
19
+ console.log(emailAddress, password);
20
+
21
+ // Start sign-up process using email and password provided
22
+ try {
23
+ await signUp.create({
24
+ emailAddress,
25
+ password,
26
+ });
27
+
28
+ // Send user an email with verification code
29
+ await signUp.prepareEmailAddressVerification({ strategy: "email_code" });
30
+
31
+ // Set 'pendingVerification' to true to display second form
32
+ // and capture OTP code
33
+ setPendingVerification(true);
34
+ } catch (err) {
35
+ // See https://clerk.com/docs/custom-flows/error-handling
36
+ // for more info on error handling
37
+ console.error(JSON.stringify(err, null, 2));
38
+ }
39
+ };
40
+
41
+ // Handle submission of verification form
42
+ const onVerifyPress = async () => {
43
+ if (!isLoaded) return;
44
+
45
+ try {
46
+ // Use the code the user provided to attempt verification
47
+ const signUpAttempt = await signUp.attemptEmailAddressVerification({
48
+ code,
49
+ });
50
+
51
+ // If verification was completed, set the session to active
52
+ // and redirect the user
53
+ if (signUpAttempt.status === "complete") {
54
+ await setActive({ session: signUpAttempt.createdSessionId });
55
+ router.replace("/");
56
+ } else {
57
+ // If the status is not complete, check why. User may need to
58
+ // complete further steps.
59
+ console.error(JSON.stringify(signUpAttempt, null, 2));
60
+ }
61
+ } catch (err) {
62
+ // See https://clerk.com/docs/custom-flows/error-handling
63
+ // for more info on error handling
64
+ console.error(JSON.stringify(err, null, 2));
65
+ }
66
+ };
67
+
68
+ if (pendingVerification) {
69
+ return (
70
+ <>
71
+ <Text>Verify your email</Text>
72
+ <TextInput
73
+ value={code}
74
+ placeholder="Enter your verification code"
75
+ onChangeText={(code) => setCode(code)}
76
+ />
77
+ <TouchableOpacity onPress={onVerifyPress}>
78
+ <Text>Verify</Text>
79
+ </TouchableOpacity>
80
+ </>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <View>
86
+ <Text>Sign up</Text>
87
+ <TextInput
88
+ autoCapitalize="none"
89
+ value={emailAddress}
90
+ placeholder="Enter email"
91
+ onChangeText={(email) => setEmailAddress(email)}
92
+ />
93
+ <TextInput
94
+ value={password}
95
+ placeholder="Enter password"
96
+ secureTextEntry={true}
97
+ onChangeText={(password) => setPassword(password)}
98
+ />
99
+ <TouchableOpacity onPress={onSignUpPress}>
100
+ <Text>Continue</Text>
101
+ </TouchableOpacity>
102
+ <View style=\{{ display: "flex", flexDirection: "row", gap: 3 }}>
103
+ <Text>Already have an account?</Text>
104
+ <Link href="/sign-in">
105
+ <Text>Sign in</Text>
106
+ </Link>
107
+ </View>
108
+ </View>
109
+ );
110
+ }
@@ -0,0 +1,27 @@
1
+ import { useClerk } from "@clerk/clerk-expo";
2
+ import { useRouter } from "expo-router";
3
+ import { Text, TouchableOpacity } from "react-native";
4
+
5
+ export const SignOutButton = () => {
6
+ // Use `useClerk()` to access the `signOut()` function
7
+ const { signOut } = useClerk();
8
+ const router = useRouter();
9
+
10
+ const handleSignOut = async () => {
11
+ try {
12
+ await signOut();
13
+ // Redirect to your desired page
14
+ router.replace("/");
15
+ } catch (err) {
16
+ // See https://clerk.com/docs/custom-flows/error-handling
17
+ // for more info on error handling
18
+ console.error(JSON.stringify(err, null, 2));
19
+ }
20
+ };
21
+
22
+ return (
23
+ <TouchableOpacity onPress={handleSignOut}>
24
+ <Text>Sign out</Text>
25
+ </TouchableOpacity>
26
+ );
27
+ };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
4
+ import { SignInButton, UserButton, useUser } from "@clerk/nextjs";
5
+ import { Authenticated, AuthLoading, Unauthenticated, useQuery } from "convex/react";
6
+
7
+ export default function Dashboard() {
8
+ const user = useUser();
9
+ const privateData = useQuery(api.privateData.get);
10
+
11
+ return (
12
+ <>
13
+ <Authenticated>
14
+ <div>
15
+ <h1>Dashboard</h1>
16
+ <p>Welcome {user.user?.fullName}</p>
17
+ <p>privateData: {privateData?.message}</p>
18
+ <UserButton />
19
+ </div>
20
+ </Authenticated>
21
+ <Unauthenticated>
22
+ <SignInButton />
23
+ </Unauthenticated>
24
+ <AuthLoading>
25
+ <div>Loading...</div>
26
+ </AuthLoading>
27
+ </>
28
+ );
29
+ }
@@ -0,0 +1,12 @@
1
+ import { clerkMiddleware } from "@clerk/nextjs/server";
2
+
3
+ export default clerkMiddleware();
4
+
5
+ export const config = {
6
+ matcher: [
7
+ // Skip Next.js internals and all static files, unless found in search params
8
+ "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
9
+ // Always run for API routes
10
+ "/(api|trpc)(.*)",
11
+ ],
12
+ };
@@ -0,0 +1,32 @@
1
+ import { SignInButton, UserButton, useUser } from "@clerk/clerk-react";
2
+ import { api } from "@{{projectName}}/backend/convex/_generated/api";
3
+ import {
4
+ Authenticated,
5
+ AuthLoading,
6
+ Unauthenticated,
7
+ useQuery,
8
+ } from "convex/react";
9
+
10
+ export default function Dashboard() {
11
+ const privateData = useQuery(api.privateData.get);
12
+ const user = useUser();
13
+
14
+ return (
15
+ <>
16
+ <Authenticated>
17
+ <div>
18
+ <h1>Dashboard</h1>
19
+ <p>Welcome {user.user?.fullName}</p>
20
+ <p>privateData: {privateData?.message}</p>
21
+ <UserButton />
22
+ </div>
23
+ </Authenticated>
24
+ <Unauthenticated>
25
+ <SignInButton />
26
+ </Unauthenticated>
27
+ <AuthLoading>
28
+ <div>Loading...</div>
29
+ </AuthLoading>
30
+ </>
31
+ );
32
+ }