diff --git a/drizzle-orm/.eslintrc.js b/drizzle-orm/.eslintrc.js new file mode 100644 index 00000000..2061cd22 --- /dev/null +++ b/drizzle-orm/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"], +}; diff --git a/drizzle-orm/.gitignore b/drizzle-orm/.gitignore new file mode 100644 index 00000000..c7fb563d --- /dev/null +++ b/drizzle-orm/.gitignore @@ -0,0 +1,7 @@ +node_modules + +/.cache +/build +/public/build +.env +*.db diff --git a/drizzle-orm/README.md b/drizzle-orm/README.md new file mode 100644 index 00000000..583c14ec --- /dev/null +++ b/drizzle-orm/README.md @@ -0,0 +1,22 @@ +# Drizzle ORM Example + +In this example we will setup Drizzle ORM with Remix. + +## Preview + +Open this example on [CodeSandbox](https://codesandbox.com): + +[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/remix-run/examples/tree/main/drizzle-orm) + +## Example + +Relevant files: + +- [drizzle](./drizzle) migrations and drizzle-related meta information +- [app/db.server.ts] setup database client +- [app/schema.ts] define database schema +- [package.json] add `generate` script and drizzle dependencies + +## Related Links + +[Drizzle ORM](https://orm.drizzle.team) diff --git a/drizzle-orm/app/db.server.ts b/drizzle-orm/app/db.server.ts new file mode 100644 index 00000000..c97f68ce --- /dev/null +++ b/drizzle-orm/app/db.server.ts @@ -0,0 +1,10 @@ +import Database from "better-sqlite3"; +import { drizzle } from "drizzle-orm/better-sqlite3"; +import { migrate } from "drizzle-orm/better-sqlite3/migrator"; +import * as schema from "./schema"; + +const sqlite = new Database("sqlite.db"); + +export const db = drizzle(sqlite, { schema }); + +migrate(db, { migrationsFolder: "drizzle" }); diff --git a/drizzle-orm/app/root.tsx b/drizzle-orm/app/root.tsx new file mode 100644 index 00000000..927a0f74 --- /dev/null +++ b/drizzle-orm/app/root.tsx @@ -0,0 +1,32 @@ +import type { MetaFunction } from "@remix-run/node"; +import { + Links, + LiveReload, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "@remix-run/react"; + +export const meta: MetaFunction = () => ({ + charset: "utf-8", + title: "New Remix App", + viewport: "width=device-width,initial-scale=1", +}); + +export default function App() { + return ( + + + + + + + + + + + + + ); +} diff --git a/drizzle-orm/app/routes/_index.tsx b/drizzle-orm/app/routes/_index.tsx new file mode 100644 index 00000000..9751ba80 --- /dev/null +++ b/drizzle-orm/app/routes/_index.tsx @@ -0,0 +1,61 @@ +import { type ActionArgs } from "@remix-run/node"; +import { Form, useLoaderData } from "@remix-run/react"; +import { sql } from "drizzle-orm"; +import { db } from "~/db.server"; +import { example } from "~/schema"; + +export async function action({ request }: ActionArgs) { + const formData = await request.formData(); + const intent = formData.get("intent"); + + if (intent === "add") { + await db.insert(example).values({}); + return new Response(null, { status: 201 }); + } + + if (intent == "reset") { + await db.delete(example); + return new Response(null, { status: 204 }); + } + + return new Response(null, { status: 400 }); +} + +export async function loader() { + const rowsQuery = db.query.example.findMany(); + + const lastUpdatedQuery = db + .select({ updated: sql`MAX(created_at)` }) + .from(example) + .execute() + .then((rows) => rows[0].updated); + + return { + rows: await rowsQuery, + lastUpdated: await lastUpdatedQuery, + }; +} + +export default function Index() { + const { rows, lastUpdated } = useLoaderData(); + + return ( +
+

Welcome to Remix

+

Last Updated: {lastUpdated ?? "--"}

+
+ + +
+
    + {rows.map((row) => ( +
  • {row.id}
  • + ))} +
