@lunarhue/expo-wa-sqlite 0.0.1
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.
- package/.claude/settings.local.json +9 -0
- package/.lovely-docs.yaml +11 -0
- package/LICENSE +21 -0
- package/demo/next.config.mjs +17 -0
- package/demo/package.json +26 -0
- package/demo/scripts/copy-wasm.mjs +14 -0
- package/demo/src/app/layout.tsx +16 -0
- package/demo/src/app/page.tsx +16 -0
- package/demo/src/app/todos.tsx +118 -0
- package/demo/src/db/schema.ts +7 -0
- package/demo/tsconfig.json +18 -0
- package/lovely-docs/drizzle-orm/arktype.md +113 -0
- package/lovely-docs/drizzle-orm/batch-api.md +35 -0
- package/lovely-docs/drizzle-orm/cache.md +145 -0
- package/lovely-docs/drizzle-orm/check-migrations.md +52 -0
- package/lovely-docs/drizzle-orm/column_types/mysql-column-types.md +76 -0
- package/lovely-docs/drizzle-orm/column_types/postgresql_column_types.md +314 -0
- package/lovely-docs/drizzle-orm/column_types/singlestore-column-types.md +171 -0
- package/lovely-docs/drizzle-orm/column_types/sqlite_column_types.md +132 -0
- package/lovely-docs/drizzle-orm/column_types.md +76 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/case-insensitive-unique-email.md +113 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/conditional-filters-in-query.md +69 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/count-rows.md +76 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/cursor-based-pagination.md +142 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/d1_http_api_configuration.md +49 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/decrementing-a-value.md +36 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/empty-array-default-value.md +43 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/full-text-search-with-generated-columns.md +73 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/gel-auth-extension.md +89 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/include-or-exclude-columns.md +51 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/incrementing-a-value.md +36 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/limit-offset-pagination.md +104 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/mysql-local-setup.md +55 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/point-datatype-psql.md +79 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgis-geometry-point.md +115 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgresql-full-text-search.md +150 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/postgresql-local-setup.md +55 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/seeding-with-option.md +69 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/seeding-with-partially-exposed-schema.md +60 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/select-parent-rows-with-at-least-one-related-child-row.md +74 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/timestamp-default-value.md +93 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/toggling-a-boolean-field.md +20 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/update-many-with-different-values.md +50 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/upsert.md +169 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes/vector-similarity-search.md +79 -0
- package/lovely-docs/drizzle-orm/common_patterns_&_recipes.md +787 -0
- package/lovely-docs/drizzle-orm/connect-aws-data-api-pg.md +47 -0
- package/lovely-docs/drizzle-orm/connect-bun-sql.md +35 -0
- package/lovely-docs/drizzle-orm/connect-bun-sqlite.md +42 -0
- package/lovely-docs/drizzle-orm/connect-cloudflare-d1.md +61 -0
- package/lovely-docs/drizzle-orm/connect-cloudflare-do.md +86 -0
- package/lovely-docs/drizzle-orm/connect-neon.md +72 -0
- package/lovely-docs/drizzle-orm/connect-nile.md +84 -0
- package/lovely-docs/drizzle-orm/connect-pglite.md +39 -0
- package/lovely-docs/drizzle-orm/connect-planetscale.md +37 -0
- package/lovely-docs/drizzle-orm/connect-prisma-postgres.md +48 -0
- package/lovely-docs/drizzle-orm/connect-sqlite-cloud.md +29 -0
- package/lovely-docs/drizzle-orm/connect-supabase.md +45 -0
- package/lovely-docs/drizzle-orm/connect-tidb-serverless.md +35 -0
- package/lovely-docs/drizzle-orm/connect-turso-database.md +28 -0
- package/lovely-docs/drizzle-orm/connect-turso.md +60 -0
- package/lovely-docs/drizzle-orm/connect-vercel-postgres.md +37 -0
- package/lovely-docs/drizzle-orm/connect-xata.md +29 -0
- package/lovely-docs/drizzle-orm/custom-migrations.md +40 -0
- package/lovely-docs/drizzle-orm/custom-types.md +137 -0
- package/lovely-docs/drizzle-orm/database-connection-overview.md +89 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sql-existing.md +59 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sqlite-existing.md +48 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/bun-sqlite-new.md +34 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/d1-new.md +74 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/expo-sqlite-setup.md +169 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/gel-existing-project.md +81 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/get-started-bun-sql.md +29 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/getting_started_with_gel.md +85 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/mysql-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/mysql-setup.md +43 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/neon-existing-project.md +39 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/neon-setup.md +45 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/nile-existing-project.md +66 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/nile-setup.md +59 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/op-sqlite_setup_guide.md +135 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/pglite-existing-project.md +67 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/pglite-setup.md +33 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/planetscale-existing-project.md +70 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/planetscale-setup.md +46 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/postgresql-existing-project.md +57 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/postgresql-setup.md +44 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/singlestore-existing-project.md +22 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/singlestore-setup.md +37 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-cloud-existing-project.md +52 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-cloud-setup.md +53 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-durable-objects-setup.md +163 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-existing-project.md +36 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/sqlite-new.md +28 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/supabase-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/tidb-existing-project.md +25 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/tidb-serverless-setup.md +41 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-database-existing.md +61 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-database-setup.md +60 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-existing.md +74 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/turso-new.md +78 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/vercel-postgres-existing-project.md +71 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/vercel-postgres-setup.md +46 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/xata-existing-project.md +32 -0
- package/lovely-docs/drizzle-orm/database_setup_guides/xata-new.md +30 -0
- package/lovely-docs/drizzle-orm/database_setup_guides.md +144 -0
- package/lovely-docs/drizzle-orm/delete.md +57 -0
- package/lovely-docs/drizzle-orm/drizzle-config-file.md +252 -0
- package/lovely-docs/drizzle-orm/drizzle-kit-up.md +43 -0
- package/lovely-docs/drizzle-orm/dynamic-query-building.md +68 -0
- package/lovely-docs/drizzle-orm/eslint-plugin.md +76 -0
- package/lovely-docs/drizzle-orm/expo-sqlite.md +101 -0
- package/lovely-docs/drizzle-orm/export.md +88 -0
- package/lovely-docs/drizzle-orm/faq.md +28 -0
- package/lovely-docs/drizzle-orm/filter-and-conditional-operators.md +169 -0
- package/lovely-docs/drizzle-orm/gel-setup.md +37 -0
- package/lovely-docs/drizzle-orm/generate.md +119 -0
- package/lovely-docs/drizzle-orm/generated-columns.md +128 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/drizzle-with-turso.md +159 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/drizzle_with_nile_database.md +195 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/neon_postgres_integration.md +157 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/supabase_integration.md +150 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/vercel-postgres-setup.md +152 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations/xata_integration.md +143 -0
- package/lovely-docs/drizzle-orm/getting_started/database_integrations.md +117 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/drizzle_with_vercel_edge_functions.md +220 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/netlify_edge_functions_with_neon_postgres.md +120 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/netlify_edge_functions_with_supabase.md +94 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration/supabase_edge_functions_integration.md +116 -0
- package/lovely-docs/drizzle-orm/getting_started/edge_functions_integration.md +63 -0
- package/lovely-docs/drizzle-orm/getting_started/todo_app_with_neon_postgres.md +323 -0
- package/lovely-docs/drizzle-orm/getting_started.md +443 -0
- package/lovely-docs/drizzle-orm/graphql.md +107 -0
- package/lovely-docs/drizzle-orm/http-proxy-driver.md +138 -0
- package/lovely-docs/drizzle-orm/indexes-constraints.md +135 -0
- package/lovely-docs/drizzle-orm/insert.mdx.md +118 -0
- package/lovely-docs/drizzle-orm/joins.md +145 -0
- package/lovely-docs/drizzle-orm/kit-overview.md +81 -0
- package/lovely-docs/drizzle-orm/migrate.md +54 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate-from-sequelize.md +335 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate-from-typeorm.md +317 -0
- package/lovely-docs/drizzle-orm/migration_guides/migrate_from_prisma_to_drizzle.md +258 -0
- package/lovely-docs/drizzle-orm/migration_guides.md +201 -0
- package/lovely-docs/drizzle-orm/migrations.md +50 -0
- package/lovely-docs/drizzle-orm/mysql-setup.md +51 -0
- package/lovely-docs/drizzle-orm/op-sqlite-setup.md +80 -0
- package/lovely-docs/drizzle-orm/overview.md +69 -0
- package/lovely-docs/drizzle-orm/postgresql-setup.md +71 -0
- package/lovely-docs/drizzle-orm/postgresql_extensions.md +93 -0
- package/lovely-docs/drizzle-orm/prepared-statements.md +77 -0
- package/lovely-docs/drizzle-orm/prisma-extension.md +46 -0
- package/lovely-docs/drizzle-orm/pull.md +134 -0
- package/lovely-docs/drizzle-orm/push.md +129 -0
- package/lovely-docs/drizzle-orm/queries-and-crud.md +72 -0
- package/lovely-docs/drizzle-orm/quick-start.md +63 -0
- package/lovely-docs/drizzle-orm/react-native-sqlite-setup.md +1 -0
- package/lovely-docs/drizzle-orm/read-replicas.md +66 -0
- package/lovely-docs/drizzle-orm/relational-queries.md +271 -0
- package/lovely-docs/drizzle-orm/relations.md +194 -0
- package/lovely-docs/drizzle-orm/release_notes/live-queries.md +27 -0
- package/lovely-docs/drizzle-orm/release_notes/pglite_driver_support.md +14 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.11.0_release.md +139 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.16.2_release_notes.md +86 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.23.2_release.md +5 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.27.2_-_unique_constraints_support.md +66 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.0_release_notes.md +80 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.1_release.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.2_release_notes.md +18 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.3_release_notes.md +48 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.4_release.md +8 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.5_release_notes.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.28.6_release_notes.md +54 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.0_release_notes.md +143 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.1_release_notes.md +72 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.2_release_notes.md +95 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.3_release.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.4_release_notes.md +40 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.29.5_release_notes.md +69 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.0_release_notes.md +31 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.10_release.md +18 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.1_release_notes.md +16 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.2_release_notes.md +7 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.3_release_notes.md +8 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.5_release_notes.md +20 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.7_release.md +5 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.8_release_notes.md +36 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.30.9_release.md +29 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.0_release_notes.md +186 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.2_tidb_cloud_serverless_support.md +16 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.3_release.md +19 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.31.4_release.md +1 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.0_release_notes.md +136 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.1_release_notes.md +15 -0
- package/lovely-docs/drizzle-orm/release_notes/v0.32.2_release.md +13 -0
- package/lovely-docs/drizzle-orm/release_notes/xata-http-driver-support.md +27 -0
- package/lovely-docs/drizzle-orm/release_notes.md +25 -0
- package/lovely-docs/drizzle-orm/rls.mdx.md +385 -0
- package/lovely-docs/drizzle-orm/schema-declaration.md +239 -0
- package/lovely-docs/drizzle-orm/schemas.md +63 -0
- package/lovely-docs/drizzle-orm/seed-generators.md +220 -0
- package/lovely-docs/drizzle-orm/seed-limitations.md +3 -0
- package/lovely-docs/drizzle-orm/seed-overview.md +155 -0
- package/lovely-docs/drizzle-orm/seed-versioning.md +85 -0
- package/lovely-docs/drizzle-orm/select.md +411 -0
- package/lovely-docs/drizzle-orm/sequences.md +62 -0
- package/lovely-docs/drizzle-orm/serverless-performance.md +21 -0
- package/lovely-docs/drizzle-orm/set-operations.md +127 -0
- package/lovely-docs/drizzle-orm/singlestore-setup.md +57 -0
- package/lovely-docs/drizzle-orm/sql-template.md +127 -0
- package/lovely-docs/drizzle-orm/sqlite-setup.md +65 -0
- package/lovely-docs/drizzle-orm/studio.md +47 -0
- package/lovely-docs/drizzle-orm/transactions.md +83 -0
- package/lovely-docs/drizzle-orm/type-helpers-and-utilities.md +160 -0
- package/lovely-docs/drizzle-orm/typebox.md +110 -0
- package/lovely-docs/drizzle-orm/update.md +79 -0
- package/lovely-docs/drizzle-orm/upgrade-to-0.21.0.md +40 -0
- package/lovely-docs/drizzle-orm/valibot.md +115 -0
- package/lovely-docs/drizzle-orm/views.md +135 -0
- package/lovely-docs/drizzle-orm/why-drizzle.md +66 -0
- package/lovely-docs/drizzle-orm/zod.md +113 -0
- package/lovely-docs/drizzle-orm.md +60 -0
- package/package.json +24 -0
- package/src/index.ts +186 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
## Edge Functions Integration
|
|
2
|
+
|
|
3
|
+
Tutorials for integrating Drizzle ORM with serverless edge function platforms.
|
|
4
|
+
|
|
5
|
+
**Netlify Edge Functions:**
|
|
6
|
+
- With Neon: Install drizzle-orm, drizzle-kit, dotenv. Create `import_map.json` with ESM imports. Configure `netlify.toml` with deno_import_map. Define schema in `netlify/edge-functions/common/schema.ts` using pgTable. Create `drizzle.config.ts` with postgresql dialect. Run `npx drizzle-kit push`. Connect via: `const sql = neon(Netlify.env.get("DATABASE_URL")!); const db = drizzle({ client: sql });`. Deploy with `netlify init`, `netlify env:import .env`, `netlify deploy --prod`.
|
|
7
|
+
- With Supabase: Use postgres-js client instead. Connect via: `const queryClient = postgres(Netlify.env.get("DATABASE_URL")!); const db = drizzle({ client: queryClient });`.
|
|
8
|
+
|
|
9
|
+
**Supabase Edge Functions:**
|
|
10
|
+
- Prerequisites: Supabase CLI, Docker Desktop. Schema in `src/schema.ts` using pgTable. Initialize: `supabase init`, `npx drizzle-kit generate`, `supabase start`, `supabase migration up`. Create function: `supabase functions new function-name`. Add `deno.json` with imports. Connect via: `const client = postgres(Deno.env.get("SUPABASE_DB_URL")!); const db = drizzle({ client });`. Use `{ prepare: false }` for Transaction pool mode. Local test: `supabase functions serve --no-verify-jwt`. Deploy: `supabase link --project-ref=<ID>`, `supabase db push`, `supabase secrets set DATABASE_URL=<URL>`, `supabase functions deploy function-name --no-verify-jwt`.
|
|
11
|
+
|
|
12
|
+
**Vercel Edge Functions:**
|
|
13
|
+
Requires edge-compatible drivers (no TCP). Choose driver by database:
|
|
14
|
+
- Neon/Vercel Postgres: @neondatabase/serverless/@vercel/postgres
|
|
15
|
+
- PlanetScale MySQL: @planetscale/database
|
|
16
|
+
- Turso SQLite: @libsql/client
|
|
17
|
+
|
|
18
|
+
Neon/Vercel Postgres: Schema in `src/db/schema.ts` using pgTable. Generate migrations: `npx drizzle-kit generate`, `npx drizzle-kit migrate` (or `push` for prototyping). Connect in `src/db/index.ts`: `import { drizzle } from 'drizzle-orm/neon-serverless'; export const db = drizzle(process.env.POSTGRES_URL!)`. For Vercel Postgres: `import { drizzle } from 'drizzle-orm/vercel-postgres'; export const db = drizzle()`. API route in `src/app/api/route.ts` with `export const runtime = 'edge'` and `export const dynamic = 'force-dynamic'`. Deploy: `vercel`, `vercel env add POSTGRES_URL`, `vercel`.
|
|
19
|
+
|
|
20
|
+
PlanetScale MySQL: Schema using mysqlTable. Connect: `import { drizzle } from "drizzle-orm/planetscale-serverless"; export const db = drizzle(process.env.MYSQL_URL!)`.
|
|
21
|
+
|
|
22
|
+
Turso SQLite: Schema using sqliteTable. Connect: `import { drizzle } from 'drizzle-orm/libsql'; export const db = drizzle({ connection: { url: process.env.TURSO_CONNECTION_URL!, authToken: process.env.TURSO_AUTH_TOKEN! }})`.
|
|
23
|
+
|
|
24
|
+
## Database Integrations
|
|
25
|
+
|
|
26
|
+
Complete setup guides for various database providers.
|
|
27
|
+
|
|
28
|
+
**Neon Postgres (neon-http):**
|
|
29
|
+
Install drizzle-orm, drizzle-kit, @neondatabase/serverless, dotenv. Create `.env` with `DATABASE_URL`. Create `src/db.ts`:
|
|
30
|
+
```typescript
|
|
31
|
+
import { drizzle } from "drizzle-orm/neon-http";
|
|
32
|
+
import { neon } from "@neondatabase/serverless";
|
|
33
|
+
import { config } from "dotenv";
|
|
34
|
+
|
|
35
|
+
config({ path: ".env" });
|
|
36
|
+
const sql = neon(process.env.DATABASE_URL!);
|
|
37
|
+
export const db = drizzle({ client: sql });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Define schema in `src/schema.ts` with pgTable, serial, text, integer, timestamp. Use `.references()` for foreign keys with `onDelete: 'cascade'`. Use `.$onUpdate(() => new Date())` for auto-updating timestamps. Infer types with `$inferInsert` and `$inferSelect`. Create `drizzle.config.ts` with dialect postgresql. Run `npx drizzle-kit generate` then `npx drizzle-kit migrate`, or `npx drizzle-kit push` for prototyping.
|
|
41
|
+
|
|
42
|
+
Query examples: `db.insert(table).values(data)`, `db.select().from(table).where(eq(table.id, id))`, `db.update(table).set(data).where(eq(...))`, `db.delete(table).where(eq(...))`. Use `leftJoin()`, `groupBy()`, `count()`, `getTableColumns()` for aggregations and joins. Use `between()` and `sql` for date filtering.
|
|
43
|
+
|
|
44
|
+
**Nile Database (Multi-tenant):**
|
|
45
|
+
Install drizzle-orm, drizzle-kit, dotenv, node-postgres, express. Get connection string from Nile dashboard, add to `.env` as `NILEDB_URL`.
|
|
46
|
+
|
|
47
|
+
Create `src/db/db.ts`:
|
|
48
|
+
```typescript
|
|
49
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
50
|
+
import { sql } from "drizzle-orm";
|
|
51
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
52
|
+
|
|
53
|
+
export const db = drizzle(process.env.NILEDB_URL);
|
|
54
|
+
export const tenantContext = new AsyncLocalStorage<string | undefined>();
|
|
55
|
+
|
|
56
|
+
export function tenantDB<T>(cb: (tx: any) => T | Promise<T>): Promise<T> {
|
|
57
|
+
return db.transaction(async (tx) => {
|
|
58
|
+
const tenantId = tenantContext.getStore();
|
|
59
|
+
if (tenantId) {
|
|
60
|
+
await tx.execute(sql`set local nile.tenant_id = '${sql.raw(tenantId)}'`);
|
|
61
|
+
}
|
|
62
|
+
return cb(tx);
|
|
63
|
+
}) as Promise<T>;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Run `npx drizzle-kit pull` to introspect Nile's built-in tenants table. In Express, use middleware to extract tenant ID from URL and store in tenantContext: `app.use('/api/tenants/:tenantId/*', (req, res, next) => { tenantContext.run(req.params.tenantId, next); })`. Wrap all queries with `tenantDB()` to automatically set nile.tenant_id in transaction context—no explicit WHERE tenant_id needed. Nile handles data isolation at database level.
|
|
68
|
+
|
|
69
|
+
**Supabase (postgres-js):**
|
|
70
|
+
Install drizzle-orm, drizzle-kit, dotenv, postgres. Create Supabase project, get connection string with pooling enabled, add to `.env` as `DATABASE_URL`.
|
|
71
|
+
|
|
72
|
+
Create `src/db/index.ts`:
|
|
73
|
+
```typescript
|
|
74
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
75
|
+
import postgres from 'postgres';
|
|
76
|
+
import { config } from 'dotenv';
|
|
77
|
+
|
|
78
|
+
config({ path: '.env' });
|
|
79
|
+
const client = postgres(process.env.DATABASE_URL!);
|
|
80
|
+
export const db = drizzle({ client });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Schema and migrations same as Neon. Create `drizzle.config.ts` with dialect postgresql. Generate migrations with `npx drizzle-kit generate`, run with `npx drizzle-kit migrate`, or use Supabase CLI: `supabase init`, `supabase link`, `supabase db push`. Queries identical to Neon examples.
|
|
84
|
+
|
|
85
|
+
**Turso (SQLite via libSQL):**
|
|
86
|
+
Install drizzle-orm, drizzle-kit, dotenv, @libsql/client. Signup with `turso auth signup`, create database with `turso db create drizzle-turso-db`, create token with `turso db tokens create drizzle-turso-db`. Add to `.env`:
|
|
87
|
+
```
|
|
88
|
+
TURSO_CONNECTION_URL=
|
|
89
|
+
TURSO_AUTH_TOKEN=
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Create `src/db/index.ts`:
|
|
93
|
+
```typescript
|
|
94
|
+
import { drizzle } from 'drizzle-orm/libsql';
|
|
95
|
+
import { config } from 'dotenv';
|
|
96
|
+
|
|
97
|
+
config({ path: '.env' });
|
|
98
|
+
export const db = drizzle({ connection: {
|
|
99
|
+
url: process.env.TURSO_CONNECTION_URL!,
|
|
100
|
+
authToken: process.env.TURSO_AUTH_TOKEN!,
|
|
101
|
+
}});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Define schema with sqliteTable instead of pgTable. Use `integer('id').primaryKey()` instead of serial(). Use `text('created_at').default(sql`(CURRENT_TIMESTAMP)`)` for timestamps. Use `integer('updated_at', { mode: 'timestamp' }).$onUpdate(() => new Date())` for auto-update. Create `drizzle.config.ts` with dialect turso. For date filtering, use `gt(postsTable.createdAt, sql`(datetime('now','-24 hour'))`)` instead of between().
|
|
105
|
+
|
|
106
|
+
**Vercel Postgres:**
|
|
107
|
+
Install drizzle-orm, drizzle-kit, dotenv, @vercel/postgres. Add `POSTGRES_URL` to `.env.local` from Vercel dashboard.
|
|
108
|
+
|
|
109
|
+
Create `src/db/index.ts`:
|
|
110
|
+
```typescript
|
|
111
|
+
import { drizzle } from 'drizzle-orm/vercel-postgres';
|
|
112
|
+
import { config } from 'dotenv';
|
|
113
|
+
|
|
114
|
+
config({ path: '.env.local' });
|
|
115
|
+
export const db = drizzle();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Schema and migrations same as Neon/Supabase. Queries identical.
|
|
119
|
+
|
|
120
|
+
**Xata (PostgreSQL):**
|
|
121
|
+
Install drizzle-orm, drizzle-kit, dotenv, postgres. Create Xata database, get PostgreSQL connection string from dashboard, add to `.env` as `DATABASE_URL`.
|
|
122
|
+
|
|
123
|
+
Create `src/db/index.ts`:
|
|
124
|
+
```typescript
|
|
125
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
126
|
+
import postgres from 'postgres';
|
|
127
|
+
import { config } from 'dotenv';
|
|
128
|
+
|
|
129
|
+
config({ path: '.env' });
|
|
130
|
+
const client = postgres(process.env.DATABASE_URL!);
|
|
131
|
+
export const db = drizzle({ client });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Schema and migrations same as Supabase. Xata features: branch-based development for isolated environments, zero-downtime schema changes, data anonymization, AI-powered performance monitoring.
|
|
135
|
+
|
|
136
|
+
## Todo App with Neon Postgres
|
|
137
|
+
|
|
138
|
+
Complete Next.js todo app tutorial.
|
|
139
|
+
|
|
140
|
+
Install dependencies: drizzle-orm, drizzle-kit, @neondatabase/serverless, dotenv. Create `.env.local` with Neon connection string.
|
|
141
|
+
|
|
142
|
+
Create `src/db/drizzle.ts`:
|
|
143
|
+
```typescript
|
|
144
|
+
import { config } from "dotenv";
|
|
145
|
+
import { drizzle } from 'drizzle-orm/neon-http';
|
|
146
|
+
|
|
147
|
+
config({ path: ".env" });
|
|
148
|
+
export const db = drizzle(process.env.DATABASE_URL!);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Create `src/db/schema.ts`:
|
|
152
|
+
```typescript
|
|
153
|
+
import { integer, text, boolean, pgTable } from "drizzle-orm/pg-core";
|
|
154
|
+
|
|
155
|
+
export const todo = pgTable("todo", {
|
|
156
|
+
id: integer("id").primaryKey(),
|
|
157
|
+
text: text("text").notNull(),
|
|
158
|
+
done: boolean("done").default(false).notNull(),
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Create `drizzle.config.ts`:
|
|
163
|
+
```typescript
|
|
164
|
+
import { config } from 'dotenv';
|
|
165
|
+
import { defineConfig } from "drizzle-kit";
|
|
166
|
+
|
|
167
|
+
config({ path: '.env' });
|
|
168
|
+
|
|
169
|
+
export default defineConfig({
|
|
170
|
+
schema: "./src/db/schema.ts",
|
|
171
|
+
out: "./migrations",
|
|
172
|
+
dialect: "postgresql",
|
|
173
|
+
dbCredentials: {
|
|
174
|
+
url: process.env.DATABASE_URL!,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Generate and run migrations: `npx drizzle-kit generate` then `npx drizzle-kit migrate`, or `npx drizzle-kit push`.
|
|
180
|
+
|
|
181
|
+
Create `src/actions/todoAction.ts` with "use server" directive:
|
|
182
|
+
```typescript
|
|
183
|
+
"use server";
|
|
184
|
+
import { eq, not } from "drizzle-orm";
|
|
185
|
+
import { revalidatePath } from "next/cache";
|
|
186
|
+
import { db } from "@/db/drizzle";
|
|
187
|
+
import { todo } from "@/db/schema";
|
|
188
|
+
|
|
189
|
+
export const getData = async () => {
|
|
190
|
+
const data = await db.select().from(todo);
|
|
191
|
+
return data;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const addTodo = async (id: number, text: string) => {
|
|
195
|
+
await db.insert(todo).values({ id, text });
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const deleteTodo = async (id: number) => {
|
|
199
|
+
await db.delete(todo).where(eq(todo.id, id));
|
|
200
|
+
revalidatePath("/");
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const toggleTodo = async (id: number) => {
|
|
204
|
+
await db.update(todo).set({ done: not(todo.done) }).where(eq(todo.id, id));
|
|
205
|
+
revalidatePath("/");
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const editTodo = async (id: number, text: string) => {
|
|
209
|
+
await db.update(todo).set({ text }).where(eq(todo.id, id));
|
|
210
|
+
revalidatePath("/");
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Create `src/types/todoType.ts`:
|
|
215
|
+
```typescript
|
|
216
|
+
export type todoType = {
|
|
217
|
+
id: number;
|
|
218
|
+
text: string;
|
|
219
|
+
done: boolean;
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Create `src/components/todo.tsx` (client component with edit/delete/toggle functionality):
|
|
224
|
+
```typescript
|
|
225
|
+
"use client";
|
|
226
|
+
import { ChangeEvent, FC, useState } from "react";
|
|
227
|
+
import { todoType } from "@/types/todoType";
|
|
228
|
+
|
|
229
|
+
interface Props {
|
|
230
|
+
todo: todoType;
|
|
231
|
+
changeTodoText: (id: number, text: string) => void;
|
|
232
|
+
toggleIsTodoDone: (id: number, done: boolean) => void;
|
|
233
|
+
deleteTodoItem: (id: number) => void;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const Todo: FC<Props> = ({
|
|
237
|
+
todo,
|
|
238
|
+
changeTodoText,
|
|
239
|
+
toggleIsTodoDone,
|
|
240
|
+
deleteTodoItem,
|
|
241
|
+
}) => {
|
|
242
|
+
const [editing, setEditing] = useState(false);
|
|
243
|
+
const [text, setText] = useState(todo.text);
|
|
244
|
+
const [isDone, setIsDone] = useState(todo.done);
|
|
245
|
+
|
|
246
|
+
const handleTextChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
247
|
+
setText(e.target.value);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const handleIsDone = async () => {
|
|
251
|
+
toggleIsTodoDone(todo.id, !isDone);
|
|
252
|
+
setIsDone((prev) => !prev);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const handleEdit = () => setEditing(true);
|
|
256
|
+
|
|
257
|
+
const handleSave = async () => {
|
|
258
|
+
changeTodoText(todo.id, text);
|
|
259
|
+
setEditing(false);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const handleCancel = () => {
|
|
263
|
+
setEditing(false);
|
|
264
|
+
setText(todo.text);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const handleDelete = () => {
|
|
268
|
+
if (confirm("Are you sure you want to delete this todo?")) {
|
|
269
|
+
deleteTodoItem(todo.id);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<div className="flex items-center gap-2 p-4 border-gray-200 border-solid border rounded-lg">
|
|
275
|
+
<input
|
|
276
|
+
type="checkbox"
|
|
277
|
+
className="text-blue-200 rounded-sm h-4 w-4"
|
|
278
|
+
checked={isDone}
|
|
279
|
+
onChange={handleIsDone}
|
|
280
|
+
/>
|
|
281
|
+
<input
|
|
282
|
+
type="text"
|
|
283
|
+
value={text}
|
|
284
|
+
onChange={handleTextChange}
|
|
285
|
+
readOnly={!editing}
|
|
286
|
+
className={`${
|
|
287
|
+
todo.done ? "line-through" : ""
|
|
288
|
+
} outline-none read-only:border-transparent focus:border border-gray-200 rounded px-2 py-1 w-full`}
|
|
289
|
+
/>
|
|
290
|
+
<div className="flex gap-1 ml-auto">
|
|
291
|
+
{editing ? (
|
|
292
|
+
<button
|
|
293
|
+
onClick={handleSave}
|
|
294
|
+
className="bg-green-600 text-green-50 rounded px-2 w-14 py-1"
|
|
295
|
+
>
|
|
296
|
+
Save
|
|
297
|
+
</button>
|
|
298
|
+
) : (
|
|
299
|
+
<button
|
|
300
|
+
onClick={handleEdit}
|
|
301
|
+
className="bg-blue-400 text-blue-50 rounded w-14 px-2 py-1"
|
|
302
|
+
>
|
|
303
|
+
Edit
|
|
304
|
+
</button>
|
|
305
|
+
)}
|
|
306
|
+
{editing ? (
|
|
307
|
+
<button
|
|
308
|
+
onClick={handleCancel}
|
|
309
|
+
className="bg-red-400 w-16 text-red-50 rounded px-2 py-1"
|
|
310
|
+
>
|
|
311
|
+
Close
|
|
312
|
+
</button>
|
|
313
|
+
) : (
|
|
314
|
+
<button
|
|
315
|
+
onClick={handleDelete}
|
|
316
|
+
className="bg-red-400 w-16 text-red-50 rounded px-2 py-1"
|
|
317
|
+
>
|
|
318
|
+
Delete
|
|
319
|
+
</button>
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export default Todo;
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Create `src/components/addTodo.tsx`:
|
|
330
|
+
```typescript
|
|
331
|
+
"use client";
|
|
332
|
+
import { ChangeEvent, FC, useState } from "react";
|
|
333
|
+
|
|
334
|
+
interface Props {
|
|
335
|
+
createTodo: (value: string) => void;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const AddTodo: FC<Props> = ({ createTodo }) => {
|
|
339
|
+
const [input, setInput] = useState("");
|
|
340
|
+
|
|
341
|
+
const handleInput = (e: ChangeEvent<HTMLInputElement>) => {
|
|
342
|
+
setInput(e.target.value);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const handleAdd = async () => {
|
|
346
|
+
createTodo(input);
|
|
347
|
+
setInput("");
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<div className="w-full flex gap-1 mt-2">
|
|
352
|
+
<input
|
|
353
|
+
type="text"
|
|
354
|
+
className="w-full px-2 py-1 border border-gray-200 rounded outline-none"
|
|
355
|
+
onChange={handleInput}
|
|
356
|
+
value={input}
|
|
357
|
+
/>
|
|
358
|
+
<button
|
|
359
|
+
className="flex items-center justify-center bg-green-600 text-green-50 rounded px-2 h-9 w-14 py-1"
|
|
360
|
+
onClick={handleAdd}
|
|
361
|
+
>
|
|
362
|
+
Add
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export default AddTodo;
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Create `src/components/todos.tsx`:
|
|
372
|
+
```typescript
|
|
373
|
+
"use client";
|
|
374
|
+
import { FC, useState } from "react";
|
|
375
|
+
import { todoType } from "@/types/todoType";
|
|
376
|
+
import Todo from "./todo";
|
|
377
|
+
import AddTodo from "./addTodo";
|
|
378
|
+
import { addTodo, deleteTodo, editTodo, toggleTodo } from "@/actions/todoAction";
|
|
379
|
+
|
|
380
|
+
interface Props {
|
|
381
|
+
todos: todoType[];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const Todos: FC<Props> = ({ todos }) => {
|
|
385
|
+
const [todoItems, setTodoItems] = useState<todoType[]>(todos);
|
|
386
|
+
|
|
387
|
+
const createTodo = (text: string) => {
|
|
388
|
+
const id = (todoItems.at(-1)?.id || 0) + 1;
|
|
389
|
+
addTodo(id, text);
|
|
390
|
+
setTodoItems((prev) => [...prev, { id, text, done: false }]);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const changeTodoText = (id: number, text: string) => {
|
|
394
|
+
setTodoItems((prev) =>
|
|
395
|
+
prev.map((todo) => (todo.id === id ? { ...todo, text } : todo))
|
|
396
|
+
);
|
|
397
|
+
editTodo(id, text);
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const toggleIsTodoDone = (id: number) => {
|
|
401
|
+
setTodoItems((prev) =>
|
|
402
|
+
prev.map((todo) => (todo.id === id ? { ...todo, done: !todo.done } : todo))
|
|
403
|
+
);
|
|
404
|
+
toggleTodo(id);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const deleteTodoItem = (id: number) => {
|
|
408
|
+
setTodoItems((prev) => prev.filter((todo) => todo.id !== id));
|
|
409
|
+
deleteTodo(id);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<main className="flex mx-auto max-w-xl w-full min-h-screen flex-col items-center p-16">
|
|
414
|
+
<div className="text-5xl font-medium">To-do app</div>
|
|
415
|
+
<div className="w-full flex flex-col mt-8 gap-2">
|
|
416
|
+
{todoItems.map((todo) => (
|
|
417
|
+
<Todo
|
|
418
|
+
key={todo.id}
|
|
419
|
+
todo={todo}
|
|
420
|
+
changeTodoText={changeTodoText}
|
|
421
|
+
toggleIsTodoDone={toggleIsTodoDone}
|
|
422
|
+
deleteTodoItem={deleteTodoItem}
|
|
423
|
+
/>
|
|
424
|
+
))}
|
|
425
|
+
</div>
|
|
426
|
+
<AddTodo createTodo={createTodo} />
|
|
427
|
+
</main>
|
|
428
|
+
);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
export default Todos;
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Update `src/app/page.tsx`:
|
|
435
|
+
```typescript
|
|
436
|
+
import { getData } from "@/actions/todoAction";
|
|
437
|
+
import Todos from "@/components/todos";
|
|
438
|
+
|
|
439
|
+
export default async function Home() {
|
|
440
|
+
const data = await getData();
|
|
441
|
+
return <Todos todos={data} />;
|
|
442
|
+
}
|
|
443
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
## drizzle-graphql
|
|
2
|
+
|
|
3
|
+
Generate a GraphQL server from a Drizzle schema with `buildSchema()`.
|
|
4
|
+
|
|
5
|
+
### Quick Start
|
|
6
|
+
|
|
7
|
+
Install: `drizzle-graphql @apollo/server graphql` or `drizzle-graphql graphql-yoga graphql`
|
|
8
|
+
|
|
9
|
+
**Apollo Server:**
|
|
10
|
+
```ts
|
|
11
|
+
import { buildSchema } from 'drizzle-graphql';
|
|
12
|
+
import { drizzle } from 'drizzle-orm/...';
|
|
13
|
+
import { ApolloServer } from '@apollo/server';
|
|
14
|
+
import { startStandaloneServer } from '@apollo/server/standalone';
|
|
15
|
+
import * as dbSchema from './schema';
|
|
16
|
+
|
|
17
|
+
const db = drizzle({ client, schema: dbSchema });
|
|
18
|
+
const { schema } = buildSchema(db);
|
|
19
|
+
const server = new ApolloServer({ schema });
|
|
20
|
+
const { url } = await startStandaloneServer(server);
|
|
21
|
+
console.log(`🚀 Server ready at ${url}`);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**GraphQL Yoga:**
|
|
25
|
+
```ts
|
|
26
|
+
import { buildSchema } from 'drizzle-graphql';
|
|
27
|
+
import { drizzle } from 'drizzle-orm/...';
|
|
28
|
+
import { createYoga } from 'graphql-yoga';
|
|
29
|
+
import { createServer } from 'node:http';
|
|
30
|
+
import * as dbSchema from './schema';
|
|
31
|
+
|
|
32
|
+
const db = drizzle({ schema: dbSchema });
|
|
33
|
+
const { schema } = buildSchema(db);
|
|
34
|
+
const yoga = createYoga({ schema });
|
|
35
|
+
const server = createServer(yoga);
|
|
36
|
+
server.listen(4000, () => console.info('Server is running on http://localhost:4000/graphql'));
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Schema example:**
|
|
40
|
+
```ts
|
|
41
|
+
import { integer, serial, text, pgTable } from 'drizzle-orm/pg-core';
|
|
42
|
+
import { relations } from 'drizzle-orm';
|
|
43
|
+
|
|
44
|
+
export const users = pgTable('users', {
|
|
45
|
+
id: serial('id').primaryKey(),
|
|
46
|
+
name: text('name').notNull(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
50
|
+
posts: many(posts),
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
export const posts = pgTable('posts', {
|
|
54
|
+
id: serial('id').primaryKey(),
|
|
55
|
+
content: text('content').notNull(),
|
|
56
|
+
authorId: integer('author_id').notNull(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const postsRelations = relations(posts, ({ one }) => ({
|
|
60
|
+
author: one(users, { fields: [posts.authorId], references: [users.id] }),
|
|
61
|
+
}));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Customizing Schema
|
|
65
|
+
|
|
66
|
+
`buildSchema()` returns `{ schema, entities }`. Use `entities` to build a custom schema with selected queries/mutations and custom resolvers:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { buildSchema } from 'drizzle-graphql';
|
|
70
|
+
import { GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema } from 'graphql';
|
|
71
|
+
import { createYoga } from 'graphql-yoga';
|
|
72
|
+
import { createServer } from 'node:http';
|
|
73
|
+
|
|
74
|
+
const db = drizzle({ schema: dbSchema });
|
|
75
|
+
const { entities } = buildSchema(db);
|
|
76
|
+
|
|
77
|
+
const schema = new GraphQLSchema({
|
|
78
|
+
query: new GraphQLObjectType({
|
|
79
|
+
name: 'Query',
|
|
80
|
+
fields: {
|
|
81
|
+
users: entities.queries.users,
|
|
82
|
+
customer: entities.queries.customersSingle,
|
|
83
|
+
customUsers: {
|
|
84
|
+
type: new GraphQLList(new GraphQLNonNull(entities.types.UsersItem)),
|
|
85
|
+
args: {
|
|
86
|
+
where: { type: entities.inputs.UsersFilters }
|
|
87
|
+
},
|
|
88
|
+
resolve: async (source, args, context, info) => {
|
|
89
|
+
// Custom logic
|
|
90
|
+
return await db.select(schema.users).where()...
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
mutation: new GraphQLObjectType({
|
|
96
|
+
name: 'Mutation',
|
|
97
|
+
fields: entities.mutations,
|
|
98
|
+
}),
|
|
99
|
+
types: [...Object.values(entities.types), ...Object.values(entities.inputs)],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const yoga = createYoga({ schema });
|
|
103
|
+
const server = createServer(yoga);
|
|
104
|
+
server.listen(4000, () => console.info('Server is running on http://localhost:4000/graphql'));
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The output is standard GraphQL SDK compatible with any library supporting it. Reuse generated types and inputs in custom fields.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
## HTTP Proxy Driver
|
|
2
|
+
|
|
3
|
+
Drizzle Proxy allows implementing custom driver communication with databases. The HTTP proxy pattern sends queries from Drizzle ORM to an HTTP server, which executes them on the database and returns raw data for Drizzle to map.
|
|
4
|
+
|
|
5
|
+
### How It Works
|
|
6
|
+
|
|
7
|
+
1. Drizzle ORM builds a query
|
|
8
|
+
2. HTTP Proxy Driver sends the built query to an HTTP server
|
|
9
|
+
3. Server executes the query on the database
|
|
10
|
+
4. Server returns raw results back to the proxy
|
|
11
|
+
5. Drizzle maps the data and returns results
|
|
12
|
+
|
|
13
|
+
### Callback Function Pattern
|
|
14
|
+
|
|
15
|
+
Drizzle supports an async callback function for executing SQL:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { drizzle } from 'drizzle-orm/pg-proxy';
|
|
19
|
+
|
|
20
|
+
const db = drizzle(async (sql, params, method) => {
|
|
21
|
+
try {
|
|
22
|
+
const rows = await axios.post('http://localhost:3000/query', { sql, params, method });
|
|
23
|
+
return { rows: rows.data };
|
|
24
|
+
} catch (e: any) {
|
|
25
|
+
console.error('Error from proxy server: ', e.response.data)
|
|
26
|
+
return { rows: [] };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Parameters:**
|
|
32
|
+
- `sql`: Query string with placeholders
|
|
33
|
+
- `params`: Array of parameters
|
|
34
|
+
- `method`: One of `run`, `all`, `values`, or `get` depending on the SQL statement
|
|
35
|
+
|
|
36
|
+
**Return value:** Must be `{rows: string[][]}` or `{rows: string[]}` (use `string[]` when method is `get`)
|
|
37
|
+
|
|
38
|
+
### Server Implementation Example (PostgreSQL)
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { Client } from 'pg';
|
|
42
|
+
import express from 'express';
|
|
43
|
+
|
|
44
|
+
const app = express();
|
|
45
|
+
app.use(express.json());
|
|
46
|
+
const client = new Client('postgres://postgres:postgres@localhost:5432/postgres');
|
|
47
|
+
|
|
48
|
+
app.post('/query', async (req, res) => {
|
|
49
|
+
const { sql, params, method } = req.body;
|
|
50
|
+
const sqlBody = sql.replace(/;/g, ''); // prevent multiple queries
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const result = await client.query({
|
|
54
|
+
text: sqlBody,
|
|
55
|
+
values: params,
|
|
56
|
+
rowMode: method === 'all' ? 'array': undefined,
|
|
57
|
+
});
|
|
58
|
+
res.send(result.rows);
|
|
59
|
+
} catch (e: any) {
|
|
60
|
+
res.status(500).json({ error: e });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
app.listen(3000);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### MySQL Implementation
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import * as mysql from 'mysql2/promise';
|
|
71
|
+
import express from 'express';
|
|
72
|
+
|
|
73
|
+
const app = express();
|
|
74
|
+
app.use(express.json());
|
|
75
|
+
const connection = await mysql.createConnection('mysql://root:mysql@127.0.0.1:5432/drizzle');
|
|
76
|
+
|
|
77
|
+
app.post('/query', async (req, res) => {
|
|
78
|
+
const { sql, params, method } = req.body;
|
|
79
|
+
const sqlBody = sql.replace(/;/g, '');
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const result = await connection.query({
|
|
83
|
+
sql: sqlBody,
|
|
84
|
+
values: params,
|
|
85
|
+
rowsAsArray: method === 'all',
|
|
86
|
+
typeCast: function(field: any, next: any) {
|
|
87
|
+
if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') {
|
|
88
|
+
return field.string();
|
|
89
|
+
}
|
|
90
|
+
return next();
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
if (method === 'all') {
|
|
94
|
+
res.send(result[0]);
|
|
95
|
+
} else if (method === 'execute') {
|
|
96
|
+
res.send(result);
|
|
97
|
+
}
|
|
98
|
+
} catch (e: any) {
|
|
99
|
+
res.status(500).json({ error: e });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
app.listen(3000);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### SQLite with Batch Support
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { drizzle } from 'drizzle-orm/sqlite-proxy';
|
|
110
|
+
|
|
111
|
+
type ResponseType = { rows: any[][] | any[] }[];
|
|
112
|
+
|
|
113
|
+
const db = drizzle(
|
|
114
|
+
async (sql, params, method) => {
|
|
115
|
+
const rows = await axios.post('http://localhost:3000/query', { sql, params, method });
|
|
116
|
+
return { rows: rows.data };
|
|
117
|
+
},
|
|
118
|
+
async (queries: { sql: string, params: any[], method: 'all' | 'run' | 'get' | 'values'}[]) => {
|
|
119
|
+
const result: ResponseType = await axios.post('http://localhost:3000/batch', { queries });
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Batch response must be an array of raw values in the same order as sent queries.
|
|
126
|
+
|
|
127
|
+
### Table Declaration Example
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { sql } from "drizzle-orm";
|
|
131
|
+
import { text, integer, sqliteTable } from "drizzle-orm/sqlite-core";
|
|
132
|
+
|
|
133
|
+
const users = sqliteTable('users', {
|
|
134
|
+
id: text('id'),
|
|
135
|
+
textModifiers: text('text_modifiers').notNull().default(sql`CURRENT_TIMESTAMP`),
|
|
136
|
+
intModifiers: integer('int_modifiers', { mode: 'boolean' }).notNull().default(false),
|
|
137
|
+
});
|
|
138
|
+
```
|