Introduction to Masonite and VueJS
Currently, I’m working for a company named Dreamed Diabetes. My daily drivers are Django and React. But, before I used those awesome technologies I used something less cool — PHP, mainly Drupal, but also Symfony and Laravel. I said less cool because let’s face it — PHP is the language that people love to hate.
But, if we scoped out of the hate for PHP we can see that there are a lot of awesome architecture decisions in Laravel. For example:
- Larvel provides an awesome CLI: need a controller? Don’t copy and paste, just use the CLI for that
- Service container and dependency injection — Yeah! free of charge
- Simple to use ORM? Yes, eloquent is pretty much good ORM
Django, which is the best apple-to-apple comparison with Laravel, lacks the above, but still looks like the go-to solution in the Python ecosystem. For a while, larval architecture has been missing to me until one day I heard an episode from the “Talk Python to Me” podcast. It was so good that I actually tweet about it:
I was sure I’ll going to contribute something back to Masonite, and now when we have a competition for writing the best blog post — I’m in!
What are we going to discuss:
- Setting up a Masonite project
- Setting up models and adding dummy content
- Create a controller for serving our records
- Connect it to a simple VueJS application
You probably wonder to yourself — “Roy, Masonite’s documentation is pretty straight forward” — and indeed you are correct. But, it’s not that straight forward when writing a decoupled project. In the end, you’ll see how easy is to set up a REST endpoint and connect it to VueJS.
Setting up our Masonite project
Installing Masonite is pretty simple. First, check that you are qualified with the framework’s requirements. The installation parts suggesting the next commands:
$ pip3 install masonite-cli :: (may need sudo if using UNIX) ::
$ craft new project
$ cd project
$ craft install
$ craft serve
Setting up the DB connection
Since this is just a simple demo of Masonite and VueJS, we’ll go with SQLite as our DB engine. Once you crafted your project, go to the .env file in the root of your project, search the DB connection section and replace it with:
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog.db
DB_USERNAME=root
DB_PASSWORD=root
Modeling our data
Let’s create our blog model which eventually stores our blog entries. For that, we have a simple craft command that will scaffold the files:
craft model Blog -m
Craft CLI created for us a file called “Blog.py” under the “app” directory with the migration file, due to the -m flag, which in my case is — 2019_12_05_204818_create_blogs_table.py.
Migration is what changes for us the DB structure. Unlike Django’s migration files, Masonite took and approach which gives us “up” and “down” methods:
- The up method designed to change the structure when creating new features in the project
- The down method gives us the option to revert the changes
I’m pretty sure Django has a way to revert migration but this approach is much insane to me because if something went wrong in production we can revert the changes easily.
Our blog will have a couple of fields: id, created and updated at, title, body and author id so we could know who wrote the blog:
Now, we need to set up our model file which will tells Masonite how to interact when pulling records. This will be done in the “Blog.py”:
A lot of the file is described in the documentation but I’d like to focus on the serialize method. The method tells orator how to format the entry when converting to JSON format(similar to Django serializers). In this case, we added the author’s name alongside the author ID.
Now, let’s seed some dummy data so we could see how our application response. As before we have a command for that:
craft seed blogs
This will create for us a file called “blog_table_seeder.py”:
This will create 25 blog entries each time we’ll hit the seed command. But that’s not over yet, we need to register our migration. Go to seeds/database_seeder.py and paste the next code:
After registering our seed file, which is what the code above is doing, we need to describe it to Masonite. Go to config/factories.py, and paste the next code:
I’ve added the function blog_factory and registered it with — factory.register(Blog, blog_factory). The blog factory pulls all the user’s IDs we have in the system, selects the first 5 and pulls a random user id just so our dummy data will have various blog authors but you can apply any other logic you desired.
Setting up controller
We need to create something that will serve the records from the DB. Don’t worry, use the next command:
craft controller BlogController
We will get a file named “BlogController”. The file should have a “show” method out of the box, which correlates to GET requests. Retrieving all the blog entries should look like that:
Notice that there’s only one line in the show method:
return Blog.all()
sweet! The next part is to stitch Masonite’s router to the method we just wrote. Go to routes/web.py and add to the ROUTES list the next member:
Get(‘/blogs’, ‘BlogController@show’)
Putting it together
After we set up all the things, let’s assemble it! First, we need to migrate our DB structure with the command:
craft migrate
This will create for us the file “blog.db” and the next output:
[OK] Migrated 2018_01_09_043202_create_users_table
[OK] Migrated 2019_12_05_204818_create_blogs_table
Next, we will need to seed our data with the command:
craft seed:run
You’ll need to see something like that:
Seeded: UserTableSeeder
Seeded: BlogTableSeeder
Database seeded!
Awesome! things look good. Now, we need to serve our application which we can do it with the command
craft serve
You’ll need to see something like that:
Starting monitor for PID 75689.
Serving at: http://127.0.0.1:8000
You made it! Go to http://127.0.0.1:8000/blogs and you should see you’re blogs entries:
Let’s talk about VueJS
VueJS is a front-end framework that exists for a couple of years and even though it’s not coming from Google or Facebook, VueJS looks like a good alternative: very simple, the learning curve is fast, has good documentation and a vibrant community.
Another awesome thing is the Vue CLI which like craft does a lot of heavy lifting for us. Let’s install it:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
Vue CLI will give us scaffold commands and provides us the option to install plugins to improve our workflow, serving for us the development version of our app or compiling it for deployment. Let’s set up the project folder:
vue create masonite-blog-front
What awesome with the CLI tool that you can choose how to set up your project environment — with Babel or without, with typescript support out of the box or not and more. After Vue CLI set up for us all the stuff let’s go to the folder and serve the app:
cd masonite-blog-front
npm run serve
You’ll get something like that:
Change “some” files
You can browse the folders we just created and see their structure. I’ll walk you through on how I usually change my VueJS app. First, go to the src/App.vue (from now on, any location I’ll specify will be relative to the “src” folder):
I remove the navigation because it’s redundant for now. The next thing is to add the Blogs component which will display for us the blog entries. Create a file called Blogs.vue in the components folder with the next content:
We’ll no longer need to display the hello world so we going to remove it. Go to the file “views/Home.vue” and change it to:
The Blogs component
Each component in the project we got from Vue CLI has three parts:
- Template — This is the view of the component. It will contain text, other components we depend on and directive for iterate over items list or register mouse events
- Logic — This is where we going to put all the logic: set the state, communicating with the server, declare dependencies on other components, etc. etc.
- Styling — This is where we going to add some vibrant to our component: colors, spacing, sizing, etc. etc.
Let’s start with some CSS. I usually do that first because then I can really see how my component will look. Luckily, there’s built-in support in SASS syntax. If you don’t know what is it I’ll just tell you that it’s an easy way to write CSS but won’t dive into it right now. You just need to add it to the “Blogs.vue” file:
Now, we going to add the skeleton of the blogs component. Replace the template section with the next code:
Wow! Look at the next result:
Let’s start to write some JS
Each component has a state — a local state — which holds data about the component logic which in our case is the list of blogs. If we want to be fancy we can add a mode which tells us if the loading has been completed but for now, a list of blogs is good enough.
We will start by adding the data method. This is the method which describes the state of the application:
public data() {
return {
'blogs': [],
};
}
After setting up the state of the app we need to interact with the server. For that, we need to know about the life cycle hooks of our component. There are several, but we will use mounted: the hook which invoked when the component has been mount(similar to React’s componentDidMount). We’ll take some async/await approach for this one:
async mounted() {
try {
const response = await fetch('http://localhost:8000/blogs');
this.blogs = await response.json();
} catch (e) {
console.error(e)
}
}
We used the fetch function that will interact with our backend and ask for the data from the “blogs” endpoint we created in the first part. Then we’ll parse the JSON response and assign it to the blogs in the state. If you’ll add the method and hope to see some results you’ll get nothing. Looking in the console you’ll find out the next error:
Access to fetch at ‘http://localhost:8000/blogs' from origin ‘http://localhost:8080' has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
If you’re not familiar with the error you should have a look at the Cross-Origin Resource Sharing which will elaborate on that. We need to fix it so let’s go back to the server for a moment.
A small fix to the server… You’ll almost there!
CORS… For me, it has always been an annoying issue I need to solve when hooking the server and the frontend together. According to MDN article, we need to add the header Access-Control-Allow-Origin, but… how? A middleware!
Middleware is similar to the life cycle hook but in a router scope. In the middleware, I can do all sorts of things that relate to the response: adding headers, maybe log something or even adding the user object we pulled from the DB after authenticating the user. Don’t worry we have a command for that:
craft middleware Cors
You've got a file named “CorsMiddleware.py” in the “middleware” folder. Open the file and paste the next code:
Don’t forget to add it to the cors list. How? Just look at the documentation. Now, restart the server with the command:
creaft server
and let’s go back to the frontend!
Back to Vue
After handling the CORS issue in the backend now, we can apply some last things in our component. If you want to see the content just print the blogs variable by adding {{blogs}} somewhere in the template.
Now, we need to iterate over the blog's records and print it. VueJS has a directive called v-for which iterate over an array. For more information just read the “List rendering” part in the VueJS documentation. The code should look like that:
and the result is:
If you want to look at the entire component just go to this gist.
Wrapping up our journey
Wow... What a journey, right? We started with nothing at all, then in a very short time, we got our self a backend with blog entries. Then, without any problems, we created a full-fledge VueJS application with a bunch of features. But, what next? It’s up to you. You can start by learning Masonite architecture and improve what we created by:
- Creating more models and bind them together — a comment system might sound good but you’ll have to implement the authentication process which is a good practice for the frontend and the backend
- Add more routes to the Masonite application and display a single entry of a given blog(this would be handy for understanding how to get parameters from a URI) and apply the same thing to the VueJS application
- Add an admin section — moderate you entries(blogs, users, comments, etc. etc.)
I hope you had fun in this blog post and I hope I inspired you with some new stuff on the way.