Connecting an Application to a Database
From the connection string to the choice between an ORM and raw queries, plus two things that cannot be skipped: credentials that do not leak, and queries that cannot be injected into.
Connecting an application to a database always consists of the same parts, whatever the language: the server address, the database name, credentials, and a driver that translates the application's commands into a protocol the database understands.
Configuration lives in the environment, not the code
Credentials go in environment variables so their values can differ between a laptop, staging and production without changing a line of code. The .env file never enters Git — only .env.example does, listing the variable names with no values.
# Laravel — .env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=vms
DB_USERNAME=vms_app
DB_PASSWORD=…
# Node / Next.js — .env.local
DATABASE_URL=postgresql://vms_app:…@127.0.0.1:5432/vmsConnection pooling
Opening a connection is expensive, so an application keeps a set of open connections to be used in turn. The number is finite — when they are all in use, the next request waits. This is a common reason an application suddenly slows under load while the queries themselves are fast: the query is not slow, it is queuing for a connection.
ORM, query builder, or raw SQL
| Approach | Strength | Suits |
|---|---|---|
| ORM (Eloquent, Prisma) | Concise, relations are easy, safe from injection by default | Everyday CRUD |
| Query builder | Still reads as code, but sits closer to SQL | Queries with dynamic conditions |
| Raw SQL | Full control, can use database-specific features | Reports and complex aggregation |
All three can coexist in one application. A healthy pattern: an ORM for ordinary operations, raw SQL for the reports that are genuinely awkward to express through it.
Never build a query by concatenating strings
Inserting user input straight into query text makes that input readable as a command. This is SQL injection. The defence is not filtering strange characters but using bound parameters — the database receives the query and the values separately, so a value can never be treated as a command.
-- Dangerous: the input becomes part of the command
"SELECT * FROM users WHERE email = '" + email + "'"
-- input: ' OR '1'='1 → every row comes back
-- Safe: the value is sent separately from the command
SELECT * FROM users WHERE email = ?Before calling it done
- 01Use a dedicated application database user with only the rights it needs — not a superuser account.
- 02Set timeouts, so a stuck query does not hold a connection forever.
- 03Handle connection failure with a clear message; databases are occasionally unreachable.
- 04Make sure the error text reaching users carries no database structure detail.
- 05Test the restore path, not just the backup process.