Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for extra bindings (Hyperdrive) #245

Merged
merged 6 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions docs/content/1.docs/3.recipes/5.postgres.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
title: PostgreSQL Database
navigation.title: PostgreSQL
description: Learn how to use PostgreSQL in your Nuxt application deployed on Cloudflare Workers / Pages and how to speed up your queries using Hyperdrive.
---

## Pre-requisites

Cloudflare does not host PostgreSQL databases, you need to setup your own PostgreSQL database.

::note{to="https://www.postgresql.org/support/professional_hosting/" target="_blank" icon="i-logos-postgresql"}
See a list of professional PostgreSQL hosting providers.
::

If you prefer to use Cloudflare services, you can use Cloudflare D1 which is built on SQLite, see our [Database](/docs/features/database) section.

## Setup

1. Make sure to use the `@nuxthub/core` module, see the [installation section](/docs/getting-started/installation#add-to-a-nuxt-project) for instructions.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxthub/core',],
});
```

::note
The module ensures that you can connect to your PostgreSQL database using [Cloudflare TCP Sockets](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/.)
::

2. Install the [`postgres`](https://www.npmjs.com/package/postgres) NPM package in your project.

```bash
npx nypm add postgres
```

::tip{icon="i-ph-rocket-launch"}
That's it, you can now use the `postgres` package to connect to your PostgreSQL database.
::

::warning
Please note that [`pg`](https://www.npmjs.com/package/pg) is not compatible at the moment.
::

## Usage

We can add our PostgreSQL database connection in our `.env` file.

```bash [.env]
NUXT_POSTGRES_URL=postgresql://user:password@localhost:5432/database
```

Then, we can create a `usePostgres()` server util to connect to our database in our API route.

```ts [server/utils/postgres.ts]
import postgres from 'postgres'

export function usePostgres () {
if (!process.env.NUXT_POSTGRES_URL) {
throw createError('Missing `NUXT_POSTGRES_URL` environment variable')
}

return postgres(process.env.NUXT_POSTGRES_URL as string, {
ssl: 'require'
})
}
```

We can now use the `usePostgres()` function to connect to our database in our API route.

```ts [server/api/db.ts]
export default eventHandler(async (event) => {
const sql = usePostgres()

const products = await sql`SELECT * FROM products`

// Ensure the database connection is closed after the request is processed
event.waitUntil(sql.end())
return products
})
```

::tip
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.
::

## Hyperdrive

[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).

::important{to="https://developers.cloudflare.com/hyperdrive/platform/pricing/" target="_blank"}
Hyperdrive is only available on the Workers Paid plan ($5/month), **learn more**.
::

To use Hyperdrive in your Nuxt application:
1. [Create a Hyperdrive configuration](https://dash.cloudflare.com/?to=/:account/workers/hyperdrive/create)
2. Add your Hyperdrive ID in your `nuxt.config.ts` file

```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxthub/core'],
hub: {
bindings: {
hyperdrive: {
// <BINDING_NAME>: <HYPERDRIVE_ID>
POSTGRES: 'your-hyperdrive-id'
}
}
}
})
```

3. Update our `usePostgres()` function to use the `POSTGRES` binding when available.

```ts [server/utils/postgres.ts]
import type { Hyperdrive } from '@cloudflare/workers-types'
import postgres from 'postgres'

export function usePostgres() {
const hyperdrive = process.env.POSTGRES as Hyperdrive | undefined
const dbUrl = hyperdrive?.connectionString || process.env.NUXT_POSTGRES_URL
if (!dbUrl) {
throw createError('Missing `POSTGRES` hyperdrive binding or `NUXT_POSTGRES_URL` env variable')
}

return postgres(dbUrl, {
ssl: !hyperdrive ? 'require' : undefined
})
}
```

::warning
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()`.
::

4. [Deploy your application](/docs/getting-started/deploy) with the NuxtHub CLI or Admin to make sure the Hyperdrive bindings are set.

