Hono is an ultrafast edge-first web framework for JavaScript runtimes, but when you need a PostgreSQL backend, you're back to provisioning databases and wiring connection strings. PandaStack provisions a managed Postgres instance and injects DATABASE_URL into your Hono app automatically, so you can focus on writing routes instead of infrastructure config.
This walkthrough builds a minimal Hono API with a Postgres-backed /users endpoint, shows how to wire the managed database, and explains how connection pooling keeps your app from exhausting the 50-connection limit on the free tier.
Starting with a basic Hono app
A minimal Hono API looks like this:
// src/index.js
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.json({ message: 'Hello Hono' }));
export default app;To run this in Node.js (not Cloudflare Workers or Deno), install @hono/node-server:
npm install hono @hono/node-serverThen add a start script:
// server.js
import { serve } from '@hono/node-server';
import app from './src/index.js';
const port = parseInt(process.env.PORT || '3000', 10);
serve({
fetch: app.fetch,
port,
});
console.log(`Server running on http://0.0.0.0:${port}`);Critical: bind to 0.0.0.0, not localhost. Container deployments route traffic via the pod IP, and localhost binds only to the loopback interface. This is the single most common reason Hono apps deploy successfully but never respond to requests—the build passes, the pod starts, but the health check times out because the port isn't reachable.
Adding PostgreSQL with pg
Install the pg driver:
npm install pgCreate a database client that reads DATABASE_URL from the environment:
// src/db.js
import pg from 'pg';
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
});
export default pool;The SSL config is necessary because PandaStack's managed Postgres instances enforce TLS in production. In local dev, you can skip it.
A users CRUD endpoint
Add a route that fetches users from Postgres:
// src/index.js
import { Hono } from 'hono';
import pool from './db.js';
const app = new Hono();
app.get('/', (c) => c.json({ message: 'Hello Hono' }));
app.get('/users', async (c) => {
const result = await pool.query('SELECT id, email, created_at FROM users ORDER BY id');
return c.json(result.rows);
});
app.post('/users', async (c) => {
const { email } = await c.req.json();
const result = await pool.query(
'INSERT INTO users (email) VALUES ($1) RETURNING id, email, created_at',
[email]
);
return c.json(result.rows[0], 201);
});
export default app;You'll need a users table. Run this migration manually via psql or a migration tool:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);PandaStack doesn't run migrations automatically—you connect to the database with psql and apply them yourself, or use a tool like node-pg-migrate in a pre-deploy hook.
Deploying the API
Create a pandastack.json at the repo root to specify the build settings:
{
"type": "container",
"language": "nodejs",
"startCommand": "node server.js",
"healthCheckPath": "/",
"env": [
{ "key": "NODE_ENV", "value": "production" }
]
}The healthCheckPath tells the load balancer where to send health checks. Hono's root route returns 200, so / works. If you don't specify this and your app takes more than 30 seconds to start, the pod is killed and restarted.
Push the repo to GitHub, then deploy via the REST API:
curl -X POST https://api.pandastack.io/v1/projects \
-H "Authorization: Bearer psk_live_your_token" \
-H "Content-Type: application/json" \
-d '{
"slug": "container",
"name": "hono-api",
"repositoryName": "yourusername/hono-api",
"branch": "main",
"autoDeploy": true
}'The response includes a projectId. Save it.
Provisioning the database
PandaStack offers managed PostgreSQL (versions 14 and 16) and MySQL (version 8.x). Create a Postgres database via the API:
curl -X POST https://api.pandastack.io/v1/databases \
-H "Authorization: Bearer psk_live_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "hono-db",
"engine": "postgresql",
"version": "16"
}'The response includes a DATABASE_URL connection string. Copy it.
Wiring the database to the app
Add DATABASE_URL as an environment variable for the project:
curl -X POST https://api.pandastack.io/v1/projects/42/env \
-H "Authorization: Bearer psk_live_your_token" \
-H "Content-Type: application/json" \
-d '{
"env": [
{ "name": "DATABASE_URL", "value": "postgresql://user:pass@host:5432/dbname?sslmode=require" }
]
}'Then trigger a redeploy:
curl -X POST https://api.pandastack.io/v1/projects/42/deploy \
-H "Authorization: Bearer psk_live_your_token"The next deploy reads DATABASE_URL at runtime and connects to the managed Postgres instance.
Alternatively, link the database in the dashboard: go to the project's Environment tab, click Link Database, and select the database. This injects DATABASE_URL automatically and redeploys the app.
Running migrations
PandaStack doesn't run migrations automatically, so you need to apply them manually. The simplest approach is to connect via psql:
psql $DATABASE_URLPaste your CREATE TABLE statement and run it. For more complex setups, use a migration tool like node-pg-migrate:
npm install --save-dev node-pg-migrateAdd a migration script:
// migrations/1_create_users.js
exports.up = (pgm) => {
pgm.createTable('users', {
id: 'id',
email: { type: 'varchar(255)', notNull: true, unique: true },
created_at: { type: 'timestamp', default: pgm.func('current_timestamp') },
});
};
exports.down = (pgm) => {
pgm.dropTable('users');
};Run migrations locally:
DATABASE_URL=postgres://localhost/dev npm run migrate upFor production, run them in a one-off cronjob or manually via the dashboard database shell.
Connection pooling
The free tier limits databases to 50 concurrent connections. If you deploy 5 replicas of your API and each opens 10 connections, you hit the limit and new connections fail with FATAL: too many connections.
The pg Pool above mitigates this by reusing connections, but if your app scales to many pods, you need an external pooler like PgBouncer. PandaStack's paid plans include connection pooling as a managed add-on—check the dashboard for availability.
Alternatively, set max: 5 in the Pool config to limit connections per pod:
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
max: 5,
});With 5 pods and 5 connections each, you stay under the 50-connection limit.
Testing the API
Once deployed, the dashboard shows the live URL (https://hono-api-xyz.pandastack.app). Test it:
curl https://hono-api-xyz.pandastack.app/users
# []
curl -X POST https://hono-api-xyz.pandastack.app/users \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com"}'
# {"id":1,"email":"test@example.com","created_at":"2026-07-29T10:00:00.000Z"}
curl https://hono-api-xyz.pandastack.app/users
# [{"id":1,"email":"test@example.com","created_at":"2026-07-29T10:00:00.000Z"}]If you get a 503 or timeout, check that:
- 1The server binds to
0.0.0.0, notlocalhost - 2The
PORTenvironment variable is read correctly (default3000) - 3The health check path (
/) returns 200 within 30 seconds of startup
The dashboard Logs tab shows build and runtime logs, including database connection errors.
Why this matters
Frameworks like Hono are designed for edge runtimes (Cloudflare Workers, Deno Deploy) where you connect to external databases like Neon or PlanetScale. But edge databases charge per request or per GB egress, and cold starts reset connections. PandaStack's managed Postgres runs in the same Kubernetes cluster as your app, so latency is sub-5ms and egress is free. You get the ergonomics of edge frameworks with the economics of traditional hosting.
The free tier includes 1 database with 7-day backup retention and 50 connections. Paid plans start at $15/mo and increase the connection limit to 300, add 15-day backup retention, and enable in-place restores.
Full project structure
hono-api/
├── src/
│ ├── index.js # Hono app + routes
│ └── db.js # Postgres pool
├── server.js # Node.js server entrypoint
├── pandastack.json # Deploy config
├── package.json
└── README.mdPush this to GitHub, create the project via the API or dashboard, link the database, and the app is live.
References
- [Hono Documentation](https://hono.dev/)
- [node-postgres Documentation](https://node-postgres.com/)
- [PandaStack Database Documentation](https://docs.pandastack.io/databases)
- [PostgreSQL Connection Pooling Best Practices](https://www.postgresql.org/docs/current/runtime-config-connection.html)