How I Built This: Full-stack Travel Tech App

Chris Castle
11 min readMar 19, 2022


Ironhack Berlin — Project 2 — A guide to help students with their Ironhack projects, based on a web app to helping tour operators choose their reservation technology.

As a new developer, it’s hard to know what process to follow to build a project.

You can know how you want the project to look at the end, you can know the majority of the code that it’ll take to get each component working but piecing it all together to get from A-B can be daunting when staring at a blank screen.

I’m taking part in a bootcamp — Ironhack, here in Berlin, evenings and weekends on top of my full-time job. This is our second project, a full-stack app built with Node/Express/MongoDB, that we had two weeks to build.

I wanted to share, in broad strokes, the process I followed to help other students along on their journey. It’ll be part step-by-step guide of the process I used, part walkthrough of my app’s functionality.

Finished Project:
GitHub repo:
Stack: Node, Express, MongoDB, Handlebars, Tailwind

Let’s go!

Start with the problem

“Everything already exists, it’s impossible to come up with new ideas.”

You don’t need to come up with anything groundbreaking and new. If there’s an existing solution out there that you think you can make a minor improvement on, or adapt that solution to target a new audience — do that. Or simply look around you.

Or hone in on a problem that you or those around you are having.

I work in the travel industry, and know that operators have a hard time choosing technology to help them run their business. Operator forums are chock-full of people asking for advice from their peers about which technology to choose.

This is the problem I selected to solve, and thought a review site specifically for this niche would be a good solution.

Think data structures and your MVP

“Software engineering is just moving data from one place to another.”

A colleague once told me “software engineering is just moving data from one place to another.” Fundamentally, that’s true. And that data is much easier to move around when it’s well structured and thought out.

After defining what my user should be able to do for an MVP (Minimum Viable Product) so that I knew what data I would want to collect and use, I used Lucid Chart (free) to help visualize what my data models and the relationships between them would look like.

You may be tempted to jump right into coding your solution, but time invested here will save you a lot more time down the road — whether that’s feature scope beyond your MVP that prevents launching and getting feedback, or saving time having to restructure data across your whole app.

Create User flows and wireframes

You can probably skip this step and be fine, but I took the time to think about how a user would flow through my app to achieve their goals — from homepage, to sign up, to leaving a review, and browsing different companies.

I’m a visual guy so I also created wireframes in Invision (free), to help me visualise the end product and save time later on.

Project Management / Workflow

By now, you’ll likely already have a long list of things that you know you need to do to execute your idea. Good documentation and establishing a workflow you can follow will help you stay productive, disciplined, and moving forward.

For this project I’m using Clickup (free) but I usually use Asana (free). I broke down all of the tasks for my app into mini-projects (eg Authentication), and then created a Kan Ban system with tickets for each task that needs completing. If you want to learn more on this topic, here’s a good introduction.

Testing with Jest following Test Driven Development Principles

Finally opened my IDE (Visual Studio)! Because I’d planned my app, I knew which data would need to be manipulated and would be liable to breaking the rest of my app.

I’m using Jest, created by Facebook.

When a user accesses the homepage, there would be a list of companies displaying their average review scores — overall and by category.

I knew this could be quite wrong, so this is what I wanted to test and pass.

I wrote out some basic tests that were failing.

Down to business! Let’s set up our database.

I’m using MongoDB for the database. I created two versions — one for dev and one for production, and connected the dev one — and set up my Schemas in line with the data structures I’d outlined in the planning phase.

After setting up my Node/Express server, I implemented a basic route that would allow me to test my models — basically saving to the database and retrieving them again.

I used Postman alongside these basic routes to ensure that database and data models were set up correctly. Essentially I created a User, a Review, and Company before checking that they were appearing as expected in Mongo Compass (including referencing each other), before deleting them again.

Later on, I’d go on to create a file to seed my database in a similar way.

Start working through tasks — First up, Authentication/Authorisation

Once my core database functionality was working, I started work on my first mini-project/Epic — Authentication.

In my signup routes, I’m using bcrypt to encrypt user passwords before saving to the database to enhance security. When au User tries to login, bcrypt will decrypt the passwords and compare it against whatever the

User has input when trying to login.

After a successful sign up or login, I’m persisting the data with express-session and connect-mongo to create sessions and store cookies on a Users browser to allow me to identify users and change what they can see and do based on who they are.

This is known as ‘Authorisation’, and was set up using custom Middleware. For example, once logged in they’re not able to access the login page, and the sign up/login buttons are replaced with a prompt to leave a review.

At this stage, everything was set up with Server Side Validation — I’d return error messages to the front end when a User’s submission was rejected, but the UX wasn’t great — more on that later.

It’s not a necessary flourish, but you see it a lot across the internet so I decided to implement it — Google SSO. I used a Passport.js strategy to authenticate a User using their Google profile and create them in my database. This required setting up OAuth credentials in Google’s Developer Console.

Core App Functionality — Leaving a Review — Forms

After auth, you’ll want to work on the core functionality of your app. For me, this was leaving and reading reviews.

At this point, I should probably point out that I created my styling using Tailwind, and leveraged templates/components for speed from a place called and Flowbite. Using components makes it super easy and quick to have something slick-looking in no time.

