Edge Functions

Integrating with Supabase Database (Postgres)

Connect to your Postgres database from Edge Functions.


Connect to your Postgres database from an Edge Function by using the supabase-js client. You can also use other Postgres clients like Deno Postgres


Using supabase-js#

The withSupabase wrapper from @supabase/server hands you a supabase-js client (ctx.supabase) already scoped to the caller's Row Level Security policies, so you don't manage keys or authorization headers yourself. It also provides ctx.supabaseAdmin for privileged operations that bypass Row Level Security. Responses are automatically formatted as JSON. This is the recommended approach for most applications:

1
import { withSupabase } from 'npm:@supabase/server@^1'
2
3
export default {
4
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
5
try {
6
// ctx.supabase respects the caller's RLS policies.
7
// ctx.supabaseAdmin bypasses RLS for privileged operations.
8
const { data, error } = await ctx.supabase.from('countries').select('*')
9
10
if (error) {
11
throw error
12
}
13
14
return Response.json({ data })
15
} catch (err) {
16
return new Response(String(err?.message ?? err), { status: 500 })
17
}
18
}),
19
}

This enables:

  • Automatic Row Level Security enforcement
  • Built-in JSON serialization
  • Consistent error handling
  • TypeScript support for database schema

Using a Postgres client#

Because Edge Functions are a server-side technology, it's safe to connect directly to your database using any popular Postgres client. This means you can run raw SQL from your Edge Functions.

Here is how you can connect to the database using Deno Postgres driver and run raw SQL. Check out the full example.

1
import { Pool } from 'https://deno.land/x/postgres@v0.17.0/mod.ts'
2
3
// Create a database pool with one connection.
4
const pool = new Pool(
5
{
6
tls: { enabled: false },
7
database: 'postgres',
8
hostname: Deno.env.get('DB_HOSTNAME'),
9
user: Deno.env.get('DB_USER'),
10
port: 6543,
11
password: Deno.env.get('DB_PASSWORD'),
12
},
13
1
14
)
15
16
export default {
17
fetch: async (_req) => {
18
try {
19
// Grab a connection from the pool
20
const connection = await pool.connect()
21
22
try {
23
// Run a query
24
const result = await connection.queryObject`SELECT * FROM animals`
25
const animals = result.rows // [{ id: 1, name: "Lion" }, ...]
26
27
// Encode the result as pretty printed JSON
28
const body = JSON.stringify(
29
animals,
30
(_key, value) => (typeof value === 'bigint' ? value.toString() : value),
31
2
32
)
33
34
// Return the response with the correct content type header
35
return new Response(body, {
36
status: 200,
37
headers: {
38
'Content-Type': 'application/json; charset=utf-8',
39
},
40
})
41
} finally {
42
// Release the connection back into the pool
43
connection.release()
44
}
45
} catch (err) {
46
console.error(err)
47
return new Response(String(err?.message ?? err), { status: 500 })
48
}
49
},
50
}
View source

Using Drizzle#

You can use Drizzle together with Postgres.js. Both can be loaded directly from npm.

Declare the dependencies in a deno.json file inside the function directory (see Managing functions dependencies for more details):

1
{
2
"imports": {
3
"drizzle-orm": "npm:drizzle-orm@0.29.1",
4
"drizzle-orm/": "npm:/drizzle-orm@0.29.1/",
5
"postgres": "npm:postgres@3.4.3"
6
}
7
}
View source

Then define your schema and query the database:

1
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core'
2
3
export const user = pgTable('user', {
4
id: serial('id'),
5
name: text('name'),
6
email: text('email'),
7
password: text('password'),
8
role: text('role').$type<'admin' | 'customer'>(),
9
createdAt: timestamp('created_at'),
10
updatedAt: timestamp('updated_at'),
11
})
12
13
export const countries = pgTable('countries', {
14
id: serial('id'),
15
name: text('name'),
16
})
View source
1
import { drizzle } from 'drizzle-orm/postgres-js'
2
import postgres from 'postgres'
3
4
import { countries } from '../_shared/schema.ts'
5
6
const connectionString = Deno.env.get('SUPABASE_DB_URL')!
7
// Disable prefetch as it is not supported for "Transaction" pool mode
8
const client = postgres(connectionString, { prepare: false })
9
const db = drizzle(client)
10
11
export default {
12
fetch: async (_req) => {
13
const allCountries = await db.select().from(countries)
14
15
return Response.json(allCountries)
16
},
17
}
View source

You can find the full example on GitHub.


SSL connections#

Production#

Deployed edge functions are pre-configured to use SSL for connections to the Supabase database. You don't need to add any extra configurations.

Local development#

If you want to use SSL connections during local development, follow these steps:

  1. Download the SSL certificate from Database Settings
  2. Add to your local .env file, add these two variables:
1
SSL_CERT_FILE=/path/to/cert.crt # set the path to the downloaded cert
2
DENO_TLS_CA_STORE=mozilla,system

Then, restart your local development server:

1
supabase functions serve your-function