Build a Laravel SaaS from Scratch
45sShows the entire process of setting up a Laravel app from scratch, appealing to developers wanting to build a SaaS.
▶ Play ClipThis video walks through setting up a local Laravel development environment from scratch, including installing the Laravel installer, creating a new project with Breeze and SQLite, and configuring code quality tools like Duster and Prettier with Husky and GitHub Actions. It also covers setting up Mailpit for email testing and verifying the database and tests work.
Use `laravel new Symposium` to create a new Laravel project with the new prompt-based installer, choosing Breeze with Blade and Alpine, skipping dark mode, and using Pest for testing.
SQLite is the new default database for Laravel apps, chosen because it requires no additional dependencies.
Duster by Tighten brings together multiple linters (Pint, TLint, PHP CodeSniffer, PHP CS Fixer) into one package. Install via Composer and set up GitHub Actions and Husky hooks for automated linting.
Prettier is configured for Tailwind class ordering and code formatting. Follow a blog post at mattstauffer.com to install dependencies, create `.prettierrc` and `.prettierignore` files, and integrate with Husky and GitHub Actions.
Mailpit provides a local mock email inbox. Install via Homebrew or Takeout to view and click through emails during development.
Run `php artisan migrate` to confirm the database works. With SQLite, the entire database is a single file (`database.sqlite`), which can be deleted and recreated.
The local Laravel environment is fully set up with linting, formatting, email testing, and a working database, ready for building the SaaS application in subsequent videos.
"Title accurately describes the video's content: setting up dev tooling and environment for a Laravel SaaS."
What is the default database for Laravel 11 apps?
SQLite
1:45
What does Duster do?
It brings together multiple linters and fixers (Pint, TLint, PHP CodeSniffer, PHP CS Fixer) into one package.
2:52
What are the three options when publishing Duster GitHub Actions?
Lint only, fix and commit, and fix and commit with git-blame ignore revs.
4:07
What is the purpose of Mailpit?
It provides a local mock email inbox to view and click through emails during development.
12:35
How can you reset a SQLite database in Laravel?
Delete the database.sqlite file and run `php artisan migrate` again.
13:49
Duster: Unified Linting
Introduces a tool that combines multiple PHP linters, simplifying code quality enforcement.
2:50Prettier for Tailwind Class Order
Shows how to enforce consistent Tailwind CSS class ordering using Prettier.
6:49Mailpit for Email Testing
Recommends a practical tool for testing email functionality locally without sending real emails.
12:35[00:02] [Music]
[00:04] all right for this first video we're
[00:06] going to get your app up and running
[00:07] from scratch and your local development
[00:09] environment and while there is laravel's
[00:11] herd Pro to provide a lot of these
[00:13] dependencies for you we're also going to
[00:14] make sure that you got a free version of
[00:16] every single dependency you need to get
[00:17] started so let's get going all right to
[00:20] get started we want to make sure that we
[00:21] have either valet or herd pointing to
[00:24] our sites directory for me but some
[00:26] primary directory we're going to use the
[00:28] laravel installer which you'll get for
[00:30] free if you have herd running but if not
[00:32] you can use the LEL documentation which
[00:34] will show you what it looks like to get
[00:35] the larel installer working we're going
[00:38] to run laral new Symposium to create our
[00:40] new project and we're going to see this
[00:42] beautiful new interface provided by
[00:44] laravel's prompts we get to choose first
[00:47] which starter kit we're going to use and
[00:48] this new prompt-based system means we
[00:50] don't have to install Lal and then
[00:52] install a whole bunch of dependencies
[00:53] afterwards we actually get to choose
[00:55] which independencies we're going to
[00:56] install as we go uh jet stream is a lot
[00:59] more complicated than what we're going
[01:01] to need in this particular project so
[01:02] I'm going to choose working with Breeze
[01:04] there's all sorts of options for B here
[01:06] we can do Blade with alpine if you're a
[01:08] reactor view developer you can get
[01:10] inertia there's also an API only version
[01:12] and of course there's all the live wire
[01:14] options but because we're keeping it
[01:15] simple on this one I'm going to stick
[01:17] with blade and Alpine I choose generally
[01:19] to stick with Blade with alpine unless I
[01:21] really know I'm going to need Live Wire
[01:23] and I'm personally not a big single page
[01:24] application guy even though if I were
[01:26] inera would be the way I would do it for
[01:28] sure we're going to skip dark mode
[01:30] support in this particular one if you're
[01:32] not familiar pest is the default testing
[01:34] framework that all larl apps use going
[01:36] forward if you want to use PHP unit
[01:38] that's fine but we're going to stick
[01:39] with pest with this one because why not
[01:40] so it's going to install our basic
[01:41] dependencies and at some point here it's
[01:43] going to ask us about database and here
[01:45] we go so sqlite is the new default
[01:47] database for larel apps and once again
[01:50] it's not my SQL which we're always
[01:52] familiar with but I would say it's the
[01:53] default it doesn't require any
[01:54] dependencies let's get started with it
[01:56] and see how far it takes
[01:58] us so we can watch it
[02:00] [Music]
[02:04] install and there we go we can CD into
[02:07] our new app simposium and as you can see
[02:10] it says CD Symposium PHP Artisan serve
[02:12] which is the fastest and easiest way to
[02:14] get up and running with serving but
[02:16] because we have either Valley or herd
[02:17] running we can assume we're going to be
[02:19] able to visit as symposium. test in our
[02:21] browser so before we do anything else
[02:23] let's go actually go to our browser and
[02:25] type that in to make sure it actually
[02:27] works all right you can see what a
[02:29] default lay app looks like in lar Val 11
[02:31] got to zoom out a little bit here so
[02:33] this is what every Stock laral app is
[02:35] going to look like in L Val 11 it's
[02:38] working the first thing we're going to
[02:40] pull in is some dependencies that make
[02:42] sure that all of your code is consistent
[02:43] across all of your contributors and all
[02:45] different environments and it's
[02:46] something I've started pulling in in all
[02:48] my projects going forward right at the
[02:49] very
[02:50] beginning so the first thing we're going
[02:52] to pull in is a project called duster by
[02:55] Titan and what duster is is it brings a
[02:57] whole bunch of different linters and
[02:58] fixers together in one place you've got
[03:01] lille's pint but you've also got tlint
[03:03] PHP code sniffer and PHP CS fixer which
[03:06] each adds some things to our linting and
[03:09] fixing situation that pint can't do for
[03:12] example tlint can handle lel's blade so
[03:16] basically what we want to do is have all
[03:18] of them together in one kind of
[03:19] organized thing where they're all
[03:20] working together in unison and that's
[03:22] what duster provides for us so we're
[03:24] going to want to click on this which
[03:26] will copy the install and then we can
[03:28] paste it in here and this will Quire
[03:30] duster in our project now one of the
[03:32] cool things you can do with duster is
[03:34] you can come over here and you can grab
[03:36] this GitHub actions publish and these
[03:38] husky hooks so with GitHub actions what
[03:40] happens is every single time somebody
[03:42] takes a certain action for example push
[03:44] a commit uh it will run your linters or
[03:47] your fixers or whatever else you want on
[03:49] your project and so we can use this to
[03:51] publish some uh GitHub actions for
[03:53] duster we also have the same option for
[03:55] Husky hooks which basically does the
[03:57] same thing but instead of in CI it's on
[03:59] your local machine so we're going to
[04:01] install both for our projects let's
[04:03] start with GitHub actions so when we run
[04:05] this command it's going to allow us to
[04:07] choose three different options we can
[04:08] lint only which means it just is going
[04:10] to error out if there is a problem if it
[04:13] doesn't pass our code uh quality you can
[04:16] fix and commit which actually makes a
[04:18] new commit to fix it for you and then
[04:20] actually uh commits that to your G
[04:21] history uh there's also a third option
[04:24] the third option is a little bit more
[04:26] complicated what it does is recognize
[04:28] that you often if you're going to do get
[04:30] blame which shows you what particular
[04:32] git commits contributed towards this
[04:34] line of code looking this way you don't
[04:36] often want to see the formatting commits
[04:38] and so what this does is it fixes it
[04:40] makes a new commit but then it also adds
[04:42] that commit to a file that says ignore
[04:45] these when we're running get blame it's
[04:47] the most complex but it's really the
[04:48] best so we're actually going to use that
[04:50] one for this
[04:51] project it says resulting commit we'll
[04:54] stop any current R yep that's fine this
[04:56] is what we expect it to do it's nothing
[04:58] that stresses out so we say yes we want
[05:00] to continue and that's
[05:02] it and now we're going to do the same
[05:04] thing for Husky hooks which again
[05:06] remember with Husky hooks what happens
[05:07] is every single time we run get commit
[05:10] that's the way we're going to set it up
[05:11] it's before you actually run get commit
[05:14] it's going to run duster for you um so
[05:17] you don't even get the whole way up to
[05:18] continuous integration before it runs we
[05:20] also get an option here we can either
[05:22] have it lint so it'll error out the
[05:24] commit or we can have it fix and commit
[05:26] which means you know it'll actually make
[05:28] a fix and then commit the fix for us you
[05:30] may have noticed that when I was running
[05:32] that we saw this little error here that
[05:34] says XC run invalid after active
[05:37] developer path sometimes when you update
[05:39] Mac OS it loses its reference to where
[05:41] the command n tools live and you need to
[05:44] basically run a command that says hey
[05:46] here's where it is and this is the
[05:47] command it's xcode select DS and then
[05:49] you just pass it the information of
[05:51] where xcode is so I went to I turned off
[05:53] recording I went to another tab I ran
[05:55] this it's everything good and now we've
[05:57] got husky hooks enabled so to dive a
[06:00] little bit further into what husky does
[06:02] it allows you to set a configuration of
[06:04] what to do at certain moments in the G
[06:06] workflow and in your do husky directory
[06:08] you can add a new file for every single
[06:10] moment we've got one here for the
[06:12] pre-commit moment which is right before
[06:14] git runs commit and basically you're
[06:16] defining what happens in that moment and
[06:17] we're saying is run the npm package lint
[06:20] staged what lint stage lets you do as
[06:22] you can see in our config file here is
[06:24] defined that at that particular moment
[06:27] it's going to run lint stage which is
[06:28] going to say only for files that are
[06:30] staged in git so that are like added
[06:32] into our current working git stuff that
[06:35] match this particular glob then take
[06:37] this action so we say for all thephp
[06:40] files we are going to run duster fix on
[06:43] them but only the PHP files and only the
[06:45] staged
[06:46] ones so that's it for duster and now
[06:49] it's time to take a look at our next
[06:51] project that we're going to pull in
[06:52] which is called prettier so prettier is
[06:55] a little bit more complicated to install
[06:56] in a laral app than duster is so I wrote
[06:58] up a blog post in my personal blog
[07:00] walking you through all the steps and
[07:01] all the independencies you need so you
[07:03] go over to Matt ster.com check out the
[07:05] Articles and find this article how to
[07:07] set up prettier on a larl app linting
[07:09] Tailwind class order and more and you
[07:11] can see that that's what I'm really
[07:13] using it for more than anything else is
[07:15] for linting my Tailwind class order and
[07:17] I wanted to find a setup where I could
[07:19] get the official Tailwind this is how to
[07:21] order them I wanted it in idees I wanted
[07:23] it in the command line I wanted it in
[07:25] cicd I wanted it everywhere and I found
[07:27] that this is the ideal setup for it so
[07:30] just we're going to walk through the
[07:31] steps here and it's going to talk
[07:32] through exactly how to get that
[07:34] done so as we read through it's going to
[07:37] show us the dependencies we need to
[07:38] install with mpm uh there's a file
[07:42] called prettier RC that we need to
[07:44] install that is basically a
[07:45] configuration file uh so as soon as we
[07:47] run that um I've got my options here or
[07:49] my examples here and here's the main
[07:51] example I'd recommend that you use but
[07:53] of course you configure it however you
[07:54] want and then there's a prettier ignore
[07:56] file that's a little bit like G ignore
[07:59] um it's instructions about how to use
[08:00] the package and
[08:02] unsurprisingly uh there's also some
[08:03] information here about what it looks
[08:05] like for you to run it
[08:07] automatically uh with Husky as one
[08:10] option uh hooks into vs code is another
[08:13] option and in GitHub actions just like
[08:16] we used for the other project so for the
[08:18] project they're working on right now we
[08:20] did the installation and now we need to
[08:22] set up our pretty RC file so let's get
[08:24] started there all right so we're going
[08:27] to paste in the sample pretty r r C
[08:29] configuration file and you can learn
[08:31] more about it in my article or their
[08:33] docs but you can see we're preferring
[08:35] around 120 character wide column and
[08:37] we're setting some specifications of how
[08:39] we want to handle quotes and tabs and
[08:41] commas and all that kind of stuff so we
[08:44] also need to add a prettier ignore file
[08:46] uh you can grab this and use it it's the
[08:48] best one I put together but I'm not an
[08:49] expert so if you are a prettier expert
[08:52] uh use your own and ignore mine butum
[08:56] Ching so now we're going to set it up
[08:58] with Husky locally
[09:00] um as you can see we're using lint
[09:02] staged uh just like we did for duster um
[09:06] and there's an example here that looks
[09:07] exactly you know not exactly but very
[09:09] similar to the one did for duster um I
[09:12] did mention I should probably tweak this
[09:13] to only run on files and the Resources
[09:15] directory so if you are a you know lint
[09:18] stage Guru feel free to tweak this for
[09:20] your own but for now we're just going to
[09:21] copy this edit our lint stage config
[09:24] file and we're just going to paste it in
[09:25] directly below um as you can see this
[09:27] one's shaped like an array but the new
[09:29] one is just a direct string so I got to
[09:31] kind to put them next to each other with
[09:32] a little bit different
[09:35] syntax so now we're going to go and
[09:38] we're also going to set it up in GitHub
[09:39] actions and as you can see there's two
[09:41] options it's the format your code option
[09:44] or it is the lint your code and fail if
[09:46] it fails option just like we had with
[09:48] duster uh for this project once again
[09:51] we're going to choose the one that says
[09:52] format your code and so let's check out
[09:54] that
[09:56] tutorial so let's look through they've
[09:59] got some sample code here that we can
[10:00] use and we're just going to find the
[10:03] ultimate sample GitHub action and copy
[10:06] it now if you're not familiar uh GitHub
[10:09] actions live in the GitHub slw workflows
[10:13] directory and every file in there is a
[10:15] specific workflow so we already have the
[10:17] duster one here as you can see it's
[10:19] defining basically when certain
[10:21] behaviors happen take certain steps and
[10:23] this is all for duster so what we need
[10:24] to do is make one now for prettier as
[10:26] well and we will name that one
[10:30] prettier
[10:32] right we'll paste in what we
[10:35] copied as you can see it just says check
[10:38] out the code and then it's going to run
[10:40] this npm run format and I actually
[10:42] showed in my blog post how to do this
[10:44] npm run format it's something you add to
[10:47] the script section of your package.json
[10:50] file and it's a shortcut basically to
[10:52] run all of this code
[10:54] here if you're a l developer you're
[10:56] familiar with you know the dev npm run
[10:58] Dev or npm run build so we're just going
[11:00] to add a new shortcut of our own
[11:05] here so let's grab it from there and
[11:07] paste it in so the one thing that you
[11:10] might notice here is that this is going
[11:12] to end up with this format command
[11:14] running and when it runs in continuous
[11:17] integration we're going to end up with a
[11:19] lot of extra commits and that's one of
[11:20] the reasons why we use continuous
[11:22] integration as a fallback not as the
[11:25] primary place with is happening because
[11:27] remember we've got husky on our local
[11:29] machine uh actually running it long
[11:31] before it actually hits this continuous
[11:33] integration um and handling those things
[11:35] we have less commits and then you can
[11:37] also install it in your IDE if you want
[11:39] and having it in your IDE means less
[11:41] commits and so this is just one piece of
[11:43] a larger puzzle and you don't have to do
[11:45] any of the pieces I would recommend you
[11:47] at bare minimum choose a code style and
[11:50] apply it across your code Basin whatever
[11:51] way makes you comfortable you don't have
[11:53] to use these particular tools or these
[11:55] particular moments of integration I just
[11:56] wanted to show you that these are the
[11:58] ones that I really like right now
[12:00] all right let's take a breath we're done
[12:02] with all the code styling and formatting
[12:04] things we have a few more steps to take
[12:06] to get our local development environment
[12:07] up and running so that we have all the
[12:09] dependencies we need and we are going to
[12:11] be ready to build this app uh for the
[12:13] rest of this uh video series so there
[12:16] were a few other dependencies we want to
[12:18] take a look at making sure we have an
[12:19] order and the first one would be a
[12:21] database but because we chose to use
[12:23] sqlite for this particular project we're
[12:24] good we don't need any dependencies
[12:26] there if we did I would highly recommend
[12:28] DB engine which is a free tool for
[12:30] setting up MySQL and other things like
[12:32] that on your Mac so let's talk now about
[12:35] mail I use a tool called mail pit which
[12:37] allows you to uh basically send mail to
[12:40] a local server and it gives you a mock
[12:43] uh email inbox that you can use to click
[12:44] around things there's a couple different
[12:46] ways to install it I used Homebrew but
[12:48] you can also use larl or Titans takeout
[12:51] uh which is a Docker based tool if you
[12:52] don't have Homebrew or you don't want to
[12:54] use Homebrew um either way I recommend
[12:56] setting up some way for you to be able
[12:58] to click through an Emil inbox you could
[13:00] just use laravel's log feature for your
[13:02] mail but I highly recommend something
[13:04] that lets you view and click on your
[13:06] actual emails and mail pits a really
[13:07] great way to set that up that's it for
[13:10] mail and for any other dependencies that
[13:12] we're going to need to deal with they're
[13:13] either going to work by default because
[13:16] larl has reasonable local drivers or
[13:18] we're going to set them up in the
[13:19] particular video so let's take a look
[13:22] and make sure our database is actually
[13:23] working we're going to hit PHP Artisan
[13:25] migrate and it says nothing to migrate
[13:28] which means we got a database up and
[13:29] running and it's actually migrated uh
[13:31] because we're using sqlite it's going to
[13:33] be in the database SL database.sql file
[13:36] we can actually open up that file and
[13:38] see that there's it's a file that just
[13:40] has content there but it's not content
[13:42] we can actually understand or see uh
[13:45] here's what's interesting though is the
[13:46] full data of the application is just in
[13:49] that file so if we delete that file and
[13:52] we create a new one we now have what's
[13:55] effectively an empty unmigrated database
[13:57] so we can run php's as migrate and now
[13:59] the database migrations will run just
[14:03] fine so let's take a look at our tests
[14:05] now make sure they all pass and they all
[14:07] pass and yeah at this point we've got a
[14:09] functioning database we got functioning
[14:11] tests we got our mail working and all
[14:12] the other dependencies we need to think
[14:14] about again we're going to deal with in
[14:15] future videos all right so you've done
[14:18] it we have a local functioning layal app
[14:21] up in the browser
[14:24] linting uh mail dependency database
[14:27] dependency uh it's everything you need
[14:29] to basically get started working so
[14:31] that's what we're going to do next time
[14:34] asterisk we also want to make sure we've
[14:36] got it up and running in a staging
[14:37] environment so the next time is actually
[14:38] going to be that and then we'll be ready
[14:40] to work so for now get add get commit
[14:43] push it up to GitHub if you want and
[14:44] I'll see you again next time
⚡ Saved you time reading this? Transcribe any YouTube video for free — no signup needed.