Now that Users were able to sign up and login, with their data persisted, I tackled their goal of wanting to leave a review about a company they personally use.

In the below, it’s mostly simple input elements. The stars are radio buttons, styled to be stars. The pros/cons are an array of inputs that would be handled in the backend. One part I particularly like is that the first question — the dropdown — is a list pulled from the database directly, so it is always up to date. There’s also an additional field that only shows if a User signed up via Google. I wanted to collect a Company Name from all Users to show with their reviews, but Google couldn’t provide that. If there isn’t a Company Name in the User’s database document, I prompt them to input it.

In the backend, I retrieve all of this data after the form is submitted, handle it, input to the relevant place in the database, and ensure that all of the documents are referenced correctly.

Pass those Jest tests

Remember a few steps ago when I created some tests? Now’s the time to pass them! For you, this step will come in whenever you start working on the code that you want to test.

I have the data from Users in my database, and I’m getting ready calculate the average scores for each company and displaying it to users.

I’m pretty sure my approach here is messy and there’s a much better way to do it, but I created a function that returns the two things I need in the form of an object.

It took some considerable trial and error, but I got the green lights I needed — the tests were passed and I could be confident that my data would be good to pass to the front end and display to the Users.


Build your front-end

At this stage, we’ll assume that you’ve got all of your server-side working, so let’s start styling. Tailwind templates were less helpful here, so I created the cards representing each company here myself. I’m using Handlebars as the templating engine to dynamically display the data from my database.

On the homepage, companies are sorted based on their overall review score. When you click through to the homepage of an individual company, it’s styled similarly and you can view all reviews left about that company.

You’ll want to ensure your design is responsive. I’m a fan of using Safari’s Responsive Design Mode to test my design on different screen sizes and ensure it’s working as expected. CSS/Flexbox can be frustratingly fiddly at times, but Tailwind definitely made it easier here.

By the end of this step, you’ll want to have satisfied all the objectives you outlined for your MVP.

Deploy with Heroku!

Once the app’s front and back-end are built, you’re ready to deploy. Congrats! Heroku makes it easy (and free) to deploy full-stack projects.

As I knew I wanted to share my app with a few peers to test and get ‘real’ reviews into the production database, I bought a domain for €3 and paid for a ‘Hobby Dyno’ for €5 p/month which gave me an SSL certificate that’s automatically managed and makes my app more performant.

Okay, the MVP is deployed!

OPTIONAL: User Testing and Additional Development/ Features

I finished my MVP with a bit of time to spare. Whilst building, I’d kept notes of ‘nice-to-have’ features that I’d implement if I had time. I also wanted to get it in the hands of real operators and get their feedback as quickly as possible so I could iterate and improve the product.

If you don’t already have a community that you can share a product with for testing, explore Twitter, Forums, Discords etc to share your project and ask for feedback. Friends and Family are good too if they can be objective and you can get them to empathise with the User you’re building for.

Ask for feedback, and ye shall receive…

Everyone I’ve reached out to has been super generous and kind with feedback — one of the things I love about both the travel and developer communities is how helpful and supportive everyone is.

OPTIONAL: Prioritizing Additional Features

Coming up with ideas isn’t usually a problem for me, but getting distracted by a new idea is! To stay focused on the impact on Users, I use RICE Scoring to help quantify the impact I think additional features will have and work through the list from most impactful to least impactful. User Stories and Story Points would be another way to do this.

Once I receive feedback about my app, I’d articulate the problem, think on a solution, RICE score it, and prioritize accordingly.

I got about halfway through this list before I had to present my app, and then we started learning React the next day so I’ve paused development for a bit.

Here’s what I was able to implement:

  • Prevent personal domains being used: to enhance trust in the reviews being left, I added validation to my form to prevent personal email addresses being used. Only professional operators should be leaving reviews. This is validated on the front-end and back-end.
  • Filters based on attribute score: I added filters to my homepage to help users find a company that matches their needs faster. This was all done using DOM Manipulation.
  • Built a Twitter bot 🤖: When a user leaves a review, it automatically gets tweeted out to a profile I created. I’m hoping it’ll attract more users and increase transparency about a companies performance. This was using the Twitter API v2.
  • Email verification: to prevent Users using reviews with a fake email, I used Nodemailer to issue a unique confirmation code to a User’s registered email address. When they click through, there’s a boolean in the Schema that switches their verified status from false to true. When there are enough reviews on the site, I’ll add in a filter to my database queries so that I only retrieve reviews left by verified users.
  • Implemented Google Analytics: This one was nice and easy — simply a script tag to add.
  • SEO Optimization by Purging Tailwind: I purged my unused classes from Tailwind, significantly reducing my CSS filesize. This improved the site’s Lighthouse score from mid 40s to mid 80s. Huge!


Overall, I’m proud of what I’ve built. A few months ago I wouldn’t have known where to start, and building my first true full-stack app has whet my appetite for more!

For my next project, I’m planning on building an app that allows Tour Operators to input a few bullet points about a tour they’ve built and have a full tour description generated by AI, saving them heaps of time. Watch this space!

If you have any feedback about my code, the process, or the app itself I’m always happy to hear it — you can connect with me on Twitter @chrisjcastle or via