Also worth stating plainly: middleware should not be your authorisation. Verifying a signature there is fine as a cheap redirect for logged-out users, but the actual check belongs where the data is read, in the route handler or the server component. If somebody ever hits your API directly, middleware is not what saves you. Treat it as UX, not as a gate, and the pressure to cram a database call in there disappears.
Rae Doughty
@two_vcpu_club
I deploy a Django app for a nonprofit on the cheapest server that will hold it. Most of my expertise comes from making builds fit in 1GB of RAM without crying.
42 credit Contributor
- From answers
- 0
- From questions
- 42
- First Question · Bronze badge · Asked your first question. · Earned July 31, 2026
- First Answer · Bronze badge · Answered somebody for the first time. · Earned July 31, 2026
- First Credit · Bronze badge · Earned credit for the first time. · Earned July 31, 2026
- Welcomed · Bronze badge · Reached 10 credit. · Earned July 31, 2026
- Helper · Bronze badge · Wrote 10 answers. · Earned July 31, 2026
- All badges
The barrel file thing is real - one import of @/lib was dragging in the email client and a PDF renderer into an endpoint that returns JSON. Server chunk went from 4.1MB to 900KB.
Far more common than the copy answers in this thread. Check the actual load waterfall on a throttled connection before rewriting a single word.
Different angle: the regulatory risk to someone sending 30 a day is not the thing that will actually cost you. The thing that costs you is one irritated recipient who decides to make a point, and then you spend a fortnight writing careful letters instead of building. That risk is managed by not being annoying - one email, one short follow-up, obvious opt-out, never a fourth message - far more than by picking the right legal basis in a document nobody reads.
Check whether your platform has a minimum instances setting before building anything. It is the supported version of what you are trying to fake, it keeps N instances alive rather than one, and it is usually priced as "you are paying for an always-on instance" - which is the same money as the container with none of the migration. If it exists, use it. If it does not, take the container.
Agreed on order. Runtime, then build tooling, then framework, then your own code. Doing it in any other order is where the whole weekend goes.
Did exactly what the comment above says three months ago and it worked, so here are the mechanics. Found the partner page buried in the docs footer, filled a form asking for company name (put my own name, sole trader, nobody cared), a one paragraph description, and expected monthly call volume. Estimated high on purpose. Got a reply in eleven days from an actual person saying the use case was fine and pointing me at a rate limit tier I didn't know existed.
It isn't a contract and they could change their mind, but it is in an email and I sleep better. They also asked me not to use their brand name in my domain, which I would absolutely have done otherwise.
Mostly right, but the once-a-year version is worth doing precisely because it catches the forgotten subscriptions, which are pure waste rather than a tradeoff. Once a year, an hour, then leave it alone.
The pooler is not the only pool in the picture. Your ORM opens its own client-side pool inside every function instance, and the default size is usually derived from CPU count - commonly something like 5 to 9 connections per instance. 120 instances times 5 is 600 clients arriving at a pooler that will happily accept a bounded number and then refuse the rest.
Append the limit to the pooled URL:
postgres://...:6543/db?pgbouncer=true&connection_limit=1
One connection per instance is correct for serverless. The instance handles one request at a time, so a pool inside it buys you nothing and costs you 4 to 8 sockets.
While you are there, confirm the host really is the pooled one. On most providers the pooled endpoint is a different hostname, not just a different port, and it is very easy to change 5432 to 6543 on the direct host and get a connection refused or a silent fallback depending on the provider.
It reads like a safety limit and it is actually a per-instance allocation. The mental model that fixes it: in serverless you are not sizing a pool for your app, you are sizing it for one request.
That is the most common answer for a first project, because scale-to-zero is usually the default on the free and cheap tiers - which is exactly right for a hobby project and surprising the moment somebody else is using it. Now you know what you are buying if you ever upgrade the plan.
Normal, and there are usually two separate sleeping things rather than one. Measure before you touch anything:
const t0 = performance.now()
// ... your handler
res.headers.set('Server-Timing', `db;dur=${tDb}, handler;dur=${tAll}`)
plus a module-scope const boot = Date.now() you log once, so you can tell a cold instance from a warm one.
Then force a cold one and read the split:
- most of the second is before your code runs: that is runtime and bundle init, and the fix is a smaller server bundle or paying for a warm instance.
- most of it is the first database call: your database is probably scaling to zero and waking up, which on managed Postgres with auto-suspend is commonly 500ms to a second. Turning off auto-suspend or picking a plan that stays warm fixes that half.
Guessing between those two costs a weekend. Measuring costs ten minutes.
Real name, real company, real sentence, or leave it empty. Anonymous initials under a stock photo are worse than nothing and everybody can spot them.
Disagreeing on the on-call part. The $7 VPS is cheap in dollars and not cheap in attention: unattended upgrades, a firewall, log rotation before the disk fills, TLS renewal you will find out has broken on a Sunday, and a deploy story you build yourself. None of it is hard, all of it is yours forever, and the failure mode is you being the only person who knows how it is wired.
Workers deletes that entire column for about $40/month. Whether $40 is worth it depends on what an hour of your time is worth and how much you enjoy sysadmin work. For me it stopped being worth it the second time I debugged a full disk at midnight.
Whatever you pick, do not run it as a coupon code that spreads. Attach it to the account at checkout based on the card, or it will end up on a deals site within a month.
MicroVM services are genuinely good and the boot times are in the low hundreds of milliseconds, so it is not the performance argument people expect. The reason I would not start there as a solo dev is that it puts a network hop and somebody else's API between you and your own test suite, and the debugging story when a run behaves differently in there is worse than the problem you were solving. Reach for it when you have untrusted input, which is a real line and you will know when you cross it.
This is the version I'd recommend to anyone with a services background. A productised implementation is a real business and it doesn't feel like giving up.