Here's a mistake I bet a lot of developers coming from traditional hosting will make on their first serverless edge project: assuming a local database file will just... work.
I made that mistake building OptiQ, a sales-tracking app for small optical shops, on Tencent EdgeOne Makers. This post is less "here's a polished tutorial" and more "here's the debugging path I took," because I think the reasoning matters more than the final answer.
The Project, Briefly
Small optical shops have a messy sales pattern that most POS templates don't handle well. A customer might buy:
- Just a frame
- Just a lens
- A complete pair (frame + lens)
- Softlens
- Small accessories (cleaning solution, frame screws) — no prescription needed On top of that, the shop needs to track refraction data (OD/OS, cylinder, axis) per customer, and sometimes link it to a sale — but not always. OptiQ is my answer to that: a small, relational, low-cost app built specifically around that flexibility.
Low-cost was non-negotiable here. I'm the only IT person at my company, so I don't get to justify infrastructure spend for a side project — it has to run on free tiers, or it doesn't ship.
Mistake #1: Treating an Edge Function Like a Regular Server
For context, EdgeOne Makers is the rebrand of EdgeOne Pages (as of June 2026) — it grew from "frontend hosting" into a full-stack platform for Web and AI Agent apps, with support for React, Vue, Next.js, Astro, and a few agent frameworks. That expanded scope is what made it worth using for something beyond a static site.
My plan was simple: static frontend on Pages, API logic on Pages Functions, SQLite file for storage. Deploy was smooth — genuinely, push-to-live in minutes, no CDN or SSL setup needed.
Then I hit the wall: edge functions are stateless. There's no persistent filesystem, and a given request might get routed to a different edge instance than the last one. A .db file written on one request could be gone — or inconsistent — by the next. This isn't an EdgeOne-specific limitation; it's true of serverless edge compute generally. But it's an easy thing to forget if your mental model still comes from VPS-style deployments.
Lesson: figure out your state strategy before you write a single API route, not after your first weird bug report.
Mistake #2 (Almost): Picking a Database That Sleeps When You Need It Awake
Once I accepted I needed a managed database, I evaluated three options:
Option 1 — EdgeOne's native KV store. Fast, simple, and built in. But it's key-value only — no joins, no relational queries. For OptiQ, that would mean hand-rolling indexes for every access pattern (transactions by customer, transactions by date range) and computing monthly totals by pulling records into app code and summing manually. It works, but it's the kind of thing that quietly turns into technical debt.
Option 2 — Supabase. Full Postgres, generous free tier, great DX. Then I read the fine print: free projects pause after ~7 days of inactivity and need a manual restore to wake back up. A shop that doesn't sell something every single day could open the app to a database that's asleep. That's a bad failure mode for a business tool, even a small one.
Option 3 — Turso. SQLite (libSQL), but run as a managed service and queried over HTTP — which means it works fine from a stateless edge function, unlike a local file. Real SQL (joins, constraints, aggregates), no idle-pause behavior. This is what I went with, and the schema I'd already sketched needed almost zero changes to work with it.
The Part of the Schema That Actually Matters
Most of OptiQ's schema is unremarkable — customers, products, transactions, transaction line items. The one decision worth calling out:
CREATE TABLE transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER REFERENCES customers(id),
refraction_id INTEGER REFERENCES refractions(id), -- nullable, on purpose
total REAL NOT NULL
);
refraction_id is nullable. That's the whole trick. Without it, I'd have needed separate transaction tables (or awkward sentinel values) for "sale with a prescription attached" vs "sale without one" — like buying a bottle of lens cleaner shouldn't require a fake prescription record just to satisfy a NOT NULL constraint. One nullable foreign key instead of a schema fork.
Where EdgeOne Makers' Full-Stack Positioning Actually Paid Off
The thing I didn't expect going in: not having to stitch together three separate vendors. Static hosting, edge API functions, and (once I wired it up) the database connection all lived in one deploy pipeline. If you've ever debugged a CORS or env-var mismatch between a frontend host, a serverless function provider, and a database vendor that don't share tooling, you'll get why that's worth mentioning.
If You're About to Build Something Similar
- Decide your state/database approach before your first API route — not as a fix after deploying.
- Check what happens to your database on inactivity, not just under load. "Free tier" often hides a failure mode in the fine print.
- Use nullable foreign keys deliberately when your domain has "sometimes this relationship exists, sometimes it doesn't" — it's cheaper than modeling every variant as a separate table. OptiQ is live at optiq.logota.biz.id — built on Tencent EdgeOne Makers as part of Codepolitan's DevHandal 2026 Batch 2.
#TencentEdgeOne #EdgeOneMakers #CODEPOLITAN #EdgeOne
Top comments (0)