Initial commit
This commit is contained in:
6
skills/neon-drizzle/templates/db-http.ts
Normal file
6
skills/neon-drizzle/templates/db-http.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { drizzle } from 'drizzle-orm/neon-http';
|
||||
import { neon } from '@neondatabase/serverless';
|
||||
|
||||
const sql = neon(process.env.DATABASE_URL!);
|
||||
|
||||
export const db = drizzle(sql);
|
||||
24
skills/neon-drizzle/templates/db-websocket.ts
Normal file
24
skills/neon-drizzle/templates/db-websocket.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { drizzle } from 'drizzle-orm/neon-serverless';
|
||||
import { Pool, neonConfig } from '@neondatabase/serverless';
|
||||
import ws from 'ws';
|
||||
|
||||
neonConfig.webSocketConstructor = ws;
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL!,
|
||||
max: 10,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 5000,
|
||||
});
|
||||
|
||||
export const db = drizzle(pool);
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
await pool.end();
|
||||
process.exit(0);
|
||||
});
|
||||
96
skills/neon-drizzle/templates/drizzle-config.ts
Normal file
96
skills/neon-drizzle/templates/drizzle-config.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Drizzle Configuration
|
||||
*
|
||||
* This file configures Drizzle ORM for use with Neon.
|
||||
* Place this in your project root or src/ directory.
|
||||
*
|
||||
* Usage: Reference this in your drizzle.config.ts
|
||||
*/
|
||||
|
||||
import { config } from 'dotenv';
|
||||
import type { Config } from 'drizzle-kit';
|
||||
|
||||
config({ path: '.env.local' });
|
||||
|
||||
/**
|
||||
* Drizzle Configuration for Neon Postgres
|
||||
*
|
||||
* Supports both HTTP and WebSocket connections.
|
||||
* Automatically detects which driver to use based on environment.
|
||||
*/
|
||||
|
||||
const dbUrl = process.env.DATABASE_URL;
|
||||
|
||||
if (!dbUrl) {
|
||||
throw new Error('DATABASE_URL environment variable is not set');
|
||||
}
|
||||
|
||||
// Determine connection type based on environment
|
||||
const isServerless = process.env.RUNTIME === 'edge' ||
|
||||
process.env.VERCEL_ENV === 'production';
|
||||
|
||||
export default {
|
||||
schema: './src/db/schema.ts', // Path to your schema file
|
||||
out: './src/db/migrations', // Output directory for migrations
|
||||
|
||||
// Database connection
|
||||
dbCredentials: {
|
||||
url: dbUrl,
|
||||
},
|
||||
|
||||
// Migration options
|
||||
migrations: {
|
||||
prefix: 'timestamp', // or 'none'
|
||||
},
|
||||
|
||||
// Verbose logging for debugging
|
||||
verbose: process.env.DEBUG === 'true',
|
||||
|
||||
// Strict mode ensures all migrations are applied
|
||||
strict: true,
|
||||
} satisfies Config;
|
||||
|
||||
/**
|
||||
* HTTP Connection Configuration (for Vercel Edge, etc.)
|
||||
*
|
||||
* export default {
|
||||
* schema: './src/db/schema.ts',
|
||||
* out: './src/db/migrations',
|
||||
* driver: 'postgres',
|
||||
* dbCredentials: {
|
||||
* url: process.env.DATABASE_URL!,
|
||||
* },
|
||||
* } satisfies Config;
|
||||
*/
|
||||
|
||||
/**
|
||||
* WebSocket Connection Configuration (for Node.js servers)
|
||||
*
|
||||
* export default {
|
||||
* schema: './src/db/schema.ts',
|
||||
* out: './src/db/migrations',
|
||||
* driver: 'pg',
|
||||
* dbCredentials: {
|
||||
* url: process.env.DATABASE_URL!,
|
||||
* },
|
||||
* } satisfies Config;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration Commands
|
||||
*
|
||||
* # Generate migration files from schema changes
|
||||
* npx drizzle-kit generate
|
||||
*
|
||||
* # Apply migrations to database
|
||||
* npx drizzle-kit migrate
|
||||
*
|
||||
* # Drop all tables (careful!)
|
||||
* npx drizzle-kit drop
|
||||
*
|
||||
* # Introspect existing database
|
||||
* npx drizzle-kit introspect
|
||||
*
|
||||
* # Push schema changes directly (development only)
|
||||
* npx drizzle-kit push
|
||||
*/
|
||||
231
skills/neon-drizzle/templates/schema-example.ts
Normal file
231
skills/neon-drizzle/templates/schema-example.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* Drizzle Schema Example
|
||||
*
|
||||
* This file demonstrates how to define database tables and relationships
|
||||
* using Drizzle ORM with Neon Postgres.
|
||||
*
|
||||
* Usage: Import these tables in your application code for type-safe queries
|
||||
*/
|
||||
|
||||
import {
|
||||
pgTable,
|
||||
serial,
|
||||
text,
|
||||
varchar,
|
||||
integer,
|
||||
timestamp,
|
||||
boolean,
|
||||
decimal,
|
||||
json,
|
||||
index,
|
||||
unique,
|
||||
foreignKey,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
/**
|
||||
* Users Table
|
||||
*
|
||||
* Stores basic user information. Can be extended with additional fields
|
||||
* as needed by your application.
|
||||
*/
|
||||
export const users = pgTable(
|
||||
'users',
|
||||
{
|
||||
id: serial('id').primaryKey(),
|
||||
email: varchar('email', { length: 255 }).notNull().unique(),
|
||||
name: varchar('name', { length: 255 }).notNull(),
|
||||
password: text('password'), // If not using external auth
|
||||
avatar: text('avatar'), // URL to avatar image
|
||||
isActive: boolean('is_active').default(true),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
emailIdx: index('users_email_idx').on(table.email),
|
||||
createdAtIdx: index('users_created_at_idx').on(table.createdAt),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Profiles Table
|
||||
*
|
||||
* Extended user information. Uses a foreign key to link with users.
|
||||
*/
|
||||
export const profiles = pgTable('profiles', {
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
bio: text('bio'),
|
||||
location: varchar('location', { length: 255 }),
|
||||
website: varchar('website', { length: 255 }),
|
||||
phone: varchar('phone', { length: 20 }),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Posts Table
|
||||
*
|
||||
* Blog posts created by users.
|
||||
*/
|
||||
export const posts = pgTable(
|
||||
'posts',
|
||||
{
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
title: varchar('title', { length: 255 }).notNull(),
|
||||
slug: varchar('slug', { length: 255 }).notNull().unique(),
|
||||
content: text('content').notNull(),
|
||||
excerpt: text('excerpt'),
|
||||
published: boolean('published').default(false),
|
||||
publishedAt: timestamp('published_at'),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userIdIdx: index('posts_user_id_idx').on(table.userId),
|
||||
publishedIdx: index('posts_published_idx').on(table.published),
|
||||
slugIdx: index('posts_slug_idx').on(table.slug),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Comments Table
|
||||
*
|
||||
* Comments on blog posts. Supports nested comments via parent_id.
|
||||
*/
|
||||
export const comments = pgTable(
|
||||
'comments',
|
||||
{
|
||||
id: serial('id').primaryKey(),
|
||||
postId: integer('post_id')
|
||||
.notNull()
|
||||
.references(() => posts.id, { onDelete: 'cascade' }),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
parentId: integer('parent_id').references(() => comments.id, {
|
||||
onDelete: 'cascade',
|
||||
}),
|
||||
content: text('content').notNull(),
|
||||
approved: boolean('approved').default(false),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
postIdIdx: index('comments_post_id_idx').on(table.postId),
|
||||
userIdIdx: index('comments_user_id_idx').on(table.userId),
|
||||
parentIdIdx: index('comments_parent_id_idx').on(table.parentId),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Tags Table
|
||||
*
|
||||
* Tags for categorizing posts.
|
||||
*/
|
||||
export const tags = pgTable('tags', {
|
||||
id: serial('id').primaryKey(),
|
||||
name: varchar('name', { length: 100 }).notNull().unique(),
|
||||
slug: varchar('slug', { length: 100 }).notNull().unique(),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
});
|
||||
|
||||
/**
|
||||
* PostTags Junction Table
|
||||
*
|
||||
* Many-to-many relationship between posts and tags.
|
||||
*/
|
||||
export const postTags = pgTable(
|
||||
'post_tags',
|
||||
{
|
||||
postId: integer('post_id')
|
||||
.notNull()
|
||||
.references(() => posts.id, { onDelete: 'cascade' }),
|
||||
tagId: integer('tag_id')
|
||||
.notNull()
|
||||
.references(() => tags.id, { onDelete: 'cascade' }),
|
||||
},
|
||||
(table) => ({
|
||||
pk: { name: 'post_tags_pk', columns: [table.postId, table.tagId] },
|
||||
postIdIdx: index('post_tags_post_id_idx').on(table.postId),
|
||||
tagIdIdx: index('post_tags_tag_id_idx').on(table.tagId),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Settings Table
|
||||
*
|
||||
* Application-wide or user-specific settings stored as JSON.
|
||||
*/
|
||||
export const settings = pgTable('settings', {
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id').references(() => users.id, {
|
||||
onDelete: 'cascade',
|
||||
}), // null = global settings
|
||||
key: varchar('key', { length: 255 }).notNull(),
|
||||
value: json('value'),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Relations (optional but recommended for better type safety)
|
||||
// ============================================================================
|
||||
|
||||
export const usersRelations = relations(users, ({ many, one }) => ({
|
||||
profile: one(profiles),
|
||||
posts: many(posts),
|
||||
comments: many(comments),
|
||||
}));
|
||||
|
||||
export const profilesRelations = relations(profiles, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [profiles.userId],
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const postsRelations = relations(posts, ({ one, many }) => ({
|
||||
author: one(users, {
|
||||
fields: [posts.userId],
|
||||
references: [users.id],
|
||||
}),
|
||||
comments: many(comments),
|
||||
tags: many(postTags),
|
||||
}));
|
||||
|
||||
export const commentsRelations = relations(comments, ({ one, many }) => ({
|
||||
post: one(posts, {
|
||||
fields: [comments.postId],
|
||||
references: [posts.id],
|
||||
}),
|
||||
author: one(users, {
|
||||
fields: [comments.userId],
|
||||
references: [users.id],
|
||||
}),
|
||||
parent: one(comments, {
|
||||
fields: [comments.parentId],
|
||||
references: [comments.id],
|
||||
}),
|
||||
replies: many(comments),
|
||||
}));
|
||||
|
||||
export const tagsRelations = relations(tags, ({ many }) => ({
|
||||
posts: many(postTags),
|
||||
}));
|
||||
|
||||
export const postTagsRelations = relations(postTags, ({ one }) => ({
|
||||
post: one(posts, {
|
||||
fields: [postTags.postId],
|
||||
references: [posts.id],
|
||||
}),
|
||||
tag: one(tags, {
|
||||
fields: [postTags.tagId],
|
||||
references: [tags.id],
|
||||
}),
|
||||
}));
|
||||
Reference in New Issue
Block a user