Skip to content

Commit 98b1262

Browse files
add postgres support
1 parent 30e16b5 commit 98b1262

File tree

21 files changed

+261
-29
lines changed

21 files changed

+261
-29
lines changed

.changeset/fifty-rocks-try.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-better-t-stack": patch
3+
---
4+
5+
Add postgres support

apps/cli/src/helpers/auth-setup.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ export async function configureAuth(
5252
const envPath = path.join(serverDir, ".env");
5353
const templateEnvPath = path.join(
5454
PKG_ROOT,
55-
options.orm === "drizzle"
56-
? "template/with-drizzle/packages/server/_env"
57-
: "template/base/packages/server/_env",
55+
getOrmTemplatePath(
56+
options.orm,
57+
options.database,
58+
"packages/server/_env",
59+
),
5860
);
5961

6062
if (!(await fs.pathExists(envPath))) {
@@ -108,7 +110,11 @@ ${options.orm === "prisma" ? 'DATABASE_URL="file:./dev.db"' : ""}
108110
const prismaAuthPath = path.join(serverDir, "src/lib/auth.ts");
109111
const defaultPrismaAuthPath = path.join(
110112
PKG_ROOT,
111-
"template/with-prisma/packages/server/src/lib/auth.ts",
113+
getOrmTemplatePath(
114+
options.orm,
115+
options.database,
116+
"packages/server/src/lib/auth.ts",
117+
),
112118
);
113119

114120
if (
@@ -147,7 +153,11 @@ ${options.orm === "prisma" ? 'DATABASE_URL="file:./dev.db"' : ""}
147153
const drizzleAuthPath = path.join(serverDir, "src/lib/auth.ts");
148154
const defaultDrizzleAuthPath = path.join(
149155
PKG_ROOT,
150-
"template/with-drizzle/packages/server/src/lib/auth.ts",
156+
getOrmTemplatePath(
157+
options.orm,
158+
options.database,
159+
"packages/server/src/lib/auth.ts",
160+
),
151161
);
152162

153163
if (
@@ -180,6 +190,24 @@ ${options.orm === "prisma" ? 'DATABASE_URL="file:./dev.db"' : ""}
180190
}
181191
}
182192

193+
function getOrmTemplatePath(
194+
orm: string,
195+
database: string,
196+
relativePath: string,
197+
): string {
198+
if (orm === "drizzle") {
199+
return database === "sqlite"
200+
? `template/with-drizzle-sqlite/${relativePath}`
201+
: `template/with-drizzle-postgres/${relativePath}`;
202+
}
203+
if (orm === "prisma") {
204+
return database === "sqlite"
205+
? `template/with-prisma-sqlite/${relativePath}`
206+
: `template/with-prisma-postgres/${relativePath}`;
207+
}
208+
return `template/base/${relativePath}`;
209+
}
210+
183211
function generateAuthSecret(length = 32): string {
184212
const characters =
185213
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

apps/cli/src/helpers/create-project.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ export async function createProject(options: ProjectConfig): Promise<string> {
2727
if (options.orm !== "none" && options.database !== "none") {
2828
const ormTemplateDir = path.join(
2929
PKG_ROOT,
30-
options.orm === "drizzle"
31-
? "template/with-drizzle"
32-
: "template/with-prisma",
30+
getOrmTemplateDir(options.orm, options.database),
3331
);
3432

3533
if (await fs.pathExists(ormTemplateDir)) {
@@ -143,3 +141,19 @@ export async function createProject(options: ProjectConfig): Promise<string> {
143141
throw error;
144142
}
145143
}
144+
145+
function getOrmTemplateDir(orm: string, database: string): string {
146+
if (orm === "drizzle") {
147+
return database === "sqlite"
148+
? "template/with-drizzle-sqlite"
149+
: "template/with-drizzle-postgres";
150+
}
151+
152+
if (orm === "prisma") {
153+
return database === "sqlite"
154+
? "template/with-prisma-sqlite"
155+
: "template/with-prisma-postgres";
156+
}
157+
158+
return "template/base";
159+
}

apps/cli/src/helpers/db-setup.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,21 @@ export async function setupDatabase(
2121
try {
2222
if (databaseType === "sqlite") {
2323
if (orm === "drizzle") {
24-
await setupDrizzleDependencies(projectDir);
25-
await setupTurso(projectDir, setupTursoDb);
24+
await setupDrizzleDependencies(projectDir, "sqlite");
25+
if (setupTursoDb) {
26+
await setupTurso(projectDir, true);
27+
}
2628
} else if (orm === "prisma") {
27-
await setupPrismaDependencies(projectDir);
28-
await setupTurso(projectDir, setupTursoDb);
29+
await setupPrismaDependencies(projectDir, "sqlite");
30+
if (setupTursoDb) {
31+
await setupTurso(projectDir, true);
32+
}
2933
}
3034
} else if (databaseType === "postgres") {
31-
log.info(
32-
pc.blue(
33-
"PostgreSQL setup is coming in a future update. Using SQLite configuration for now.",
34-
),
35-
);
3635
if (orm === "drizzle") {
37-
await setupDrizzleDependencies(projectDir);
38-
await setupTurso(projectDir, setupTursoDb);
36+
await setupDrizzleDependencies(projectDir, "postgres");
3937
} else if (orm === "prisma") {
40-
await setupPrismaDependencies(projectDir);
41-
await setupTurso(projectDir, setupTursoDb);
38+
await setupPrismaDependencies(projectDir, "postgres");
4239
}
4340
}
4441
} catch (error) {
@@ -50,7 +47,10 @@ export async function setupDatabase(
5047
}
5148
}
5249

53-
async function setupDrizzleDependencies(projectDir: string): Promise<void> {
50+
async function setupDrizzleDependencies(
51+
projectDir: string,
52+
dbType: string,
53+
): Promise<void> {
5454
const serverDir = path.join(projectDir, "packages/server");
5555

5656
const packageJsonPath = path.join(serverDir, "package.json");
@@ -60,9 +60,14 @@ async function setupDrizzleDependencies(projectDir: string): Promise<void> {
6060
packageJson.dependencies = {
6161
...packageJson.dependencies,
6262
"drizzle-orm": "^0.38.4",
63-
"@libsql/client": "^0.14.0",
6463
};
6564

65+
if (dbType === "sqlite") {
66+
packageJson.dependencies["@libsql/client"] = "^0.14.0";
67+
} else if (dbType === "postgres") {
68+
packageJson.dependencies.postgres = "^3.4.5";
69+
}
70+
6671
packageJson.devDependencies = {
6772
...packageJson.devDependencies,
6873
"drizzle-kit": "^0.30.4",
@@ -79,7 +84,10 @@ async function setupDrizzleDependencies(projectDir: string): Promise<void> {
7984
}
8085
}
8186

82-
async function setupPrismaDependencies(projectDir: string): Promise<void> {
87+
async function setupPrismaDependencies(
88+
projectDir: string,
89+
dbType: string,
90+
): Promise<void> {
8391
const serverDir = path.join(projectDir, "packages/server");
8492

8593
const packageJsonPath = path.join(serverDir, "package.json");
@@ -89,10 +97,15 @@ async function setupPrismaDependencies(projectDir: string): Promise<void> {
8997
packageJson.dependencies = {
9098
...packageJson.dependencies,
9199
"@prisma/client": "^5.7.1",
92-
"@prisma/adapter-libsql": "^5.7.1",
93-
"@libsql/client": "^0.14.0",
94100
};
95101

102+
if (dbType === "sqlite") {
103+
packageJson.dependencies["@prisma/adapter-libsql"] = "^5.7.1";
104+
packageJson.dependencies["@libsql/client"] = "^0.14.0";
105+
} else if (dbType === "postgres") {
106+
// PostgreSQL specific dependencies if needed
107+
}
108+
96109
packageJson.devDependencies = {
97110
...packageJson.devDependencies,
98111
prisma: "^5.7.1",
@@ -112,7 +125,10 @@ async function setupPrismaDependencies(projectDir: string): Promise<void> {
112125
if (await fs.pathExists(envPath)) {
113126
const envContent = await fs.readFile(envPath, "utf8");
114127
if (!envContent.includes("DATABASE_URL")) {
115-
const databaseUrlLine = `\nDATABASE_URL="file:./dev.db"`;
128+
const databaseUrlLine =
129+
dbType === "sqlite"
130+
? `\nDATABASE_URL="file:./dev.db"`
131+
: `\nDATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"`;
116132
await fs.appendFile(envPath, databaseUrlLine);
117133
}
118134
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from "drizzle-kit";
2+
3+
export default defineConfig({
4+
schema: "./src/db/schema.ts",
5+
out: "./migrations",
6+
dialect: "postgresql",
7+
dbCredentials: {
8+
url: process.env.POSTGRES_URL!,
9+
},
10+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { drizzle } from "drizzle-orm/postgres-js";
2+
import postgres from "postgres";
3+
4+
const queryClient = postgres(process.env.DATABASE_URL);
5+
const db = drizzle({ client: queryClient });
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
2+
3+
export const user = pgTable("user", {
4+
id: text("id").primaryKey(),
5+
name: text('name').notNull(),
6+
email: text('email').notNull().unique(),
7+
emailVerified: boolean('email_verified').notNull(),
8+
image: text('image'),
9+
createdAt: timestamp('created_at').notNull(),
10+
updatedAt: timestamp('updated_at').notNull()
11+
});
12+
13+
export const session = pgTable("session", {
14+
id: text("id").primaryKey(),
15+
expiresAt: timestamp('expires_at').notNull(),
16+
token: text('token').notNull().unique(),
17+
createdAt: timestamp('created_at').notNull(),
18+
updatedAt: timestamp('updated_at').notNull(),
19+
ipAddress: text('ip_address'),
20+
userAgent: text('user_agent'),
21+
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' })
22+
});
23+
24+
export const account = pgTable("account", {
25+
id: text("id").primaryKey(),
26+
accountId: text('account_id').notNull(),
27+
providerId: text('provider_id').notNull(),
28+
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }),
29+
accessToken: text('access_token'),
30+
refreshToken: text('refresh_token'),
31+
idToken: text('id_token'),
32+
accessTokenExpiresAt: timestamp('access_token_expires_at'),
33+
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
34+
scope: text('scope'),
35+
password: text('password'),
36+
createdAt: timestamp('created_at').notNull(),
37+
updatedAt: timestamp('updated_at').notNull()
38+
});
39+
40+
export const verification = pgTable("verification", {
41+
id: text("id").primaryKey(),
42+
identifier: text('identifier').notNull(),
43+
value: text('value').notNull(),
44+
expiresAt: timestamp('expires_at').notNull(),
45+
createdAt: timestamp('created_at'),
46+
updatedAt: timestamp('updated_at')
47+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
BETTER_AUTH_SECRET=jdUstNuiIZLVh897KOMMS8EmTP0QkD32
2+
BETTER_AUTH_URL=http://localhost:3000
3+
TURSO_CONNECTION_URL=http://127.0.0.1:8080
4+
CORS_ORIGIN=http://localhost:3001

0 commit comments

Comments
 (0)