DEV Community

Olga Braginskaya
Olga Braginskaya Subscriber

Posted on • Originally published at datobra.com on

Build in Public: Week 8. We Finally Deployed This Thing

Last week technically never happened.

We didn’t skip a post, didn’t disappear into Christmas and New Year food comas and definitely didn’t spend a suspicious amount of time eating baked goods instead of shipping software. Let’s assume we simply compressed time and released everything at once.

Because this week we finally deployed Wykra!

It’s deployed in exactly the state you’d expect at this stage. The UI is minimal, testing is uneven and some limits are deliberately strict. We spent more time than planned just getting everything wired together, but the system is now live, reachable and doing real work, which was the point.

What’s live now

The web UI (also used by the Telegram mini app):

https://app.wykra.io/

The Telegram Mini App:

https://t.me/wykra_bot

Authentication in the web UI is done via GitHub, while the Telegram mini app uses Telegram’s Web App data validation flow as described in the official documentation: https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app. At the moment the API is protected by a fairly strict rate limiter, five requests per hour per API token, not because this is the ideal user experience, but because we want to observe how the system behaves under real usage, understand where the actual bottlenecks are and avoid discovering those limits the hard way.

There is also a known limitation on search at the moment, because of course there is. Full creator discovery is still being stabilized, so the UI currently exposes only profile analysis. After logging in you’ll land in a chat interface where you can ask for an analysis of a specific profile instead of running an open search. This keeps the surface area small while we validate the core analysis flow. This is an example of a profile analysis generated for a randomly chosen public account:

At this point you’re probably wondering how we actually deployed all of this, so let’s talk about that.

How this started (spoiler: with a domain)

The deployment push started in a very unglamorous way. After publishing the original challenge post

https://dev.to/olgabraginskaya/wykra-web-you-know-real-time-analysis-20i3

I bought a domain for the project wykra.io, for an almost embarrassing amount of money.

Why Railway and not AWS

At first we obviously wanted to do this like adults - AWS, Hetzner, a serious setup and a lot of infrastructure feelings. Then we remembered what stage this project is actually at and that we mostly want it to be live now, not perfectly architected sometime later.

So we went with something simpler and faster for the moment and chose a managed service like Railway. It let us deploy multiple services quickly and keep the focus on the product instead of turning infrastructure into another side project. I also genuinely enjoy how it automatically picks up repository changes and how clean the UI feels.

Deployment setup (the non-romantic version)

After logging into Railway via GitHub and approving the Railway app for the repository, we added the following services:

First, a Postgres volume. We use it as the main system of record: it stores users, chat history, profile analyses, search results and task state. Railway gives you both private networking for internal service-to-service communication and public networking for external TCP access if needed. We use private networking for everything inside the system.

We also added a Redis instance, mainly for caching and a few short-lived things that shouldn’t live in Postgres.

Then the core services:

  • wykra-api Built from the Dockerfile in the project root. All environment variables are configured directly in the Railway service, except database credentials, which are taken from the Postgres private networking configuration. This service is exposed via api.wykra.io.
  • wykra-web The React frontend used both for the web UI and the Telegram mini app. Built from the Dockerfile in /apps/web and exposed via app.wykra.io.
  • Grafana Built from /apps/grafana and exposed via grafana.wykra.io.
  • Prometheus Built from /monitoring/prometheus and used internally for metrics collection.

Grafana and Prometheus handle observability, we already wrote about why this matters and how we set it up in Week 5: https://dev.to/olgabraginskaya/build-in-public-week-5-the-week-we-finally-measured-things-instead-of-just-hoping-for-the-best-2kok

Domains, DNS and Things We Broke

Railway supports custom domains per service and gives you a neat DNS setup with a CNAME pointing to a *.up.railway.app address. This works perfectly fine unless your domain registrar is GoDaddy, which, as you’ve probably guessed, is exactly our case.

GoDaddy doesn’t support CNAME flattening or dynamic ALIAS records, so adding the record fails with a familiar “Record data is invalid” error. The recommended workaround (and the one we followed) is moving DNS management to Cloudflare. We switched the nameservers, added the domain there and configured the Railway CNAME records in Cloudflare instead. After that, everything became reachable.

Except for email.

We forgot to re-add Google Mail DNS records, which broke email for roughly three days, and the internal postmortem title involved DNS and poor life choices. I laughed for at least two hours, mostly at how we eventually figured out what the actual issue was, even if my brother didn’t find it nearly as funny.

No further comments.

Rate limiting (on purpose)

When you deploy something publicly that actively uses two paid APIs - Bright Data and OpenRouter - and you do it for free, there is a very natural moment where you stop and think about how not to accidentally burn all your money in a weekend.

That’s where rate limiting comes in.

The API uses a token-based rate limiting system implemented with the NestJS Throttler module, where each incoming request is tracked per API token. The token provided in the Authorization header is hashed using SHA-256 and then used as the rate-limiting key, so all requests made with the same token are counted together. In its current configuration, the system allows up to five requests per hour per token within a sixty-minute window, with counters stored in memory.

Rate limiting is applied globally through a custom guard registered as an APP_GUARD, which means it affects all routes by default. Once the limit is exceeded, the API responds with a 429 error and a clear message explaining why the request was rejected. Public routes are excluded from rate limiting and authenticated routes can explicitly opt out when needed.

Trying the API via Postman

For anyone who wants to poke the API directly, there is a Postman collection available here:

https://github.com/wykra-io/wykra-api/tree/main/postman-api

The flow looks like this:

  • Import the postman-api folder into Postman.
  • Create an environment variable apiUrl with the value https://wykra.io.
  • Generate a GitHub Personal Access Token from your GitHub settings.
  • Call the /api/v1/auth/githubAuth endpoint with that token as a Bearer token.
  • The response will contain a Wykra API token, which is subject to the five-requests-per-hour limit.
  • Use that token as the Authorization Bearer token for all other API endpoints.

Where this leaves us

Wykra is now deployed, with a domain, a UI, an API, a Telegram mini app and basic metrics and monitoring wired in. None of it is perfect, and yes, the current frontend still makes me wince a little in a very “students built this as a lab assignment” kind of way, but well, what can you do - that’s real developer life and there always will be another week.

If you want to support the project, starring the repo and following along helps more than you’d think:

Repo: https://github.com/wykra-io/wykra-api

Website: https://app.wykra.io/

Twitter/X: https://x.com/ohthatdatagirl

Blog: https://www.datobra.com/

Top comments (4)

Collapse
 
iscander01 profile image
Novea Madundo

Amazing! Congrats on the successful release!

Collapse
 
zlayaboroda_3f2ffdd0279ac profile image
ZlayaBoroda

Rate limiting makes sense

Collapse
 
__8f6cbf9bb574 profile image
PinkoPonko

Telegram mini app + web UI sharing the same frontend is a smart simplification

Collapse
 
__123f2e8d26 profile image
John Rusliksheld

DNS problems never miss.