Laravel Travel Agency API From Scratch

Client Specification into Plan of Action

In this lesson, we will transform the initial job description from the client to an actual to-do list: I call this a "plan of action". The goal is to make predictions, what actions rely on what other actions, and minimize the unpleasant surprises along the way.

Job Description From Client

I usually advise reading the job description three times, with different goals in mind:

  • Reading 1: just get familiar with the overall idea of what the project is about
  • Reading 2: try to come up with DB structure - list the tables and their relationships (luckily, in this case, the client did it for us)
  • Reading 3: try to come up with a plan of action, re-arranging the list of tasks from the client to the order in which things would be actually built

Along the way, another result of this should be the list of questions for the client. If you don't ask them right away, you may suffer from "unpleasant surprises" or misunderstandings in the future, leading to many painful code changes.

With those goals in mind, here's the full job description from the client.

You can also read it in a separate Google Docs format here if you prefer.

Laravel hiring test

Create a Laravel APIs application for a travel agency.


  • Travel is the main unit of the project: it contains all the necessary information, like the number of days, the images, title, etc. An example is Japan: road to Wonder or Norway: the land of the ICE;
  • Tour is a specific dates-range of a travel with its own price and details. Japan: road to Wonder may have a tour from 10 to 27 May at €1899, another one from 10 to 15 September at €669 etc. At the end, you will book a tour, not a travel.


At the end, the project should have:

  1. A private (admin) endpoint to create new users. If you want, this could also be an artisan command, as you like. It will mainly be used to generate users for this exercise;
  2. A private (admin) endpoint to create new travels;
  3. A private (admin) endpoint to create new tours for a travel;
  4. A private (editor) endpoint to update a travel;
  5. A public (no auth) endpoint to get a list of paginated travels. It must return only public travels;
  6. A public (no auth) endpoint to get a list of paginated tours by the travel slug (e.g. all the tours of the travel foo-bar). Users can filter (search) the results by priceFrom, priceTo, dateFrom (from that startingDate) and dateTo (until that startingDate). User can sort the list by price asc and desc. They will always be sorted, after every additional user-provided filter, by startingDate asc.



  • ID
  • Email
  • Password
  • Roles (M2M relationship)


  • ID
  • Name


  • ID
  • Is Public (bool)
  • Slug
  • Name
  • Description
  • Number of days
  • Number of nights (virtual, computed by numberOfDays - 1)


  • ID
  • Travel ID (M2O relationship)
  • Name
  • Starting date
  • Ending date
  • Price (integer, see below)


  • Feel free to use the native Laravel authentication.
  • We use UUIDs as primary keys instead of incremental IDs, but it's not required for you to use them, although highly appreciated;
  • Tours prices are integer multiplied by 100: for example, €999 euro will be 99900, but, when returned to Frontends, they will be formatted (99900 / 100);
  • Every admin user will also have the editor role;
  • Every creation endpoint, of course, should create one and only one resource. You can't, for example, send an array of resource to create;
  • Usage of php-cs-fixer and larastan are a plus;
  • Creating docs is big plus;
  • Feature tests are a big big plus.

Plan of Action

Recently I posted a tweet about a typical process of building a Laravel project:

In our case, it's not a web project, we have only API, so our list is even shorter:

  1. DB structure: models/migrations/factories/seeders
  2. CRUDs for those Models
  3. Auth: roles/permissions on who can access which CRUD features

So this is precisely what our initial plan of action will look like, which will roughly correlate to the list of lessons.

  1. DB structure and schema, with UUIDs and price mutators
  2. First public endpoint: /travels
  3. Second public endpoint: travels/slug/tours
  4. Tours filtering and ordering
  5. Create users artisan command
  6. Admin endpoint: create new travels, with middleware role:xxxxx
  7. Admin endpoint: create new tours
  8. Editor endpoint: update travel
  9. Laravel Pint
  10. Larastan
  11. API docs

I deliberately emphasized that it's the initial plan, as there will always be unpredictable things you will encounter while building the project. But it's still beneficial to have some plan than start coding without any plan.

Re-Reading For The Details

When we have a "rough" action plan, I suggest another re-reading of the initial description. This time, your goal is to find "small details" which you would assign (in your mind or written form) to one of the points in the plan.

This aims to minimize the surprises and ask the client how exactly some features would work. Also, you need to "read between the lines" and identify things the client didn't specify, but you would still need to implement them.

Here's the list of potential "special attention" things I've identified:

  1. Slug in Travel should be auto-generated and unique
  2. UUIDs should be everywhere, including foreign keys
  3. We may or may not use a package for roles/permissions
  4. ... (you may add more)

You may start thinking about the solutions immediately or leave them until you build the functions.

As I mentioned, one of the goals for this re-reading process was to form the questions for the client, but in this case, the description is very clear and understandable: I don't have any questions here.

What about Automated Tests?

The client emphasized: "Feature tests are a big big plus."

So we need to implement them. The question is when? There are different approaches:

  • TDD: writing tests first, before the code
  • Tests at the end: the opposite approach to TDD
  • Tests with every feature: my personal favorite

So, we will write automated tests at the end of this lesson or the finished feature. It's much easier to do because we still remember how that particular feature works, it's still fresh, and we can simulate and reproduces the situations easier.

With TDD, we don't know 100% how features should work. And with tests at the end, we have the opposite effect: we likely have forgotten the details about some features created much earlier.

Let's Start Building?

Enough of preparation. It looks like we have a vision, time to implement it.


Great example - will be following along on this one.


Great information for learners, I request to make series for how to handle freelance project (i.e Client documentation, timeline, cost etc.) with realtime client project ? It will be good for us. Thanks


I don't have specifically that series and not planning to do it, but I have an e-book called 15 Years of Freelancing: My Lessons & Advice


Should not the tour.ending_date field also be virtual and calculated according to travel.number_of_days? This is not a big deal given the purpose of this course, but I see a logical issue in this description.

👍 1

Good point, we could argue with the client about it.


I must note that the course is prepared very well, there is a video and a text version, as well as links to additional informations.