Skip to content

Commit 6e548e5

Browse files
authored
feat: add support for extra bindings (Hyperdrive) (#245)
1 parent d397864 commit 6e548e5

File tree

15 files changed

+497
-342
lines changed

15 files changed

+497
-342
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
title: PostgreSQL Database
3+
navigation.title: PostgreSQL
4+
description: Learn how to use PostgreSQL in your Nuxt application deployed on Cloudflare Workers / Pages and how to speed up your queries using Hyperdrive.
5+
---
6+
7+
## Pre-requisites
8+
9+
Cloudflare does not host PostgreSQL databases, you need to setup your own PostgreSQL database.
10+
11+
::note{to="https://www.postgresql.org/support/professional_hosting/" target="_blank" icon="i-logos-postgresql"}
12+
See a list of professional PostgreSQL hosting providers.
13+
::
14+
15+
If you prefer to use Cloudflare services, you can use Cloudflare D1 which is built on SQLite, see our [Database](/docs/features/database) section.
16+
17+
## Setup
18+
19+
1. Make sure to use the `@nuxthub/core` module, see the [installation section](/docs/getting-started/installation#add-to-a-nuxt-project) for instructions.
20+
21+
```ts [nuxt.config.ts]
22+
export default defineNuxtConfig({
23+
modules: ['@nuxthub/core',],
24+
});
25+
```
26+
27+
::note
28+
The module ensures that you can connect to your PostgreSQL database using [Cloudflare TCP Sockets](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/.)
29+
::
30+
31+
2. Install the [`postgres`](https://www.npmjs.com/package/postgres) NPM package in your project.
32+
33+
```bash
34+
npx nypm add postgres
35+
```
36+
37+
::tip{icon="i-ph-rocket-launch"}
38+
That's it, you can now use the `postgres` package to connect to your PostgreSQL database.
39+
::
40+
41+
::warning
42+
Please note that [`pg`](https://www.npmjs.com/package/pg) is not compatible at the moment.
43+
::
44+
45+
## Usage
46+
47+
We can add our PostgreSQL database connection in our `.env` file.
48+
49+
```bash [.env]
50+
NUXT_POSTGRES_URL=postgresql://user:password@localhost:5432/database
51+
```
52+
53+
Then, we can create a `usePostgres()` server util to connect to our database in our API route.
54+
55+
```ts [server/utils/postgres.ts]
56+
import postgres from 'postgres'
57+
58+
export function usePostgres () {
59+
if (!process.env.NUXT_POSTGRES_URL) {
60+
throw createError('Missing `NUXT_POSTGRES_URL` environment variable')
61+
}
62+
63+
return postgres(process.env.NUXT_POSTGRES_URL as string, {
64+
ssl: 'require'
65+
})
66+
}
67+
```
68+
69+
We can now use the `usePostgres()` function to connect to our database in our API route.
70+
71+
```ts [server/api/db.ts]
72+
export default eventHandler(async (event) => {
73+
const sql = usePostgres()
74+
75+
const products = await sql`SELECT * FROM products`
76+
77+
// Ensure the database connection is closed after the request is processed
78+
event.waitUntil(sql.end())
79+
return products
80+
})
81+
```
82+
83+
::tip
84+
You may notice that we don't import `usePostgres()`. This is because Nuxt auto-imports the exported variables & functions from `server/utils/*.ts` when used.
85+
::
86+
87+
## Hyperdrive
88+
89+
[Hyperdrive](https://developers.cloudflare.com/hyperdrive/) is a Cloudflare service that accelerates queries you make to existing databases, making it faster to access your data from across the globe. By maintaining a connection pool to your database within Cloudflare’s network, Hyperdrive reduces seven round-trips to your database before you can even send a query: the TCP handshake (1x), TLS negotiation (3x), and database authentication (3x).
90+
91+
::important{to="https://developers.cloudflare.com/hyperdrive/platform/pricing/" target="_blank"}
92+
Hyperdrive is only available on the Workers Paid plan ($5/month), **learn more**.
93+
::
94+
95+
To use Hyperdrive in your Nuxt application:
96+
1. [Create a Hyperdrive configuration](https://dash.cloudflare.com/?to=/:account/workers/hyperdrive/create)
97+
2. Add your Hyperdrive ID in your `nuxt.config.ts` file
98+
99+
```ts [nuxt.config.ts]
100+
export default defineNuxtConfig({
101+
modules: ['@nuxthub/core'],
102+
hub: {
103+
bindings: {
104+
hyperdrive: {
105+
// <BINDING_NAME>: <HYPERDRIVE_ID>
106+
POSTGRES: 'your-hyperdrive-id'
107+
}
108+
}
109+
}
110+
})
111+
```
112+
113+
3. Update our `usePostgres()` function to use the `POSTGRES` binding when available.
114+
115+
```ts [server/utils/postgres.ts]
116+
import type { Hyperdrive } from '@cloudflare/workers-types'
117+
import postgres from 'postgres'
118+
119+
export function usePostgres() {
120+
const hyperdrive = process.env.POSTGRES as Hyperdrive | undefined
121+
const dbUrl = hyperdrive?.connectionString || process.env.NUXT_POSTGRES_URL
122+
if (!dbUrl) {
123+
throw createError('Missing `POSTGRES` hyperdrive binding or `NUXT_POSTGRES_URL` env variable')
124+
}
125+
126+
return postgres(dbUrl, {
127+
ssl: !hyperdrive ? 'require' : undefined
128+
})
129+
}
130+
```
131+
132+
::warning
133+
Hyperdrive is currently not available in development mode at the moment. We are working on a solution to make it work in development mode and remote storage with an upcoming `hubHyperdrive()`.
134+
::
135+
136+
4. [Deploy your application](/docs/getting-started/deploy) with the NuxtHub CLI or Admin to make sure the Hyperdrive bindings are set.
137+
138+
::tip{icon="i-ph-rocket-launch"}
139+
You can now access your PostgreSQL database from anywhere in the world at maximum speed.
140+
::
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: Hyperdrive bindings
3+
description: "It is now possible to use PostgreSQL & Cloudflare Hyperdrive in your Nuxt application."
4+
date: 2024-08-28
5+
image: '/images/changelog/hyperdrive-bindings.png'
6+
category: Admin
7+
authors:
8+
- name: Sebastien Chopin
9+
avatar:
10+
src: https://avatars.githubusercontent.com/u/904724?v=4
11+
to: https://x.com/atinux
12+
username: atinux
13+
---
14+
15+
If you are not comfortable with Cloudflare D1 database (SQLite), you can now use your own PostgreSQL database.
16+
17+
We wrote a recipe on [How to use a PostgreSQL database](/docs/recipes/postgres) with NuxtHub.
18+
19+
This recipe explains how to connect to your PostgreSQL database and how to leverage Hyperdrive bindings in your Nuxt application to accelerate your database responses.
20+
21+
::callout
22+
Right now, hyperdrive bindings are not available in development mode, we are working with the Cloudflare team to make them available in development mode as well with remote storage.
23+
::
24+
25+
::tip
26+
This feature is available on both [free and pro plans](/pricing) and in [`@nuxthub/core >= v0.7.6`](https://github.com/nuxt-hub/core/releases/tag/v0.7.6).
27+
::

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@iconify-json/heroicons": "^1.1.24",
14+
"@iconify-json/logos": "^1.1.44",
1415
"@iconify-json/ph": "^1.1.14",
1516
"@iconify-json/simple-icons": "^1.1.114",
1617
"@nuxt/content": "^2.13.2",
1020 KB
Loading

docs/utils/index.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,5 @@ export const asideLinks = [
2222
label: 'Chat on Discord',
2323
to: 'https://discord.gg/e25qqXb2mF',
2424
target: '_blank'
25-
}, {
26-
icon: 'i-simple-icons-nuxtdotjs',
27-
label: 'Nuxt Docs',
28-
to: 'https://nuxt.com',
29-
target: '_blank'
3025
}
3126
]

playground/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Used for /api/hyperdrive
2+
NUXT_POSTGRES_URL=

playground/nuxt.config.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ export default defineNuxtConfig({
1616
database: true,
1717
kv: true,
1818
blob: true,
19-
cache: true
19+
cache: true,
20+
bindings: {
21+
// Used for /api/hyperdrive
22+
hyperdrive: {
23+
POSTGRES: '08f7bc805d1d409aac17e72af502abd0'
24+
}
25+
}
2026
// projectUrl: ({ branch }) => branch === 'main' ? 'https://playground.nuxt.dev' : `https://${encodeHost(branch).replace(/\//g, '-')}.playground-to39.pages.dev`
2127
},
2228

23-
ui: {
24-
icons: ['heroicons', 'simple-icons']
25-
},
26-
2729
basicAuth: {
2830
enabled: process.env.NODE_ENV === 'production',
2931
allowedRoutes: ['/api/_hub/'],

playground/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"@nuxt/ui": "^2.18.4",
1414
"@nuxthub/core": "latest",
1515
"drizzle-orm": "^0.33.0",
16-
"nuxt": "^3.13.0"
16+
"nuxt": "^3.13.0",
17+
"postgres": "^3.4.4"
1718
},
1819
"devDependencies": {
1920
"@nuxt/devtools": "latest"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { Hyperdrive } from '@cloudflare/workers-types'
2+
import postgres from 'postgres'
3+
4+
export default eventHandler(async (event) => {
5+
const hyperdrive = process.env.POSTGRES as Hyperdrive | undefined
6+
const dbURL = hyperdrive?.connectionString || process.env.NUXT_POSTGRES_URL
7+
8+
if (!dbURL) {
9+
throw createError({
10+
statusCode: 500,
11+
message: 'No process.env.NUXT_POSTGRES_URL or provess.env.HYPERDRIVE found'
12+
})
13+
}
14+
const sql = postgres(dbURL, {
15+
ssl: import.meta.dev ? 'require' : false
16+
})
17+
18+
// Create products table
19+
// await db.query('CREATE TABLE IF NOT EXISTS products (id SERIAL PRIMARY KEY, name VARCHAR(255), price DECIMAL(10, 2))')
20+
// // Insert 10 products
21+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 1', 10.00])
22+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 2', 20.00])
23+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 3', 30.00])
24+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 4', 40.00])
25+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 5', 50.00])
26+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 6', 60.00])
27+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 7', 70.00])
28+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 8', 80.00])
29+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 9', 90.00])
30+
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 10', 100.00])
31+
32+
console.log('query products')
33+
const products = await sql`SELECT * FROM products`
34+
35+
event.waitUntil(sql.end())
36+
return products
37+
})

0 commit comments

Comments
 (0)