::tip{icon="i-ph-rocket-launch"}
You can now access your PostgreSQL database from anywhere in the world at maximum speed.
::
27 changes: 27 additions & 0 deletions docs/content/4.changelog/hyperdrive-bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: Hyperdrive bindings
description: "It is now possible to use PostgreSQL & Cloudflare Hyperdrive in your Nuxt application."
date: 2024-08-28
image: '/images/changelog/hyperdrive-bindings.png'
category: Admin
authors:
- name: Sebastien Chopin
avatar:
src: https://avatars.githubusercontent.com/u/904724?v=4
to: https://x.com/atinux
username: atinux
---

If you are not comfortable with Cloudflare D1 database (SQLite), you can now use your own PostgreSQL database.

We wrote a recipe on [How to use a PostgreSQL database](/docs/recipes/postgres) with NuxtHub.

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.

::callout
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.
::

::tip
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).
::
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@iconify-json/heroicons": "^1.1.24",
"@iconify-json/logos": "^1.1.44",
"@iconify-json/ph": "^1.1.14",
"@iconify-json/simple-icons": "^1.1.114",
"@nuxt/content": "^2.13.2",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 0 additions & 5 deletions docs/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,5 @@ export const asideLinks = [
label: 'Chat on Discord',
to: 'https://discord.gg/e25qqXb2mF',
target: '_blank'
}, {
icon: 'i-simple-icons-nuxtdotjs',
label: 'Nuxt Docs',
to: 'https://nuxt.com',
target: '_blank'
}
]
2 changes: 2 additions & 0 deletions playground/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Used for /api/hyperdrive
NUXT_POSTGRES_URL=
12 changes: 7 additions & 5 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ export default defineNuxtConfig({
database: true,
kv: true,
blob: true,
cache: true
cache: true,
bindings: {
// Used for /api/hyperdrive
hyperdrive: {
POSTGRES: '08f7bc805d1d409aac17e72af502abd0'
}
}
// projectUrl: ({ branch }) => branch === 'main' ? 'https://playground.nuxt.dev' : `https://${encodeHost(branch).replace(/\//g, '-')}.playground-to39.pages.dev`
},

ui: {
icons: ['heroicons', 'simple-icons']
},

basicAuth: {
enabled: process.env.NODE_ENV === 'production',
allowedRoutes: ['/api/_hub/'],
Expand Down
3 changes: 2 additions & 1 deletion playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"@nuxt/ui": "^2.18.4",
"@nuxthub/core": "latest",
"drizzle-orm": "^0.33.0",
"nuxt": "^3.13.0"
"nuxt": "^3.13.0",
"postgres": "^3.4.4"
},
"devDependencies": {
"@nuxt/devtools": "latest"
Expand Down
37 changes: 37 additions & 0 deletions playground/server/api/hyperdrive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Hyperdrive } from '@cloudflare/workers-types'
import postgres from 'postgres'

export default eventHandler(async (event) => {
const hyperdrive = process.env.POSTGRES as Hyperdrive | undefined
const dbURL = hyperdrive?.connectionString || process.env.NUXT_POSTGRES_URL

if (!dbURL) {
throw createError({
statusCode: 500,
message: 'No process.env.NUXT_POSTGRES_URL or provess.env.HYPERDRIVE found'
})
}
const sql = postgres(dbURL, {
ssl: import.meta.dev ? 'require' : false
})

// Create products table
// await db.query('CREATE TABLE IF NOT EXISTS products (id SERIAL PRIMARY KEY, name VARCHAR(255), price DECIMAL(10, 2))')
// // Insert 10 products
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 1', 10.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 2', 20.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 3', 30.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 4', 40.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 5', 50.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 6', 60.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 7', 70.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 8', 80.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 9', 90.00])
// await db.query('INSERT INTO products (name, price) VALUES ($1, $2)', ['Product 10', 100.00])

console.log('query products')
const products = await sql`SELECT * FROM products`

event.waitUntil(sql.end())
return products
})
Loading