Build a Restaurant Telegram Mini App with Google Stitch and Google AI Studio
AI-assisted development becomes much easier to understand when we use it
to build a real application.
In this tutorial, we will create a Restaurant Telegram Mini App
using Google Stitch, Google AI Studio, GitHub, Visual Studio Code,
deployment tools, and Telegram.
Rather than generating everything with one enormous prompt, we will
build the application incrementally and test every important stage.
Our final goal is a restaurant application that customers can open
online or directly inside Telegram.
The application will eventually allow customers to:
- view the restaurant
- browse the menu
- search for dishes
- view dish details and prices
- reserve a table
- enter their contact information
- receive a reservation confirmation
- use the application on desktop and mobile
- open the application inside Telegram
This article assumes that you have already read Google AI App
Development Fundamentals — Stitch, AI Studio, GitHub & Deployment,
where we introduced the technologies used in this project.
1. What Are We Going to Build?
Our project will be called:
Restaurant Telegram Mini App
For development purposes, we can initially use the restaurant name:
Test Restaurant
The name can easily be changed later for a real customer.
The project will progress through several stages:
Google Stitch
↓
UI/UX Design
↓
Google AI Studio
↓
Functional Web Application
↓
GitHub
↓
Visual Studio Code
↓
Testing and Improvements
↓
Deployment
↓
Public HTTPS URL
↓
Telegram Bot
↓
Telegram Mini App
The important point is that we are not simply asking AI to create a
website.
We are following a complete application-development workflow.
2. Project Requirements
Before opening an AI development tool, we should define what we want to
build.
This prevents the AI from making too many assumptions.
Main Application Sections
Our first version should contain:
- Home
- About
- Menu
- Dish Details
- Reservation
- Contact
Later versions can include additional functionality.
Menu Features
The menu should eventually support:
- starters
- main courses
- desserts
- drinks
- food images
- dish descriptions
- prices
- menu search
- dish detail view
Reservation Features
The reservation form should eventually contain:
- customer name
- phone number
- number of guests
- date
- available time
- special requests
- reservation confirmation
Language
The application should eventually support:
- English
- Swedish
We will initially design the application in English and add multilingual
functionality later.
3. Why Mobile-First Design Is Important
This project will eventually run as a Telegram Mini App.
Many customers will therefore use it from a smartphone.
For that reason, our design should be mobile-first.
That means we should prioritize:
- readable text
- large buttons
- touch-friendly controls
- simple navigation
- fast interaction
- responsive images
- uncluttered screens
The application should still look professional on desktop computers, but
the mobile experience has priority.
4. Step 1 — Open Google Stitch
Open a modern web browser such as Google Chrome or Microsoft Edge and go
to:
Google Stitch: Go to
https://stitch.withgoogle.com/ and
click Try Stitch in the upper-right corner (in the first time). Sign
in with your Google account if requested; you will then enter the Stitch
workspace, at the upper-right you have two choices:
- + Start with your design — start from an existing
design/image. - + Blank project — create a new empty project.
For our Restaurant Telegram Mini App, click + Blank project. and in
the uper left you see New project with edit button click to it and give
the project name, for me i give the name:
restaurant-telegram-mini-app
After creating the Restaurant Telegram Mini App project, Stitch
opens the design workspace. “What would you like to change or
create?”, in the next step you will use to generate the first design.
5. Step 2 — Create the First Restaurant Design
We will begin with one structured prompt.
Do not ask Stitch to create the database, email system, Telegram
integration, and every other feature yet.
At this stage, our objective is design.
Copy the following prompt into Google Stitch prompt field labeled (
“What would you like to change or create?”), paste the following
prompt, and then click the ↑ arrow on the right to generate the
first design.
Google Stitch Prompt — Version 1
Create a modern, elegant, mobile-first restaurant web application.
Restaurant name: Test Restaurant.
The application should be designed primarily for smartphones and
should also work beautifully as a future Telegram Mini App.Create the following sections:
Home:
- Elegant restaurant hero section
- Restaurant name
- Short welcome message
- Book a Table button
- View Menu button
About:
- Short restaurant introduction
- Elegant food or restaurant imagery
- Brief description of the restaurant experience
Menu:
- Categories for Starters, Main Courses, Desserts and Drinks
- Food cards containing image, dish name, short description and
price- Search interface
- Selecting a dish should provide access to more detailed
informationReservation:
- Customer name
- Phone number
- Number of guests
- Date
- Reservation time
- Special requests
- Reserve Table button
Contact:
- Address
- Telephone
- Opening hours
- Map placeholder
Language:
- Prepare the interface so that English and Swedish can be
supported.Design requirements:
- Premium Scandinavian restaurant style
- Modern and elegant
- Mobile-first responsive design
- Large touch-friendly buttons
- Clear typography
- High-quality food imagery
- Simple navigation
- Suitable for use inside a Telegram Mini App
- Avoid overcrowding the interface
- The desktop version should also look professional and responsive.
Wait design process is finished, when it finished Stitch shows in the
left side what have been created and in the right side shows design.
6. What Should Happen After the Prompt?
Stitch should now interpret our requirements and create an initial
restaurant interface.
Do not immediately accept everything that AI generates.
Examine the result carefully.
Look at:
- header
- navigation
- hero section
- typography
- colors
- menu presentation
- food cards
- reservation form
- contact section
- mobile layout
Ask yourself:
Would I be comfortable showing this design to a restaurant customer?
If the answer is no, we improve it before continuing.
This is one of the most important principles in AI-assisted development:
Generate → Review → Improve → Test
not:
Generate → Accept Everything
7. Step 3 — Refine the Stitch Design
We should make improvements using smaller prompts.
For example:
Make the restaurant design feel more premium and elegant. Reduce
unnecessary visual elements and use more whitespace.
Another prompt could be:
Improve the mobile navigation. Make all important controls easy to use
with one hand on a smartphone.
Another:
Make the menu cards more visually appealing. Use larger food images
and make the dish name and price immediately visible.
And another:
Improve the reservation form for mobile users. Use large input
controls and clear labels.
Notice that these prompts are focused.
We are not regenerating the entire application every time.
8. Step 4 — Test the Design Before Moving On
Before leaving Stitch, check the major screens.
Home
Does the visitor immediately understand that this is a restaurant?
Are View Menu and Book a Table easy to find?
Menu
Are food items easy to browse?
Are prices visible?
Reservation
Can a smartphone user comfortably complete the form?
Contact
Can the visitor quickly find:
- address
- phone
- opening hours
Mobile Experience
Does everything fit comfortably on a narrow screen?
If something does not look right, correct it in Stitch before moving to
application development.
9. Development Checkpoint 1
At this point we should stop and record what Stitch actually created.
For this tutorial, we will document:
- the final Stitch prompt
- screenshots of the generated design
- changes we requested
- problems we encountered
- the final accepted UI
This gives us our first project milestone:
Version 1 — Restaurant UI Design
We should not continue until this version looks good.
10. Step 5 — Export the Stitch Design to Google AI Studio
Once you are satisfied with the restaurant design in Google Stitch, the
next stage is to turn the visual design into a working application.
In the Stitch project workspace, look at the upper-right corner. You
should see controls including:
- Preview/Run
- Export
- Share
For this project, we want to continue development in Google AI
Studio.
Select the Restaurant Screens
Click Export.
If no screens are currently selected, Stitch displays:
No screens selected
Select the screens that you want to use in the application.
Because we want to continue with the complete restaurant project, we can
select all the screens by pressing:
Ctrl + A
After selecting the screens, the Export panel displays the available
export formats.
Depending on the current version of Stitch, you may see options such as:
- AI Studio
- Figma
- MCP
- Netlify
- Lovable
- Bolt
.zip- Code to Clipboard
- Project Brief
Some options may be marked Preview, and the available choices may
change as Google continues developing Stitch.
Export to AI Studio
Select:
AI Studio
Stitch explains that AI Studio will open and that the required HTML and
image files will be transferred/downloaded in the background. If many
screens are selected, this process may take a minute or two.
Now click:
Build with AI Studio
This is an important step. We are not creating another restaurant
application from scratch. Instead, we are handing the screens and
assets from our existing Stitch project to Google AI Studio.
Our development workflow is now:
Google Stitch → Export to AI Studio → Build Application → Test &
Improve → GitHub → VS Code → Deployment → Telegram Mini App
11. Step 6 — Build the Exported Stitch Design in Google AI Studio
After clicking Build with AI Studio in Google Stitch, Google AI
Studio opens automatically.
If Google asks you to sign in, use your Google account.
AI Studio opens its application-building environment and prepares a new
build request based on the screens and assets that we exported from
Stitch.
Check the Exported Stitch Files
Before starting the build, look at the files attached to the AI Studio
request.
You should see several files representing parts of the Stitch project,
such as:
- PNG images
- JPEG images
- SVG assets
- HTML files
- Other design-related files
These files are important because they give AI Studio visual and
structural information about the application we designed in Stitch.
AI Studio also automatically prepares an initial instruction similar to:
Build me an app with screens that look like this. You can hotlink
images from the html
This confirms that AI Studio is using the exported Stitch screens as
references for generating the application.
Do Not Create Another New App
You may notice + New app under the BUILD section in the AI
Studio menu.
Do not use + New app for this project at this stage.
We already started the application in Google Stitch and exported its
screens directly to AI Studio.
Creating another app separately and entering another restaurant prompt
would start a separate AI Studio project instead of continuing from the
design that we already created in Stitch.
Our workflow is:
Stitch Design → Export to AI Studio → Build → Test → Improve
Start the Initial Build
Check that the exported Stitch files are visible in the AI Studio build
request.
At this point, do not remove the automatically attached files or replace
the initial instruction.
Click the Build button.
AI Studio will now analyze the exported Stitch screens and assets and
begin generating the application.
Wait for the Build to Finish
During the build process, you may see AI Studio creating and modifying
many source-code files.
For example, AI Studio may generate:
.html— HTML files used by the web application.css— application styling and visual presentation.ts— TypeScript files containing data, types, utilities, and
application logic.tsx— React components written using TypeScript and JSX.json— application configuration and metadata
Do not worry if you did not create these files yourself.
This is one of the important differences between Stitch and AI
Studio.
Stitch helped us create the visual design and user experience.
AI Studio is now interpreting that design and generating the actual
application source code.
The build may take several minutes, particularly when many Stitch
screens and images have been exported.
Allow AI Studio to finish before making additional changes.
When the build is complete, AI Studio indicates that the application is
ready and displays the generated application under Preview.
Review the Action History
After the build, AI Studio may display an Action history showing
which files Gemini created or modified.
For example, our restaurant application generated files such as:
metadata.jsonindex.htmlsrc/index.csssrc/types.tssrc/data/restaurantData.ts- React components such as
Header.tsxandNavigation.tsx
You do not need to understand every generated file at this stage.
The important point is that we have moved beyond a visual prototype. AI
Studio has now generated a real application structure containing source
code that we can later inspect, modify, test, and store in GitHub.
Review the AI Studio Build Summary
AI Studio may also provide a written summary explaining what Gemini
created.
For our restaurant project, AI Studio reported that it had built the
Test Restaurang & Matsal application and described features
including:
- Scandinavian restaurant styling and typography
- Home screen and restaurant hero section
- Seasonal menu
- Dish search and filtering
- Table reservation flow
- About and restaurant information
- Contact and location information
- English and Swedish language controls
- Guest and reservation-related interface elements
Your own build summary may be slightly different depending on the Stitch
screens, prompts, assets, and the version of AI Studio being used.
The summary is useful because it tells us what Gemini intended to
implement.
However, there is an important rule:
Do not assume that a feature works simply because AI Studio lists it
in the build summary.
For example, AI Studio may mention:
reservation confirmation, real-time search, Telegram
integration, or guest reservations.
At this stage, some of these may only be frontend interfaces,
demonstrations, or simulated interactions.
They do not necessarily mean that we already have:
- A production database
- A real reservation backend
- Email confirmation
- Telegram Bot integration
- Persistent customer data
- A production booking system
We will add and verify those capabilities step by step later.
Preview the Initial Application
Now look at the Preview area.
AI Studio should display the running application that it generated from
the Stitch design.
Do not immediately give AI Studio another large prompt or start adding
more features.
First, explore what has already been generated.
Check areas such as:
- Home page
- Navigation
- Menu
- Menu categories
- Food images
- Dish information
- Search and filters
- Reservation interface
- Contact information
- About page
- Mobile navigation
- Buttons and controls
- English and Swedish language switching
- Overall visual design
Click the navigation buttons and interact with the application just as a
restaurant customer would.
Compare AI Studio with the Stitch Design
The generated AI Studio application does not have to reproduce every
Stitch screen pixel-for-pixel.
Stitch provided the design foundation and visual references, while
AI Studio interpreted those references and converted them into
application code.
AI Studio may therefore make some design or implementation decisions of
its own.
This is normal.
What matters is that the application maintains the intended restaurant
experience and gives us a good technical foundation that we can continue
improving.
At this point, our project has moved from:
Designing the application in Google Stitch to: Building and
testing the application in Google AI Studio
12. Step 7 — Test the Initial AI Studio Application
Before giving AI Studio additional development requirements, we should
test the application it has already generated.
This is an important part of AI-assisted development.
AI Studio may already have created more functionality than we expected
from the initial Stitch export. Instead of immediately asking Gemini to
rebuild or add features, we first need to determine:
What already works? What works only partially? What is still
missing?
For now, do not modify the generated code and do not give Gemini
another development prompt.
We are going to use the Preview area in Google AI Studio and test
the generated application just as a restaurant customer would.
Where Do We Test the Application?
Look at the Preview panel on the right side of Google AI Studio.
This is the running version of the application that AI Studio generated
from our Stitch design.
You do not need to open another application or browser window for this
initial test.
At the bottom of our generated restaurant application, we have the main
navigation:
- Home
- Menu
- Book Table
- About
- Contact
We will test these sections one by one.
Do not try to fix problems immediately. First, identify and record them.
After testing the complete application, we can give AI Studio a more
precise development prompt.
Test 1 — Home
Start with Home.
You are probably already on this screen when the application first
opens.
Scroll slowly from the top of the Home page to the bottom.
Check:
- Does the restaurant hero image load correctly?
- Is the restaurant name displayed correctly?
- Is all text readable?
- Does the page fit correctly on the mobile-sized screen?
- Are any elements overlapping or cut off?
- Are buttons large enough to use comfortably?
- Are images displayed correctly?
- Is the visual style consistent?
- Is English and Swedish text being mixed unexpectedly?
Also inspect important Home buttons such as:
Book a Table
and:
Explore Seasonal Menu
Do not assume that a button works simply because it appears on the
screen. We will test the actions as part of our application testing.
Test 2 — Menu
At the bottom of the Preview, click:
Menu
Check whether the Menu screen opens correctly.
Then inspect:
- Menu categories
- Dish names
- Dish descriptions
- Food images
- Prices
- Search
- Category filters
- Dietary filters, if available
- Dish details
- Tasting-menu information, if available
Try the interactive controls.
For example, if there is a search box, search for one of the displayed
dishes.
If there are category buttons such as Starters, Main Courses,
Desserts, or Drinks, click them and check whether the displayed
dishes change correctly.
If clicking a dish opens additional information, test that as well.
Test 3 — Book Table
Next, click:
Book Table
Check whether the reservation interface opens correctly.
Inspect whether you can select or enter information such as:
- Number of guests
- Reservation date
- Reservation time
- Seating option, if available
- Customer name
- Phone number
- Special requests, if available
Use test information, not real customer information.
For example:
Name: Test Customer Email: test@example.com
Try submitting the reservation and observe what happens.
If AI Studio displays a confirmation and booking reference, make a note
of it.
However, remember that a confirmation screen does not necessarily mean
that the reservation has been permanently stored. We have not yet
intentionally implemented and verified a production database.
Test 4 — About
Click:
About
Check that the About screen opens correctly.
Inspect:
- Restaurant description
- Images
- Chef or restaurant information
- Typography
- Page layout
- Expandable sections, if available
- Mobile presentation
If AI Studio has generated interactive elements, test those as well.
Test 5 — Contact
Click:Contact and Check:
- Restaurant address
- Telephone number
- Opening hours
- Map or location information
- Contact buttons
- Telegram-related controls, if present
- Any expandable FAQ sections
Again, click interactive controls where appropriate and note anything
that does not work as expected.
Test the English and Swedish Languages
After testing the main sections, test the language controls:
EN / SV
First select EN and navigate through several screens.
Then select SV and repeat the test.
Check whether:
- Headings change language
- Navigation changes language
- Buttons are translated
- Menu information changes
- Reservation labels change
- Contact information remains correct
- English and Swedish are not unintentionally mixed
Our initial generated Home screen, for example, contained both Swedish
and English text. This is exactly the kind of issue that this test
should identify.
Test the Mobile Experience
Because our application will eventually be used as a Telegram Mini
App, mobile usability is particularly important.
While testing each section, check:
- Text readability
- Button size
- Navigation
- Scrolling
- Image sizing
- Form controls
- Spacing
- Bottom navigation
- Whether content extends outside the screen
- Whether important actions are easy to reach
The application should be comfortable to use on a smartphone, not merely
able to shrink to a mobile screen.
Keep a Simple Test List
As you test the application, make a simple list of what works and what
needs improvement.
For example:
Working
- Home screen loads
- Main navigation works
- Menu opens
- Food images display
- Reservation form opens
- Mobile bottom navigation works
Needs Improvement
- English and Swedish are mixed
- Restaurant name is inconsistent
- A menu filter does not work
- Reservation validation is incomplete
- Some buttons do nothing
- Confirmation needs improvement
Your results will probably be different. The purpose is to create a list
based on the actual generated application.
Do Not Fix Problems One by One Yet
If you discover a problem, resist the temptation to immediately send
Gemini a new prompt for every small issue.
Complete the initial test first.
We want to understand the state of the entire application before making
changes.
Once we know what works and what does not, we can give AI Studio a clear
and focused development request while telling it to preserve the parts
that already work.
This gives us a much safer AI-assisted development cycle:
Build → Preview → Test → Identify Problems → Give Requirements →
Rebuild → Test Again
13. Step 8 — Give AI Studio the Development Requirements
Now that we have tested the initial application, we know what already
works and what needs to be corrected.
This is a better time to give AI Studio additional development
instructions.
Instead of asking Gemini to rebuild the entire application, we should
provide a specific list of improvements while telling it to preserve
the parts that already work.
Use the Test Results as Development Requirements
During our initial test, we discovered several issues:
- Some English and Swedish text is mixed.
- The restaurant name is inconsistent.
- The reservation date is incorrect.
- The reservation date cannot be changed properly.
- Reservation navigation needs improvement.
- Done / Klart does not correctly start a new reservation.
- The purpose of View Flow in the tasting menu is unclear.
- Telephone and email actions on the Contact page are not functional.
- Email reservation confirmation has not yet been implemented.
Instead of fixing these problems one at a time, we can give AI Studio a
clear development prompt based on our test results.
AI Studio Development Prompt — Version 1
In Google AI Studio, look at the bottom-left area below the Gemini
conversation. You should see an input box labeled:
“Make changes, add new features, ask for anything”
Click inside this box. This is where we can give Gemini additional
instructions for modifying the existing application.
Paste the following development prompt:
Continue developing the existing Test Restaurang application.
Preserve the current visual design, mobile-first layout, navigation,
menu structure, images, typography, and functionality that already
works.Do not redesign the application.
Make the following corrections and improvements:
1. Restaurant name
Use Test Restaurang consistently throughout the entire
application. Remove “& Matsal” from the restaurant name.2. English and Swedish
Correct the EN/SV language switching throughout the application.
When EN is selected, all interface text should be in English.
When SV is selected, all interface text should be in Swedish.
For example:
EN: Welcome to Test Restaurang
SV: Välkommen till Test Restaurang
Check all pages, navigation labels, buttons, reservation controls,
menu text, confirmation text, and other interface labels for language
consistency.3. Reservation date
Fix the reservation date handling.
The application must use the actual current date instead of a
hard-coded example date.Allow the customer to navigate through available future reservation
dates.Do not allow reservation dates in the past.
4. Reservation navigation
Make the reservation flow easy to navigate.
Allow the customer to move forward and backward through the
reservation steps where appropriate without losing previously entered
information.5. Done / Klart
After a reservation reaches the confirmation screen, clicking Done
or Klart should close the completed reservation and return the
customer to the beginning of the reservation process.Reset the reservation form so another reservation can be created.
6. Tasting menu
Replace the unclear View Flow label with:
View Tasting Menu in English
and an appropriate Swedish translation when SV is selected.
When the customer selects it, display the six courses included in the
tasting menu with the course name and a short description.7. Contact actions
Make the telephone action functional so that selecting the telephone
number can initiate a phone call on supported devices.Make the email action functional so that selecting the email address
can open the user’s email application.8. Preserve existing functionality
Do not remove or unnecessarily change features that already work.
Keep the existing menu, search, filters, dish information, mobile
navigation, reservation interface, About page, Contact page, and
responsive layout unless a change is required to fix one of the issues
above.Do not add a production database, authentication system, backend API,
or real reservation email service yet.First implement these frontend corrections and then make the updated
application available for testing.
Why We Use a Detailed Prompt
Compare the prompt above with simply telling AI Studio:
Fix the app.
The detailed prompt tells the coding agent:
what is wrong → what behavior we expect → what should remain
unchanged
This reduces the chance that Gemini changes parts of the application
that already work correctly.
Why We Are Not Adding the Database or Email Service Yet
At this stage, we are deliberately concentrating on the frontend and
reservation workflow.
Before connecting a database, email service, backend API, or Telegram
integration, we want to make sure that:
- Navigation works
- Languages work correctly
- Dates work correctly
- Reservation steps work
- Forms behave correctly
- Confirmation works
- Mobile usability is good
This makes problems much easier to identify and correct.
Let AI Studio Make the Changes
Submit the development prompt and allow AI Studio to modify the
application.
While it works, you may again see Gemini creating or editing .ts,
.tsx, .css, and other project files.
Wait until AI Studio reports that the updated application is ready.
Do not assume that all requested changes were implemented correctly
simply because the build completed.
Our next task is to test the updated application again.
14. Step 9 — Test the Improvements
After AI Studio finishes applying the development prompt, it provides an
Action history showing the files that were changed and a summary of
the improvements it attempted to implement.
In our project, AI Studio reported that it:
- Standardized the restaurant name to Test Restaurang
- Corrected the EN/SV language switching
- Replaced the hard-coded reservation date with a dynamic current-date
system - Added forward and backward navigation to the reservation process
- Added a reservation review step
- Changed Done / Klart so that it resets the reservation flow
- Added functional telephone and email links
- Preserved the existing application design and functionality
AI Studio also created or modified several application files, including
React/TypeScript components such as the reservation view, menu view,
contact view, header, and tasting-menu modal.
However, the AI Studio summary tells us what Gemini attempted to
implement. We should still verify each change ourselves.
Retest Only the Changed Areas
Return to the Preview and test the problems we identified earlier.
Restaurant Name
Check several screens.
The restaurant should now consistently be called:
Test Restaurang
There should no longer be references to:
Test Restaurang & Matsal
English and Swedish
Select EN.
Check Home, Menu, Book Table, About, Contact, and the reservation
confirmation.
All interface text should be English.
Then select SV and repeat the test.
All interface text should be Swedish.
Pay particular attention to the Home heading that previously remained:
Välkommen till Test Restaurang & Matsal
when English was selected.
Reservation Date
Open Book Table.
The reservation calendar should begin with the actual current date
rather than the previously hard-coded October date.
Check that:
- Today’s date is correct
- Future dates can be selected
- Past dates cannot be selected
- Day and month information is correct
- EN/SV changes the date labels appropriately
Reservation Flow
Complete a test reservation.
Verify that you can move:
Table & Time → Guest Details → Review
and also move backward without losing the information you already
entered.
Do not use real personal information during testing.
Done / Klart
Complete the test reservation until the confirmation appears.
Click:
Done in English
or:
Klart in Swedish.
The confirmation should close, the reservation form should reset, and
the application should return to the beginning of the reservation
process ready for a new booking.
Tasting Menu
Open Menu and find the six-course tasting menu.
The unclear View Flow label should have been replaced with a clearer
action such as:
View Tasting Menu
Click it.
Verify that the application displays the six courses with their names
and descriptions.
Also test the Swedish version.
Telephone and Email
Open Contact.
Test the telephone action. On a supported device or browser, it should
use a telephone link.
Test the email action. It should open the configured email application
using an email link.
At this stage, this is not the same as automatically sending a
reservation-confirmation email. That functionality will be implemented
later.
Some Problems May Still Remain
Our second test showed that several requested improvements were
implemented successfully, but not everything worked correctly.
For example:
Working:
- The reservation date now starts with the correct current date.
- Telegram contact functionality works.
Still needs correction:
- After the current date, the reservation date selection unexpectedly
jumps to November. - The customer cannot properly navigate forward and backward through
reservation dates/months. - The telephone action still does not work.
- The email action still does not work.
This demonstrates an important principle when working with AI coding
agents:
Do not assume that a change works simply because the agent reports
that it implemented it. Always test the actual application.
When a problem remains, give the coding agent another small, focused
correction prompt instead of asking it to rebuild the entire
application.
AI Studio Correction Prompt — Version 2
Now go back to the bottom-left input:
Make changes, add new features, ask for anything
and use this smaller prompt:
Continue working on the existing Test Restaurang application.
Preserve the current design and all functionality that is already
working. Do not redesign the application.The previous changes improved the application, but the following
problems remain after testing:1. Reservation date selection
The reservation date correctly begins with today’s actual date, but
the following available dates incorrectly jump to November.Fix the date selector so future dates continue chronologically from
the current date.The customer must be able to navigate forward through future dates and
months and back to earlier available dates/months, but never select a
date before today.Add clear previous and next navigation controls to the date selector.
The month and year heading must update correctly as the customer
navigates.Preserve the selected reservation information when navigating between
dates.2. Telephone action
The telephone action on the Contact screen is still not functional.
Implement it as a real clickable
tel:link using the restaurant
telephone number so that supported devices can open their phone/dialer
application.3. Email action
The email action on the Contact screen is still not functional.
Implement it as a real clickable
mailto:link using the restaurant
email address so that the user’s configured email application can
open.This is only a contact email link. Do not implement automatic
reservation-confirmation email yet.4. Preserve working functionality
Telegram contact functionality is currently working correctly. Do not
change or remove it.Preserve the existing restaurant name, EN/SV language functionality,
menu, reservation flow, navigation, responsive layout, and other
features that are already working.Do not add a database, backend API, authentication, or production
email service.Make only the corrections described above and make the updated
application available for testing.
Then let Gemini finish.
Test → Find Problem → Give Precise Instruction → Rebuild → Retest
Final Frontend Retest
After applying Correction Prompt — Version 2, we tested the
application again.
The reservation date selector and navigation now work correctly.
The Telegram contact action also works correctly.
The telephone action invokes the device/browser telephone-link handling.
On a desktop computer, this may display a list of applications capable
of handling telephone calls rather than immediately making a call. On a
smartphone, a tel: link would normally open the phone application.
The email action still requires verification. A mailto: link depends
on the browser and operating system having an email application or mail
handler configured. If nothing happens, this does not automatically mean
that the application code is incorrect.
Once these frontend corrections pass our tests, we can continue to the
next development stage.
15. Step 10 — Connect the Project to GitHub
Our application is now working in Google AI Studio. The next step is to connect it to GitHub.
This gives us version control, stores the source code in a repository, and prepares the project for development in Visual Studio Code.
Our workflow is now:
Google Stitch → Google AI Studio ↔ GitHub → Visual Studio Code → Backend → Deployment → Telegram Mini App
Open GitHub Sync in Google AI Studio
Stay inside the existing Test Restaurang project.
In the top-right area of Google AI Studio, click: Settings (gear) the GitHub
The GitHub sync panel opens.
If GitHub has not yet been connected, AI Studio asks you to sign in to GitHub.
Follow the sign-in process and authorize Google AI Studio.
Install and Authorize Google AI Studio on GitHub
GitHub may open a page called:
Install & Authorize Google AI Studio
Make sure your correct GitHub account is displayed.
GitHub then asks which repositories Google AI Studio may access.
The available options include:
All repositories
or:
Only select repositories
For our tutorial, we selected:
All repositories
This was useful because AI Studio was going to create a new repository that did not yet exist.
Review the requested permissions and click:
Install & Authorize
GitHub redirects you back to Google AI Studio.
If AI Studio Reports Insufficient Permissions
During our test, the connection did not work immediately. Google AI Studio displayed:
Failed to create GitHub repository, Insufficient permissions to create the GitHub repository.
If you receive this message, do not repeatedly create or delete repositories.
Instead, verify the GitHub authorization.
Open GitHub, click your profile picture, and go to:
Settings → Applications
Here you will find several tabs, including:
Installed GitHub Apps
and:
Authorized GitHub Apps
Check Authorized GitHub Apps
First open:
Authorized GitHub Apps
Look for:
Google AI Studio
This confirms that your GitHub account has authorized Google AI Studio.
In our case, Google AI Studio appeared here, but the repository connection was still not working correctly.
We used Revoke to remove the existing authorization and then went through the Google AI Studio authorization process again.
Use Revoke only when the existing authorization appears to be incorrect or the connection continues to fail. Normally, you should not need to do this.
Check Installed GitHub Apps
Next open:
Settings → Applications → Installed GitHub Apps
Look for:
Google AI Studio
After reconnecting, Google AI Studio appeared correctly in our Installed GitHub Apps list.
Click:
Configure
Under Repository access, verify which repositories Google AI Studio can access.
For our tutorial, we selected:
All repositories
The configuration also showed that Google AI Studio had the required read and write permissions for code and repository operations.
Save the configuration if GitHub asks you to do so.
Complete the Authorization
Return to Google AI Studio and continue the GitHub connection.
GitHub may display another authorization screen showing that Google AI Studio wants permission to:
- Verify your GitHub identity
- Know which resources you can access
- Act on your behalf
Click:
Authorize
After authorization, you should be redirected back to Google AI Studio.
Create the Repository from Google AI Studio
Now return to:
Google AI Studio → GitHub sync
Click:
Create new repository
For our project, we used:
Repository name:
restaurant-telegram-mini-app
Repository description:
Restaurant Telegram Mini App built with Google Stitch and Google AI Studio.
Visibility:
Public
Then click:
Create GitHub repository
Do not manually create a repository with the same name first. In this workflow, Google AI Studio creates the repository and establishes the synchronization automatically.
Push the Project to GitHub
After the repository is created, Google AI Studio displays the files that are ready to be pushed.
In our project, AI Studio reported 24 changed files.
For the first commit, we used:
Initial Test Restaurang application from Google AI Studio
Then click:
Push changes to GitHub
When synchronization finishes, AI Studio displays:
GitHub & Google AI Studio are currently in sync
Our repository is connected to the:
main
branch.
Verify the Files on GitHub
Open the repository from the GitHub Sync panel.
Our repository is:
restaurant-telegram-mini-app
We verified that GitHub contained the application source code, including:
src/.env.example.gitignoreindex.htmlmetadata.jsonpackage.jsontsconfig.jsonvite.config.ts
We could also see our first commit:
Initial Test Restaurang application from Google AI Studio
This confirms that the project has been successfully transferred from Google AI Studio to GitHub.
GitHub Connection Complete
We now have the connection we wanted:
Google AI Studio ↔ GitHub
This is more than simply storing a copy of the project on GitHub. AI Studio’s GitHub Sync allows the project and repository to stay synchronized as development continues.
Our next stage is therefore:
GitHub → Visual Studio Code
We will clone the repository to our computer, open it in VS Code, inspect the generated React/TypeScript project, install its dependencies, and run Test Restaurang locally before beginning the backend development.
16. Why We Use GitHub
GitHub gives us:
- source-code history
- backup
- commits
- version tracking
- collaboration
- easier deployment
- local development
Most importantly, it prevents our application from existing only inside
an AI development tool.
Our project now becomes a normal software project.
17. Suggested Git Milestones
We should create meaningful milestones.
For example:
Initial restaurant design from Stitch
Add functional restaurant frontend
Add menu search and dish details
Add reservation validation
Add reservation confirmation
Later:
Add backend reservation API
Add database
Add Telegram Mini App integration
This makes it much easier to understand how the project evolved.
19. Step 12 — Install the Development Environment and Run the Application
Before running the application, the dependencies required by the React/Vite project must be installed.
Check Node.js and npm
The first time we tried:
<span class="ͼm">npm</span> <span class="ͼm">install</span>
We verified this with:
<span class="ͼg">where</span>.<span class="ͼm">exe</span> <span class="ͼm">node</span>
We therefore installed the Node.js LTS version.
On Windows, this can be installed with:
<span class="ͼm">winget</span> <span class="ͼm">install</span> <span class="ͼm">OpenJS</span>.<span class="ͼm">NodeJS</span>.<span class="ͼm">LTS</span>
PATH.Then verify Node.js and npm.
<span class="ͼm">node</span> <span class="ͼg">--</span><span class="ͼm">version</span>
<span class="ͼm">npm</span> <span class="ͼg">--</span><span class="ͼm">version</span>
11.17.0
When we first tried:
<span class="ͼm">npm</span> <span class="ͼm">install</span>
npm.ps1 cannot be loaded.
The file is not digitally signed.
As a temporary solution, we could use:
<span class="ͼm">npm</span>.<span class="ͼm">cmd</span> <span class="ͼm">install</span>
<span class="ͼm">npm</span>.<span class="ͼm">cmd</span> <span class="ͼm">run</span> <span class="ͼm">dev</span>
<span class="ͼm">npm</span> <span class="ͼm">install</span>
<span class="ͼm">npm</span> <span class="ͼm">run</span> <span class="ͼm">dev</span>
<span class="ͼl">Get-ExecutionPolicy</span> <span class="ͼg">-</span><span class="ͼm">List</span>
CurrentUser Undefined
LocalMachine AllSigned
<span class="ͼl">Set-ExecutionPolicy</span> <span class="ͼg">-</span><span class="ͼm">Scope</span> <span class="ͼm">CurrentUser</span> <span class="ͼg">-</span><span class="ͼm">ExecutionPolicy</span> <span class="ͼm">RemoteSigned</span>
<span class="ͼl">Get-ExecutionPolicy</span> <span class="ͼg">-</span><span class="ͼm">List</span>
CurrentUser RemoteSigned
LocalMachine AllSigned
<span class="ͼm">npm</span> <span class="ͼg">--</span><span class="ͼm">version</span>
Unrestricted or Bypass merely to make npm work. Understand your organization’s security requirements before changing a managed computer.Next, we ran:
<span class="ͼm">npm</span> <span class="ͼm">install</span>
However, we discovered another issue in the application generated by Google AI Studio.
npm reported a dependency conflict between Vite and esbuild.
The generated package.json contained:
<span class="ͼk">"vite"</span>: <span class="ͼk">"^8.3.0"</span>
but under devDependencies it contained:
<span class="ͼk">"esbuild"</span>: <span class="ͼk">"^0.25.0"</span>
Vite 8.3.0 required a newer compatible esbuild version.
Instead of bypassing npm’s dependency checking with:
--force
or:
--legacy-peer-deps
We changed:
<span class="ͼk">"esbuild"</span>: <span class="ͼk">"^0.25.0"</span>
<span class="ͼk">"esbuild"</span>: <span class="ͼk">"^0.28.0"</span>
package.json and ran:<span class="ͼm">npm</span> <span class="ͼm">install</span>
again.
The installation succeeded and npm created the local dependency environment, including:
node_modules/
package-lock.json
The node_modules directory contains the installed packages and should normally remain excluded from Git through .gitignore.
The package-lock.json file, however, should normally be committed because it records the resolved dependency versions used by the project.
Understand the Development Command
Our package.json contains this script:
<span class="ͼk">"scripts"</span>: {
"dev": <span class="ͼk">"vite --port=3000 --host=0.0.0.0"</span>
}
Therefore, when we run:
<span class="ͼm">npm</span> <span class="ͼm">run</span> <span class="ͼm">dev</span>
npm executes:
vite --port=3000 --host=0.0.0.0
Vite then starts the development server.
This explains why we use:
<span class="ͼm">npm</span> <span class="ͼm">run</span> <span class="ͼm">dev</span>
instead of:
<span class="ͼm">python</span> <span class="ͼm">app</span>.<span class="ͼm">py</span>
The current application is a React/TypeScript frontend usi
VITE v8.3.0 ready
Local: http://localhost:3000/
We could then open:
http://localhost:3000/
The Test Restaurang application was now running directly from our local Visual Studio Code project.
Vite Warning
Vite also displayed a warning concerning __dirname in vite.config.ts.
The application nevertheless started and worked correctly.
Because this was a warning rather than a runtime failure, we decided not to change the Vite configuration at this stage. We will only modify it if it becomes necessary later.
24. Step 17 — Prepare the Application for Production Deployment
Nordic Ember has now moved far beyond the original Google Stitch design.
At this stage, the application contains a complete frontend and backend:
Nordic Ember
│
├── React + TypeScript frontend
├── Vite build system
├── Responsive restaurant interface
├── Swedish and English support
├── Menu and dish information
├── Reservation interface
│
├── Express backend
├── REST API
├── Server-side validation
├── SQLite reservation database
├── Reservation reference generation
│
└── Email confirmation
└── Nodemailer + SMTP for local development
The application works locally, but local development and production deployment are two different environments.
Before deploying Nordic Ember to the Internet, we therefore need to make sure the project can be built and started correctly outside our development computer.
24.1 Understand What We Are Deploying
Nordic Ember is not only a static website.
This is important.
A simple React/Vite website could potentially be deployed by uploading the generated dist folder to a static web host.
Our restaurant application requires more.
Conceptually, it contains two major parts:
FRONTEND
React
↓
TypeScript
↓
Vite
↓
dist/
and:
BACKEND
Node.js
↓
Express
↓
REST API
↓
SQLite
↓
Email Service
The browser displays the React application, but reservations are processed by the backend.
For example:
Customer
↓
Book Table
↓
React Frontend
↓
POST /api/reservations
↓
Express Backend
↓
Validation
↓
SQLite
↓
Email Confirmation
Therefore, we need a hosting platform capable of running the Node.js backend, not merely serving HTML files.
This becomes one of the reasons we later choose Railway.
24.2 Build the Production Frontend
Before deploying anything, we should verify that the frontend can be compiled successfully for production.
Open a terminal in the project directory:
C:\Utvecklingprogram\AI\restaurant-telegram-mini-app
Run:
npm run build
Vite now compiles the React and TypeScript source code into optimized production files.
For Nordic Ember, the build completed successfully:
vite v8.3.0 building client environment for production...
✓ 28 modules transformed.
dist/index.html
dist/assets/index-DCW1T9UC.css
dist/assets/index-C1AfRZaB.js
✓ built in 732ms
The exact filenames can change between builds.
For example:
index-DCW1T9UC.css
index-C1AfRZaB.js
contain generated hashes.
That is normal.
The important result is:
✓ built
This confirms that Vite can create the production frontend without compilation errors.
24.3 Understand the dist Folder
After:
npm run build
Vite creates:
dist/
A simplified structure looks like:
restaurant-telegram-mini-app/
│
├── dist/
│ ├── index.html
│ │
│ └── assets/
│ ├── index-XXXXXXXX.css
│ └── index-XXXXXXXX.js
│
├── server/
├── src/
├── public/
├── package.json
└── ...
The dist directory contains the optimized frontend that will be delivered to the customer’s browser.
It does not represent our complete application.
24.4 Why dist Alone Is Not Enough
Our backend contains functionality that cannot run from static HTML, CSS, and JavaScript files alone.
For example:
POST /api/reservations
needs the Express server.
The server is responsible for:
- receiving reservation requests;
- validating reservation information;
- generating booking references;
- storing reservations;
- communicating with the email service;
- returning API responses to the frontend.
Therefore:
dist/
is only one part of the production application.
The deployment platform must run both:
Frontend production files
+
Node.js / Express backend
24.5 Test the Production Build Before Deployment
A very useful rule is:
Do not wait until cloud deployment to discover basic build errors.
We therefore test as much as possible locally before pushing the production version.
At minimum, verify that:
npm run build
finishes successfully.
We also want to confirm that important application files are present and that no required source files were accidentally excluded.
The production preparation checklist now includes:
React frontend ✅
TypeScript compilation ✅
Vite production build ✅
Express backend ✅
Reservation API ✅
SQLite integration ✅
Email module ✅
Environment variable support ✅
Git repository ✅
24.6 Prepare the Application Start Process
A cloud platform needs to know how to start the application.
During development we may use commands intended for development mode, but production requires the application to start its server in a predictable way.
The important concept is:
Cloud Platform
↓
Install dependencies
↓
Build application
↓
Start Node.js server
↓
Express becomes available
↓
Serve frontend + API
Later, Railway will perform these operations from the code stored in GitHub.
This means our repository must contain everything necessary to recreate the application.
24.7 Use the Platform Port
There is another important difference between a local computer and cloud hosting.
Locally, we might think of the server as running on a fixed port.
For example:
localhost:3001
But a cloud platform can assign the application its own port.
Therefore, a production Node.js application should be prepared to use a port supplied by the hosting environment.
Conceptually:
Railway
↓
PORT environment variable
↓
Express server
rather than assuming that one particular local port will always be available.
This allows the hosting platform to route Internet traffic correctly to the application.
24.8 Separate Configuration from Source Code
We already followed this principle for SMTP, but it becomes even more important during deployment.
Configuration should not be hard-coded into the application.
Examples include:
SMTP_HOST
SMTP_PORT
SMTP_USER
SMTP_PASS
SMTP_FROM
Later we will also introduce:
RESEND_API_KEY
RESEND_FROM
and Telegram-related configuration.
The principle is:
SOURCE CODE
↓
GitHub
SECRETS / PRODUCTION CONFIGURATION
↓
Hosting Environment Variables
This means GitHub can contain:
process.env.RESEND_API_KEY
but it must not contain the actual Resend API key.
The same rule applies to SMTP passwords and Telegram bot tokens.
24.9 Check .gitignore Before Deployment
Before pushing production changes to GitHub, check:
.gitignore
Sensitive or machine-specific files should not be committed.
In particular:
.env
must remain private.
Typical generated or local files should also be reviewed before committing.
A useful principle is:
Application source → GitHub
Configuration examples → GitHub
Passwords → Never
API keys → Never
Bot tokens → Never
Local secret .env → Never
This becomes especially important because Railway will later be connected directly to the GitHub repository.
24.10 Keep .env.example
Although the real .env should not be committed, keeping:
.env.example
is useful.
It documents the configuration expected by the application without exposing real credentials.
For example:
SMTP_HOST=
SMTP_PORT=
SMTP_USER=
SMTP_PASS=
SMTP_FROM=
Later, as the production email system evolves, the example configuration can also document variables such as:
RESEND_API_KEY=
RESEND_FROM=
without including the real values.
This makes the project easier to understand, reproduce, and maintain.
24.11 Understand Local and Production Environments
At this point, it is useful to clearly distinguish the two environments.
Local development:
Developer PC
↓
VS Code
↓
React + Express
↓
SQLite
↓
Nodemailer
↓
Gmail SMTP
Production:
Internet / Telegram
↓
Railway
↓
React + Express
↓
SQLite
↓
Production Email Provider
At this stage we had not yet finalized the production email provider.
That decision would come after deployment testing.
This is important because we did not begin the project knowing that Resend would be necessary.
We first deployed the working application and tested the existing SMTP configuration.
Only after seeing the production behavior did we decide to introduce Resend.
24.12 Make Sure GitHub Contains the Latest Working Version
Railway will later obtain the application source code from GitHub.
Therefore, before connecting Railway, the GitHub repository should contain the latest tested version.
The project repository is:
restaurant-telegram-mini-app
The basic workflow is:
Local Project
↓
Git
↓
GitHub
↓
Railway
This relationship is very important.
Railway is not our source-code editor.
GitHub remains the source repository.
When we later make a change in VS Code, the normal workflow becomes:
Edit locally
↓
Test
↓
git add
↓
git commit
↓
git push
↓
GitHub updated
↓
Railway detects change
↓
New deployment
This is why we spent time establishing a clean Git workflow before production deployment.
24.13 Understand Automatic Deployment
Once Railway is connected to the GitHub repository, future deployments can become largely automatic.
For example:
VS Code
↓
git push origin main
↓
GitHub
↓
Railway detects new commit
↓
Build
↓
Deploy
This is extremely convenient.
However, it also means that a broken commit can trigger a broken production deployment.
Therefore, the better workflow is:
Modify
↓
Test locally
↓
Build locally
↓
Commit
↓
Push
↓
Railway deploy
↓
Check deployment logs
↓
Test production
We later saw why checking Railway deployment logs is important when one of our deployments crashed after a GitHub push.
24.14 Understand the SQLite Production Consideration
Our reservation system currently uses SQLite.
SQLite is excellent for:
- local development;
- prototypes;
- tutorials;
- demonstrations;
- relatively simple applications.
However, deployment introduces an important consideration.
SQLite stores information in a database file.
Conceptually:
Express
↓
SQLite
↓
Database File
On a cloud platform, we need to think about whether that file survives application restarts and redeployments.
This is different from a managed database server such as PostgreSQL.
For our Nordic Ember project, SQLite is useful for demonstrating the complete reservation architecture, but for a larger real restaurant production system we would evaluate persistent storage carefully.
Possible future production options include:
SQLite + persistent volume
or:
PostgreSQL
For this project, we continue with the current architecture while keeping that production consideration in mind.
24.15 Do Not Configure Telegram Yet
Although this project is intended to become a Telegram Mini App, we do not need to configure the Telegram Web App before we have a working public deployment.
Telegram needs a publicly accessible HTTPS address for the application.
Locally we have something such as:
http://localhost:...
but Telegram ultimately needs something similar to:
https://our-public-application...
Therefore, the correct sequence is:
Finish application
↓
Prepare production build
↓
Push to GitHub
↓
Deploy publicly
↓
Test public application
↓
Configure Telegram Web App
This prevents us from trying to configure Telegram with a local development address.
24.16 Choose a Cloud Deployment Platform
We now need a platform capable of hosting our full-stack Node.js application.
For Nordic Ember, we selected Railway.
Railway is suitable for this project because it can connect directly to a GitHub repository and deploy a Node.js application.
Our intended deployment flow becomes:
VS Code
↓
Git
↓
GitHub
↓
Railway
↓
Public HTTPS Application
Once the application is publicly available, Telegram can open that HTTPS application as a Mini App.
The larger architecture will eventually become:
Customer
↓
Telegram
↓
Nordic Ember Mini App
↓
Railway
↓
Express API
↓
Reservation Database
↓
Email Confirmation
24.17 Production Preparation Checklist
Before moving to Railway, we now have the following checklist:
Frontend works locally ✅
Reservation flow works ✅
Backend API works ✅
SQLite reservation storage works ✅
Local SMTP email works ✅
Customer email tested ✅
Production Vite build works ✅
Environment variables supported ✅
Secrets kept outside source code ✅
Git repository prepared ✅
GitHub repository available ✅
We are now ready to deploy Nordic Ember to a real cloud environment.
24.18 What Comes Next
The next step is one of the biggest milestones in the project.
We will take Nordic Ember from:
localhost
to:
Internet
using:
GitHub
↓
Railway
↓
Public HTTPS URL
During that process, we will:
- create or open a Railway account;
- create the Railway project;
- connect Railway to GitHub;
- select the Nordic Ember repository;
- configure the application;
- add environment variables;
- deploy the application;
- inspect the deployment logs;
- test the public URL;
- test reservations in production.
Most importantly, this is where we discovered that an email configuration that works perfectly on a developer’s computer does not necessarily behave the same way in a cloud production environment.
That discovery will lead us to Resend, domain verification, and the DNS configuration in Miss Hosting.
25. Step 18 — Deploy Nordic Ember to Railway
After preparing the Nordic Ember application for production, the next step is to make it available on the Internet.
Until now, the application has mainly been running on the development computer using a local address such as:
http://localhost:...
That is useful during development, but it cannot be used by real customers or opened as a Telegram Mini App.
We therefore need a cloud hosting platform that can run our complete application:
React + TypeScript frontend
+
Express backend
+
Reservation API
+
SQLite database
+
Email service
For this project, we selected Railway.
25.1 Why We Chose Railway
Nordic Ember is not simply a collection of static HTML files.
The application needs a running Node.js server because Express handles:
/api/reservations
and other backend operations.
Railway is useful for this architecture because it can connect directly to a GitHub repository, build the Node.js application, run the server, provide environment variables, create a public HTTPS address, and automatically redeploy the application when new code is pushed to GitHub.
Our deployment architecture therefore becomes:
VS Code
↓
Git
↓
GitHub
↓
Railway
↓
Nordic Ember Production Application
This also gives us the public HTTPS address that we will later need for Telegram.
25.2 Open Railway and Create an Account
Open the Railway website and create an account or sign in to an existing account.
Railway can integrate with GitHub, which is particularly useful because our Nordic Ember source code is already stored there.
After signing in, open the Railway dashboard.
From here we can create a new project.
25.3 Create a New Railway Project
Create a new project in Railway.
Rather than starting with an empty application and manually uploading files, connect Railway to the existing GitHub repository.
Our repository is:
restaurant-telegram-mini-app
The deployment relationship becomes:
restaurant-telegram-mini-app
GitHub
↓
Railway
↓
Production Service
Railway now knows where the application source code is located.
25.4 Authorize Railway to Access GitHub
The first time Railway connects to GitHub, GitHub may ask you to authorize Railway.
This permission allows Railway to read the selected repository and deploy its contents.
Only give Railway access to the repositories it needs.
After authorization, select:
restaurant-telegram-mini-app
Railway can now create a service based on the repository.
25.5 What Railway Does with the GitHub Repository
Railway does not simply display the files stored in GitHub.
It creates a deployment environment for the application.
Conceptually:
GitHub Repository
↓
Railway downloads source
↓
Dependencies installed
↓
Application built
↓
Node.js server started
↓
Application becomes available
This is why the preparation work in Step 17 was important.
The repository must contain enough information for another computer—in this case Railway—to build and start the application without depending on our local VS Code environment.
25.6 Railway Build Process
Railway detects that Nordic Ember is a Node.js project from files such as:
package.json
It then installs the dependencies required by the application.
The production build ultimately needs to create the frontend files with Vite and start the Express backend.
Conceptually:
npm install
↓
npm run build
↓
Vite creates dist/
↓
Node.js / Express starts
The exact commands used by a project depend on its package.json configuration.
This is why we first confirmed locally that:
npm run build
worked successfully.
25.7 Configure Railway Environment Variables
Our local computer uses:
.env
for private configuration.
That local .env file is deliberately not stored in GitHub.
Railway therefore cannot obtain those secrets from the repository—and it should not.
Instead, production configuration is entered using Railway Variables.
The principle is:
LOCAL DEVELOPMENT
.env
↓
Environment Variables
↓
Application
while production uses:
RAILWAY
Railway Variables
↓
Environment Variables
↓
Application
The application can use the same Node.js mechanism:
process.env.VARIABLE_NAME
without needing to know whether the value came from a local .env file or Railway.
25.8 Do Not Put Secrets in GitHub
This point is especially important during cloud deployment.
Values such as these must remain private:
SMTP password
Gmail App Password
Resend API Key
Telegram Bot Token
They should never be written directly into:
server/email.ts
or any other source file committed to GitHub.
The correct architecture is:
GitHub
↓
Application code only
Railway
↓
Production secrets
Later, when we configure Resend, its API key will also be stored as a Railway variable rather than in GitHub.
25.9 First Railway Deployment
After the GitHub repository is connected, Railway begins the deployment process.
The deployment passes through several stages.
Conceptually:
GitHub
↓
Source downloaded
↓
Build started
↓
Dependencies installed
↓
Application compiled
↓
Server started
↓
Deployment available
Railway provides deployment status information while this is happening.
A successful build is an important milestone, but it does not automatically prove that every feature of the application works.
We must still test the production application.
25.10 Use Railway Deployment Logs
One of the most useful tools during deployment is the Railway log.
If a deployment fails or the application crashes, Railway records information about what happened.
Instead of guessing, open the failed deployment and inspect its logs.
The general troubleshooting process is:
Deployment failed
↓
Open Railway
↓
Open service
↓
Open deployment
↓
Read deployment/runtime logs
↓
Identify error
↓
Correct locally
↓
Commit
↓
Push to GitHub
↓
Railway redeploys
We later used exactly this workflow when Railway reported a Deploy Crashed notification after one of our GitHub pushes.
The important lesson is:
A Railway crash notification does not tell us the actual programming problem. The deployment logs do.
25.11 Create the Public Railway Address
Once the service is running, Railway can expose it through a public HTTPS domain.
Our Nordic Ember production application became available at:
https://restaurant-telegram-mini-app-production.up.railway.app
This is an important milestone.
The application is no longer limited to:
localhost
It is now accessible from the Internet.
The architecture has changed from:
Developer Computer
↓
localhost
to:
Internet
↓
Railway HTTPS URL
↓
Nordic Ember
This URL will later become especially important because Telegram requires a publicly accessible HTTPS Web App URL.
25.12 Test the Railway Website Before Telegram
Before connecting Telegram, test the Railway application directly in a normal browser.
Open:
https://restaurant-telegram-mini-app-production.up.railway.app
Verify the major application areas:
Home
Menu
Book Table
About
Contact
EN / SV
Mobile layout
The production version should behave like the tested local application.
25.13 Test the Reservation API in Production
The most important feature is not simply whether the Home page appears.
We need to know whether the complete reservation workflow still functions.
The production reservation flow is:
Railway Website
↓
Book Table
↓
Reservation Form
↓
POST /api/reservations
↓
Express
↓
Validation
↓
Reservation Stored
This confirms that both the frontend and backend are running in Railway.
25.14 The Important SMTP Problem
At this point, we discovered an important difference between local development and cloud production.
Locally, our email architecture worked:
Nordic Ember
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
The confirmation email had already been successfully delivered during our local tests.
However, after deploying to Railway, the SMTP-based production email path caused problems.
This is an important real-world lesson:
A feature that works locally does not automatically behave the same way after cloud deployment.
Cloud platforms have different networking environments, security considerations, connection behavior, and deployment constraints.
We therefore did not remove our working local SMTP implementation.
Instead, we decided to keep:
LOCAL DEVELOPMENT
↓
Nodemailer
↓
SMTP
and introduce a different email delivery method for production:
RAILWAY PRODUCTION
↓
Resend API
25.15 Why We Chose an Email API for Production
Traditional SMTP requires the application to establish an SMTP connection to a mail server.
For our Railway production deployment, we wanted a more suitable cloud email mechanism.
We therefore selected Resend, a transactional email service with an API.
Instead of:
Railway
↓
SMTP connection
↓
Gmail
we could use:
Railway
↓
HTTPS API
↓
Resend
↓
Customer
This also gave us the possibility of sending production emails from a professional restaurant address rather than a personal Gmail address.
Our target became:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
25.16 Keep SMTP Locally and Resend in Production
We did not need to throw away the Nodemailer work from Step 16.
Instead, the application was designed to support both environments.
The final idea is:
Is RESEND_API_KEY configured?
│
┌────┴────┐
│ │
YES NO
│ │
▼ ▼
Resend SMTP
│ │
Production Local
This means:
LOCAL
No RESEND_API_KEY
↓
Nodemailer / SMTP
and:
RAILWAY
RESEND_API_KEY configured
↓
Resend
This is a useful pattern because development and production can use different infrastructure without requiring two separate reservation systems.
25.17 Automatic Deployment from GitHub
After Railway is connected to GitHub, a normal code update follows this workflow:
VS Code
↓
Modify files
↓
Test locally
↓
git add
↓
git commit
↓
git push origin main
↓
GitHub
↓
Railway detects new commit
↓
Automatic deployment
This means we normally do not need to manually upload the application to Railway every time it changes.
GitHub acts as the source of the deployment.
25.18 Why We Received Railway Deployment Emails
During development, we received Railway notifications such as:
Deploy Crashed!
after some GitHub pushes.
This happens because a push to the connected production branch can automatically trigger another deployment.
Therefore:
git push
↓
GitHub changes
↓
Railway notices change
↓
New deployment starts
If the new deployment cannot start successfully, Railway can send a failure notification.
The correct response is not simply to keep restarting the application.
Instead:
Open Railway
↓
Check deployment
↓
Read logs
↓
Find actual cause
This became an important part of our development and deployment workflow.
25.19 Test After Every Production Deployment
A green deployment status is not enough.
After an important production change, test the application.
For Nordic Ember, a useful production checklist is:
Railway deployment running ✅
Public URL opens ✅
Home page works ✅
Menu works ✅
Book Table works ✅
Reservation API responds ✅
Reservation is created ✅
Email system checked ✅
Mobile layout checked ✅
Later we will add:
Telegram Mini App works ✅
Mobile Telegram works ✅
Customer receives email ✅
25.20 Railway Is the Application Host, Not the Email Provider
It is useful to understand the responsibility of each service.
Railway hosts the application:
Railway
↓
React
Express
Reservation API
Application runtime
Railway does not become the restaurant’s email provider.
For production email we will use:
Resend
And for the restaurant’s domain DNS records we will use the existing domain management at:
softsolutionsahand.com
through Miss Hosting.
Eventually, the architecture becomes:
CUSTOMER
│
▼
Nordic Ember Application
│
▼
Railway
/ \
/ \
▼ ▼
Reservation Resend
Backend │
│ ▼
▼ Customer Email
SQLite
Later Telegram will be added in front of this:
Telegram
↓
Nordic Ember Mini App
↓
Railway
25.21 Current Production Address
At this stage, the public application address is:
https://restaurant-telegram-mini-app-production.up.railway.app
This address is perfectly suitable for testing and for initially connecting the Telegram Mini App.
Later, we can also configure a cleaner address under our own domain, similar to how another application can use a custom subdomain.
For example:
restaurant.softsolutionsahand.com
However, that custom web-domain configuration is a separate task from the Resend email-domain configuration.
We should not confuse:
Website hosting DNS
with:
Email authentication DNS
Both can involve DNS records, but they serve different purposes.
25.22 Railway Deployment Milestone
We have now reached another major milestone.
Nordic Ember has moved from:
Local development application
to:
Public cloud application
The deployment architecture is now:
VS Code
↓
Git
↓
GitHub
↓
Railway
↓
Nordic Ember
↓
Public HTTPS
But one important production feature still needs to be solved properly:
Customer Email Confirmation
SMTP worked locally, but our Railway deployment showed us that we needed a better production email solution.
That brings us directly to the next step.
26. Step 19 — Configure Production Email with Resend
Our Nordic Ember application is now deployed publicly on Railway.
The website and reservation API work in production, but we discovered an important problem: the SMTP solution that worked correctly on our local computer was not the best solution for our Railway production environment.
Locally, we had:
Nordic Ember
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
This worked successfully.
In production, however, we wanted a more reliable cloud-oriented method of sending transactional email.
We therefore introduced Resend.
26.1 What Is Resend?
Resend is an email delivery service designed for applications.
Instead of requiring our Node.js application to communicate with a traditional SMTP server, the application can send email through the Resend API.
Our production architecture becomes:
Customer makes reservation
↓
Nordic Ember
↓
Railway
↓
Express Reservation API
↓
Reservation stored
↓
Resend API
↓
Confirmation Email
↓
Customer
This is different from the local architecture:
LOCAL DEVELOPMENT
Nordic Ember
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
We deliberately keep both methods.
26.2 Why Keep SMTP and Resend?
We already had a working local SMTP configuration.
There was no reason to remove it.
Instead, we designed the email service so that Nordic Ember can select the appropriate provider depending on the environment.
The basic logic is:
Is RESEND_API_KEY available?
│
┌───┴───┐
│ │
YES NO
│ │
▼ ▼
Resend SMTP
│ │
Railway Local
Therefore:
LOCAL DEVELOPMENT
No RESEND_API_KEY
↓
Nodemailer
↓
Gmail SMTP
while:
PRODUCTION
RESEND_API_KEY exists
↓
Resend
↓
Customer Email
This gives us a flexible application without maintaining two separate reservation systems.
26.3 Why Use an Email API in Production?
An email API is particularly convenient for a cloud-hosted application.
Instead of creating an SMTP connection:
Railway
↓
SMTP Server
our backend can make a normal HTTPS API request:
Railway
↓
Resend API
↓
Email Infrastructure
This also gives us a path toward using a professional restaurant sender address.
Our final target will be:
Nordic Ember
<booking@restaurant.softsolutionsahand.com>
Before we can use that custom address, however, we first need to connect Railway to Resend.
26.4 Create a Resend Account
Open the Resend website and create an account.
After registration, sign in to the Resend dashboard.
The dashboard provides access to important areas such as:
Emails
Domains
API Keys
Logs
For Nordic Ember, the two most important areas initially are:
API Keys
Domains
The API key allows Railway to communicate with Resend.
The Domains area will later allow us to verify:
restaurant.softsolutionsahand.com
and send email from our own domain.
26.5 Create a Resend API Key
In the Resend dashboard, open the API Keys section.
Create a new API key for the Nordic Ember application.
Give it a descriptive name so that we can later recognize what the key is used for.
For example:
Nordic Ember Railway
After the key is created, Resend displays the secret API key.
It will have a value similar in concept to:
re_xxxxxxxxxxxxxxxxxxxxxxxxx
The real key is a secret.
Do not:
- publish it in the blog;
- put it in GitHub;
- place it directly in
server/email.ts; - include it in screenshots;
- store it in public documentation.
Instead, we will store it securely in Railway.
26.6 Add the Resend Package to the Project
The Node.js backend needs the Resend package so it can communicate with the Resend API.
The project uses the Resend Node.js library in the email service.
Conceptually:
server/email.ts
↓
Resend library
↓
Resend API
Our email module therefore imports both email mechanisms:
Nodemailer
+
Resend
This does not mean that every email is sent twice.
The application chooses one provider depending on its environment.
26.7 Update the Email Service
The existing email functionality was already located in:
server/email.ts
Instead of creating a completely separate reservation system for production, we extended this email service.
The important idea is:
sendReservationConfirmation()
↓
Check RESEND_API_KEY
↓
┌───────────────────────┐
│ │
▼ ▼
Resend SMTP
Production Local
The reservation API therefore does not need to know the details of Gmail or Resend.
It simply asks the email service:
Send the reservation confirmation.
The email service decides which provider should handle it.
This is a cleaner design because the reservation system remains independent from the email provider.
26.8 Add RESEND_API_KEY to Railway
Now return to the Nordic Ember project in Railway.
Open the production service and find its Variables section.
Add a new variable:
RESEND_API_KEY
For its value, paste the real API key generated by Resend.
Conceptually:
Railway Variables
RESEND_API_KEY = re_xxxxxxxxxxxxxxxxx
Do not use the example value above.
Use the actual private key from your own Resend account.
Railway stores this configuration outside the GitHub source code.
The application can then access it through:
process.env.RESEND_API_KEY
This is the same security principle we used for local SMTP configuration.
26.9 Add RESEND_FROM
We also use an environment variable for the production sender address:
RESEND_FROM
Eventually, our final production sender will be:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
The application can therefore use:
process.env.RESEND_FROM
instead of permanently hard-coding the sender configuration.
This makes it much easier to change the sender later without rewriting the email logic.
Our production configuration therefore includes:
RESEND_API_KEY
RESEND_FROM
The first controls authentication.
The second controls the sender identity.
26.10 Railway and Resend Are Now Connected
Once the API key is stored in Railway, the production application has everything required to communicate with Resend.
The connection becomes:
Nordic Ember
↓
Railway
↓
RESEND_API_KEY
↓
Resend API
When the application starts in Railway:
process.env.RESEND_API_KEY
contains a value.
The email service therefore knows:
Production environment detected
↓
Use Resend
Locally, if RESEND_API_KEY is not defined:
No RESEND_API_KEY
↓
Use SMTP
26.11 Redeploy After Changing Railway Variables
When production environment variables are added or changed, the running application needs to use the new configuration.
Railway can redeploy or restart the application with the updated variables.
The flow becomes:
Add Railway Variable
↓
Railway Deployment
↓
Application Starts
↓
process.env.RESEND_API_KEY
↓
Resend Enabled
After deployment, check the Railway logs.
Our application can indicate which provider is being used.
For example:
Email provider: Resend
This is extremely useful during testing because it confirms that production is no longer trying to use the local SMTP path.
26.12 First Resend Test
Before configuring our own restaurant domain, we first needed to confirm that the basic connection between:
Railway
↓
Resend
actually worked.
During the initial test, Resend could send using its provided testing/onboarding sender.
For example:
onboarding@resend.dev
This was useful as a temporary test.
It allowed us to separate two questions:
Question 1:
Can Railway communicate with Resend?
Question 2:
Can we send from our own restaurant domain?
We should solve Question 1 first.
26.13 Make a Real Reservation
With the Railway application running, open the production restaurant website and make a test reservation.
For example:
Name: Mehrdad Zandi
Email: customer test address
Guests: 4
Date: future date
Time: 20:00
Seating: Main Dining Room
Submit the reservation.
The production flow should now be:
Browser
↓
Nordic Ember on Railway
↓
POST /api/reservations
↓
Reservation stored
↓
sendReservationConfirmation()
↓
RESEND_API_KEY detected
↓
Resend
↓
Customer Email
26.14 Verify the Email
The customer should receive a confirmation similar to:
Nordic Ember
Hello Mehrdad Zandi,
Thank you for your reservation at Nordic Ember.
Booking reference: TR-XXXXXXXX-XXXX
Date: 2026-10-04
Time: 20:00
Guests: 4
Seating area: Main Dining Room (Matsal)
Please keep your booking reference if you need
to contact the restaurant about your reservation.
Best regards,
Nordic Ember
At this first stage, the important result is not yet the final sender address.
The important result is:
Railway → Resend → Customer
works.
26.15 Check Railway Logs
After the reservation, open the Railway logs.
We want to see that the application selected Resend.
For example:
Email provider: Resend
and a successful delivery message.
This verifies that production is not falling back to SMTP.
The production test now confirms:
Railway application ✅
Reservation API ✅
Reservation stored ✅
RESEND_API_KEY detected ✅
Resend selected ✅
Resend API communication ✅
Customer email delivered ✅
26.16 Check the Resend Dashboard
We can also return to Resend and inspect the email activity.
This provides another useful troubleshooting location.
Our email system now has two places where we can investigate problems:
APPLICATION SIDE
Railway Logs
and:
EMAIL PROVIDER SIDE
Resend Dashboard
This is useful when diagnosing a production email problem.
For example:
Reservation created
↓
Railway says email sent
↓
Check Resend
↓
Check delivery status
26.17 The First Test Revealed Another Limitation
Our first Resend connection worked, but the email sender was not yet the professional restaurant address we wanted.
Instead, the message could arrive from the Resend testing sender, such as:
Nordic Ember
From: onboarding@resend.dev
This proves that Resend works, but it is not the final production configuration.
We want:
Nordic Ember
From: booking@restaurant.softsolutionsahand.com
To use that address, Resend needs proof that we control the domain.
This is an important anti-spam and security requirement.
Otherwise, anyone could create a Resend account and pretend to send email from someone else’s domain.
26.18 Why Domain Verification Is Required
We own:
softsolutionsahand.com
and want to use a restaurant subdomain:
restaurant.softsolutionsahand.com
for the Nordic Ember email identity.
The desired sender is:
booking@restaurant.softsolutionsahand.com
Resend therefore needs to verify that we control the DNS configuration for that domain.
The verification process will connect three systems:
Resend
↓
provides DNS records
Miss Hosting
↓
hosts/manages DNS for softsolutionsahand.com
DNS
↓
proves domain ownership/authentication
Once the required records are published and verified, Resend can authorize email sending for the restaurant domain.
26.19 Do Not Confuse the API Key with Domain Verification
These are two different parts of the configuration.
The API key answers:
Is this Railway application authorized
to use our Resend account?
The domain verification answers:
Is this Resend account authorized
to send email for this domain?
Therefore:
RESEND_API_KEY
↓
Railway ↔ Resend authentication
while:
DNS records
↓
Domain ↔ Resend authentication
We need both for the final production system.
26.20 Production Email Architecture So Far
At this point we have:
Customer
↓
Nordic Ember
↓
Railway
↓
Express
↓
Reservation Database
↓
Resend API
↓
Customer Email
But the sender identity is not yet finished.
The next step will transform:
Nordic Ember
<onboarding@resend.dev>
into:
Nordic Ember
<booking@restaurant.softsolutionsahand.com>
26.21 Resend Milestone
We have now solved the main Railway email-provider problem.
Our project supports:
LOCAL DEVELOPMENT
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
and:
PRODUCTION
↓
Railway
↓
Resend API
↓
Customer
This gives Nordic Ember a much cleaner separation between development and production email delivery.
But one major production task remains:
verify our own restaurant email domain.
That requires changes outside both VS Code and Railway.
We must configure DNS records for:
restaurant.softsolutionsahand.com
in Miss Hosting and verify them in Resend.
27. Step 20 — Verify the Restaurant Email Domain with Resend and Miss Hosting
Resend is now connected to the Nordic Ember application running on Railway.
Our first production test proved that this path works:
Nordic Ember
↓
Railway
↓
Resend API
↓
Customer Email
However, during the first Resend test, the message was sent using Resend’s testing sender:
Nordic Ember <onboarding@resend.dev>
That was useful for testing, but it is not appropriate as the final sender for a real restaurant application.
We want customers to receive their reservation confirmation from:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
To do that, we must prove to Resend that we control the domain used in the sender address.
This requires DNS configuration.
For our project, the DNS for:
softsolutionsahand.com
is managed through Miss Hosting.
The next task is therefore to connect:
Resend
↕
DNS
↕
Miss Hosting
↕
softsolutionsahand.com
27.1 Why Resend Requires Domain Verification
Suppose an email service allowed anyone to send messages such as:
booking@some-company.com
without checking ownership of some-company.com.
That would make it very easy to impersonate other organizations.
For this reason, Resend requires domain verification before we can use our own domain as the sender.
We want to use:
booking@restaurant.softsolutionsahand.com
Therefore, we need to verify the sending domain:
restaurant.softsolutionsahand.com
The verification is performed through DNS records.
27.2 Understand the Domain Structure
Our main domain is:
softsolutionsahand.com
For this project, we use the subdomain:
restaurant.softsolutionsahand.com
and the production sender:
booking@restaurant.softsolutionsahand.com
The structure is therefore:
softsolutionsahand.com
│
└── restaurant.softsolutionsahand.com
│
└── booking@restaurant.softsolutionsahand.com
The booking part is the mailbox-style sender name.
The domain that must be authenticated for email sending is:
restaurant.softsolutionsahand.com
27.3 Add the Restaurant Domain in Resend
Open the Resend dashboard.
Go to:
Domains
and add:
restaurant.softsolutionsahand.com
Resend now generates DNS records that must be added to the DNS provider.
In our case, the DNS provider is Miss Hosting.
Do not invent these DNS values manually.
Always copy the values provided by Resend because they identify and authenticate the specific sending domain.
27.4 Why DNS Is Involved
DNS is often associated with connecting a domain name to a website.
For example:
findflight.softsolutionsahand.com
↓
PythonAnywhere
But DNS has several other purposes.
It can also be used for email authentication.
This is exactly what we are doing with Resend.
Therefore, the DNS configuration for Nordic Ember is not simply:
Domain
↓
Website
It also includes:
Domain
↓
Email Authentication
↓
Resend
This distinction becomes important when comparing Nordic Ember with our FlightFinder application.
27.5 Why FlightFinder Has Only One Record
For FlightFinder, we previously configured:
findflight.softsolutionsahand.com
with a CNAME record pointing to the web application:
findflight.softsolutionsahand.com
↓
webapp-2897254.pythonanywhere.com
That record has one primary purpose:
DNS
↓
Website Hosting
Nordic Ember’s Resend records have a completely different purpose.
They are used for:
DNS
↓
Email Authentication / Email Delivery
Therefore, it is completely normal for Resend to require several DNS records while FlightFinder needed only one web-hosting CNAME.
27.6 Open DNS Management in Miss Hosting
Log in to Miss Hosting and open the DNS management area for:
softsolutionsahand.com
Be careful when editing DNS.
The domain may already contain records for:
- the main WordPress website;
- email;
- FlightFinder;
- other services.
Do not delete unrelated DNS records.
We only add the records required for the Nordic Ember / Resend configuration.
27.7 The Three Resend DNS Records We Added
For our Nordic Ember configuration, we ultimately added three DNS records.
The resulting records were:
resend._domainkey.restaurant.softsolutionsahand.com.
Type:
TXT
and:
send.restaurant.softsolutionsahand.com.
Type:
CNAME
pointing to:
send.forge.rmta.net
and:
rsend.restaurant.softsolutionsahand.com.
Type:
CNAME
pointing to:
rsend-euw1.forge.rmta.net
The first TXT record contained the public DKIM key supplied by Resend.
The final DNS structure was therefore:
restaurant.softsolutionsahand.com
│
├── resend._domainkey.restaurant...
│ TXT
│ ↓
│ DKIM public key
│
├── send.restaurant...
│ CNAME
│ ↓
│ send.forge.rmta.net
│
└── rsend.restaurant...
CNAME
↓
rsend-euw1.forge.rmta.net
Important: These are the records used in our Nordic Ember configuration. For another domain or another Resend account, always use the exact records shown by Resend rather than copying values blindly from this tutorial.
27.8 What Is the DKIM TXT Record?
One of the records begins with:
resend._domainkey
and has the type:
TXT
This is associated with DKIM.
DKIM stands for:
DomainKeys Identified Mail
It allows outgoing email to be cryptographically signed.
Conceptually:
Nordic Ember
↓
Resend signs email
↓
Receiving mail server
↓
Checks DNS DKIM public key
↓
Signature verified
This helps receiving mail systems verify that the message was authorized by the sending domain and was not modified after being signed.
The TXT value supplied by Resend begins with something similar to:
p=MIGfMA0GCSq...
This is a public key.
It is not our Resend API key.
That distinction is important.
DKIM public key
→ stored in DNS
RESEND_API_KEY
→ private secret stored in Railway
The Resend API key must never be published in DNS.
27.9 What Are the send and rsend CNAME Records?
The other records are CNAME records.
In our configuration:
send.restaurant.softsolutionsahand.com
points to:
send.forge.rmta.net
and:
rsend.restaurant.softsolutionsahand.com
points to:
rsend-euw1.forge.rmta.net
These records are part of the sending-domain configuration supplied by Resend.
The important lesson for a reader is not to memorize these destinations.
Instead:
Add exactly the DNS records Resend provides for your domain.
Resend’s required values may differ depending on its current infrastructure or configuration.
27.10 Enter the Records Carefully
DNS configuration is very sensitive to spelling.
For example, these are different names:
send.restaurant.softsolutionsahand.com
and:
rsend.restaurant.softsolutionsahand.com
Similarly:
resend._domainkey.restaurant.softsolutionsahand.com
must be entered correctly.
A missing character can prevent verification.
We experienced this ourselves when one of the new records did not remain visible after saving and refreshing the Miss Hosting page.
That is why it is important to verify the DNS table after every change.
27.11 Save and Refresh the DNS Page
After adding a DNS record in Miss Hosting:
- Save the record.
- Wait for the interface to finish saving.
- Refresh the DNS page.
- Confirm that the new record is still present.
During our configuration, the save operation appeared busy at one point.
After refreshing with:
Ctrl + F5
one newly entered record was no longer present.
We therefore added it again and confirmed that all three records were visible.
The final result was:
DKIM TXT ✅
send CNAME ✅
rsend CNAME ✅
This is a useful practical lesson:
Do not assume that a DNS record was saved simply because you clicked Save. Reopen or refresh the DNS list and verify it.
27.12 DNS Changes May Take Time
After adding DNS records, verification may not happen immediately.
DNS information must propagate.
The process can therefore look like:
Add record in Miss Hosting
↓
Save
↓
DNS propagation
↓
Resend checks DNS
↓
Verification
During this period, Resend may still show the domain as pending.
That does not necessarily mean the configuration is wrong.
First confirm that the records are correct and then allow some time for DNS propagation.
27.13 Return to Resend and Verify the Domain
Once the DNS records are present, return to the Resend dashboard.
Open:
Domains
and select:
restaurant.softsolutionsahand.com
Use Resend’s verification function.
Resend checks the public DNS records.
When the required records are found and accepted, the domain becomes verified.
Conceptually:
Miss Hosting DNS
↓
Public DNS
↓
Resend checks records
↓
Domain verified
At this point, Resend is authorized to send email for the restaurant domain.
27.14 Change the Sender Address
Before domain verification, our test email used:
Nordic Ember <onboarding@resend.dev>
After the domain was successfully verified, we could change the production sender to:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
Our email configuration was designed to use:
RESEND_FROM
with a fallback sender in the application.
The production value should therefore represent:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
This is much more professional than sending restaurant reservations from a generic testing address.
27.15 Update Railway Configuration
The production sender is part of the Railway environment configuration.
Railway now contains the information needed for production email:
RESEND_API_KEY
RESEND_FROM
Conceptually:
Railway
│
├── RESEND_API_KEY
│ ↓
│ Authenticate with Resend
│
└── RESEND_FROM
↓
Nordic Ember
<booking@restaurant.softsolutionsahand.com>
The actual API key remains private.
Never publish it in the article or commit it to GitHub.
27.16 Update server/email.ts
Our email module was adjusted so that the Resend sender can come from the environment configuration.
Conceptually:
RESEND_FROM
↓
server/email.ts
↓
Resend
↓
Customer
We used a production sender equivalent to:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
This change was made locally in VS Code and then committed to Git.
27.17 Commit the Email Change to GitHub
Because Railway is connected to GitHub, changing the source code locally is not enough.
The deployment workflow is:
VS Code
↓
Modify server/email.ts
↓
Test / review
↓
git add
↓
git commit
↓
git push origin main
↓
GitHub
↓
Railway automatic deployment
This is another example of why GitHub is central to our production workflow.
27.18 Railway Automatically Redeploys
After the push to GitHub, Railway detects the new commit.
It then starts another deployment.
This means:
GitHub push
↓
Railway deployment
↓
New application version
During our project, Railway also sent deployment notifications when a deployment crashed.
When that happens, the correct place to investigate is:
Railway
↓
Deployment
↓
Logs
Do not assume that the DNS or Resend configuration caused the crash.
The deployment logs show the actual application startup problem.
27.19 Test the Custom Sender from the Railway Application
Once Railway was running with the updated configuration, we tested the restaurant again.
We opened the production application and made a real reservation.
The reservation included information such as:
Customer
Guests
Date
Time
Seating area
Special requests
The reservation was successfully processed.
Most importantly, the customer received the confirmation email from:
Nordic Ember
From: booking@restaurant.softsolutionsahand.com
instead of:
onboarding@resend.dev
This confirmed that the custom sending domain was working.
27.20 Verify the Reservation Details
The received confirmation included:
Hello Mehrdad Zandi,
Thank you for your reservation at Nordic Ember.
Booking reference: TR-...
Date: ...
Time: ...
Guests: ...
Seating area: Main Dining Room (Matsal)
Special requests: ...
Please keep your booking reference if you need
to contact the restaurant about your reservation.
Best regards,
Nordic Ember
This test verified much more than just DNS.
It proved the complete production path:
Customer
↓
Nordic Ember on Railway
↓
Reservation API
↓
Reservation created
↓
Email service
↓
RESEND_API_KEY
↓
Resend
↓
Verified restaurant domain
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Inbox
27.21 Local and Production Email Are Now Different by Design
We can now clearly describe the final email architecture.
Local development:
Nordic Ember
↓
Express
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
Production:
Nordic Ember
↓
Railway
↓
Express
↓
Resend API
↓
Verified Restaurant Domain
↓
Customer
This is intentional.
We are not trying to make every environment identical.
Instead, we use an appropriate email delivery mechanism for each environment while keeping the same reservation logic.
27.22 Website DNS and Email DNS Are Different
This is worth emphasizing because it can initially be confusing.
For FlightFinder we have a DNS record such as:
findflight.softsolutionsahand.com
CNAME
↓
webapp-2897254.pythonanywhere.com
That means:
DNS
↓
Website Hosting
The Resend records mean:
DNS
↓
Email Authentication / Sending
Therefore:
FlightFinder record
≠
Resend records
The fact that Nordic Ember uses three email-related DNS records does not mean its website requires three hosting records.
Later, if we decide to give the Railway application a custom public address such as:
restaurant.softsolutionsahand.com
that will be a web-domain configuration associated with Railway.
It is conceptually separate from the Resend email-authentication records we configured in this step.
27.23 Final Production Email Configuration
At this point, the important production configuration is:
Application:
Nordic Ember
Hosting:
Railway
Production Email Provider:
Resend
Email Sending Domain:
restaurant.softsolutionsahand.com
Customer-Facing Sender:
Nordic Ember <booking@restaurant.softsolutionsahand.com>
DNS Provider:
Miss Hosting
The email-related DNS configuration contains:
resend._domainkey.restaurant.softsolutionsahand.com
TXT
→ DKIM public key supplied by Resend
send.restaurant.softsolutionsahand.com
CNAME
→ send.forge.rmta.net
rsend.restaurant.softsolutionsahand.com
CNAME
→ rsend-euw1.forge.rmta.net
Again, another project should use the DNS records generated for that project by Resend rather than assuming these exact values will always be the same.
27.24 Production Email Milestone
We have now completed one of the most important infrastructure parts of Nordic Ember.
The complete production email path is working:
CUSTOMER
│
▼
Nordic Ember
│
▼
Railway
│
▼
Reservation API
│
┌─────┴─────┐
│ │
▼ ▼
Reservation Resend API
Stored │
▼
Verified DNS Domain
│
▼
booking@restaurant.softsolutionsahand.com
│
▼
Customer Email
We have successfully moved from:
onboarding@resend.dev
to:
booking@restaurant.softsolutionsahand.com
and verified the result using a real restaurant reservation.
The production email system is therefore complete.
What Comes Next?
Now that Nordic Ember is:
Deployed on Railway ✅
Publicly accessible ✅
Reservation API working ✅
Production email working ✅
Custom sender verified ✅
Customer email tested ✅
we are finally ready to connect the application to Telegram.
The next stage is to create and configure the Nordic Ember Telegram bot:
@NordicEmberBot
and then connect the Railway-hosted restaurant application to Telegram as a real Telegram Mini App.
28. Step 21 — Create and Connect the Telegram Bot
Nordic Ember is now running as a production application on Railway.
We have also completed the production email system:
Customer
↓
Nordic Ember
↓
Railway
↓
Reservation API
↓
Resend
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Email
The next major part of the project is Telegram.
Our goal is to allow customers to open Nordic Ember directly inside Telegram instead of requiring them to open a separate browser manually.
The final architecture will look like:
Telegram
↓
@NordicEmberBot
↓
Nordic Ember Web App
↓
Railway
↓
Reservation API
↓
Resend
↓
Customer Email
Before configuring the Web App itself, however, we need a Telegram bot.
28.1 What Is a Telegram Bot?
A Telegram bot is a special Telegram account controlled through the Telegram Bot API.
Unlike a normal personal Telegram account, a bot can be connected to applications and automated services.
For Nordic Ember, the bot will act as the entry point to our restaurant application.
Our bot is:
@NordicEmberBot
The customer will eventually be able to use the bot to open the Nordic Ember restaurant application directly inside Telegram.
Conceptually:
Customer
↓
Telegram
↓
@NordicEmberBot
↓
Open Restaurant
↓
Nordic Ember Mini App
28.2 What Is BotFather?
Telegram provides an official bot called:
@BotFather
BotFather is used to create and manage Telegram bots.
Through BotFather we can configure things such as:
Bot name
Bot username
Bot token
Bot description
Bot commands
Menu button
Web Apps
We will also use BotFather in the next step to connect our Railway application as a Telegram Web App.
28.3 Open BotFather
Open Telegram and search for:
@BotFather
Make sure you are using the official BotFather account.
Open the conversation and press Start if necessary.
BotFather provides commands for managing Telegram bots.
One of the most important is:
/newbot
This starts the process of creating a new bot.
28.4 Create the Nordic Ember Bot
Send:
/newbot
BotFather asks for a name for the bot.
For our restaurant project, we use:
Nordic Ember
This is the human-readable name customers can see in Telegram.
BotFather then asks for a username.
We use:
NordicEmberBot
Telegram bot usernames normally end with:
bot
Our final Telegram bot therefore becomes:
@NordicEmberBot
We now have:
Restaurant:
Nordic Ember
Telegram Bot:
@NordicEmberBot
28.5 Bot Name and Bot Username Are Different
It is useful to understand the difference.
The name is:
Nordic Ember
This is the friendly display name.
The username is:
@NordicEmberBot
The username uniquely identifies the bot on Telegram.
Conceptually:
Display Name
Nordic Ember
↓
Telegram Username
@NordicEmberBot
We will use the username when identifying the bot during later BotFather configuration.
28.6 BotFather Generates the Bot Token
After successfully creating the bot, BotFather provides an HTTP API token.
The token is a secret credential that identifies and authenticates the bot.
Conceptually it looks something like:
1234567890:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxx
That is only an example.
Never copy a real bot token into a public article, screenshot, GitHub repository, or source file.
The token is essentially a password for controlling the bot.
28.7 Protect the Bot Token
This was previously a separate section in our article, but it belongs naturally here because protecting the token is part of configuring the Telegram bot.
Treat the Telegram bot token as a secret.
Never write something like this directly into application source code:
const BOT_TOKEN = "1234567890:AA...";
and then commit that code to GitHub.
Instead, secrets should be supplied through environment variables when the application needs them.
The principle is the same one we already followed with Resend:
SOURCE CODE
↓
GitHub
SECRETS
↓
Environment Variables
For example:
RESEND_API_KEY
TELEGRAM_BOT_TOKEN
should be treated as secrets.
28.8 Why the Bot Token Must Stay Private
Anyone who obtains a valid bot token may potentially use Telegram’s Bot API as that bot.
Therefore:
Bot Token
=
Secret Credential
Do not put it in:
README.md
Blog articles
GitHub source code
Public screenshots
Documentation
Frontend JavaScript
React components
The last point is particularly important.
A secret bot token should never be included in frontend code sent to the customer’s browser.
28.9 What If a Bot Token Is Accidentally Exposed?
If a real token is accidentally published—for example in GitHub or a screenshot—do not continue using it as though nothing happened.
Return to BotFather and revoke/regenerate the affected token using the available bot-management options.
Then update the private environment configuration with the replacement token.
The old exposed token should no longer be trusted.
28.10 Do We Need the Bot Token in the React Frontend?
No.
Our React application should not contain the bot token.
Remember that frontend code ultimately runs on the customer’s device.
Anything included in the browser application can potentially be inspected.
Therefore:
React Frontend
↓
NO secret bot token
If future functionality requires authenticated Telegram Bot API operations, those operations should normally be performed from a trusted backend.
Conceptually:
React / Telegram Mini App
↓
Our Backend
↓
TELEGRAM_BOT_TOKEN
↓
Telegram Bot API
This keeps the secret on the server side.
28.11 Do We Need the Bot Token Just to Open the Mini App?
For the next part of our project, our immediate goal is simpler.
We want Telegram to open the already deployed Nordic Ember web application:
https://restaurant-telegram-mini-app-production.up.railway.app
inside Telegram.
For that configuration, much of the work can be performed directly through BotFather.
The Web App will point to the public Railway URL.
Conceptually:
@NordicEmberBot
↓
Telegram Web App
↓
Railway HTTPS URL
↓
Nordic Ember
The bot token is still important and must remain protected, but we do not need to put it into the React frontend simply to make the Railway website available as a Web App.
28.12 Why HTTPS Matters Here
Earlier in the article we had a separate section called:
Why We Need HTTPS
We removed that as a separate main step because HTTPS makes more sense to explain here, where it becomes directly relevant.
Our local development application may use an address such as:
http://localhost:...
But that is not the address we give to Telegram users.
Our Railway deployment provides a public HTTPS address:
https://restaurant-telegram-mini-app-production.up.railway.app
Therefore, our architecture can become:
Telegram
↓
HTTPS
↓
Railway
↓
Nordic Ember
This is another reason why we deployed the application before completing the Telegram configuration.
28.13 Why We Created the Bot After Deployment
The order of the project now makes sense:
Build Nordic Ember
↓
Create Reservation Backend
↓
Create SQLite Database
↓
Add Local SMTP Email
↓
Deploy to Railway
↓
Configure Resend
↓
Verify Restaurant Email Domain
↓
Create / Configure Telegram Bot
↓
Configure Telegram Web App
By the time we configure Telegram, the restaurant application already has a public production URL.
We do not need to point Telegram to:
localhost
Instead, we can point it directly to the working Railway deployment.
28.14 Open the Nordic Ember Bot
After creating the bot, search Telegram for:
@NordicEmberBot
Open it.
At this stage, simply having the bot does not mean the restaurant Web App is fully configured.
We still need to tell Telegram:
Which web application belongs to this bot?
That is what we will do next through BotFather.
28.15 The Bot Is Only One Part of the Telegram Mini App
It is useful to separate these concepts.
We have:
Telegram Bot
@NordicEmberBot
and we have:
Web Application
Nordic Ember
Hosted on Railway
The next task is to connect them.
Conceptually:
@NordicEmberBot
│
│ connect
▼
Nordic Ember Web App
│
▼
Railway
Once connected, Telegram becomes another way of accessing our existing application.
We are not building another separate restaurant application for Telegram.
That is an important architectural advantage.
28.16 One Application, Multiple Entry Points
Nordic Ember can eventually be opened through several routes.
For example:
Normal Web Browser
│
▼
Railway
│
▼
Nordic Ember
and:
Telegram
│
▼
@NordicEmberBot
│
▼
Telegram Mini App
│
▼
Railway
│
▼
Nordic Ember
Both routes ultimately use the same deployed application.
That means we do not have to maintain one reservation system for the web and another one for Telegram.
28.17 Existing Backend Features Continue to Work
Because Telegram opens the same Railway-hosted application, our existing backend architecture remains useful:
Telegram Customer
↓
Nordic Ember Mini App
↓
Railway
↓
POST /api/reservations
↓
Express
↓
Reservation Storage
↓
Resend
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Email
This is exactly what we want.
Telegram is the interface through which the customer reaches the application; Railway continues to run the actual application and backend.
28.18 Bot Configuration Milestone
At this point, we have the important Telegram foundation:
Telegram Bot created ✅
Bot name: Nordic Ember ✅
Username: @NordicEmberBot ✅
Bot token generated ✅
Token treated as secret ✅
Production application online ✅
HTTPS Railway URL available ✅
But the bot does not yet provide the complete polished Mini App experience.
We still need to configure the actual Telegram Web App.
That next step will include several things we actually configured during this project:
Nordic Ember Web App
↓
Title
↓
Description
↓
640 × 360 image
↓
Demo GIF / skip
↓
Railway Web App URL
↓
Short name: restaurant
↓
Direct Telegram Web App link
↓
Menu button
↓
🔥 Open Restaurant
28.19 What Comes Next?
The next step is where Nordic Ember becomes a real Telegram Mini App.
Using BotFather, we will configure the Web App associated with:
@NordicEmberBot
We will give Telegram our Railway production URL:
https://restaurant-telegram-mini-app-production.up.railway.app
and configure the short name:
restaurant
which gives us the direct Web App address:
t.me/NordicEmberBot/restaurant
We will also configure the bot’s menu button as:
🔥 Open Restaurant
Finally, we will test the application both on desktop Telegram and on a real mobile phone, make a reservation, and verify that the customer receives the confirmation from:
Nordic Ember
booking@restaurant.softsolutionsahand.com
29. Step 22 — Configure the Nordic Ember Telegram Mini App
We now have the two main components required for the Telegram integration.
First, our production restaurant application is running on Railway:
https://restaurant-telegram-mini-app-production.up.railway.app
Second, we have created the Telegram bot:
@NordicEmberBot
The next task is to connect them so customers can open Nordic Ember directly inside Telegram as a Telegram Mini App.
The architecture will become:
Telegram
↓
@NordicEmberBot
↓
Nordic Ember Mini App
↓
Railway
↓
Express Reservation API
↓
Reservation
↓
Resend
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Email
29.1 What Is a Telegram Mini App?
A Telegram Mini App is essentially a web application that can be opened inside Telegram.
This is particularly useful for Nordic Ember because we have already built a complete responsive web application.
We do not need to create another restaurant application specifically for Telegram.
Instead:
Existing Nordic Ember Web App
+
Telegram Bot
↓
Telegram Mini App
The same React frontend and Express backend continue to be used.
29.2 Why We Deploy Before Configuring the Mini App
During local development, Nordic Ember runs using a local address.
For example:
http://localhost:...
That address is useful on the developer’s computer, but it is not the public production address we want Telegram customers to use.
Railway gives us a public HTTPS address:
https://restaurant-telegram-mini-app-production.up.railway.app
Therefore, Telegram can open:
Telegram
↓
HTTPS
↓
Railway
↓
Nordic Ember
This is why deployment was completed before the final Telegram Mini App configuration.
29.3 Open BotFather
Open Telegram and go to:
@BotFather
We will use BotFather to configure the Web App belonging to:
@NordicEmberBot
BotFather provides several commands for managing bots and their Web Apps.
For this part of the project, we use the Web App configuration provided by BotFather.
29.4 Select the Nordic Ember Bot
When BotFather asks which bot should be configured, select:
@NordicEmberBot
We are now configuring the Telegram Web App associated with Nordic Ember.
The relationship will eventually be:
@NordicEmberBot
↓
restaurant
↓
Nordic Ember Web App
↓
Railway
29.5 Create a New Web App
During our configuration, BotFather started the Web App creation process and displayed:
Creating a new web app for @NordicEmberBot.
Please enter a title for the web app.
We now need to provide information describing the application.
For the title, use:
Nordic Ember
This keeps the Telegram Web App branding consistent with the restaurant application.
29.6 Enter the Web App Description
BotFather then asks:
Please enter a short description of the web app.
Use a concise description explaining what customers can do.
For example:
Explore Nordic Ember's seasonal menu and reserve
your table directly from Telegram.
The description should be short and customer-oriented.
There is no need to explain Railway, React, Express, Resend, or the technical architecture here.
Those technologies are implementation details.
29.7 Upload the Web App Image
BotFather next asks for an image:
Please upload a photo, 640x360 pixels.
This requirement is important.
The image must have the exact dimensions:
640 × 360 pixels
This is a 16:9 image.
During our configuration, an image that looked correct was initially rejected by BotFather with:
Sorry, the image dimensions are invalid.
Must be 640x360
This demonstrates an important point:
The image can look visually correct and still be rejected if its actual pixel dimensions are not exactly 640 × 360.
29.8 PNG Is Fine
The image does not need to be converted simply because it is a PNG.
A file such as:
nordic-ember-telegram-app.png
is suitable as long as its actual dimensions are:
Width: 640 pixels
Height: 360 pixels
The critical requirement is the dimensions, not changing PNG into another format.
29.9 Store the Telegram Image in the Project
It is useful to keep the Telegram artwork together with the project instead of leaving the only copy in Downloads or another temporary folder.
For example, we can use:
restaurant-telegram-mini-app/
│
├── Docs/
│
├── public/
│ └── telegram/
│ └── nordic-ember-telegram-app.png
│
├── server/
├── src/
└── package.json
This makes it clear that the image belongs to the Nordic Ember project.
It also makes the project easier to maintain later.
29.10 Verify the Image Dimensions Before Uploading
Before sending the image to BotFather, check its properties.
On Windows, you can right-click the file and inspect its image dimensions.
Confirm:
640 × 360
Do not rely only on how large the image appears on screen.
Two images can look almost identical in Windows while having completely different pixel dimensions.
After correcting the image to exactly 640 × 360, BotFather accepted it.
29.11 Demo GIF Is Optional
After accepting the image, BotFather asks:
Now upload a demo GIF or send /empty to skip this step.
A demo GIF could be useful later for showing a short preview of the restaurant application.
For our first production version, however, it was unnecessary.
We therefore sent:
/empty
BotFather confirmed that a GIF can be added later using its editing functions.
This means the GIF is optional and does not prevent us from completing the Mini App.
29.12 Enter the Web App URL
BotFather then asks:
Now please send me the Web App URL that will be
opened when users follow a web app direct link.
This is where our Railway deployment becomes essential.
We provide:
https://restaurant-telegram-mini-app-production.up.railway.app
The relationship is now:
Telegram Web App
↓
Railway URL
↓
Nordic Ember
Telegram does not host our React application.
Railway continues to host it.
Telegram simply provides another way for customers to open it.
29.13 Choose the Web App Short Name
BotFather then asks us to choose a short name.
It explains that the short name must contain:
3–30 characters
a-z
A-Z
0-9
_
and will be used in URLs similar to:
t.me/NordicEmberBot/myapp
For Nordic Ember, we selected:
restaurant
This is short, clear, and easy to understand.
BotFather confirmed:
You can now use restaurant as the short_name parameter
value in Bot API.
29.14 Our Direct Telegram Web App Link
After the short name was accepted, BotFather generated our Web App link:
t.me/NordicEmberBot/restaurant
This is an important result.
Customers can now reach the Nordic Ember Web App through:
Telegram
↓
t.me/NordicEmberBot/restaurant
↓
Nordic Ember
The short name:
restaurant
therefore becomes part of the Telegram Mini App identity.
29.15 Test the Direct Web App Link
Open:
t.me/NordicEmberBot/restaurant
in Telegram.
Nordic Ember should open inside Telegram.
Test the main navigation:
Home
Menu
Book Table
About
Contact
Also test:
Swedish / English
and verify that the application behaves properly inside Telegram’s Web App environment.
At this stage we have already connected:
@NordicEmberBot
↓
restaurant
↓
Railway
↓
Nordic Ember
But we can make the user experience even better.
29.16 Configure the Bot Menu Button
Instead of expecting customers to remember or manually open the direct Web App link, we can configure a menu button for the bot.
Return to BotFather and open the menu-button configuration for:
@NordicEmberBot
BotFather asks:
Send me the URL that will be opened by tapping
the menu button.
Use /empty to use the default behavior for
the menu button.
We want the button to open the restaurant application.
Therefore, provide the Railway Web App URL:
https://restaurant-telegram-mini-app-production.up.railway.app
29.17 Give the Menu Button a Better Name
BotFather then asks:
Send me the title to be displayed on the button
instead of 'Menu'.
Use /empty to use the default behavior for
the menu button.
Rather than leaving the generic:
Menu
we used a clearer customer action:
🔥 Open Restaurant
BotFather then confirmed:
Success! Menu settings updated.
The bot now provides a much more obvious way to launch the restaurant.
29.18 Why “Open Restaurant” Is Better Than “Menu”
The word:
Menu
can be ambiguous in a restaurant application.
A customer could reasonably think it means:
Food Menu
rather than:
Open the entire application
Using:
🔥 Open Restaurant
makes the purpose clearer.
The customer understands that the button opens Nordic Ember itself.
29.19 The Two Ways to Open Nordic Ember in Telegram
We now have two useful Telegram entry points.
Method 1 — Direct Web App link
t.me/NordicEmberBot/restaurant
This can be shared in Telegram messages, documentation, websites, or other suitable locations.
Method 2 — Bot menu button
Open:
@NordicEmberBot
and use:
🔥 Open Restaurant
Both ultimately open the same Nordic Ember application.
Conceptually:
Direct Web App Link ─────┐
│
▼
Nordic Ember
▲
│
Bot Menu Button ─────────┘
29.20 Test Nordic Ember in Telegram Desktop
After configuring the Web App and menu button, open:
@NordicEmberBot
in Telegram Desktop.
Use:
🔥 Open Restaurant
The Nordic Ember application should open inside Telegram.
Test the main functionality one section at a time:
Home ✅
Menu ✅
Book Table ✅
About ✅
Contact ✅
Also verify the language selector and responsive layout.
29.21 Test the Reservation Flow Inside Telegram
The most important test is not simply opening the Home screen.
Create an actual test reservation from inside Telegram.
For example:
Guests: 3
Date: future date
Time: 20:00
Seating area: Main Dining Room (Matsal)
Name: Mehrdad Zandi
Email: test customer email
Special requests: test message
Submit the reservation.
The complete flow should now be:
Telegram
↓
Nordic Ember Mini App
↓
Railway
↓
POST /api/reservations
↓
Express
↓
Reservation saved
↓
Resend
↓
Customer Email
This is a much more important test than simply checking that the UI appears.
29.22 Test on a Real Mobile Phone
After the Telegram Desktop test, test the Mini App on a real mobile device.
This is especially important because Telegram Mini Apps are frequently used from phones.
Open Telegram on the phone.
Find:
@NordicEmberBot
and open the restaurant.
Verify:
Application opens ✅
Layout fits mobile screen ✅
Navigation works ✅
Menu works ✅
Booking form works ✅
Date/time selection works ✅
Reservation submits ✅
We performed this test with Nordic Ember and confirmed that the application worked from Telegram on mobile.
29.23 Our First Mobile Email Test
During an earlier mobile test, the restaurant application itself worked, but a confirmation email did not appear immediately.
Because email delivery involves several systems, this could potentially involve:
Telegram
Railway
Reservation API
Resend
Receiving mail provider
Spam filtering
Delivery timing
Rather than immediately changing the code, we tested the complete flow again.
On the later test, the email arrived successfully.
This is an important troubleshooting lesson:
Reproduce the problem and check the relevant logs before changing working production code.
A single missing email does not automatically prove that the email implementation is broken.
29.24 Successful Telegram Mobile Reservation
We then made another reservation through the Telegram Mini App on mobile.
The reservation was successfully created.
The confirmation email arrived with:
Nordic Ember
From:
booking@restaurant.softsolutionsahand.com
and included the customer’s reservation information.
For example:
Hello Mehrdad Zandi,
Thank you for your reservation at Nordic Ember.
Booking reference: TR-...
Date: ...
Time: 20:00
Guests: 3
Seating area: Main Dining Room (Matsal)
Special requests: ...
Please keep your booking reference if you need
to contact the restaurant about your reservation.
This was a very important milestone because it tested several systems together.
29.25 What the Successful Mobile Test Proves
The successful test confirms the complete production chain:
Mobile Phone
↓
Telegram
↓
@NordicEmberBot
↓
Nordic Ember Mini App
↓
Railway
↓
React Frontend
↓
Express API
↓
Reservation
↓
Resend
↓
Verified Email Domain
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Inbox
This means we are no longer testing isolated components.
We are testing the actual customer workflow.
29.26 Telegram Does Not Replace Railway
It is important to understand the role of Telegram.
Telegram is not hosting our application.
The architecture remains:
Telegram
↓
opens
↓
Railway Application
Railway continues to run:
React
Express
Reservation API
Email integration
Telegram provides the Mini App container and customer entry point.
This separation is useful because Nordic Ember can continue working as a normal web application even outside Telegram.
29.27 Telegram Does Not Replace Resend Either
Similarly, Telegram does not send our reservation confirmation emails.
That path remains:
Nordic Ember
↓
Railway Backend
↓
Resend
↓
booking@restaurant.softsolutionsahand.com
↓
Customer
Telegram and Resend solve different problems:
Telegram
→ customer access / Mini App
Railway
→ application hosting
Resend
→ production email delivery
Miss Hosting DNS
→ domain authentication
GitHub
→ source code
VS Code
→ development
Together they form the production system.
29.28 The Complete Nordic Ember Architecture
We can now see the complete architecture of the project:
CUSTOMER
│
▼
Telegram
│
▼
@NordicEmberBot
│
▼
Nordic Ember Mini App
│
▼
Railway
│
┌───────┴───────┐
│ │
▼ ▼
React Frontend Express Backend
│
▼
Reservation API
│
┌────────┴────────┐
│ │
▼ ▼
Reservation Resend
Storage │
▼
Verified Domain
│
▼
booking@restaurant.softsolutionsahand.com
│
▼
Customer Email
Development and deployment are connected through:
VS Code
↓
Git
↓
GitHub
↓
Railway
This means a code change can move from the development computer to production through a controlled Git workflow.
29.29 Final Telegram Configuration
Our completed Telegram configuration is:
Bot Name:
Nordic Ember
Bot Username:
@NordicEmberBot
Web App Title:
Nordic Ember
Web App Hosting:
Railway
Web App URL:
https://restaurant-telegram-mini-app-production.up.railway.app
Web App Short Name:
restaurant
Direct Mini App Link:
t.me/NordicEmberBot/restaurant
Menu Button:
🔥 Open Restaurant
Web App Image:
640 × 360 pixels
Demo GIF:
Skipped for now
This information is worth keeping in the project documentation because it makes future maintenance much easier.
29.30 Nordic Ember Telegram Milestone
Nordic Ember has now progressed through the complete path:
Google Stitch
↓
Google AI Studio
↓
React + TypeScript
↓
Express Backend
↓
SQLite
↓
Local SMTP
↓
GitHub
↓
Railway
↓
Resend
↓
Miss Hosting DNS
↓
Verified Restaurant Email
↓
Telegram Bot
↓
Telegram Mini App
↓
Mobile Test
↓
Successful Reservation
↓
Customer Confirmation Email
The application is now accessible as both a normal web application and a Telegram Mini App.
Most importantly, we have tested the actual customer journey:
Open Telegram
↓
Open Nordic Ember
↓
Browse Restaurant
↓
Book Table
↓
Reservation Created
↓
Confirmation Email Received
That completes the core Telegram Mini App integration for Nordic Ember.
30. Final Nordic Ember Architecture
Nordic Ember has now evolved from an AI-generated restaurant design into a complete full-stack application that can run as both a normal web application and a Telegram Mini App.
The project combines design tools, frontend and backend development, cloud deployment, reservation storage, transactional email, domain authentication, and Telegram integration.
The final system can be summarized as:
Google Stitch
↓
UI / UX Design
↓
Google AI Studio
↓
React + TypeScript + Vite
↓
Express Backend
↓
Reservation API
↓
SQLite
↓
GitHub
↓
Railway
↓
Production Application
↓
Resend
↓
Verified Restaurant Domain
↓
Telegram Mini App
30.1 Development Architecture
The project began with the visual design in Google Stitch.
The Stitch screens were exported to Google AI Studio, where the initial application structure was generated and developed further.
The application was then continued locally using VS Code.
The development workflow became:
Google Stitch
↓
Visual Restaurant Design
↓
Google AI Studio
↓
Generated Application
↓
VS Code
↓
React + TypeScript
↓
Vite
↓
Express
↓
SQLite
Each tool therefore has a different role.
Google Stitch was primarily used for the visual design.
Google AI Studio transformed the design into a working application foundation.
VS Code became the main development environment where we continued improving, testing, and maintaining the project.
30.2 Frontend Architecture
The customer-facing application is built using:
React
+
TypeScript
+
Vite
The frontend provides the restaurant experience, including:
Home
Menu
Book Table
About
Contact
English / Swedish
Responsive mobile interface
The same frontend is used whether the customer opens Nordic Ember in a normal browser or through Telegram.
We therefore do not maintain separate web and Telegram applications.
30.3 Backend Architecture
The server side is built with Node.js and Express.
Its responsibilities include:
Receive reservation
↓
Validate reservation
↓
Generate booking reference
↓
Store reservation
↓
Trigger confirmation email
↓
Return result to frontend
The main reservation communication occurs through the API:
POST /api/reservations
This keeps the frontend separated from the reservation-processing logic.
30.4 Reservation Architecture
A reservation follows this general path:
Customer
↓
Book Table
↓
React Frontend
↓
POST /api/reservations
↓
Express Backend
↓
Validation
↓
Reservation Reference
↓
SQLite
↓
Email Confirmation
The generated reservation reference gives each booking a unique identifier such as:
TR-XXXXXXXX-XXXX
The customer receives this reference in the confirmation email and can retain it when contacting the restaurant.
30.5 Local Development Email Architecture
During local development, Nordic Ember uses Nodemailer and SMTP.
The local email path is:
Nordic Ember
↓
Express
↓
Nodemailer
↓
Gmail SMTP
↓
Customer
The SMTP configuration is stored using local environment variables rather than hard-coded credentials.
For example:
SMTP_HOST
SMTP_PORT
SMTP_USER
SMTP_PASS
SMTP_FROM
This configuration proved useful during development and remains available for local testing.
30.6 Production Email Architecture
The production application uses a different email mechanism.
When Nordic Ember runs on Railway and:
RESEND_API_KEY
is available, the application selects Resend.
The production email path is:
Nordic Ember
↓
Railway
↓
Express
↓
Resend API
↓
Verified Restaurant Domain
↓
Customer
The final customer-facing sender is:
Nordic Ember
<booking@restaurant.softsolutionsahand.com>
This gives us a clear separation:
LOCAL
Nodemailer + Gmail SMTP
PRODUCTION
Railway + Resend
The reservation logic itself does not need to change between these environments.
30.7 How the Application Chooses the Email Provider
The email service uses the environment configuration to decide which provider to use.
Conceptually:
sendReservationConfirmation()
↓
Is RESEND_API_KEY set?
│
┌─────┴─────┐
│ │
YES NO
│ │
▼ ▼
Resend Nodemailer
│ │
▼ ▼
Production Local
This allows us to maintain one application while supporting two different email environments.
30.8 Domain and DNS Architecture
Our main domain is:
softsolutionsahand.com
For Nordic Ember email, we use:
restaurant.softsolutionsahand.com
and the sender:
booking@restaurant.softsolutionsahand.com
The DNS is managed through Miss Hosting.
Resend supplied the DNS records needed to authenticate the sending domain.
Our configuration included:
resend._domainkey.restaurant.softsolutionsahand.com
TXT
↓
DKIM public key
along with:
send.restaurant.softsolutionsahand.com
CNAME
↓
send.forge.rmta.net
and:
rsend.restaurant.softsolutionsahand.com
CNAME
↓
rsend-euw1.forge.rmta.net
These records authenticate the email-sending domain.
They should not be confused with a website-hosting DNS record.
30.9 GitHub and Deployment Architecture
Our source code is maintained locally and stored in GitHub.
The deployment workflow is:
VS Code
↓
Git
↓
GitHub
↓
Railway
↓
Production
After making and testing a change locally, we can use the normal Git workflow:
git add
↓
git commit
↓
git push origin main
Railway detects the updated GitHub repository and can automatically start a new deployment.
Therefore:
Local Code Change
↓
GitHub Push
↓
Railway Deployment
↓
Updated Production Application
Deployment logs in Railway are important whenever a new version fails to start.
30.10 Production Hosting Architecture
Railway hosts the complete Nordic Ember production application.
Our Railway application is available through the public HTTPS address:
https://restaurant-telegram-mini-app-production.up.railway.app
This allows Nordic Ember to be accessed from a normal web browser.
It also provides the HTTPS application required for our Telegram integration.
30.11 Telegram Architecture
Our Telegram bot is:
@NordicEmberBot
The Web App short name is:
restaurant
giving us the direct Mini App link:
t.me/NordicEmberBot/restaurant
The bot also provides the menu button:
🔥 Open Restaurant
The Telegram path therefore becomes:
Customer
↓
Telegram
↓
@NordicEmberBot
↓
🔥 Open Restaurant
↓
Nordic Ember Mini App
↓
Railway
Telegram does not host the restaurant application itself.
It opens the application hosted by Railway.
30.12 Browser and Telegram Use the Same Application
Nordic Ember now has two principal entry points.
A customer can use a normal browser:
Customer
↓
Web Browser
↓
Railway
↓
Nordic Ember
or Telegram:
Customer
↓
Telegram
↓
@NordicEmberBot
↓
Mini App
↓
Railway
↓
Nordic Ember
Both routes reach the same production application.
This is an important architectural advantage because we only need to maintain one frontend and one backend.
30.13 Complete Customer Reservation Flow
The complete Telegram customer journey is now:
Customer's Mobile Phone
↓
Telegram
↓
@NordicEmberBot
↓
🔥 Open Restaurant
↓
Nordic Ember Mini App
↓
Railway
↓
React Frontend
↓
Book Table
↓
POST /api/reservations
↓
Express Backend
↓
Validation
↓
Booking Reference
↓
SQLite
↓
Resend
↓
Verified Restaurant Domain
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Confirmation Email
We tested this flow on a real mobile device and successfully received the reservation confirmation email.
30.14 Complete System Architecture
Putting everything together gives us the final Nordic Ember architecture:
CUSTOMER
│
┌─────────┴─────────┐
│ │
▼ ▼
Web Browser Telegram
│
▼
@NordicEmberBot
│
▼
🔥 Open Restaurant
│ │
└─────────┬─────────┘
│
▼
Railway
│
┌─────────┴─────────┐
│ │
▼ ▼
React + TypeScript Express
Frontend Backend
│
▼
Reservation API
│
┌─────────────┴─────────────┐
│ │
▼ ▼
SQLite Resend
│
▼
Verified DNS Domain
│
▼
booking@restaurant.softsolutionsahand.com
│
▼
Customer Email
Behind the production system, our development pipeline is:
Google Stitch
↓
Google AI Studio
↓
VS Code
↓
Git
↓
GitHub
↓
Railway
And the supporting services are:
Miss Hosting
↓
DNS
↓
Resend Domain Verification
30.15 Technologies Used in the Final Project
The completed Nordic Ember project brings together several technologies:
| Area | Technology |
|---|---|
| UI design | Google Stitch |
| AI-assisted development | Google AI Studio |
| Frontend | React |
| Programming language | TypeScript |
| Build system | Vite |
| Backend | Node.js + Express |
| Reservation API | REST |
| Database | SQLite |
| Local email | Nodemailer + SMTP |
| Production email | Resend |
| Domain/DNS | Miss Hosting |
| Source control | Git |
| Repository | GitHub |
| Cloud deployment | Railway |
| Messaging platform | Telegram |
| Telegram bot | @NordicEmberBot |
| Mini App short name | restaurant |
The important point is not simply the number of technologies used. Each technology solves a specific part of the application.
30.16 What We Have Achieved
Nordic Ember began as a restaurant interface designed with AI tools.
It is now a functioning full-stack application with:
Responsive restaurant UI ✅
English and Swedish ✅
Menu browsing ✅
Table reservations ✅
Server-side reservation API ✅
Reservation storage ✅
Unique booking references ✅
Local SMTP email ✅
Production Resend email ✅
Custom restaurant sender ✅
Verified email domain ✅
GitHub source control ✅
Railway cloud deployment ✅
Public HTTPS application ✅
Telegram bot ✅
Telegram Mini App ✅
Desktop Telegram test ✅
Mobile Telegram test ✅
End-to-end reservation test ✅
Customer confirmation email ✅
Most importantly, the final result is not merely an AI-generated design.
It demonstrates how an AI-generated starting point can be developed step by step into a working application with a frontend, backend, database, cloud deployment, authenticated production email, and Telegram integration
31. Showing Nordic Ember to Restaurant Customers
Nordic Ember is now more than a development experiment. It is a working demonstration application that can be shown to restaurant owners to demonstrate what a modern restaurant website and Telegram Mini App could provide for their business.
The purpose of Nordic Ember is not to sell this exact restaurant application unchanged to every restaurant.
Instead, it demonstrates a reusable concept:
Nordic Ember Demo
↓
Show to Restaurant Owner
↓
Discuss Requirements
↓
Customize
↓
Restaurant's Own Application
A restaurant owner can see and test the application before deciding which features, design, branding, languages, and integrations they would like for their own restaurant.
31.1 Two Ways to Demonstrate Nordic Ember
Nordic Ember can currently be demonstrated in two main ways.
The first is as a normal web application:
Customer / Restaurant Owner
↓
Web Browser
↓
Railway
↓
Nordic Ember
The production application is available at:
https://restaurant-telegram-mini-app-production.up.railway.app
The second method is through Telegram:
Customer / Restaurant Owner
↓
Telegram
↓
@NordicEmberBot
↓
🔥 Open Restaurant
↓
Nordic Ember Mini App
The direct Telegram Mini App link is:
t.me/NordicEmberBot/restaurant
Both methods open the same production application.
31.2 Start with the Customer Experience
When demonstrating the application to a restaurant owner, it is usually better to start with the customer experience rather than explaining React, TypeScript, Railway, Resend, or the database.
Open Nordic Ember and demonstrate the application as a restaurant customer would use it.
For example:
Home
↓
Restaurant presentation
↓
Menu
↓
Browse dishes
↓
Book Table
↓
Enter reservation details
↓
Submit reservation
↓
Booking reference created
↓
Confirmation email received
After the restaurant owner understands what the customer sees, the technical architecture can be explained if they are interested.
31.3 Demonstrate the Home Page
Begin with the Home page.
Show how the restaurant can present its:
Restaurant name
Brand identity
Hero images
Introduction
Seasonal information
Featured dishes
Calls to action
Explain that Nordic Ember is only the demonstration branding.
For a real restaurant, these elements would be replaced with the customer’s own content.
For example:
Nordic Ember
↓
Customer's Restaurant Name
Nordic Ember images
↓
Customer's Restaurant Photos
Nordic Ember colors
↓
Customer's Brand Colors
The application is therefore not tied permanently to the Nordic Ember identity.
31.4 Demonstrate the Menu
Next, open:
Menu
Show how customers can browse the restaurant’s dishes.
A production version can be customized with the restaurant’s:
Starters
Main courses
Desserts
Drinks
Prices
Descriptions
Food images
Dietary information
Categories
Seasonal dishes
The exact structure should depend on the real restaurant’s menu.
A café, pizzeria, fine-dining restaurant, fast-food restaurant, and hotel restaurant may all require different menu structures.
31.5 Demonstrate English and Swedish
Nordic Ember supports both:
English
Swedish
Demonstrate switching between the languages.
For restaurants serving international customers, multilingual support can be an important feature.
The same concept can later be extended to additional languages if required.
For example:
Swedish
English
German
French
Spanish
Turkish
The languages should be selected according to the restaurant’s actual customers rather than adding unnecessary translations.
31.6 Demonstrate the Reservation System
The Book Table feature is one of the most important parts of the demonstration.
Open the reservation page and create a test reservation.
Show fields such as:
Number of guests
Date
Time
Seating area
Customer name
Phone
Email
Special requests
For example, a customer could request:
Quiet corner requested for anniversary dinner.
1 pescatarian guest.
Submit the reservation.
This demonstrates that Nordic Ember is not simply a static restaurant website.
It contains an actual backend reservation workflow.
31.7 Show the Booking Reference
After the reservation is created, Nordic Ember generates a unique booking reference.
For example:
TR-XXXXXXXX-XXXX
Explain that this provides a useful identifier for the reservation.
The customer can keep the reference when contacting the restaurant about the booking.
Conceptually:
Reservation
↓
Unique Reference
↓
Customer
+
Restaurant
This is more useful than simply displaying:
Thank you!
without identifying the reservation.
31.8 Demonstrate the Confirmation Email
One of the strongest parts of the demonstration is the complete email flow.
After making the reservation, show the confirmation email received by the customer.
The production email arrives from:
Nordic Ember
booking@restaurant.softsolutionsahand.com
and contains information such as:
Customer name
Booking reference
Date
Time
Number of guests
Seating area
Special requests
This demonstrates the complete flow:
Customer
↓
Reservation
↓
Railway Backend
↓
Reservation Created
↓
Resend
↓
Professional Restaurant Email
↓
Customer Inbox
A real restaurant implementation could use its own sender, for example:
booking@restaurant-name.se
provided the restaurant’s domain and email configuration are properly set up.
31.9 Demonstrate the Telegram Mini App
Next, show the restaurant owner that Nordic Ember can also run inside Telegram.
Open:
@NordicEmberBot
and press:
🔥 Open Restaurant
Nordic Ember opens inside Telegram.
This is an important demonstration because the restaurant owner can see that customers do not necessarily need to install another dedicated restaurant application.
Instead:
Telegram
↓
Restaurant Bot
↓
Mini App
↓
Menu + Reservation
The direct Nordic Ember Mini App link is:
t.me/NordicEmberBot/restaurant
31.10 Make a Reservation from Telegram
Do not demonstrate only the Telegram Home page.
Complete a reservation.
For example:
Open Telegram
↓
@NordicEmberBot
↓
🔥 Open Restaurant
↓
Book Table
↓
Enter reservation
↓
Submit
↓
Reservation stored
↓
Confirmation email received
We tested this complete process on mobile and confirmed that the customer could make a reservation through Telegram and receive the confirmation email.
That is a much stronger demonstration than simply showing screenshots.
31.11 Demonstrate the Application on a Mobile Phone
Restaurant customers frequently visit websites from mobile devices.
Therefore, show Nordic Ember on an actual phone whenever possible.
Demonstrate:
Responsive Home page
Menu
Navigation
Language selection
Booking form
Telegram Mini App
Confirmation process
This allows the restaurant owner to experience the application in approximately the same way many of their customers would.
31.12 Explain That Nordic Ember Is a Demonstration Restaurant
When showing Nordic Ember to a potential customer, make it clear that:
Nordic Ember
is the demonstration restaurant used for this project.
A real implementation would be customized.
For example:
NORDIC EMBER DEMO
Name:
Nordic Ember
Domain:
Demo / development domain
Email:
booking@restaurant.softsolutionsahand.com
Telegram:
@NordicEmberBot
could become:
CUSTOMER RESTAURANT
Name:
Customer's restaurant name
Domain:
Customer's domain
Email:
booking@customer-domain.se
Telegram:
Customer's own bot
The underlying concept can remain similar while the branding and configuration change.
31.13 What Can Be Customized for a Restaurant?
A real customer implementation could be adapted to the restaurant’s requirements.
Typical areas include:
Restaurant name
Logo
Colors
Typography
Photos
Food menu
Prices
Opening hours
Languages
Address
Map
Phone number
Email address
Reservation rules
Available booking times
Maximum party size
Seating areas
Special requests
Domain
Confirmation email
Telegram bot
Not every restaurant needs every feature.
The purpose of the customer discussion is to determine which features actually provide value to that restaurant.
31.14 The Restaurant Can Use Its Own Domain
Nordic Ember currently runs on the Railway production address:
https://restaurant-telegram-mini-app-production.up.railway.app
For a real customer, the application could instead be connected to an appropriate custom domain or subdomain.
For example:
restaurant-name.se
or:
booking.restaurant-name.se
The exact configuration would depend on the customer’s existing website and domain setup.
This allows the customer-facing application to use the restaurant’s own branding rather than a Railway-generated address.
31.15 The Restaurant Can Use Its Own Email Domain
The same principle applies to confirmation email.
Our demonstration uses:
booking@restaurant.softsolutionsahand.com
A customer implementation could instead use something such as:
booking@customer-domain.se
The restaurant’s domain would need to be properly verified and configured with the selected email provider.
Our Nordic Ember work with Resend and DNS demonstrates how such a production configuration can be implemented.
31.16 The Restaurant Can Have Its Own Telegram Bot
Nordic Ember uses:
@NordicEmberBot
A real restaurant should normally use its own bot identity.
For example:
@CustomerRestaurantBot
The customer’s Telegram flow could then become:
Customer
↓
@CustomerRestaurantBot
↓
Open Restaurant
↓
Customer's Restaurant App
The bot name, description, image, Web App, and menu button can all be configured for the customer’s brand.
31.17 What the Restaurant Owner Does Not Need to Understand
A restaurant owner does not necessarily need to understand:
React
TypeScript
Vite
Express
REST APIs
SQLite
Git
DNS records
DKIM
Railway environment variables
Resend API keys
Those are implementation details for the developer.
From the restaurant’s perspective, the important questions are simpler:
Does the site represent my restaurant well?
Can customers easily see my menu?
Can customers book a table?
Does it work well on mobile?
Can customers receive confirmation?
Can I use my restaurant's name, domain and email?
Can customers access it through Telegram?
The technical architecture supports these business requirements.
31.18 Explain the Developer’s Role
The developer handles the technical configuration behind the application.
For example:
Restaurant Owner
↓
Provides business requirements
↓
Developer
↓
Configures application
↓
Deploys and tests
↓
Restaurant receives working system
The restaurant would normally provide information such as:
Restaurant name
Logo
Photos
Menu
Prices
Opening hours
Contact information
Reservation requirements
Preferred languages
Domain information
The developer then adapts the application.
31.19 Nordic Ember as a Portfolio Demonstration
Nordic Ember can also serve as a practical portfolio project.
Instead of only saying:
I can build restaurant websites.
we can demonstrate:
Here is a working restaurant application.
The restaurant owner can then:
Open it
Browse it
Use the menu
Make a reservation
Test it on mobile
Open it in Telegram
Receive a confirmation email
This provides a concrete example of what can be built.
31.20 Add Nordic Ember to Softsolution Sahand
A useful future step is to create a dedicated Nordic Ember demonstration page on Softsolution Sahand, similar in concept to the existing FlightFinder presentation page.
The Softsolution Sahand page would not need to host the Nordic Ember backend itself.
Instead, it could explain the project and provide a launch button.
Conceptually:
softsolutionsahand.com
↓
Nordic Ember Project Page
↓
Project Description
↓
Features
↓
Screenshots
↓
🚀 Launch Nordic Ember
↓
Railway Application
A second button could launch the Telegram version:
📱 Open Nordic Ember in Telegram
↓
t.me/NordicEmberBot/restaurant
This would make Nordic Ember easier to show to potential customers and blog visitors.
31.21 Suggested Softsolution Sahand Presentation
The future project page could contain a short introduction such as:
Nordic Ember — Restaurant Web App & Telegram Mini App
Nordic Ember is a full-stack restaurant demonstration
application built to show how modern web technologies,
AI-assisted development, cloud deployment and Telegram
can be combined into a complete restaurant experience.
Customers can browse the restaurant, explore the menu,
reserve a table and receive an email confirmation.
The same application can also run directly inside Telegram
as a Mini App.
Then present key functionality such as:
Responsive restaurant website
Multilingual EN / SV interface
Interactive food menu
Table reservations
Unique booking references
Customer confirmation email
Mobile-friendly design
Telegram Mini App
Cloud deployment
And provide clear launch actions:
🚀 Launch Nordic Ember Web App
📱 Open Nordic Ember in Telegram
This would make the project much easier for a potential restaurant customer to discover and test.
31.22 From Demonstration to Customer Project
If a restaurant owner is interested after seeing Nordic Ember, the next stage should not simply be copying the demo and changing the restaurant name.
First gather the customer’s actual requirements.
A simple workflow could be:
Demonstrate Nordic Ember
↓
Restaurant is interested
↓
Collect requirements
↓
Agree on features
↓
Prepare restaurant content
↓
Customize design
↓
Configure menu
↓
Configure reservations
↓
Configure domain/email
↓
Create Telegram integration if wanted
↓
Test
↓
Customer approval
↓
Production deployment
This turns Nordic Ember from a demonstration into a reusable starting point for real restaurant projects.
31.23 What Should Be Prepared Before a Customer Deployment?
Before deploying a version for a real restaurant, collect and verify the required information.
For example:
Restaurant identity
Logo and branding
Food/menu information
Prices
Restaurant photos
Opening hours
Reservation hours
Maximum guests per booking
Available seating areas
Contact details
Domain information
Email requirements
Languages
Telegram requirements
This reduces the need to change fundamental configuration after the customer application has already gone live.
31.24 Nordic Ember Demonstration Milestone
Nordic Ember can now demonstrate much more than a restaurant website.
It demonstrates an end-to-end solution:
Modern Restaurant UI
↓
Responsive Web Application
↓
Multilingual Interface
↓
Food Menu
↓
Table Reservation
↓
Backend API
↓
Reservation Storage
↓
Professional Confirmation Email
↓
Cloud Deployment
↓
Telegram Mini App
For a potential restaurant customer, the most important demonstration is therefore simple:
Open Nordic Ember
↓
Experience it as a customer
↓
Make a reservation
↓
Receive confirmation
↓
Imagine the same system
with the restaurant's own
brand, menu and domain
Nordic Ember can therefore serve both as a technical learning project and as a practical demonstration of what a customized restaurant application could provide.
32. Important Development Principles and Lessons Learned
Building Nordic Ember taught us more than how to create a restaurant website or Telegram Mini App.
The project moved through several different environments and technologies:
Google Stitch
↓
Google AI Studio
↓
VS Code
↓
React + TypeScript
↓
Express + SQLite
↓
GitHub
↓
Railway
↓
Resend
↓
Miss Hosting / DNS
↓
Telegram
During that process, we encountered real development problems: frontend bugs, reservation logic issues, SMTP differences between local and production environments, DNS configuration, Railway deployment crashes, Telegram configuration, and mobile testing.
These experiences lead to several useful development principles.
32.1 Build and Improve the Application Step by Step
One of the most important principles is:
Do not try to build and configure everything at the same time.
Nordic Ember developed gradually:
Design
↓
Frontend
↓
Frontend Testing
↓
Backend
↓
Database
↓
Reservation Logic
↓
Local Email
↓
Deployment
↓
Production Email
↓
Telegram
↓
Mobile Testing
This approach makes problems much easier to identify.
If we had introduced the database, email, Railway, Resend, DNS, and Telegram simultaneously, determining which component caused a failure would have been much more difficult.
32.2 Design First, Then Add Functionality
Google Stitch was primarily useful for designing the restaurant experience.
Google AI Studio then gave us a working application foundation.
The development process therefore followed:
Stitch
↓
Design
AI Studio
↓
Application Foundation
VS Code
↓
Detailed Development
This separation helped us concentrate first on what the application should look like and then on how it should work.
32.3 AI-Generated Code Is a Starting Point, Not the Finished Product
Google AI Studio generated a substantial amount of code for us, but that did not mean the application was production-ready.
We still discovered issues involving:
Language handling
Restaurant naming
Dates
Reservation navigation
Buttons
Contact functionality
Booking logic
Email
Backend behavior
Deployment
The correct workflow is therefore:
AI Generates
↓
Developer Reviews
↓
Developer Tests
↓
Problems Identified
↓
Developer Improves
↓
Test Again
AI can accelerate development considerably, but testing and engineering decisions remain essential.
32.4 Test Locally Before Deploying
Before sending an important change to GitHub and Railway, test it locally whenever possible.
A useful workflow is:
Modify Code
↓
Run Locally
↓
Test
↓
npm run build
↓
Fix Problems
↓
Commit
↓
Push
This reduces unnecessary production failures.
It also helps distinguish:
Application Problem
from:
Deployment / Production Environment Problem
32.5 A Successful Local Test Does Not Guarantee Production Success
This was one of the most important lessons from Nordic Ember.
Our email functionality worked locally using:
Nodemailer
↓
SMTP
↓
Gmail
But after deploying to Railway, production email required a different approach.
We eventually used:
Railway
↓
Resend API
↓
Customer
Therefore:
Works on localhost
≠
Guaranteed to work in production
Production has different networking, security, configuration, domain, and hosting conditions.
Both environments must be tested.
32.6 Separate Configuration from Source Code
Another major principle is to avoid placing environment-specific configuration directly inside the application.
Instead of hard-coding:
SMTP credentials
API keys
Bot tokens
Production sender configuration
we use environment variables.
For example:
SMTP_HOST
SMTP_PORT
SMTP_USER
SMTP_PASS
RESEND_API_KEY
RESEND_FROM
TELEGRAM_BOT_TOKEN
This gives us:
Application Code
↓
GitHub
Private Configuration
↓
Environment Variables
This is both safer and easier to maintain.
32.7 Never Commit Secrets to GitHub
GitHub should contain source code—not private credentials.
Never commit:
Passwords
SMTP App Passwords
Resend API Keys
Telegram Bot Tokens
Private credentials
A .env file containing real secrets should normally remain outside source control.
The repository can instead contain documentation or an example configuration without real secret values.
For example:
.env.example
can show:
RESEND_API_KEY=
RESEND_FROM=
without exposing the real values.
32.8 Keep Frontend Secrets Out of the Browser
This is particularly important for React applications.
Frontend code runs on the user’s device.
Therefore, a secret such as:
TELEGRAM_BOT_TOKEN
must not be embedded in React components or browser JavaScript.
The principle is:
Browser
↓
Public Information Only
Server
↓
Private Credentials
When a private API operation is required, the frontend should normally communicate with our backend, and the backend can use the secret securely.
32.9 Separate Responsibilities
Nordic Ember works better because each part of the system has a specific responsibility.
For example:
React
→ User Interface
Express
→ Backend Logic
SQLite
→ Reservation Storage
Railway
→ Production Hosting
Resend
→ Production Email
Miss Hosting
→ DNS Management
Telegram
→ Mini App Entry Point
GitHub
→ Source Control
No single service needs to perform every task.
This makes the architecture easier to understand and maintain.
32.10 Keep Local and Production Environments Flexible
We deliberately kept two email mechanisms:
LOCAL
↓
Nodemailer
↓
SMTP
and:
PRODUCTION
↓
Resend
The application decides which provider to use according to the environment.
This is preferable to maintaining two completely separate reservation applications.
The shared application logic remains:
Reservation
↓
sendReservationConfirmation()
↓
Choose Appropriate Provider
32.11 Read the Logs Before Guessing
During development, Railway sent notifications such as:
Deploy Crashed!
A common mistake would be to immediately start changing code without knowing why the deployment failed.
The better process is:
Something Failed
↓
Read Logs
↓
Find Actual Error
↓
Understand Cause
↓
Make Targeted Change
↓
Test Again
Railway logs tell us what happened on the application side.
Resend also provides information about email activity.
Different systems provide different diagnostic information.
Use it.
32.12 Do Not Assume the Last Change Caused Every Problem
Suppose we change:
RESEND_FROM
push to GitHub, and later receive:
Deploy Crashed!
It is tempting to conclude immediately:
RESEND_FROM caused the crash.
But timing alone does not prove causation.
Instead:
Deployment Failed
↓
Inspect Logs
↓
Identify Actual Error
This avoids unnecessary changes to working code.
32.13 Do Not Change Working Code After One Unclear Failure
We experienced another useful example during mobile testing.
The Telegram Mini App worked, but one reservation confirmation email did not appear as expected.
It would have been easy to immediately rewrite the email code.
Instead, we tested again.
The next complete test succeeded:
Telegram Mobile
↓
Reservation
↓
Railway
↓
Resend
↓
booking@restaurant.softsolutionsahand.com
↓
Customer Email
The lesson is:
Reproduce a problem before making unnecessary changes to working production code.
Check logs, repeat the test, and determine whether the failure is consistent.
32.14 Test One Layer at a Time
When troubleshooting a multi-service application, isolate the layers.
For Nordic Ember, we could test:
Does the UI work?
↓
Does the reservation API work?
↓
Is the reservation stored?
↓
Does Railway call Resend?
↓
Does Resend accept the email?
↓
Does the customer receive it?
This is much easier than treating the entire system as one black box.
32.15 Then Perform End-to-End Testing
Component testing is necessary, but eventually the entire customer journey must also be tested.
Our final test was:
Real Mobile Phone
↓
Telegram
↓
@NordicEmberBot
↓
🔥 Open Restaurant
↓
Book Table
↓
Submit Reservation
↓
Railway
↓
Reservation API
↓
Resend
↓
Customer Inbox
This type of test verifies that the individual components also work together.
32.16 Test on Real Devices
A desktop browser alone is not enough for an application designed for mobile customers.
We tested Nordic Ember using Telegram on a real mobile phone.
This allowed us to verify:
Responsive layout
Navigation
Form fields
Buttons
Telegram Web App
Reservation flow
Email confirmation
Real-device testing can reveal problems that are difficult to notice in a desktop development environment.
32.17 Verify DNS Changes After Saving
DNS configuration taught us another practical lesson.
When adding the Resend records in Miss Hosting, clicking Save was not enough.
We needed to confirm that the records were actually present.
The safer workflow is:
Add DNS Record
↓
Save
↓
Refresh
↓
Verify Record Still Exists
↓
Wait for DNS Propagation
↓
Verify in Resend
During our configuration, one record did not remain visible after refreshing and had to be added again.
Always verify important DNS changes.
32.18 Do Not Confuse Different Types of DNS Configuration
DNS can serve many purposes.
For example, FlightFinder uses DNS to direct a subdomain toward a web application.
Nordic Ember’s Resend configuration uses DNS for email authentication.
Therefore:
Website DNS
≠
Email Authentication DNS
Understanding the purpose of each record makes DNS configuration much easier.
32.19 Use Git as a Development Checkpoint
Git is not only a way to upload code to GitHub.
It also gives us checkpoints during development.
A good workflow is:
Working Version
↓
Commit
↓
Next Change
↓
Test
↓
Commit
This creates a history of stable development stages.
If something goes wrong later, it becomes much easier to understand what changed.
32.20 Do Not Commit Before Checking git status
Before committing, use:
git status
This shows which files have changed and which files are staged.
For example, during our project we saw:
modified:
server/email.ts
new file:
Docs/Nordic_Ember_Complete_Deployment_Railway_Resend_Telegram_Manual.md
Then we deliberately staged the files we wanted.
This is better than blindly committing everything in the project.
32.21 GitHub and Deployment Are Connected
Once Railway is connected to the GitHub main branch, a push can trigger a production deployment.
Therefore:
git push origin main
is no longer merely:
Save code to GitHub
It can also mean:
Deploy a new production version
That makes local testing and reviewing changes before pushing even more important.
32.22 Keep Documentation with the Project
As a project becomes more complex, it becomes difficult to remember every configuration step.
Nordic Ember now involves:
Google Stitch
Google AI Studio
VS Code
GitHub
Railway
SMTP
Resend
DNS
Miss Hosting
Telegram
BotFather
For this reason, keeping project documentation is valuable.
We created project documentation under:
Docs/
including the deployment and configuration manual.
Documentation should explain not only what was configured, but also important information such as:
Why Resend is used
Which environment uses SMTP
Where DNS is managed
Which domain is verified
How Railway is deployed
How Telegram is connected
This becomes especially valuable months later when configuration needs to be changed.
32.23 Document Configuration, but Never Document Secret Values
There is an important difference between documenting a variable name and documenting its secret value.
Good documentation:
Railway requires:
RESEND_API_KEY
RESEND_FROM
Bad public documentation:
RESEND_API_KEY=re_actual_private_key_here
Similarly, documentation can say:
Telegram Bot:
@NordicEmberBot
but should not contain the private bot token.
32.24 Build One Application and Reuse It
Another important architectural decision was not to create:
Web Restaurant Application
+
Separate Telegram Restaurant Application
Instead, we built:
One Nordic Ember Application
↑
│
┌────┴────┐
│ │
Browser Telegram
The same frontend, backend, reservation system, and production email infrastructure are reused.
This reduces duplication and future maintenance.
32.25 Keep the Customer Experience Separate from the Infrastructure
Customers do not need to know that their reservation passes through:
React
Express
Railway
SQLite
Resend
DNS
They should experience something much simpler:
Open Restaurant
↓
Choose Table
↓
Enter Details
↓
Confirm Reservation
↓
Receive Email
The infrastructure exists to make that experience reliable.
A technically sophisticated system should not necessarily feel complicated to the customer.
32.26 Use the Right Tool for Each Job
Nordic Ember demonstrates that modern application development often involves several specialized services.
For example:
Stitch
→ Design
AI Studio
→ AI-assisted application creation
VS Code
→ Development
GitHub
→ Source control
Railway
→ Hosting
Resend
→ Transactional email
Miss Hosting
→ DNS
Telegram
→ Mini App distribution
Using multiple tools is not necessarily a weakness.
The important question is whether each tool has a clear responsibility and whether the integrations remain understandable.
32.27 Build for Change
Nordic Ember is a demonstration restaurant.
A future customer will probably have different:
Branding
Menu
Opening hours
Languages
Reservation rules
Domain
Email
Telegram bot
Therefore, the application should be structured so these things can be changed without rewriting the entire system.
The goal is:
Reusable Architecture
+
Customer Configuration
↓
Customized Restaurant Application
rather than:
Copy Everything
↓
Rewrite Everything
32.28 Finish Features Before Adding More Features
It is easy during development to continually add new ideas.
But a smaller feature that works completely is more valuable than many partially implemented features.
For Nordic Ember, completing:
Reservation
↓
Storage
↓
Email
↓
Production
↓
Telegram
↓
Mobile Test
was more important than immediately adding additional AI features, analytics, payment systems, or administrative functionality.
Those can become future versions.
32.29 A Useful Development Cycle
The development approach used throughout Nordic Ember can be summarized as:
PLAN
↓
BUILD
↓
TEST
↓
IDENTIFY PROBLEM
↓
CHECK LOGS
↓
FIX
↓
TEST AGAIN
↓
DOCUMENT
↓
COMMIT
↓
DEPLOY
↓
TEST PRODUCTION
Then repeat the cycle for the next feature.
This simple process applies far beyond restaurant applications.
32.30 The Most Important Lesson
Perhaps the most important lesson from Nordic Ember is that creating the first application screen is only the beginning.
A real application requires much more:
Design
+
Frontend
+
Backend
+
Data
+
Validation
+
Security
+
Email
+
Deployment
+
DNS
+
Integrations
+
Testing
+
Documentation
AI tools such as Google Stitch and Google AI Studio can dramatically accelerate the beginning of the process.
But turning that starting point into a functioning production application still requires careful development, testing, debugging, deployment, configuration, and verification.
Nordic Ember demonstrates that complete journey:
AI-Generated Idea
↓
Working Prototype
↓
Full-Stack Application
↓
Cloud Deployment
↓
Production Email
↓
Telegram Mini App
↓
Real Mobile Test
↓
Working Customer Experience
That is one of the most valuable outcomes of the project: not simply learning how to generate an application with AI, but learning how to take an AI-generated starting point and develop it into a complete working system.
33. What Comes Next for Nordic Ember?
Nordic Ember has reached an important milestone.
What began as a restaurant interface designed with Google Stitch and developed with Google AI Studio has become a working full-stack application with:
React + TypeScript frontend
↓
Express backend
↓
Reservation system
↓
SQLite storage
↓
Local SMTP email
↓
GitHub
↓
Railway deployment
↓
Resend production email
↓
Verified restaurant email domain
↓
Telegram Bot
↓
Telegram Mini App
↓
Desktop and mobile testing
The current application is already suitable as a working demonstration project.
However, this does not mean development has to stop here.
Nordic Ember can become the foundation for several future improvements and, eventually, customized applications for real restaurant customers.
33.1 Where Nordic Ember Is Today
Before discussing future features, it is useful to clearly separate what is already working from what is still planned.
The current version provides:
Restaurant presentation ✅
Responsive web interface ✅
English / Swedish ✅
Food menu ✅
Table reservation ✅
Reservation API ✅
Reservation storage ✅
Unique booking reference ✅
Local SMTP email ✅
Production Resend email ✅
Verified custom sender ✅
GitHub source control ✅
Railway deployment ✅
Public HTTPS application ✅
Telegram bot ✅
Telegram Mini App ✅
Desktop testing ✅
Mobile testing ✅
End-to-end reservation test ✅
Customer confirmation email ✅
These are completed features.
The ideas below are possible future versions, not features we are claiming to have already implemented.
33.2 Create a Restaurant Admin Dashboard
One of the most useful future improvements would be an administration area for restaurant staff.
Currently, the customer-facing reservation workflow is the main focus.
A future version could provide:
Restaurant Staff
↓
Secure Login
↓
Admin Dashboard
↓
Reservations
The dashboard could display information such as:
Today's reservations
Upcoming reservations
Customer name
Date
Time
Number of guests
Seating area
Special requests
Booking reference
Reservation status
Instead of working directly with the underlying data, restaurant staff would have a proper interface for managing bookings.
33.3 Add Reservation Status Management
A reservation could eventually have statuses such as:
Pending
Confirmed
Cancelled
Completed
No-show
Restaurant staff could change the status from the admin dashboard.
For example:
New Reservation
↓
Pending
↓
Restaurant Reviews
↓
Confirmed
This would turn the reservation system into a more complete operational tool for the restaurant.
33.4 Allow Customers to Modify or Cancel Reservations
The current booking reference already provides a useful identifier.
A future version could use it to let customers manage their reservations.
For example:
Customer
↓
Booking Reference
↓
Find Reservation
↓
Change Date / Time
or
Cancel Reservation
Security would need to be designed carefully so that knowing a simple booking reference alone does not allow unauthorized changes.
33.5 Add Restaurant Notifications
At present, customer confirmation is an important part of the email system.
A future version could also notify restaurant staff whenever a new reservation is created.
For example:
Customer Books Table
↓
Reservation Created
↓
├── Customer Confirmation
│
└── Restaurant Notification
The restaurant notification could contain:
Booking reference
Customer name
Phone
Email
Date
Time
Guests
Seating area
Special requests
This could make the system more practical for restaurants that want immediate notification of new bookings.
33.6 Improve the Production Database
SQLite has been very useful for developing and demonstrating the reservation system.
For a larger production application, we could later evaluate a server-based database such as PostgreSQL.
The architecture could become:
Nordic Ember
↓
Express
↓
Reservation API
↓
PostgreSQL
This should not be changed merely because PostgreSQL is more sophisticated.
The database should be selected according to the application’s actual production requirements.
For a demonstration project, SQLite is simple and useful.
For a restaurant system with greater concurrency, administration, backups, reporting, or future expansion, a managed production database may become more appropriate.
33.7 Add Menu Management
The current restaurant menu is part of the application.
A future admin system could allow restaurant staff to maintain it without changing source code.
For example:
Admin Dashboard
↓
Menu Management
↓
Add Dish
Edit Dish
Remove Dish
Change Price
Change Description
Upload Image
Mark Unavailable
This would make the application much more practical for a real restaurant where menus and prices change regularly.
33.8 Add Opening Hours and Booking Rules Management
Restaurant staff could also manage:
Opening hours
Booking hours
Closed days
Holiday schedules
Available time slots
Maximum guests
Minimum advance booking time
Seating areas
The booking interface could then use these settings dynamically.
For example:
Restaurant Configuration
↓
Available Dates
↓
Available Times
↓
Customer Booking Form
This would reduce the need to modify application code when restaurant operating hours change.
33.9 Improve Reservation Availability
A more advanced version could check existing reservations before offering availability.
Instead of merely allowing a customer to select a date and time, the system could calculate whether sufficient capacity remains.
Conceptually:
Customer selects
Date + Time + Guests
↓
Backend checks reservations
↓
Capacity available?
/ \
Yes No
↓ ↓
Book Suggest
another time
This would move Nordic Ember toward a more complete restaurant reservation platform.
33.10 Add Restaurant Tables and Capacity
Another future improvement could be actual table management.
For example:
Table 1 → 2 guests
Table 2 → 2 guests
Table 3 → 4 guests
Table 4 → 6 guests
The reservation system could then assign or suggest tables according to party size and availability.
That would require additional business rules and should be designed according to the needs of a real restaurant.
33.11 Add Better Customer Communication
The confirmation email is already working.
Future versions could add additional transactional messages such as:
Reservation confirmation
Reservation changed
Reservation cancelled
Reminder before reservation
Restaurant cancellation
Important booking update
These messages could continue to use the production email architecture we have already established with Resend.
33.12 Add Telegram Notifications
Because Nordic Ember already has:
@NordicEmberBot
future development could make greater use of Telegram.
For example, customers might eventually receive appropriate reservation-related interactions through the bot.
Restaurant staff could potentially also use Telegram for operational notifications.
This would require additional backend integration with the Telegram Bot API and careful handling of user identity and permissions.
It is therefore a future feature rather than part of the current implementation.
33.13 Use Telegram User Information
A Telegram Mini App can potentially integrate more deeply with Telegram than simply opening a web application.
Future development could explore appropriate Telegram Mini App capabilities to improve the experience.
For example:
Telegram User
↓
Nordic Ember Mini App
↓
Recognize Telegram Session
↓
Improve Customer Experience
Any such implementation should validate Telegram-provided data on the backend rather than blindly trusting information supplied by the browser.
33.14 Create a Custom Nordic Ember Web Address
Nordic Ember currently runs from the Railway production URL:
https://restaurant-telegram-mini-app-production.up.railway.app
That works technically, but a future improvement could provide a cleaner demonstration address under Softsolution Sahand.
For example, we could investigate a suitable address such as:
restaurant.softsolutionsahand.com
or another dedicated subdomain.
This would be conceptually similar to the way FlightFinder is presented through:
findflight.softsolutionsahand.com
However, the Nordic Ember subdomain would need to be planned carefully because restaurant.softsolutionsahand.com is already being used as the authenticated email-sending domain for Resend.
Any web-domain changes should therefore be configured deliberately without disturbing the working production email DNS records.
33.15 Create a Nordic Ember Page on Softsolution Sahand
This is one of the most useful near-term improvements.
Nordic Ember can have its own presentation page on the Softsolution Sahand website.
The purpose would be similar to the FlightFinder project presentation: explain the project to visitors and provide a clear way to launch it.
The page could contain:
Nordic Ember
Restaurant Web App & Telegram Mini App
Introduction
↓
Main Features
↓
Technical Overview
↓
Screenshots
↓
Launch Web Application
↓
Open Telegram Mini App
For example, visitors could see:
🚀 Launch Nordic Ember
📱 Open Nordic Ember in Telegram
The first button could open the Railway-hosted application.
The second could open:
t.me/NordicEmberBot/restaurant
This would make Nordic Ember part of the Softsolution Sahand portfolio rather than leaving it accessible only through its technical deployment URL.
33.16 Present Nordic Ember as a Portfolio Project
A dedicated Softsolution Sahand page could describe the project in business-oriented language.
For example:
Nordic Ember is a full-stack restaurant demonstration
application that combines a responsive multilingual
website, digital menu, table reservations, transactional
email and Telegram Mini App integration.
The project demonstrates how AI-assisted design and
development tools can be combined with modern web
technologies to build and deploy a complete restaurant
experience.
Then provide key features:
Responsive restaurant interface
English / Swedish
Interactive menu
Table reservations
Unique booking references
Customer confirmation email
Cloud deployment
Telegram Mini App
Mobile support
This makes the project easier to understand for visitors who have not followed the entire technical tutorial.
33.17 Add Screenshots and a Short Demo
The future Softsolution Sahand page could also include screenshots showing:
Home
Menu
Book Table
Reservation confirmation
Telegram Mini App
Mobile layout
We could also eventually create a short demonstration GIF or video.
Remember that during BotFather configuration we skipped the optional demo GIF using:
/empty
A polished demo could therefore serve two purposes:
Softsolution Sahand portfolio
+
Telegram Web App preview
This is optional and can be added later.
33.18 Create Customer-Specific Restaurant Versions
Nordic Ember can eventually serve as a reference implementation for real customer projects.
A customer version could follow:
Nordic Ember Architecture
↓
Customer Requirements
↓
Replace Branding
↓
Replace Menu
↓
Configure Reservations
↓
Configure Domain
↓
Configure Email
↓
Configure Telegram
↓
Test
↓
Deploy
Each restaurant should receive its own configuration rather than simply using Nordic Ember’s identity.
33.19 Separate the Demo from Customer Production Systems
If Nordic Ember becomes the basis for commercial customer work, the demo and customer environments should remain separate.
For example:
Nordic Ember Demo
↓
Softsolution Sahand Portfolio
Customer Restaurant A
↓
Own Deployment
Own Domain
Own Email
Own Database
Own Telegram Bot
Customer Restaurant B
↓
Own Deployment
Own Domain
Own Email
Own Database
Own Telegram Bot
A customer’s production data should not be mixed with Nordic Ember demonstration data.
33.20 Add Authentication for Administration
A future admin dashboard must not simply be available to anyone who knows its URL.
It would require authentication.
Conceptually:
/admin
↓
Login
↓
Authentication
↓
Authorized?
/ \
Yes No
↓ ↓
Admin Denied
Dashboard
Authentication should be implemented before exposing reservation-management functionality publicly.
33.21 Add Reporting and Analytics
A more mature restaurant application could eventually provide useful reports.
For example:
Reservations today
Reservations this week
Popular booking times
Average party size
Cancellation rate
Most requested seating area
These features would become more meaningful once the application contains enough real reservation data.
33.22 Add Automated Tests
As Nordic Ember grows, manual testing alone will become increasingly time-consuming.
Future development could add automated tests for important backend functionality.
For example:
Reservation validation
Booking reference generation
Invalid dates
Guest limits
API responses
Email provider selection
Frontend tests could also verify important customer workflows.
The goal would be:
Code Change
↓
Automated Tests
↓
Build
↓
Deployment
This becomes increasingly valuable as the project grows.
33.23 Improve Production Monitoring
We already learned the importance of Railway logs when a deployment crashed.
A future production system could improve monitoring further.
Important events might include:
Deployment failure
Application startup failure
Reservation API error
Database error
Email delivery failure
The goal is to discover production problems quickly rather than waiting for a customer to report them.
33.24 Add Backups and Data-Retention Planning
A demonstration database is different from a real restaurant’s customer data.
Before using the system commercially, we would need to decide:
Where is reservation data stored?
How is it backed up?
How long is it retained?
Who can access it?
How can it be restored?
These questions become important once the application stores real customer reservations.
33.25 Review Privacy and Data Protection
A real restaurant deployment processes personal information such as:
Customer name
Phone number
Email address
Reservation details
Special requests
Before deploying for real customers, privacy requirements should therefore be reviewed carefully.
The application should collect only information that is actually necessary and define how reservation data is stored, accessed, retained, and deleted.
This becomes particularly important when moving from a demonstration application to a real commercial system.
33.26 Possible Future AI Features
Because this project began with AI-assisted development, we could eventually add AI functionality to the restaurant itself.
Possible future experiments might include:
Restaurant FAQ assistant
↓
Menu questions
↓
Dietary information
↓
Opening hours
↓
Reservation guidance
Or:
Customer preferences
↓
Menu information
↓
AI-assisted dish suggestions
These features should only be added when they provide a clear benefit.
Nordic Ember does not need AI inside every feature simply because AI was used to help build the application.
33.27 Possible Development Roadmap
A practical future roadmap could be:
CURRENT VERSION
↓
Working Nordic Ember Demo
↓
Softsolution Sahand Project Page
↓
Custom Demonstration URL
↓
Restaurant Admin Dashboard
↓
Reservation Management
↓
Restaurant Notifications
↓
Menu Management
↓
Production Database Upgrade
↓
Customer Modify / Cancel
↓
Automated Testing
↓
First Customer-Specific Version
This is not a fixed requirement.
The order can change according to what is most useful next.
33.28 What Should We Do Next?
For this particular project, the most practical immediate next tasks are relatively small.
We do not need to start another large development phase immediately.
The remaining project-completion work includes:
Finalize project documentation
↓
Commit documentation changes
↓
Create the next stable Git version/tag
↓
Create Nordic Ember presentation page
on Softsolution Sahand
↓
Link to Web App
↓
Link to Telegram Mini App
After that, Nordic Ember can remain as a stable demonstration version while we decide which larger feature deserves the next development version.
33.29 From Tutorial Project to Reusable Product Foundation
The most interesting future direction is not necessarily adding as many features as possible.
It is making the architecture reusable.
Today:
Nordic Ember
Tomorrow:
Nordic Ember Architecture
↓
Restaurant A
Restaurant B
Restaurant C
Each restaurant could have its own:
Brand
Menu
Domain
Email
Reservations
Telegram Bot
while sharing proven architectural ideas from Nordic Ember.
That turns this project from a one-time tutorial into a foundation for future restaurant applications.
33.30 The Project Is Ready for Its Next Phase
Nordic Ember has already completed a substantial journey:
Idea
↓
Stitch Design
↓
AI Studio Application
↓
Frontend Improvements
↓
Backend
↓
Database
↓
Reservation System
↓
Local Email
↓
GitHub
↓
Railway
↓
Resend
↓
Verified Domain
↓
Telegram Bot
↓
Telegram Mini App
↓
Mobile Test
↓
Working Production Demo
The next phase does not need to begin immediately.
The current version can first be documented, tagged, presented on Softsolution Sahand, and used as a working portfolio demonstration.
From there, we can choose the next direction based on a real need—an admin dashboard, stronger reservation management, a production database, additional Telegram functionality, or the first customized version for a restaurant customer.
33.31 prepating Nordic Ember for a Real Restaurant Customer From
The latest step in the project is moving from a successfully developed application to a customer-ready restaurant solution.
At this stage, the focus is no longer only on writing code. A real restaurant needs a complete delivery process covering customization, hosting, domain configuration, email, Telegram, administration, testing, and documentation.
For a new restaurant, the developer can start from the tested Nordic Ember template and replace the restaurant-specific information, including:
restaurant name and branding
address, telephone and email
menu, dishes, prices and images
reservation rules and seating areas
Telegram bot information
production domain
The current deployment architecture uses GitHub → Railway for source control and production hosting, with SQLite stored on persistent production storage. Resend handles production reservation confirmation email, while Telegram provides the restaurant bot and Mini App entry point.
The production workflow is therefore:
“`text
Customer requirements
↓
Nordic Ember template
↓
Customer-specific customization
↓
GitHub
↓
Railway production deployment
↓
Domain + Email + Telegram
↓
Complete testing
↓
Customer handover
“`
The V10 language configuration also improves customer delivery. English and Swedish are enabled by default, while Farsi and Turkish can be enabled later by the restaurant administrator directly from the Admin Dashboard. No source-code change or developer script is required for normal language administration.
Before handover, the developer should perform a complete production check: create and manage a test reservation, verify database persistence, verify the confirmation email, test the Telegram Mini App, test the Admin Dashboard, verify language settings, and remove all test data afterwards.
This makes the project more than a demonstration of AI-generated software. It becomes a reusable restaurant application template with a repeatable process for delivering customized installations to real customers.
Conclusion
Nordic Ember has evolved from an AI-generated restaurant design into a complete working full-stack application.
Starting with Google Stitch and Google AI Studio, we continued development with React, TypeScript, Express, and SQLite, then deployed the application to Railway. We added local email with Nodemailer/SMTP, production email with Resend and a verified domain, and finally connected the application to Telegram as a Mini App.
The complete reservation flow has been tested successfully on both desktop and mobile, including customer confirmation emails.
Nordic Ember now demonstrates how AI-assisted development can be taken beyond a prototype and turned into a real, deployed application.
Source Code
The complete source code and project documentation are available on:
Try Nordic Ember
📱 Open Nordic Ember in Telegram
This post is part of Google AI App Development – Step by Step.