├── .dockerignore ├── .env.example ├── .gitignore ├── .vscode └── extensions.json ├── Dockerfile ├── README.md ├── biome.json ├── bun.lockb ├── package-lock.json ├── package.json ├── src ├── db │ ├── index.ts │ ├── migrate.ts │ ├── migrations │ │ ├── 0000_flaky_rictor.sql │ │ ├── 0001_unique_living_lightning.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ └── _journal.json │ ├── schema.ts │ └── seed.ts ├── index.ts └── lib │ └── env.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "biomejs.biome" 4 | ] 5 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1 2 | 3 | # Adjust BUN_VERSION as desired 4 | ARG BUN_VERSION=1.0.16 5 | FROM oven/bun:${BUN_VERSION}-slim as base 6 | 7 | # Bun app lives here 8 | WORKDIR /app 9 | 10 | # Set production environment 11 | ENV NODE_ENV="production" 12 | 13 | 14 | # Throw-away build stage to reduce size of final image 15 | FROM base as build 16 | 17 | # Install packages needed to build node modules 18 | RUN apt-get update -qq && \ 19 | apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3 20 | 21 | # Install node modules 22 | COPY --link bun.lockb package-lock.json package.json ./ 23 | RUN bun install --ci 24 | 25 | # Copy application code 26 | COPY --link . . 27 | 28 | 29 | # Final stage for app image 30 | FROM base 31 | 32 | # Copy built application 33 | COPY --from=build /app /app 34 | 35 | # Start the server by default, this can be overwritten at runtime 36 | EXPOSE 3000 37 | CMD [ "bun", "run", "start" ] 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drizzle-demo 2 | 3 | 4 | This is a demo app for showing how to get Started with Drizzle ORM. It was used in this video: https://www.youtube.com/watch?v=hIYNOiZXQ7Y 5 | 6 | 7 | ## Getting Started 8 | 9 | This project uses [Bun](https://bun.sh) as the runtime. To get started, you can run the following commands: 10 | 11 | ```bash 12 | bun install 13 | ``` 14 | 15 | Copy the `.env.example` file to `.env` and update the `DATABASE_URL`. you can sign up for free and create a Postgres database at https://neon.tech 16 | 17 | 18 | ```bash 19 | cp .env.example .env 20 | ``` 21 | 22 | To apply the database migrations, you can run the following command: 23 | 24 | ```bash 25 | bun db:migrate 26 | ``` 27 | 28 | To seed the database with some data, you can run the following command: 29 | 30 | ```bash 31 | bun db:seed 32 | ``` 33 | 34 | Then you can run the following command to start the server: 35 | 36 | ```bash 37 | bun dev 38 | ``` 39 | 40 | You can then visit [http://localhost:3000](http://localhost:3000) to see the app. 41 | 42 | 43 | ## Deployment 44 | 45 | This project is ready to be deployed to [Railway](https://railway.app), [Render](https://render.com) and[ Fly.io](https://fly.io). 46 | 47 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neondatabase-labs/drizzle-overview/d20db3e5ccd2c9a6c242fe9b49fe6074d7551981/bun.lockb -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drizzle-demo", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "drizzle-demo", 8 | "dependencies": { 9 | "drizzle-orm": "^0.29.3", 10 | "postgres": "^3.4.3" 11 | }, 12 | "devDependencies": { 13 | "@biomejs/biome": "1.5.3", 14 | "bun-types": "latest", 15 | "drizzle-kit": "^0.20.14" 16 | }, 17 | "peerDependencies": { 18 | "typescript": "^5.0.0" 19 | } 20 | }, 21 | "node_modules/@biomejs/biome": { 22 | "version": "1.5.3", 23 | "dev": true, 24 | "hasInstallScript": true, 25 | "license": "MIT OR Apache-2.0", 26 | "bin": { 27 | "biome": "bin/biome" 28 | }, 29 | "engines": { 30 | "node": ">=14.*" 31 | }, 32 | "funding": { 33 | "type": "opencollective", 34 | "url": "https://opencollective.com/biome" 35 | }, 36 | "optionalDependencies": { 37 | "@biomejs/cli-darwin-arm64": "1.5.3", 38 | "@biomejs/cli-darwin-x64": "1.5.3", 39 | "@biomejs/cli-linux-arm64": "1.5.3", 40 | "@biomejs/cli-linux-arm64-musl": "1.5.3", 41 | "@biomejs/cli-linux-x64": "1.5.3", 42 | "@biomejs/cli-linux-x64-musl": "1.5.3", 43 | "@biomejs/cli-win32-arm64": "1.5.3", 44 | "@biomejs/cli-win32-x64": "1.5.3" 45 | } 46 | }, 47 | "node_modules/@biomejs/cli-darwin-arm64": { 48 | "version": "1.5.3", 49 | "cpu": [ 50 | "arm64" 51 | ], 52 | "dev": true, 53 | "license": "MIT OR Apache-2.0", 54 | "optional": true, 55 | "os": [ 56 | "darwin" 57 | ], 58 | "engines": { 59 | "node": ">=14.*" 60 | } 61 | }, 62 | "node_modules/@drizzle-team/studio": { 63 | "version": "0.0.39", 64 | "dev": true, 65 | "license": "UNLICENSED", 66 | "dependencies": { 67 | "superjson": "^2.2.1" 68 | } 69 | }, 70 | "node_modules/@esbuild-kit/core-utils": { 71 | "version": "3.3.2", 72 | "dev": true, 73 | "license": "MIT", 74 | "dependencies": { 75 | "esbuild": "~0.18.20", 76 | "source-map-support": "^0.5.21" 77 | } 78 | }, 79 | "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { 80 | "version": "0.18.20", 81 | "dev": true, 82 | "hasInstallScript": true, 83 | "license": "MIT", 84 | "bin": { 85 | "esbuild": "bin/esbuild" 86 | }, 87 | "engines": { 88 | "node": ">=12" 89 | }, 90 | "optionalDependencies": { 91 | "@esbuild/android-arm": "0.18.20", 92 | "@esbuild/android-arm64": "0.18.20", 93 | "@esbuild/android-x64": "0.18.20", 94 | "@esbuild/darwin-arm64": "0.18.20", 95 | "@esbuild/darwin-x64": "0.18.20", 96 | "@esbuild/freebsd-arm64": "0.18.20", 97 | "@esbuild/freebsd-x64": "0.18.20", 98 | "@esbuild/linux-arm": "0.18.20", 99 | "@esbuild/linux-arm64": "0.18.20", 100 | "@esbuild/linux-ia32": "0.18.20", 101 | "@esbuild/linux-loong64": "0.18.20", 102 | "@esbuild/linux-mips64el": "0.18.20", 103 | "@esbuild/linux-ppc64": "0.18.20", 104 | "@esbuild/linux-riscv64": "0.18.20", 105 | "@esbuild/linux-s390x": "0.18.20", 106 | "@esbuild/linux-x64": "0.18.20", 107 | "@esbuild/netbsd-x64": "0.18.20", 108 | "@esbuild/openbsd-x64": "0.18.20", 109 | "@esbuild/sunos-x64": "0.18.20", 110 | "@esbuild/win32-arm64": "0.18.20", 111 | "@esbuild/win32-ia32": "0.18.20", 112 | "@esbuild/win32-x64": "0.18.20" 113 | } 114 | }, 115 | "node_modules/@esbuild-kit/core-utils/node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { 116 | "version": "0.18.20", 117 | "cpu": [ 118 | "arm64" 119 | ], 120 | "dev": true, 121 | "license": "MIT", 122 | "optional": true, 123 | "os": [ 124 | "darwin" 125 | ], 126 | "engines": { 127 | "node": ">=12" 128 | } 129 | }, 130 | "node_modules/@esbuild-kit/esm-loader": { 131 | "version": "2.6.5", 132 | "dev": true, 133 | "license": "MIT", 134 | "dependencies": { 135 | "@esbuild-kit/core-utils": "^3.3.2", 136 | "get-tsconfig": "^4.7.0" 137 | } 138 | }, 139 | "node_modules/@esbuild/darwin-arm64": { 140 | "version": "0.19.12", 141 | "cpu": [ 142 | "arm64" 143 | ], 144 | "dev": true, 145 | "license": "MIT", 146 | "optional": true, 147 | "os": [ 148 | "darwin" 149 | ], 150 | "engines": { 151 | "node": ">=12" 152 | } 153 | }, 154 | "node_modules/@types/node": { 155 | "version": "20.11.16", 156 | "devOptional": true, 157 | "license": "MIT", 158 | "dependencies": { 159 | "undici-types": "~5.26.4" 160 | } 161 | }, 162 | "node_modules/@types/ws": { 163 | "version": "8.5.10", 164 | "devOptional": true, 165 | "license": "MIT", 166 | "dependencies": { 167 | "@types/node": "*" 168 | } 169 | }, 170 | "node_modules/balanced-match": { 171 | "version": "1.0.2", 172 | "dev": true, 173 | "license": "MIT" 174 | }, 175 | "node_modules/brace-expansion": { 176 | "version": "2.0.1", 177 | "dev": true, 178 | "license": "MIT", 179 | "dependencies": { 180 | "balanced-match": "^1.0.0" 181 | } 182 | }, 183 | "node_modules/buffer-from": { 184 | "version": "1.1.2", 185 | "dev": true, 186 | "license": "MIT" 187 | }, 188 | "node_modules/bun-types": { 189 | "version": "1.0.26", 190 | "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.26.tgz", 191 | "integrity": "sha512-VcSj+SCaWIcMb0uSGIAtr8P92zq9q+unavcQmx27fk6HulCthXHBVrdGuXxAZbFtv7bHVjizRzR2mk9r/U8Nkg==", 192 | "devOptional": true, 193 | "dependencies": { 194 | "@types/node": "~20.11.3", 195 | "@types/ws": "~8.5.10" 196 | } 197 | }, 198 | "node_modules/camelcase": { 199 | "version": "7.0.1", 200 | "dev": true, 201 | "license": "MIT", 202 | "engines": { 203 | "node": ">=14.16" 204 | }, 205 | "funding": { 206 | "url": "https://github.com/sponsors/sindresorhus" 207 | } 208 | }, 209 | "node_modules/chalk": { 210 | "version": "5.3.0", 211 | "dev": true, 212 | "license": "MIT", 213 | "engines": { 214 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 215 | }, 216 | "funding": { 217 | "url": "https://github.com/chalk/chalk?sponsor=1" 218 | } 219 | }, 220 | "node_modules/cli-color": { 221 | "version": "2.0.3", 222 | "dev": true, 223 | "license": "ISC", 224 | "dependencies": { 225 | "d": "^1.0.1", 226 | "es5-ext": "^0.10.61", 227 | "es6-iterator": "^2.0.3", 228 | "memoizee": "^0.4.15", 229 | "timers-ext": "^0.1.7" 230 | }, 231 | "engines": { 232 | "node": ">=0.10" 233 | } 234 | }, 235 | "node_modules/commander": { 236 | "version": "9.5.0", 237 | "dev": true, 238 | "license": "MIT", 239 | "engines": { 240 | "node": "^12.20.0 || >=14" 241 | } 242 | }, 243 | "node_modules/copy-anything": { 244 | "version": "3.0.5", 245 | "dev": true, 246 | "license": "MIT", 247 | "dependencies": { 248 | "is-what": "^4.1.8" 249 | }, 250 | "engines": { 251 | "node": ">=12.13" 252 | }, 253 | "funding": { 254 | "url": "https://github.com/sponsors/mesqueeb" 255 | } 256 | }, 257 | "node_modules/d": { 258 | "version": "1.0.1", 259 | "dev": true, 260 | "license": "ISC", 261 | "dependencies": { 262 | "es5-ext": "^0.10.50", 263 | "type": "^1.0.1" 264 | } 265 | }, 266 | "node_modules/debug": { 267 | "version": "4.3.4", 268 | "dev": true, 269 | "license": "MIT", 270 | "dependencies": { 271 | "ms": "2.1.2" 272 | }, 273 | "engines": { 274 | "node": ">=6.0" 275 | }, 276 | "peerDependenciesMeta": { 277 | "supports-color": { 278 | "optional": true 279 | } 280 | } 281 | }, 282 | "node_modules/difflib": { 283 | "version": "0.2.4", 284 | "dev": true, 285 | "dependencies": { 286 | "heap": ">= 0.2.0" 287 | } 288 | }, 289 | "node_modules/dreamopt": { 290 | "version": "0.8.0", 291 | "dev": true, 292 | "dependencies": { 293 | "wordwrap": ">=0.0.2" 294 | }, 295 | "engines": { 296 | "node": ">=0.4.0" 297 | } 298 | }, 299 | "node_modules/drizzle-kit": { 300 | "version": "0.20.14", 301 | "dev": true, 302 | "license": "MIT", 303 | "dependencies": { 304 | "@drizzle-team/studio": "^0.0.39", 305 | "@esbuild-kit/esm-loader": "^2.5.5", 306 | "camelcase": "^7.0.1", 307 | "chalk": "^5.2.0", 308 | "commander": "^9.4.1", 309 | "env-paths": "^3.0.0", 310 | "esbuild": "^0.19.7", 311 | "esbuild-register": "^3.5.0", 312 | "glob": "^8.1.0", 313 | "hanji": "^0.0.5", 314 | "json-diff": "0.9.0", 315 | "minimatch": "^7.4.3", 316 | "semver": "^7.5.4", 317 | "zod": "^3.20.2" 318 | }, 319 | "bin": { 320 | "drizzle-kit": "bin.cjs" 321 | } 322 | }, 323 | "node_modules/drizzle-orm": { 324 | "version": "0.29.3", 325 | "license": "Apache-2.0", 326 | "peerDependencies": { 327 | "@aws-sdk/client-rds-data": ">=3", 328 | "@cloudflare/workers-types": ">=3", 329 | "@libsql/client": "*", 330 | "@neondatabase/serverless": ">=0.1", 331 | "@opentelemetry/api": "^1.4.1", 332 | "@planetscale/database": ">=1", 333 | "@types/better-sqlite3": "*", 334 | "@types/pg": "*", 335 | "@types/react": ">=18", 336 | "@types/sql.js": "*", 337 | "@vercel/postgres": "*", 338 | "better-sqlite3": ">=7", 339 | "bun-types": "*", 340 | "expo-sqlite": ">=13.2.0", 341 | "knex": "*", 342 | "kysely": "*", 343 | "mysql2": ">=2", 344 | "pg": ">=8", 345 | "postgres": ">=3", 346 | "react": ">=18", 347 | "sql.js": ">=1", 348 | "sqlite3": ">=5" 349 | }, 350 | "peerDependenciesMeta": { 351 | "@aws-sdk/client-rds-data": { 352 | "optional": true 353 | }, 354 | "@cloudflare/workers-types": { 355 | "optional": true 356 | }, 357 | "@libsql/client": { 358 | "optional": true 359 | }, 360 | "@neondatabase/serverless": { 361 | "optional": true 362 | }, 363 | "@opentelemetry/api": { 364 | "optional": true 365 | }, 366 | "@planetscale/database": { 367 | "optional": true 368 | }, 369 | "@types/better-sqlite3": { 370 | "optional": true 371 | }, 372 | "@types/pg": { 373 | "optional": true 374 | }, 375 | "@types/react": { 376 | "optional": true 377 | }, 378 | "@types/sql.js": { 379 | "optional": true 380 | }, 381 | "@vercel/postgres": { 382 | "optional": true 383 | }, 384 | "better-sqlite3": { 385 | "optional": true 386 | }, 387 | "bun-types": { 388 | "optional": true 389 | }, 390 | "expo-sqlite": { 391 | "optional": true 392 | }, 393 | "knex": { 394 | "optional": true 395 | }, 396 | "kysely": { 397 | "optional": true 398 | }, 399 | "mysql2": { 400 | "optional": true 401 | }, 402 | "pg": { 403 | "optional": true 404 | }, 405 | "postgres": { 406 | "optional": true 407 | }, 408 | "react": { 409 | "optional": true 410 | }, 411 | "sql.js": { 412 | "optional": true 413 | }, 414 | "sqlite3": { 415 | "optional": true 416 | } 417 | } 418 | }, 419 | "node_modules/env-paths": { 420 | "version": "3.0.0", 421 | "dev": true, 422 | "license": "MIT", 423 | "engines": { 424 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 425 | }, 426 | "funding": { 427 | "url": "https://github.com/sponsors/sindresorhus" 428 | } 429 | }, 430 | "node_modules/es5-ext": { 431 | "version": "0.10.62", 432 | "dev": true, 433 | "hasInstallScript": true, 434 | "license": "ISC", 435 | "dependencies": { 436 | "es6-iterator": "^2.0.3", 437 | "es6-symbol": "^3.1.3", 438 | "next-tick": "^1.1.0" 439 | }, 440 | "engines": { 441 | "node": ">=0.10" 442 | } 443 | }, 444 | "node_modules/es6-iterator": { 445 | "version": "2.0.3", 446 | "dev": true, 447 | "license": "MIT", 448 | "dependencies": { 449 | "d": "1", 450 | "es5-ext": "^0.10.35", 451 | "es6-symbol": "^3.1.1" 452 | } 453 | }, 454 | "node_modules/es6-symbol": { 455 | "version": "3.1.3", 456 | "dev": true, 457 | "license": "ISC", 458 | "dependencies": { 459 | "d": "^1.0.1", 460 | "ext": "^1.1.2" 461 | } 462 | }, 463 | "node_modules/es6-weak-map": { 464 | "version": "2.0.3", 465 | "dev": true, 466 | "license": "ISC", 467 | "dependencies": { 468 | "d": "1", 469 | "es5-ext": "^0.10.46", 470 | "es6-iterator": "^2.0.3", 471 | "es6-symbol": "^3.1.1" 472 | } 473 | }, 474 | "node_modules/esbuild": { 475 | "version": "0.19.12", 476 | "dev": true, 477 | "hasInstallScript": true, 478 | "license": "MIT", 479 | "bin": { 480 | "esbuild": "bin/esbuild" 481 | }, 482 | "engines": { 483 | "node": ">=12" 484 | }, 485 | "optionalDependencies": { 486 | "@esbuild/aix-ppc64": "0.19.12", 487 | "@esbuild/android-arm": "0.19.12", 488 | "@esbuild/android-arm64": "0.19.12", 489 | "@esbuild/android-x64": "0.19.12", 490 | "@esbuild/darwin-arm64": "0.19.12", 491 | "@esbuild/darwin-x64": "0.19.12", 492 | "@esbuild/freebsd-arm64": "0.19.12", 493 | "@esbuild/freebsd-x64": "0.19.12", 494 | "@esbuild/linux-arm": "0.19.12", 495 | "@esbuild/linux-arm64": "0.19.12", 496 | "@esbuild/linux-ia32": "0.19.12", 497 | "@esbuild/linux-loong64": "0.19.12", 498 | "@esbuild/linux-mips64el": "0.19.12", 499 | "@esbuild/linux-ppc64": "0.19.12", 500 | "@esbuild/linux-riscv64": "0.19.12", 501 | "@esbuild/linux-s390x": "0.19.12", 502 | "@esbuild/linux-x64": "0.19.12", 503 | "@esbuild/netbsd-x64": "0.19.12", 504 | "@esbuild/openbsd-x64": "0.19.12", 505 | "@esbuild/sunos-x64": "0.19.12", 506 | "@esbuild/win32-arm64": "0.19.12", 507 | "@esbuild/win32-ia32": "0.19.12", 508 | "@esbuild/win32-x64": "0.19.12" 509 | } 510 | }, 511 | "node_modules/esbuild-register": { 512 | "version": "3.5.0", 513 | "dev": true, 514 | "license": "MIT", 515 | "dependencies": { 516 | "debug": "^4.3.4" 517 | }, 518 | "peerDependencies": { 519 | "esbuild": ">=0.12 <1" 520 | } 521 | }, 522 | "node_modules/event-emitter": { 523 | "version": "0.3.5", 524 | "dev": true, 525 | "license": "MIT", 526 | "dependencies": { 527 | "d": "1", 528 | "es5-ext": "~0.10.14" 529 | } 530 | }, 531 | "node_modules/ext": { 532 | "version": "1.7.0", 533 | "dev": true, 534 | "license": "ISC", 535 | "dependencies": { 536 | "type": "^2.7.2" 537 | } 538 | }, 539 | "node_modules/ext/node_modules/type": { 540 | "version": "2.7.2", 541 | "dev": true, 542 | "license": "ISC" 543 | }, 544 | "node_modules/fs.realpath": { 545 | "version": "1.0.0", 546 | "dev": true, 547 | "license": "ISC" 548 | }, 549 | "node_modules/get-tsconfig": { 550 | "version": "4.7.2", 551 | "dev": true, 552 | "license": "MIT", 553 | "dependencies": { 554 | "resolve-pkg-maps": "^1.0.0" 555 | }, 556 | "funding": { 557 | "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 558 | } 559 | }, 560 | "node_modules/glob": { 561 | "version": "8.1.0", 562 | "dev": true, 563 | "license": "ISC", 564 | "dependencies": { 565 | "fs.realpath": "^1.0.0", 566 | "inflight": "^1.0.4", 567 | "inherits": "2", 568 | "minimatch": "^5.0.1", 569 | "once": "^1.3.0" 570 | }, 571 | "engines": { 572 | "node": ">=12" 573 | }, 574 | "funding": { 575 | "url": "https://github.com/sponsors/isaacs" 576 | } 577 | }, 578 | "node_modules/glob/node_modules/minimatch": { 579 | "version": "5.1.6", 580 | "dev": true, 581 | "license": "ISC", 582 | "dependencies": { 583 | "brace-expansion": "^2.0.1" 584 | }, 585 | "engines": { 586 | "node": ">=10" 587 | } 588 | }, 589 | "node_modules/hanji": { 590 | "version": "0.0.5", 591 | "dev": true, 592 | "license": "ISC", 593 | "dependencies": { 594 | "lodash.throttle": "^4.1.1", 595 | "sisteransi": "^1.0.5" 596 | } 597 | }, 598 | "node_modules/heap": { 599 | "version": "0.2.7", 600 | "dev": true, 601 | "license": "MIT" 602 | }, 603 | "node_modules/inflight": { 604 | "version": "1.0.6", 605 | "dev": true, 606 | "license": "ISC", 607 | "dependencies": { 608 | "once": "^1.3.0", 609 | "wrappy": "1" 610 | } 611 | }, 612 | "node_modules/inherits": { 613 | "version": "2.0.4", 614 | "dev": true, 615 | "license": "ISC" 616 | }, 617 | "node_modules/is-promise": { 618 | "version": "2.2.2", 619 | "dev": true, 620 | "license": "MIT" 621 | }, 622 | "node_modules/is-what": { 623 | "version": "4.1.16", 624 | "dev": true, 625 | "license": "MIT", 626 | "engines": { 627 | "node": ">=12.13" 628 | }, 629 | "funding": { 630 | "url": "https://github.com/sponsors/mesqueeb" 631 | } 632 | }, 633 | "node_modules/json-diff": { 634 | "version": "0.9.0", 635 | "dev": true, 636 | "license": "MIT", 637 | "dependencies": { 638 | "cli-color": "^2.0.0", 639 | "difflib": "~0.2.1", 640 | "dreamopt": "~0.8.0" 641 | }, 642 | "bin": { 643 | "json-diff": "bin/json-diff.js" 644 | }, 645 | "engines": { 646 | "node": "*" 647 | } 648 | }, 649 | "node_modules/lodash.throttle": { 650 | "version": "4.1.1", 651 | "dev": true, 652 | "license": "MIT" 653 | }, 654 | "node_modules/lru-cache": { 655 | "version": "6.0.0", 656 | "dev": true, 657 | "license": "ISC", 658 | "dependencies": { 659 | "yallist": "^4.0.0" 660 | }, 661 | "engines": { 662 | "node": ">=10" 663 | } 664 | }, 665 | "node_modules/lru-queue": { 666 | "version": "0.1.0", 667 | "dev": true, 668 | "license": "MIT", 669 | "dependencies": { 670 | "es5-ext": "~0.10.2" 671 | } 672 | }, 673 | "node_modules/memoizee": { 674 | "version": "0.4.15", 675 | "dev": true, 676 | "license": "ISC", 677 | "dependencies": { 678 | "d": "^1.0.1", 679 | "es5-ext": "^0.10.53", 680 | "es6-weak-map": "^2.0.3", 681 | "event-emitter": "^0.3.5", 682 | "is-promise": "^2.2.2", 683 | "lru-queue": "^0.1.0", 684 | "next-tick": "^1.1.0", 685 | "timers-ext": "^0.1.7" 686 | } 687 | }, 688 | "node_modules/minimatch": { 689 | "version": "7.4.6", 690 | "dev": true, 691 | "license": "ISC", 692 | "dependencies": { 693 | "brace-expansion": "^2.0.1" 694 | }, 695 | "engines": { 696 | "node": ">=10" 697 | }, 698 | "funding": { 699 | "url": "https://github.com/sponsors/isaacs" 700 | } 701 | }, 702 | "node_modules/ms": { 703 | "version": "2.1.2", 704 | "dev": true, 705 | "license": "MIT" 706 | }, 707 | "node_modules/next-tick": { 708 | "version": "1.1.0", 709 | "dev": true, 710 | "license": "ISC" 711 | }, 712 | "node_modules/once": { 713 | "version": "1.4.0", 714 | "dev": true, 715 | "license": "ISC", 716 | "dependencies": { 717 | "wrappy": "1" 718 | } 719 | }, 720 | "node_modules/postgres": { 721 | "version": "3.4.3", 722 | "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.3.tgz", 723 | "integrity": "sha512-iHJn4+M9vbTdHSdDzNkC0crHq+1CUdFhx+YqCE+SqWxPjm+Zu63jq7yZborOBF64c8pc58O5uMudyL1FQcHacA==", 724 | "engines": { 725 | "node": ">=12" 726 | }, 727 | "funding": { 728 | "type": "individual", 729 | "url": "https://github.com/sponsors/porsager" 730 | } 731 | }, 732 | "node_modules/resolve-pkg-maps": { 733 | "version": "1.0.0", 734 | "dev": true, 735 | "license": "MIT", 736 | "funding": { 737 | "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 738 | } 739 | }, 740 | "node_modules/semver": { 741 | "version": "7.6.0", 742 | "dev": true, 743 | "license": "ISC", 744 | "dependencies": { 745 | "lru-cache": "^6.0.0" 746 | }, 747 | "bin": { 748 | "semver": "bin/semver.js" 749 | }, 750 | "engines": { 751 | "node": ">=10" 752 | } 753 | }, 754 | "node_modules/sisteransi": { 755 | "version": "1.0.5", 756 | "dev": true, 757 | "license": "MIT" 758 | }, 759 | "node_modules/source-map": { 760 | "version": "0.6.1", 761 | "dev": true, 762 | "license": "BSD-3-Clause", 763 | "engines": { 764 | "node": ">=0.10.0" 765 | } 766 | }, 767 | "node_modules/source-map-support": { 768 | "version": "0.5.21", 769 | "dev": true, 770 | "license": "MIT", 771 | "dependencies": { 772 | "buffer-from": "^1.0.0", 773 | "source-map": "^0.6.0" 774 | } 775 | }, 776 | "node_modules/superjson": { 777 | "version": "2.2.1", 778 | "dev": true, 779 | "license": "MIT", 780 | "dependencies": { 781 | "copy-anything": "^3.0.2" 782 | }, 783 | "engines": { 784 | "node": ">=16" 785 | } 786 | }, 787 | "node_modules/timers-ext": { 788 | "version": "0.1.7", 789 | "dev": true, 790 | "license": "ISC", 791 | "dependencies": { 792 | "es5-ext": "~0.10.46", 793 | "next-tick": "1" 794 | } 795 | }, 796 | "node_modules/type": { 797 | "version": "1.2.0", 798 | "dev": true, 799 | "license": "ISC" 800 | }, 801 | "node_modules/typescript": { 802 | "version": "5.3.3", 803 | "license": "Apache-2.0", 804 | "peer": true, 805 | "bin": { 806 | "tsc": "bin/tsc", 807 | "tsserver": "bin/tsserver" 808 | }, 809 | "engines": { 810 | "node": ">=14.17" 811 | } 812 | }, 813 | "node_modules/undici-types": { 814 | "version": "5.26.5", 815 | "devOptional": true, 816 | "license": "MIT" 817 | }, 818 | "node_modules/wordwrap": { 819 | "version": "1.0.0", 820 | "dev": true, 821 | "license": "MIT" 822 | }, 823 | "node_modules/wrappy": { 824 | "version": "1.0.2", 825 | "dev": true, 826 | "license": "ISC" 827 | }, 828 | "node_modules/yallist": { 829 | "version": "4.0.0", 830 | "dev": true, 831 | "license": "ISC" 832 | }, 833 | "node_modules/zod": { 834 | "version": "3.22.4", 835 | "dev": true, 836 | "license": "MIT", 837 | "funding": { 838 | "url": "https://github.com/sponsors/colinhacks" 839 | } 840 | } 841 | } 842 | } 843 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drizzle-demo", 3 | "module": "src/index.ts", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "bun --watch src/index.ts", 7 | "db:generate": "drizzle-kit generate:pg --schema ./src/db/schema.ts --out=./src/db/migrations", 8 | "db:migrate": "bun run ./src/db/migrate.ts", 9 | "db:seed": "bun run ./src/db/seed.ts", 10 | "format": "bunx @biomejs/biome format ./src --write", 11 | "lint": "bunx @biomejs/biome lint ./src", 12 | "start": "bun run src/index.ts" 13 | }, 14 | "devDependencies": { 15 | "@biomejs/biome": "1.5.3", 16 | "bun-types": "latest", 17 | "drizzle-kit": "^0.20.14" 18 | }, 19 | "peerDependencies": { 20 | "typescript": "^5.0.0" 21 | }, 22 | "dependencies": { 23 | "@neondatabase/serverless": "^0.8.1", 24 | "drizzle-orm": "^0.29.3", 25 | "hono": "^3.12.12", 26 | "postgres": "^3.4.3" 27 | } 28 | } -------------------------------------------------------------------------------- /src/db/index.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/neon-http"; 2 | import { neon } from "@neondatabase/serverless"; 3 | import * as schema from "./schema"; 4 | 5 | const sql = neon(process.env.DATABASE_URL); 6 | 7 | export const db = drizzle(sql, { 8 | schema, 9 | }); 10 | -------------------------------------------------------------------------------- /src/db/migrate.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from "drizzle-orm/neon-http"; 2 | import { migrate } from "drizzle-orm/neon-http/migrator"; 3 | import { neon } from "@neondatabase/serverless"; 4 | 5 | const sql = neon(process.env.DATABASE_URL); 6 | 7 | const db = drizzle(sql); 8 | 9 | const main = async () => { 10 | try { 11 | await migrate(db, { 12 | migrationsFolder: "src/db/migrations", 13 | }); 14 | 15 | console.log("Migration successful"); 16 | } catch (error) { 17 | console.error(error); 18 | process.exit(1); 19 | } 20 | }; 21 | 22 | main(); 23 | -------------------------------------------------------------------------------- /src/db/migrations/0000_flaky_rictor.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "comments" ( 2 | "id" serial PRIMARY KEY NOT NULL, 3 | "post_id" integer, 4 | "user_id" integer, 5 | "text" text NOT NULL 6 | ); 7 | --> statement-breakpoint 8 | CREATE TABLE IF NOT EXISTS "posts" ( 9 | "id" serial PRIMARY KEY NOT NULL, 10 | "title" text NOT NULL, 11 | "content" text NOT NULL, 12 | "user_id" integer NOT NULL, 13 | "created_at" timestamp DEFAULT now() NOT NULL, 14 | "updated_at" timestamp DEFAULT now() NOT NULL 15 | ); 16 | --> statement-breakpoint 17 | CREATE TABLE IF NOT EXISTS "users" ( 18 | "id" serial PRIMARY KEY NOT NULL, 19 | "name" text NOT NULL, 20 | "email" text NOT NULL, 21 | "created_at" timestamp DEFAULT now() NOT NULL, 22 | "updated_at" timestamp DEFAULT now() NOT NULL 23 | ); 24 | --> statement-breakpoint 25 | DO $$ BEGIN 26 | ALTER TABLE "comments" ADD CONSTRAINT "comments_post_id_posts_id_fk" FOREIGN KEY ("post_id") REFERENCES "posts"("id") ON DELETE no action ON UPDATE no action; 27 | EXCEPTION 28 | WHEN duplicate_object THEN null; 29 | END $$; 30 | --> statement-breakpoint 31 | DO $$ BEGIN 32 | ALTER TABLE "comments" ADD CONSTRAINT "comments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE no action ON UPDATE no action; 33 | EXCEPTION 34 | WHEN duplicate_object THEN null; 35 | END $$; 36 | --> statement-breakpoint 37 | DO $$ BEGIN 38 | ALTER TABLE "posts" ADD CONSTRAINT "posts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE no action ON UPDATE no action; 39 | EXCEPTION 40 | WHEN duplicate_object THEN null; 41 | END $$; 42 | -------------------------------------------------------------------------------- /src/db/migrations/0001_unique_living_lightning.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "comments" ADD COLUMN "created_at" timestamp DEFAULT now() NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "comments" ADD COLUMN "updated_at" timestamp DEFAULT now() NOT NULL; -------------------------------------------------------------------------------- /src/db/migrations/meta/0000_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "d5b300bc-45c4-4f7b-96c2-4c488c5bb692", 3 | "prevId": "00000000-0000-0000-0000-000000000000", 4 | "version": "5", 5 | "dialect": "pg", 6 | "tables": { 7 | "comments": { 8 | "name": "comments", 9 | "schema": "", 10 | "columns": { 11 | "id": { 12 | "name": "id", 13 | "type": "serial", 14 | "primaryKey": true, 15 | "notNull": true 16 | }, 17 | "post_id": { 18 | "name": "post_id", 19 | "type": "integer", 20 | "primaryKey": false, 21 | "notNull": false 22 | }, 23 | "user_id": { 24 | "name": "user_id", 25 | "type": "integer", 26 | "primaryKey": false, 27 | "notNull": false 28 | }, 29 | "text": { 30 | "name": "text", 31 | "type": "text", 32 | "primaryKey": false, 33 | "notNull": true 34 | } 35 | }, 36 | "indexes": {}, 37 | "foreignKeys": { 38 | "comments_post_id_posts_id_fk": { 39 | "name": "comments_post_id_posts_id_fk", 40 | "tableFrom": "comments", 41 | "tableTo": "posts", 42 | "columnsFrom": [ 43 | "post_id" 44 | ], 45 | "columnsTo": [ 46 | "id" 47 | ], 48 | "onDelete": "no action", 49 | "onUpdate": "no action" 50 | }, 51 | "comments_user_id_users_id_fk": { 52 | "name": "comments_user_id_users_id_fk", 53 | "tableFrom": "comments", 54 | "tableTo": "users", 55 | "columnsFrom": [ 56 | "user_id" 57 | ], 58 | "columnsTo": [ 59 | "id" 60 | ], 61 | "onDelete": "no action", 62 | "onUpdate": "no action" 63 | } 64 | }, 65 | "compositePrimaryKeys": {}, 66 | "uniqueConstraints": {} 67 | }, 68 | "posts": { 69 | "name": "posts", 70 | "schema": "", 71 | "columns": { 72 | "id": { 73 | "name": "id", 74 | "type": "serial", 75 | "primaryKey": true, 76 | "notNull": true 77 | }, 78 | "title": { 79 | "name": "title", 80 | "type": "text", 81 | "primaryKey": false, 82 | "notNull": true 83 | }, 84 | "content": { 85 | "name": "content", 86 | "type": "text", 87 | "primaryKey": false, 88 | "notNull": true 89 | }, 90 | "user_id": { 91 | "name": "user_id", 92 | "type": "integer", 93 | "primaryKey": false, 94 | "notNull": true 95 | }, 96 | "created_at": { 97 | "name": "created_at", 98 | "type": "timestamp", 99 | "primaryKey": false, 100 | "notNull": true, 101 | "default": "now()" 102 | }, 103 | "updated_at": { 104 | "name": "updated_at", 105 | "type": "timestamp", 106 | "primaryKey": false, 107 | "notNull": true, 108 | "default": "now()" 109 | } 110 | }, 111 | "indexes": {}, 112 | "foreignKeys": { 113 | "posts_user_id_users_id_fk": { 114 | "name": "posts_user_id_users_id_fk", 115 | "tableFrom": "posts", 116 | "tableTo": "users", 117 | "columnsFrom": [ 118 | "user_id" 119 | ], 120 | "columnsTo": [ 121 | "id" 122 | ], 123 | "onDelete": "no action", 124 | "onUpdate": "no action" 125 | } 126 | }, 127 | "compositePrimaryKeys": {}, 128 | "uniqueConstraints": {} 129 | }, 130 | "users": { 131 | "name": "users", 132 | "schema": "", 133 | "columns": { 134 | "id": { 135 | "name": "id", 136 | "type": "serial", 137 | "primaryKey": true, 138 | "notNull": true 139 | }, 140 | "name": { 141 | "name": "name", 142 | "type": "text", 143 | "primaryKey": false, 144 | "notNull": true 145 | }, 146 | "email": { 147 | "name": "email", 148 | "type": "text", 149 | "primaryKey": false, 150 | "notNull": true 151 | }, 152 | "created_at": { 153 | "name": "created_at", 154 | "type": "timestamp", 155 | "primaryKey": false, 156 | "notNull": true, 157 | "default": "now()" 158 | }, 159 | "updated_at": { 160 | "name": "updated_at", 161 | "type": "timestamp", 162 | "primaryKey": false, 163 | "notNull": true, 164 | "default": "now()" 165 | } 166 | }, 167 | "indexes": {}, 168 | "foreignKeys": {}, 169 | "compositePrimaryKeys": {}, 170 | "uniqueConstraints": {} 171 | } 172 | }, 173 | "enums": {}, 174 | "schemas": {}, 175 | "_meta": { 176 | "columns": {}, 177 | "schemas": {}, 178 | "tables": {} 179 | } 180 | } -------------------------------------------------------------------------------- /src/db/migrations/meta/0001_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "6a9a4150-107b-44a3-bd5d-8f474d072c7b", 3 | "prevId": "d5b300bc-45c4-4f7b-96c2-4c488c5bb692", 4 | "version": "5", 5 | "dialect": "pg", 6 | "tables": { 7 | "comments": { 8 | "name": "comments", 9 | "schema": "", 10 | "columns": { 11 | "id": { 12 | "name": "id", 13 | "type": "serial", 14 | "primaryKey": true, 15 | "notNull": true 16 | }, 17 | "post_id": { 18 | "name": "post_id", 19 | "type": "integer", 20 | "primaryKey": false, 21 | "notNull": false 22 | }, 23 | "user_id": { 24 | "name": "user_id", 25 | "type": "integer", 26 | "primaryKey": false, 27 | "notNull": false 28 | }, 29 | "text": { 30 | "name": "text", 31 | "type": "text", 32 | "primaryKey": false, 33 | "notNull": true 34 | }, 35 | "created_at": { 36 | "name": "created_at", 37 | "type": "timestamp", 38 | "primaryKey": false, 39 | "notNull": true, 40 | "default": "now()" 41 | }, 42 | "updated_at": { 43 | "name": "updated_at", 44 | "type": "timestamp", 45 | "primaryKey": false, 46 | "notNull": true, 47 | "default": "now()" 48 | } 49 | }, 50 | "indexes": {}, 51 | "foreignKeys": { 52 | "comments_post_id_posts_id_fk": { 53 | "name": "comments_post_id_posts_id_fk", 54 | "tableFrom": "comments", 55 | "tableTo": "posts", 56 | "columnsFrom": [ 57 | "post_id" 58 | ], 59 | "columnsTo": [ 60 | "id" 61 | ], 62 | "onDelete": "no action", 63 | "onUpdate": "no action" 64 | }, 65 | "comments_user_id_users_id_fk": { 66 | "name": "comments_user_id_users_id_fk", 67 | "tableFrom": "comments", 68 | "tableTo": "users", 69 | "columnsFrom": [ 70 | "user_id" 71 | ], 72 | "columnsTo": [ 73 | "id" 74 | ], 75 | "onDelete": "no action", 76 | "onUpdate": "no action" 77 | } 78 | }, 79 | "compositePrimaryKeys": {}, 80 | "uniqueConstraints": {} 81 | }, 82 | "posts": { 83 | "name": "posts", 84 | "schema": "", 85 | "columns": { 86 | "id": { 87 | "name": "id", 88 | "type": "serial", 89 | "primaryKey": true, 90 | "notNull": true 91 | }, 92 | "title": { 93 | "name": "title", 94 | "type": "text", 95 | "primaryKey": false, 96 | "notNull": true 97 | }, 98 | "content": { 99 | "name": "content", 100 | "type": "text", 101 | "primaryKey": false, 102 | "notNull": true 103 | }, 104 | "user_id": { 105 | "name": "user_id", 106 | "type": "integer", 107 | "primaryKey": false, 108 | "notNull": true 109 | }, 110 | "created_at": { 111 | "name": "created_at", 112 | "type": "timestamp", 113 | "primaryKey": false, 114 | "notNull": true, 115 | "default": "now()" 116 | }, 117 | "updated_at": { 118 | "name": "updated_at", 119 | "type": "timestamp", 120 | "primaryKey": false, 121 | "notNull": true, 122 | "default": "now()" 123 | } 124 | }, 125 | "indexes": {}, 126 | "foreignKeys": { 127 | "posts_user_id_users_id_fk": { 128 | "name": "posts_user_id_users_id_fk", 129 | "tableFrom": "posts", 130 | "tableTo": "users", 131 | "columnsFrom": [ 132 | "user_id" 133 | ], 134 | "columnsTo": [ 135 | "id" 136 | ], 137 | "onDelete": "no action", 138 | "onUpdate": "no action" 139 | } 140 | }, 141 | "compositePrimaryKeys": {}, 142 | "uniqueConstraints": {} 143 | }, 144 | "users": { 145 | "name": "users", 146 | "schema": "", 147 | "columns": { 148 | "id": { 149 | "name": "id", 150 | "type": "serial", 151 | "primaryKey": true, 152 | "notNull": true 153 | }, 154 | "name": { 155 | "name": "name", 156 | "type": "text", 157 | "primaryKey": false, 158 | "notNull": true 159 | }, 160 | "email": { 161 | "name": "email", 162 | "type": "text", 163 | "primaryKey": false, 164 | "notNull": true 165 | }, 166 | "created_at": { 167 | "name": "created_at", 168 | "type": "timestamp", 169 | "primaryKey": false, 170 | "notNull": true, 171 | "default": "now()" 172 | }, 173 | "updated_at": { 174 | "name": "updated_at", 175 | "type": "timestamp", 176 | "primaryKey": false, 177 | "notNull": true, 178 | "default": "now()" 179 | } 180 | }, 181 | "indexes": {}, 182 | "foreignKeys": {}, 183 | "compositePrimaryKeys": {}, 184 | "uniqueConstraints": {} 185 | } 186 | }, 187 | "enums": {}, 188 | "schemas": {}, 189 | "_meta": { 190 | "columns": {}, 191 | "schemas": {}, 192 | "tables": {} 193 | } 194 | } -------------------------------------------------------------------------------- /src/db/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "dialect": "pg", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "5", 8 | "when": 1707459441278, 9 | "tag": "0000_flaky_rictor", 10 | "breakpoints": true 11 | }, 12 | { 13 | "idx": 1, 14 | "version": "5", 15 | "when": 1707460041509, 16 | "tag": "0001_unique_living_lightning", 17 | "breakpoints": true 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/db/schema.ts: -------------------------------------------------------------------------------- 1 | import { relations } from "drizzle-orm"; 2 | import { serial, text, timestamp, integer, pgTable } from "drizzle-orm/pg-core"; 3 | 4 | export const users = pgTable("users", { 5 | id: serial("id").primaryKey(), 6 | name: text("name").notNull(), 7 | email: text("email").notNull(), 8 | createdAt: timestamp("created_at").notNull().defaultNow(), 9 | updatedAt: timestamp("updated_at").notNull().defaultNow(), 10 | }); 11 | 12 | export const posts = pgTable("posts", { 13 | id: serial("id").primaryKey(), 14 | title: text("title").notNull(), 15 | content: text("content").notNull(), 16 | userId: integer("user_id") 17 | .notNull() 18 | .references(() => users.id), 19 | createdAt: timestamp("created_at").notNull().defaultNow(), 20 | updatedAt: timestamp("updated_at").notNull().defaultNow(), 21 | }); 22 | 23 | export const comments = pgTable("comments", { 24 | id: serial("id").primaryKey(), 25 | postId: integer("post_id").references(() => posts.id), 26 | userId: integer("user_id").references(() => users.id), 27 | text: text("text").notNull(), 28 | createdAt: timestamp("created_at").notNull().defaultNow(), 29 | updatedAt: timestamp("updated_at").notNull().defaultNow(), 30 | }); 31 | 32 | export const postsRelations = relations(posts, ({ one, many }) => ({ 33 | user: one(users, { fields: [posts.userId], references: [users.id] }), 34 | comments: many(comments), 35 | })); 36 | 37 | export const usersRelations = relations(users, ({ many }) => ({ 38 | posts: many(posts), 39 | })); 40 | 41 | export const commentsRelations = relations(comments, ({ one }) => ({ 42 | post: one(posts, { fields: [comments.postId], references: [posts.id] }), 43 | user: one(users, { fields: [comments.userId], references: [users.id] }), 44 | })); 45 | -------------------------------------------------------------------------------- /src/db/seed.ts: -------------------------------------------------------------------------------- 1 | import { comments, posts, users } from "./schema"; 2 | import { drizzle } from "drizzle-orm/neon-http"; 3 | import { neon } from "@neondatabase/serverless"; 4 | import * as schema from "./schema"; 5 | 6 | const sql = neon(process.env.DATABASE_URL); 7 | 8 | const db = drizzle(sql, { 9 | schema, 10 | }); 11 | 12 | const main = async () => { 13 | try { 14 | console.log("Seeding database"); 15 | // Delete all data 16 | await db.delete(comments); 17 | await db.delete(posts); 18 | await db.delete(users); 19 | 20 | await db.insert(users).values([ 21 | { 22 | id: 1, 23 | name: "Alice Johnson", 24 | email: "alice.johnson@example.com", 25 | }, 26 | { 27 | id: 2, 28 | name: "Bob Smith", 29 | email: "bob.smith@example.com", 30 | }, 31 | ]); 32 | 33 | await db.insert(posts).values([ 34 | { 35 | id: 1, 36 | userId: 1, 37 | title: "Introduction", 38 | content: "Hello, World! Excited to join this community.", 39 | }, 40 | { 41 | id: 2, 42 | userId: 2, 43 | title: "Reply", 44 | content: "Hello, Alice! Welcome to the community!", 45 | }, 46 | { 47 | id: 3, 48 | userId: 1, 49 | title: "Reply", 50 | content: "Thanks, Bob! Glad to be here.", 51 | }, 52 | ]); 53 | 54 | await db.insert(comments).values([ 55 | { 56 | id: 1, 57 | text: "Welcome, Alice! Looking forward to your posts.", 58 | userId: 2, 59 | postId: 1, 60 | }, 61 | { 62 | id: 2, 63 | text: "Thank you, Bob! Excited to be part of the conversation.", 64 | userId: 1, 65 | postId: 2, 66 | }, 67 | ]); 68 | } catch (error) { 69 | console.error(error); 70 | throw new Error("Failed to seed database"); 71 | } 72 | }; 73 | 74 | main(); 75 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | import { db } from "./db"; 3 | 4 | const PORT = process.env.PORT || 3000; 5 | 6 | const app = new Hono(); 7 | 8 | app.get("/", async (c) => { 9 | try { 10 | const data = await db.query.posts.findMany({ 11 | with: { 12 | comments: true, 13 | user: true, 14 | }, 15 | }); 16 | return c.json({ 17 | data, 18 | }); 19 | } catch (error) { 20 | return c.json({ error }); 21 | } 22 | }); 23 | 24 | Bun.serve({ 25 | port: PORT, 26 | fetch: app.fetch, 27 | }); 28 | 29 | if (process.env.NODE_ENV === "development") { 30 | console.log(`Server is running at http://localhost:${PORT}`); 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { z, TypeOf } from "zod"; 2 | 3 | const zodEnv = z.object({ 4 | DATABASE_URL: z.string(), 5 | }); 6 | 7 | declare global { 8 | namespace NodeJS { 9 | interface ProcessEnv extends TypeOf {} 10 | } 11 | } 12 | try { 13 | zodEnv.parse(process.env); 14 | } catch (err) { 15 | if (err instanceof z.ZodError) { 16 | const { fieldErrors } = err.flatten(); 17 | const errorMessage = Object.entries(fieldErrors) 18 | .map(([field, errors]) => 19 | errors ? `${field}: ${errors.join(", ")}` : field, 20 | ) 21 | .join("\n "); 22 | throw new Error(`Missing environment variables:\n ${errorMessage}`); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "module": "esnext", 5 | "target": "esnext", 6 | "moduleResolution": "bundler", 7 | "moduleDetection": "force", 8 | "allowImportingTsExtensions": true, 9 | "noEmit": true, 10 | "composite": true, 11 | "strict": true, 12 | "downlevelIteration": true, 13 | "skipLibCheck": true, 14 | "jsx": "react-jsx", 15 | "allowSyntheticDefaultImports": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "allowJs": true, 18 | "types": [ 19 | "bun-types" // add Bun global 20 | ] 21 | } 22 | } 23 | --------------------------------------------------------------------------------