+
+ ); +} diff --git a/drizzle-orm/app/schema.ts b/drizzle-orm/app/schema.ts new file mode 100644 index 00000000..98495e3a --- /dev/null +++ b/drizzle-orm/app/schema.ts @@ -0,0 +1,7 @@ +import { sql } from 'drizzle-orm' +import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core"; + +export const example = sqliteTable('example', { + id: integer('id').primaryKey(), + createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`), +}) diff --git a/drizzle-orm/drizzle.config.ts b/drizzle-orm/drizzle.config.ts new file mode 100644 index 00000000..3ea082b0 --- /dev/null +++ b/drizzle-orm/drizzle.config.ts @@ -0,0 +1,6 @@ +import type { Config } from "drizzle-kit"; + +export default { + schema: "./app/schema.ts", + out: "./drizzle", +} satisfies Config; diff --git a/drizzle-orm/drizzle/0000_mute_groot.sql b/drizzle-orm/drizzle/0000_mute_groot.sql new file mode 100644 index 00000000..691bc165 --- /dev/null +++ b/drizzle-orm/drizzle/0000_mute_groot.sql @@ -0,0 +1,4 @@ +CREATE TABLE `example` ( + `id` integer PRIMARY KEY NOT NULL, + `created_at` text DEFAULT CURRENT_TIMESTAMP +); diff --git a/drizzle-orm/drizzle/meta/0000_snapshot.json b/drizzle-orm/drizzle/meta/0000_snapshot.json new file mode 100644 index 00000000..5fc7695f --- /dev/null +++ b/drizzle-orm/drizzle/meta/0000_snapshot.json @@ -0,0 +1,38 @@ +{ + "version": "5", + "dialect": "sqlite", + "id": "0be79972-b6bf-4225-88d7-95a723174903", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "example": { + "name": "example", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/drizzle-orm/drizzle/meta/_journal.json b/drizzle-orm/drizzle/meta/_journal.json new file mode 100644 index 00000000..4745c3ec --- /dev/null +++ b/drizzle-orm/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "5", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1697441145549, + "tag": "0000_mute_groot", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json new file mode 100644 index 00000000..7e9db94c --- /dev/null +++ b/drizzle-orm/package.json @@ -0,0 +1,34 @@ +{ + "private": true, + "sideEffects": false, + "scripts": { + "build": "remix build", + "dev": "remix dev", + "generate": "drizzle-kit generate:sqlite", + "start": "remix-serve build", + "typecheck": "tsc" + }, + "dependencies": { + "@remix-run/node": "^1.19.3", + "@remix-run/react": "^1.19.3", + "@remix-run/serve": "^1.19.3", + "better-sqlite3": "^9.0.0", + "drizzle-orm": "^0.28.6", + "isbot": "^3.6.5", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@remix-run/dev": "^1.19.3", + "@remix-run/eslint-config": "^1.19.3", + "@types/better-sqlite3": "^7.6.5", + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.8", + "drizzle-kit": "^0.19.13", + "eslint": "^8.27.0", + "typescript": "^4.8.4" + }, + "engines": { + "node": ">=14.0.0" + } +} diff --git a/drizzle-orm/public/favicon.ico b/drizzle-orm/public/favicon.ico new file mode 100644 index 00000000..8830cf68 Binary files /dev/null and b/drizzle-orm/public/favicon.ico differ diff --git a/drizzle-orm/remix.config.js b/drizzle-orm/remix.config.js new file mode 100644 index 00000000..ca00ba94 --- /dev/null +++ b/drizzle-orm/remix.config.js @@ -0,0 +1,11 @@ +/** @type {import('@remix-run/dev').AppConfig} */ +module.exports = { + future: { + v2_routeConvention: true, + }, + ignoredRouteFiles: ["**/.*"], + // appDirectory: "app", + // assetsBuildDirectory: "public/build", + // publicPath: "/build/", + // serverBuildPath: "build/index.js", +}; diff --git a/drizzle-orm/remix.env.d.ts b/drizzle-orm/remix.env.d.ts new file mode 100644 index 00000000..dcf8c45e --- /dev/null +++ b/drizzle-orm/remix.env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/drizzle-orm/sandbox.config.json b/drizzle-orm/sandbox.config.json new file mode 100644 index 00000000..f92e0250 --- /dev/null +++ b/drizzle-orm/sandbox.config.json @@ -0,0 +1,7 @@ +{ + "hardReloadOnChange": true, + "template": "remix", + "container": { + "port": 3000 + } +} diff --git a/drizzle-orm/tsconfig.json b/drizzle-orm/tsconfig.json new file mode 100644 index 00000000..20f8a386 --- /dev/null +++ b/drizzle-orm/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "resolveJsonModule": true, + "target": "ES2019", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +}