Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Masonite tries to make static files extremely easy and comes with whitenoise out of the box. Whitenoise wraps the WSGI application and listens for certain URI requests that can be registered in your configuration files and serves those assets.
All configurations that are specific to static files can be found in config/filesystem.py
. In this file you'll find a constant file called STATICFILES
which is simply a dictionary of directories as keys and aliases as the value.
The directories to include as keys are simply the location of your static file locations as a relative path starting from the base of your application. For example, if your css files are in storage/assets/css
then put that folder location as the key. For the value, put the alias you want to use in your templates. For this example, we will use css/
as the alias.
For this setup, our STATICFILES
constant should look like:
Now in our templates we can use:
Which will get the storage/assets/css/style.css
file.
All templates have a static function that can be used to assist in getting locations of static files. You can specify the driver and locations you want using the driver name or dot notation.
Take this for example:
this will render:
You can also make the config location a dictionary and use dot notation:
and use the dot notation like so:
Sometimes you may need to serve files that are normally in the root of your application such as a robots.txt
or manifest.json
. These files can be aliased in your STATICFILES
directory in config/filesystem.py
. They do not have to be in the root of your project but instead could be in a storage/root
or storage/public
directory and aliased with a simple /
.
For example a basic setup would have this as your directory:
and you can alias this in your STATICFILES
constant:
You will now be able to access localhost:8000/robots.txt
and you will have your robots.txt served correctly and it can be indexed by search engines properly.
Thats it! Static files are extremely simple. You are now a master at static files!
The request and the response in Masonite work together to form a well formed response that a browser can read and render. The Request class is used to fetch any incoming cookies, headers, URL's, paths, request methods and other incoming data.
Although both the request and response classes have headers and cookies, in most instances, when fetching cookies and headers, it should be fetched from the Request class. When setting headers and cookies it should be done on the response class.
To get cookies you can do so simply:
This will fetch the cookie from the incoming request headers.
You can also set cookies on the request:
Note that setting cookies on the request will NOT return the cookie as part of the response and therefore will NOT keep the cookie from request to request.
Although both the request and response classes have headers and cookies, in most instances, when fetching cookies and headers, it should be fetched from the Request class. When setting headers and cookies it should be done on the response class.
To get a request header you can do so simply:
You can also set a header on the request class:
Note that setting headers on the request will NOT return the header as part of the response.
You can get the current request URI:
You can get the current request method:
You can check if the current path contains a glob style schema:
You can get the current subdomain from the host:
You can get the current host:
Inputs can be from any kind of request method. In Masonite, fetching the input is the same no matter which request method it is.
To fetch an input we can do:
If an input doesn't exist you can pass in a default:
To fetch all inputs from request we can do:
Or we can only fetch some inputs:
To fetch all inputs from request we can do:
If your input is a dictionary you can access nested data in two ways. Take this code example:
You can either access it normally:
Or you can use dot notation to fetch the value for simplicity:
You can also use a * wildcard to get all values from a dictionary list.:
Route parameters are parameters specified in a route and captured from the URL:
If you have a route like this:
You can get the value of the @user_id
parameter like this:
As a convenient way to fetch the user, you can do so directly on the request class if a user is logged in:
If the user is not authenticated then this will be set to None
You can fetch the IP address (ipv4) from which the request has been made by adding the IpMiddleware
to the HTTP middlewares of the project:
Then you can use the ip()
helper:
IP address is retrieved from various HTTP request headers in the following order:
HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR
HTTP_X_FORWARDED
HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR
HTTP_FORWARDED
REMOTE_ADDR
The first non-private and non-reserved IP address found by resolving the different headers will be returned by the helper.
The headers used and their order can be customized by overriding the IpMiddleware
and changing the headers
attribute:
There are plenty of ways to contribute to open source. Many of which don't even rely on writing code. A great open source project should have excellent documentation, a thriving community and have as little bugs as possible. Below I will explain how to contribute to this project in different ways both including and exluding code contributions.
This is not an exhaustive list and not the only ways to contribute but they are the most common. If you know of other ways to contribute then please let us know.
Of course the project requires contributions to the main development aspects but it's not the only way. But if you would like to contribute to development then a great way to get started is to simply read through this documentation. Get acquainted with how the framework works, how Controllers and Routing work and read the Architectural Concepts documentation starting with the Request Lifecycle, then the Service Providers and finally the Service Container.
It would also be good to read about the Release Cycle to get familiar with how Masonite does releases (SemVer and RomVer).
Feature Maintainers are people who are in charge of specific features (such as Caching or Creating Packages). These developers will be in charge of reviewing PR's and merging them into the development branch and also have direct contact with the repository owner to discuss.
Feature maintainers must already have significant contributions to development of the repository they are trying to be a Feature Maintainer for. Although they do not have to be contributors to the actual feature they plan to maintain.
If you don't want to touch the code and instead want to just look at it and figure it out, contribute some comments! Comments are an excellent way for future developers to read and understand the framework. Masonite strives on being extremely commented. Although most of the code itself does not need to be commented, some of the classes, modules, methods and functions do (although a lot of them already are).
Comments don't affect the working code so if you want to get used to contributing to open source or you just don't quite understand what a class method is doing or you are afraid of contributing and breaking the project (there are tests) then contributing comments is right for you!
Masonite package requires testing. If you want to search through all the tests in the tests directories of those repositories and write additional tests and use cases then that will be great! There are already over 100 tests but you can always write more. With more testing comes more stability. Especially as people start to contribute to the project. Check the tests that are already there and write any use cases that are missing. These tests can be things such as special characters in a url or other oddities that may not have been thought of when using TDD for that feature.
Once familiar with the project (by either contributing or by building application using the framework) it would be excellent if you could write or record tutorials and put them on Medium or YouTube. In order for the framework to be successful, it needs to have a plethora of documentation even outside of this documentation. It needs to have notoriety and if people are seeing the framework pop up in their favorite locations they will be more inclined to use the framework and contribute to it as well.
Plus there will be fantastic tutorials out there for beginners to find and watch and you could also build a following off the back of Masonite.
This documentation is fantastic but there are spots where it could be improved. Maybe we haven't explained something fully or something just doesn't make sense to you. Masonite uses Gitbook.com to host it's documentation and with that you are able to comment directly on the documentation which will start a discussion between you and the documentation collaborators. So if you want to cycle through the documentation page by page and get acquainted with the framework but at the same time contribute to the documentation, this is perfect for you.
If you just don't want to contribute code to the main project you may instead simply report bugs or improvements. You can go ahead and build any of your applications as usual and report any bugs you encounter to the GitHub.com issues page.
Look at the issues page on GitHub.com for any issues, bugs or enhancements that you are willing to fix. If you don't know how to work on them, just comment on the issue and Joseph Mancuso or other core contributors will be more than happy explaining step by step on how you can go about fixing or developing that issue.
If you have a large following on any social media or no following at all, you can contribute by trying to build up a following around Masonite. Any open source project requires an amazing community around the framework. You can either build up a community personally and be the leader of that community or you can simply send them to Masonite's GitHub repository where we can build up a community around there.
Another idea is to use Masonite to build applications such as a screencast website like LaraCasts.com or an official Masonite website or even a social network around Masonite. Every great framework needs it's "ecosystem" so you may be apart of that by building these applications with the Masonite branding and logos. Although copying the branding requires an OK from Joseph Mancuso, as long as the website was built with Masonite and looks clean it shouldn't be a problem at all.
Questions will come in eventually either through the GitHub issues or through websites like StackOverflow. You could make it a priority to be the first to answer these peoples questions or if you don't know the answer you can redirect one of the core maintainers or contributors to the question so we can answer it further.
Most pull requests will sit inside GitHub for a few days while it gets quality tested. The main develop
branch pull requests could sit there for as long as 6 months and will only be merged in on releases. With that being said, you can look at the file changes of these pull requests and ensure they meet the community guidelines, the API is similar to other aspects of the project and that they are being respectful and following pull requests rules in accordance with the Contributing Guide documentation.
Every now and then will be a requires discussion
label on an issue or pull request. If you see this label then be sure to add your thoughts on an issue. All issues are open for discussion and Masonite strives off of developer input so feel free to enter a discussion.
Every framework needs great packages and we as the maintainers of Masonite can only do so much with coming out with great packages and maintaining the framework at the same time. We look forward to our community coming out with awesome additions to the Masonite ecosystem. If you have any issues then be sure to open in the gitter chatroom on the Github homepage.
The request and the response in Masonite work together to form a well formed response that a browser can read and render. The Response class is responsible for what data is returned and how it is formatted. In this case, a response can have cookies and headers. The Request class can also have cookies and headers. In most times, generally, when you have to fetch headers or cookies you should do so on the request class and when you set cookies and headers you should do so on the response class.
Cookies on the response class are attached to the response and rendered as part of the response headers. Any incoming cookies you would likely fetch from the request class but you can fetch them from the response if you have a reason to:
You can also set cookies on the response:
You can also delete cookies:
You can redirect the user to any number of URL's.
First you can redirect to a simple URL:
You can redirect back to where the user came from:
If using the back method as part of a form request then you will need to use the back view helper as well:
You can also redirect to a route by its name:
This will find a named route users.home
like:
You may also pass parameters to a route if the URL requires it:
Finally you may also pass query string parameters to the url or route to redirect:
You can redirect by flashing a success message into session:
You can redirect by flashing an error message or errors messages into session:
You can directly use validation errors:
You can redirect by flashing form input into session, to re-populate the form when there are errors:
Response headers are any headers that will be found on the response. Masonite takes care of all the important headers for you but there are times where you want to set you own custom headers.
Most incoming headers you want to get can be found on the request class but if you need to get any headers on the response:
Any responses you want to be returned should be set on the response class:
This will set the header on the response.
You can set the status on the response simply:
You can also very simply download assets like images, PDF's or other files:
This will set all the proper headers required and render the file in the browser.
When setting the name, the file extension will be picked up from the file type. This example will download the invoice as the name
invoice-2021-01.pdf
If you want to force download it, or automatically download the file when the response in rendered, you can add a force
parameter and set it to True
:
Logging is a pretty crucial part of any application. Logging allows you to see errors your application is throwing as well as allow you to log your own messages in several different alert levels.
Masonite Logging currently contains the ability to log to a file, syslog and slack.
To enable logging in your application, ensure that LoggingProvider
is added to your application providers:
You are browsing the development
version of the documentation for an upcoming version of Masonite. It is subject to change.
Stop using old frameworks with just a few confusing features. Masonite is the developer focused dev tool with all the features you need for the rapid development you deserve. Masonite is perfect for beginners getting their first web app deployed or advanced developers and businesses that need to reach for the full fleet of features available.
Masonite works hard to be fast and easy from install to deployment so developers can go from concept to creation in as quick and efficiently as possible. Use it for your next SaaS! Try it once and you’ll fall in love.
Mail support for sending emails quickly.
Queue support to speed your application up by sending jobs to run on a queue or asynchronously.
Notifications for sending notifications to your users simply and effectively.
Task scheduling to run your jobs on a schedule (like everyday at midnight) so you can set and forget your tasks.
Events you can listen for to execute listeners that perform your tasks when certain events happen in your app.
A BEAUTIFUL Active Record style ORM called Masonite ORM. Amazingness at your fingertips.
Many more features you need which you can find in the docs!
These, among many other features, are all shipped out of the box and ready to go. Use what you need when you need it.
In order to use Masonite, you’ll need:
Python 3.7+
Latest version of OpenSSL
Pip3
All commands of python and pip in this documentation is assuming they are pointing to the correct Python 3 versions. For example, anywhere you see the python
command ran it is assuming that is a Python 3.7+ Python installation. If you are having issues with any installation steps just be sure the commands are for Python 3.7+ and not 2.7 or below.
If you are running on a Linux flavor, you’ll need the Python dev package and the libssl package. You can download these packages by running:
Debian and Ubuntu based Linux distributions
Or you may need to specify your python3.x-dev
version:
Enterprise Linux based distributions (Fedora, CentOS, RHEL, ...)
Masonite excels at being simple to install and get going. If you are coming from previous versions of Masonite, the order of some of the installation steps have changed a bit.
Firstly, open a terminal and head to a directory you want to create your application in. You might want to create it in a programming directory for example:
If you are on windows you can just create a directory and open the directory in the Powershell.
Although this step is technically optional, it is highly recommended. You can create a virtual environment if you don't want to install all of masonite's dependencies on your systems Python. If you use virtual environments then create your virtual environment by running:
or if you are on Windows:
The python
command here is utilizing Python 3. Your machine may run Python 2 (typically 2.7) by default for UNIX machines. You may set an alias on your machine for Python 3 or simply run python3
anytime you see the python
command.
For example, you would run python3 -m venv venv
instead of python -m venv venv
First install the Masonite package:
Then start a new project:
This will create a new project in the current directory.
If you want to create the project in a new directory (e.g. my_project
) you must provide the directory name with project start my_project
.
Then install Masonite dependencies:
If you have created the project in a new directory you must go to this directory before running project install
.
Once installed you can run the development server:
Congratulations! You’ve setup your first Masonite project! Keep going to learn more about how to use Masonite to build your applications.
Middleware is an extremely important aspect of web applications as it allows you to run important code either before or after every request or even before or after certain routes. In this documentation we'll talk about how middleware works, how to consume middleware and how to create your own middlewar
Middleware classes are placed inside the Kernel
class. All middleware are just classes that contain a before
method and after
method.
There are four types of middleware in total:
Middleware ran before every request
Middleware ran after every request
Middleware ran before certain routes
Middleware ran after certain routes
We have one of two configuration attributes we need to work with. These attributes both reside in our Kernel
file and are http_middleware
and route_middleware
.
http_middleware
is a simple list which should contain your middleware classes. This attribute is a list because all middleware will simply run in succession one after another, similar to Django middleware
In our Kernel
file this type of middleware may look something like:
Middleware will run on every inbound request to the application whether or not a route was found or not.
Route middleware is also simple but instead of a list, it is a dictionary with a custom name as the key and a list of middleware. This is so we can specify the middleware based on the key in our routes file.
In our config/middleware.py
file this might look something like:
By default, all routes inside the
web.py
file will run theweb
middleware list
You can pass parameters from your routes to your middleware in cases where a middleware should act differently depending on your route.
You can do this with a :
symbol next to your route middleware name and then pass in those parameters to the before
and after
middleware methods.
For example, we may be creating a middleware for request throttling and in our routes we have something like this:
notice the throttle:2,100 syntax. The 2 and the 100 will then be passed into the before and after methods of your middleware:
Similiar to the way we can pass values to a middleware using the :
splice we can also use the @
in the value to pass the value of the parameter.
For example, we may create a route and a middleware like this
If we go to a route like /dashboard/152/settings
then the value of 152 will be passed to the middleware before and after methods.
Middleware:
can live anywhere in your project,
Inherit from Masonite's base middleware class
Contain a before and after method that accepts request and response parameters
It's important to note that in order for the request lifecycle to continue, you must return the request class. If you do not return the request class, no other middleware will run after that middleware.
That's it! Now we just have to make sure our route picks this up. If we wanted this to execute after a request, we could use the exact same logic in the after
method instead.
If we are using a route middleware, we'll need to specify which route should execute the middleware. To specify which route we can just append a .middleware()
method onto our routes. This will look something like:
If you are more of a visual learner you can watch Masonite related tutorial videos at
Be sure to join the for help or guidance.
Adding API Support to your Masonite project is very simple. Masonite comes with supporting for returning JSON responses already but there are a few API features for authenticating and authorizing that are helpful.
Default projects don't come with these features so you'll have to simply register them.
First, register the ApiProvider in your project by adding the service provider to your PROVIDERS list:
This will register some required classes used for API development.
You should now create an api.py
file for adding your API routes to:
You will then have to add a binding to the container for the location of this file. You can do so in your Kernel.py
file inside the register_routes
method:
Any routes inside this file will be grouped inside an api middleware stack.
Next, you must choose a model you want to be responsible for authentication. This is typically your User model. You will have to inherit the AuthenticatesTokens
class onto this model:
This will allow the model to save tokens onto the table.
Next you will add a column to the models table for saving the token. Here we are naming it api_token
but this is configurable by adding a __TOKEN_COLUMN__
attribute to your model. Your migration file should look like this:
Then migrate your migrations by running:
Next, you can create a new API config file. You can do so simply by running the install command:
This will create a new api.py
config file in your configuration directory that looks like this:
This will attempt to import your user model but if you have a different model or if its in a different location you can change it in that model.
This command will also generate a secret key, you should store that secret key in an environment variable called JWT_SECRET
. This will be used as a salt for encoding and decoding the JWT token.
The authenticates
key is used as a check to check against the database on every request to see if the token is set on the user. By default, the database is not called to check if the token is assigned to a user. One of the benefits of JWT is the need to not have to make a database call to validate the user but if you want that behavior, you can set this option to True
You should add some routes to your web.py
file which can be used to authenticate users to give them JWT tokens:
The above parameters are the defaults already so if you don't want to change them then you don't have to specify them.
Since the routes in your api.py
file are wrapped in an api
middleware, you should add a middleware stack in your route middleware in your Kernel file:
This middleware will allow any routes set on this stack to be protected by JWT authorization.
By default, all routes in the
routes/api.py
file already have theapi
middleware stack on them so there is no need to specify the stack on all your API routes.
Once these steps are done, you may now create your API's
Once the setup is done, we may start building the API.
One of the ways to build an API is using controller and route resources.
A controller resource is a controller with several methods on them used to specify each action within an entity in the application (like users).
To create a controller resource you can run the controller command with an -a
flag:
This will create a controller with the following methods:
index
show
store
update
destroy
You can then create a route resource:
This will create the below routes which will match up with your API Controller methods:
GET
/users
index
users.show
GET
/users/@id
show
users.show
POST
/users
store
users.store
PUT/PATCH
/users/@id
update
users.update
DELETE
/users/@id
destroy
users.destroy
The routes we added earlier contain 2 authentication methods. The /api/auth
route can be used to get a new authentication token:
First, send a POST
request with a username
and password
to get back a JWT token:
You should then send this token with either a token
input or a Authorization
header:
If you do not set a value for the expires
key in the configuration file then your JWT tokens will not expire and will be valid forever.
If you do set an expiration time in your configuration file then the JWT token will expire after that amount of minutes. If the token expires, you will need to reauthenticate to get a new token. This can be done easily by sending the old token to get back a new one:
You can do this by sending a POST request to /api/reauth
with a token
input containing the current JWT token. This will check the table for the token and if found, will generate a new token.
One of the issues with JWT tokens is there is little that can be done to invalidate JWT tokens. Once a JWT token is valid, it is typically valid forever.
One way to invalid JWT tokens, and force users to reauthenticate, is to specify a version. A JWT token authenticated will contain a version number if one exists. When the JWT token is validated, the version number in the token will check against the version number in the configuration file. If they do not match then the token is considered invalid and the user will have to reauthenticate to get back a new token.
Since we store the active api_token on the table we are able to retrieve the user using the LoadUserMiddleware
and a new guard
route middleware stack:
First add the guard middleware stack and add the LoadUserMiddleware
to the api
stack.
Lastly, in your route or route groups you can specify the guard middleware and specify the guard name:
Masonite also provides a simple way to authorize user actions against a given resource. This is achieved with two concepts: gates and policies. Gates are as the name suggests an authorization check that you will be able to invoke to verify user access. Policies are a way to groupe authorization logic around a model.
Gates are simple callable that will define if a user is authorized to perform a given action. A handy Gate
facade is available to easily manipulate gates.
Gates receive a user instance as their first argument and may receive additionals arguments such as a Model instance. You needs to define Gates
in boot()
method of your service provider.
In the following example we are adding gates to verify if a user can create and update posts. Users can create posts if they are administrators and can update posts they have created only.
You can then check if a gate exists or has been registed by using has()
which is returning a boolean:
If a unknown gate is used a GateDoesNotExist
exception will be raised.
Then anywhere (often in a controller) in your code you can use those gates to check if the current authenticated user is authorized to perform the given action defined by the gate.
Gates exposes different methods to perform verification: allows()
, denies()
, none()
, any()
, authorize()
inspect()
.
allows()
, denies()
, none()
and any()
return a boolean indicating if user is authorized
authorize()
does not return a boolean but will raise an AuthorizationException
exception instead that will be rendered as an HTTP response with a 403 status code.
Finally for better control over the authorization check you can analyse the response with inspect()
:
Authorizes
class can be added to your User model to allow quick permission checks:
A fluent authorization api will now be available on User
instances:
All of those methods receive the gate name as first argument and then some additional arguments if required.
You can use the for_user()
method on the Gate facade to make the verification against a given user instead of the authenticated user.
During the gate authorization process, before
and after
hooks can be triggered.
A before
hook can be added like this:
The after
hook works the same way:
If the after
callback is returning a value it will take priority over the gate result check.
Policies are classes that organize authorization logic around a specific model.
You can run the craft command:
You can also create a policy with a set of predefined gates by using the --model
flag:
A model policy comes with common actions that we can perform on a model:
You are free to add any other methods on your policies:
Then in your service provider (as for defining gates) you should register the policies and bind them with a model:
An example policy for the Post model may look like this:
If an unknown policy is used then a PolicyDoesNotExist
exception will be raised.
You can then use the Gate
facade methods to authorize actions defined in your policies. With the previously defined PostPolicy
we could make the following calls:
The create()
or view_any()
methods do not take a model instance, that is why the model class should be provided so that Gate mechanism can infer from which policy those methods are belonging to.
Environment variables in Masonite are defined in a .env
file and should contain all environment variables needed for your project.
You can have multiple environment files that are loaded when the server first starts. It is often helpful to have different variable values depending on the environment where the application is running (locally, during tests or on a production server).
Also it might be useful to have more global environment variables that can be shared across your team for 3rd party services like Stripe or Mailgun and then have more developer specific values like database connections or different storage drivers for development.
We'll walk through how to configure your environments in this documentation.
Environment variables should be set on a project per project basis inside your .env
file.
Never commit any of your .env
files into source control ! It would be a security risk in the event someone gained access to your repository since sensitive credentials and data would get exposed.
That is why .env
and .env.*
are in the project .gitignore
file by default, so you should not worry about accidentally committing those files to source control.
In a fresh Masonite installation, a .env.example
file located at project root directory will define minimum and common configuration values for a Masonite application. During the installation process, this file will be copied to .env
file.
If you have installed Masonite but do not see this .env
file then you can create it manually and copy and paste the contents of .env-example
file.
Environment files are loaded in this order:
Masonite will load the .env
file located at your project root into the Python environment.
Masonite will look for an APP_ENV
variable inside the already loaded .env
. If it is defined it will try to load the .env.{APP_ENV}
file corresponding to this environment name.
For example, if APP_ENV
is set to local
, Masonite will additionally load the .env.local
environment file.
When the server is ready all those variables will be loaded into the current environment ready to be accessed in the different Masonite configuration files or directly with env()
helper.
If some variables contain spaces you should put variable content into double quotes:
You can use Python standard os.getenv()
method to get an environment variable value. It looks like:
Notice that this method does not cast types, so here we got a string instead of a boolean value.
You can also use Masonite helper env
to read an environment variable value. It looks like:
Note that you can provide a default value if the environment variable is not defined. Default value is ""
. For convenience this helper is casting types. Here are different examples of variables type casting:
5432
5432
(int)
true
True
(bool)
None
(None)
""
""
(str)
True
True
(bool)
false
False
(bool)
False
False
(bool)
smtp
smtp
(string)
If you do not wish to cast the value then you can provide a third parameter cast=False
:
The current Masonite environment is defined through the APP_ENV
variable located in your .env
file. You can access it easily through the Masonite app environment()
helper:
When running tests the environment will be set to testing
. You can use is_running_tests()
helper to check if environment is testing
:
You can also check if the environment is a production environment with:
The debug mode is controlled by the APP_DEBUG
environment variable used in config/application.py
configuration file. When crafting a new project, the debug mode is enabled (APP_ENV=True
). It should stay enabled for local development.
When debug mode is enabled all exceptions (or routes not found) are rendered as an HTML debug error page containing a lot of information to help you debug the problem. When disabled, the default 500
, 404
, 403
error pages are rendered.
You can check if debug mode is enabled through the Masonite app is_debug()
helper or with the config
helper:
Never deploy an application in production with debug mode enabled ! This could lead to expose some sensitive configuration data and environment variables to the end user.
Configuration files in Masonite are gathered in one folder named config
in default projects.
Each feature can have some options in its own file named after the feature. For example you will find mail related options in config/mail.py
file.
The Configuration
class is responsible for loading all configuration files before the application starts.
It will load all files located at path defined through config.location
binding which default to config/
.
Then values are accessed based on the file they belong to and a dotted path can be used to access nested options.
Given the following config/mail.py
file:
Accessing mail
will return a dictionary with all the options.
Accessing mail.from_email
will return the FROM_EMAIL
value
Accessing mail.drivers.smtp.port
will return the port value for smtp driver.
To read a configuration value one can use the Config
facade:
or the config
helper:
Setting configuration values is achieved through projet configuration files.
However, you can override on the fly a configuration value with the Config
facade:
This should be done sparingly as this could have unexpected side effects depending at which time you override the configuration option.
This is mostly useful during tests, when you want to override a configuration option to test a specific behaviour:
But if you simply want to have different configuration depending on the environment (development, testing or production) you should rely instead on environment variables used to define configuration options.
Controllers are a place where most of your business logic will be. Controllers are where you put the responses you see in the web browser. Responses can be dictionaries, lists, views or any class that can render a response.
You may use a craft command to create a new basic controller or simply make a new controller manually. Controllers are classes with methods that are mapped to a route.
Your route may look something like this:
In this case this route will call the WelcomeController
classes show
method.
To create a basic controller via a craft command simply run:
This will create a new controller class to get you setup quickly. This controller class will look like a basic class like this:
You may start building your controller out and adding the responses you need.
Note that the controllers inherit Masonite base
Controller
class. This is required for Masonite to pick up your controller class in routes.
Your controller's constructor and controller methods are resolved by Masonite's Service Container. Because of this, you can simply typehint most of Masonite's classes in either the constructor or the methods:
Read more about the benefits of the Service Container.
Controllers can have different response types based on what you need to return.
If you want to return a JSON response you can return a dictionary or a list:
This will return an application/json
response.
You can return strings:
If you want to return a view you can resolve the view class and use the render method:
If you are using Masonite ORM, you can return a model directly:
If you want to return a redirect you can resolve the response class and use the redirect method:
You can return any class that contains a get_response()
method. This method needs to return any one of the above response types.
If you had a parameter in your route, you may fetch it by specifying the parameter in the controller's method:
Since the id
parameter is in the route we can fetch the value of this parameter by specifying it in the controller method signature:
Another way to fetch route parameters is through the request class:
Masonite will be able to pick up controllers (inheriting Controller
class) using string binding in the registered controllers locations.
The default registered controllers location is app/controllers
and is defined in project Kernel.py
configuration file:
You can override the registered controllers location in Kernel.py
file by editing the default binding controllers.location
.
You can multiple additional controller locations with add_controller_locations
:
The best place to do this is in your Kernel.py
file in the register_routes()
method.
You should do it before registering routes, else registering routes will fail as Masonite will fail to resolve controller classes.
This section of the documentation will contain various tutorials. These are guides that are designed to take you from beginning to end on building various types of projects with Masonite. We may not explain things in much detail for each section as this part of the documentation is designed to just get you familiar with the inner workings of Masonite.
Since this section of the documentation is designed to just get you up and coding with Masonite, any further explanations that should be presented are inside various "hint blocks." Once you are done with the tutorial or simply want to learn more about a topic it is advised that you go through each hint block and follow the links to dive deeper into the reference documentation which does significantly more explaining.
You will see various hint blocks throughout the tutorials. Below are examples of what the various colors represent.
You'll see hint blocks that are green which you should follow if you want to learn more information about the topic currently being discussed.
You'll also see hint blocks that are blue. These should not be ignored and typically contain background information you need to further understand something.
This tutorial will assume you have already installed Masonite. If you haven't, be sure to read the Installation guide to get a fresh install of Masonite up and running. Once you have one up and running or if you already have it running, go ahead and continue on.
In this tutorial we will walk through how to create a blog. We will touch on all the major systems of Masonite and it should give you the confidence to try the more advanced tutorials or build an application yourself.
Typically your first starting point for your Masonite development flow will be to create a route. All routes are located in routes/web.py
and are extremely simple to understand. They consist of a request method and a route method. Routing is simply stating what incoming URI's should direct to which controllers.
For example, to create a GET
request route it will look like:
We'll talk more about the controller in a little bit.
You can read more about routes in the Routing documentation
We will start off by creating a view and controller to create a blog post.
A controller is a simply a class that inherits from Masonite's Controller class and contains controller methods. These controller methods will be what our routes will call so they will contain most of our application's business logic.
Think of a controller method as a function in the views.py
file if you are coming from the Django framework
Let's create our first route now. We can put all routes inside routes/web.py
and inside the ROUTES
list. You'll see we have a route for the home page. Let's add a route for creating blogs.
You'll notice here we have a BlogController@show
string. This means "use the blog controller's show method to render this route". The only problem here is that we don't yet have a blog controller.
All controllers are located in the app/controllers
directory by default and Masonite promotes a 1 controller per file structure. This has proven efficient for larger application development because most developers use text editors with advanced search features such as Sublime, VSCode or Atom. Switching between classes in this instance is simple and promotes faster development. It's easy to remember where the controller exactly is because the name of the file is the controller.
You can of course move controllers around wherever you like them but the craft command line tool will default to putting them in separate files. If this seems weird to you it might be worth a try to see if you like this opinionated layout.
Like most parts of Masonite, you can scaffold a controller with a craft command:
This will create a controller in app/controllers
directory that looks like this:
Simple enough, right? You'll notice we have a show
method we were looking for. These are called "controller methods" and are similiar to what Django calls a "view."
But also notice we now have our show method that we specified in our route earlier.
We can return a lot of different things in our controller but for now we can return a view from our controller. A view in Masonite are html files or "templates". They are not Python objects themselves like other Python frameworks. Views are what the users will see (or view).
This is important as this is our first introduction to Masonite's IOC container. We specify in our parameter list that we need a view class and Masonite will inject it for us.
For now on we won't focus on the whole controller but just the sections we are worried about. A ...
means there is code in between that we are not worried about:
Notice here we "type hinted" the View
class. This is what Masonite calls "Auto resolving dependency injection". If this doesn't make sense to you right now don't worry. The more you read on the more you will understand.
You'll notice now that we are returning the blog
view but it does not exist yet.
All views are in the templates
directory. We can create a new file called templates/blog.html
.
We can put some text in this file like:
Let's run the migration for the first time:
and then run the server
and open up http://localhost:8000/blog
. You will see "This is a blog" in your web browser.
Most applications will require some form of authentication. Masonite comes with a craft command to scaffold out an authentication system for you. This should typically be ran on fresh installations of Masonite since it will create controllers, routes, and views for you.
For our blog, we will need to setup some form of registration so we can get new users to start posting to our blog. We can create an authentication system by running the craft command:
We should get a success message saying that some new assets were created. You can check your controllers folder and you should see a few new controllers there that should handle registrations.
You can then add the authentication routes to your project:
We will check what was created for us in a bit.
In order to register these users, we will need a database. By default, Masonite uses SQLite. If you want to use a different database you can change the options that start with DB_
in your .env
file. For running MySQL or Postgres you will need to have those databases setup already.
We have already run the migration command before, which was:
If you want to use MySQL, open up the .env
file in the root of your project and change the DB_DATABASE
to mysql
. Also, feel free to change the DB_DATABASE
name to something else.
Once you have set the correct credentials, we can go ahead and migrate the database. Out of the box, Masonite has a migration for a users table which will be the foundation of our user. You can edit this user migration before migrating but the default configuration will suit most needs just fine and you can always add or remove columns at a later date.
This will create our users table for us along with a migrations table to keep track of any migrations we add later.
Now that we have the authentication and the migrations all migrated in, let's create our first user. Remember that we ran craft auth
so we have a few new templates and controllers.
Go ahead and run the server:
and head over to http://localhost:8000/register and fill out the form. You can use whatever name and email you like but for this purpose we will use:
We have looked at running migrations but let's look at how to create a new migration.
Now that we have our authentication setup and we are comfortable with migrating our application, let's create a new migration where we will store our posts.
Our posts table should have a few obvious columns that we will simplify for this tutorial part.
Not surprisingly, we have a command to create migrations. You can read more about Database Migrations here but we'll simplify it down to the command and explain a little bit of what's going on:
This command simply creates the start of a migration file that we will use to create the posts table. By convention, table names should be plural (and model names should be singular but more on this later).
This will create a migration in the databases/migrations
folder. Let's open that up and starting on line 6 we should see something that looks like:
Lets add a title, an author, and a body to our posts tables.
This should be fairly straight forward but if you want to learn more, be sure to read the Database Migrations documentation.
Now we can migrate this migration to create the posts table
Now that we have our tables and migrations all done and we have a posts table, let's create a model for it.
Models in Masonite are a bit different than other Python frameworks. Masonite uses an Active Record ORM. Models and migrations are separate in Masonite. Our models will take shape of our tables regardless of what the table looks like.
Again, we can use a craft command to create our model:
Notice we used the singular form for our model. By default, Masonite ORM will check for the plural name of the class in our database (in this case posts) by assuming the name of the table is the plural word of the model name. We will talk about how to specify the table explicitly in a bit.
The model created now resides inside app/models/Post.py
and when we open it up it should look like:
Simple enough, right? Like previously stated, we don't have to manipulate the model. The model will take shape of the table as we create or change migrations.
Again, the table name that the model is attached to is the plural version of the model name but if you called your table something different such as "user_posts" instead of "posts" we can specify the table name explicitly:
Masonite ORM by default protects against mass assignment as a security measure so we will explicitly need to set what columns we would like to be fillable (this way we can pass the column names into the create
and update
methods later).
The relationship is pretty straight forward here. Remember that we created a foreign key in our migration. We can create that relationship in our model like so:
Because of how Masonite does models, some models may rely on each other so it is typically better to perform the import inside the relationship like we did above to prevent any possibilities of circular imports.
We won't go into much more detail here about different types of relationships but to learn more, refer to Masonite ORM Relationships documentation.
Let's setup a little HTML so we can learn a bit more about how views work. In this part we will setup a really basic template in order to not clog up this part with too much HTML but we will learn the basics enough that you can move forward and create a really awesome blog template (or collect one from the internet).
Now that we have all the models and migrations setup, we have everything in the backend that we need to create a layout and start creating and updating blog posts.
We will also check if the user is logged in before creating a template.
The URL for creating will be located at /blog/create
and will be a simple form for creating a blog post
Notice here we have a {{ csrf_field }}
below the <form>
open tag. Masonite comes with CSRF protection so we need a token to render the hidden field with the CSRF token.
Now we need to make sure the user is logged in before creating this so let's change up our template a bit:
auth()
is a view helper function that either returns the current user or returns None
.
Masonite uses Jinja2 templating so if you don't understand this templating, be sure to Read Their Documentation.
For simplicity sake, we won't be styling our blog with something like Bootstrap but it is important to learn how static files such as CSS works with Masonite so let's walk through how to add a CSS file and add it to our blog.
Firstly, head to storage/static/
and make a blog.css
file and throw anything you like in it. For this tutorial we will make the html page slightly grey.
Now we can add it to our template like so right at the top:
That's it. Static files are really simple. It's important to know how they work but for this tutorial we will ignore them for now and focus on more of the backend.
Javascript files are the same exact thing:
For more information on static files, checkout the Static Files documentaton.
Notice that our action is going to /blog/create
so we need to direct a route to our controller method. In this case we will direct it to a store
method.
Let's open back up the routes/web.py
file and create a new route. Just add this to the ROUTES
list:
and create a new store method on our controller:
Now notice above in the form we are going to be receiving 2 form inputs: title and body. So let's import the Post
model and create a new post with the input.
Notice that we now used request: Request
here. This is the Request
object. Where did this come from? This is the power and beauty of Masonite and your first introduction to the Service Container. The Service Container is an extremely powerful implementation as allows you to ask Masonite for an object (in this case Request
) and get that object. This is an important concept to grasp so be sure to read the documentation further.
Also notice we used an input()
method. Masonite does not discriminate against different request methods so getting input on a GET
or a POST
request are done exactly the same way by using this input
method.
Go ahead and run the server using craft serve again and head over to http://localhost:8000/blog
and create a post. This should hit the /blog/create
route with the POST
request method and we should see "post created".
Lets go ahead and show how we can show the posts we just created. Now that we are more comfortabale using the framework, in this part we will create 2 new templates to show all posts and an individual post.
Let's create 2 new templates.
templates/posts.html
templates/single.html
Let's start with showing all posts
Let's create a controller for the posts to separate it out from the BlogController
.
Great! So now in our show
method we will show all posts and then we will create a single
method to show a specific post.
Let's get the show
method to return the posts view with all the posts:
We need to add a route for this method:
Our posts view can be very simple:
Go ahead and run the server and head over to http://localhost:8000/posts
route. You should see a basic representation of your posts. If you only see 1, go to http://localhost:8000/blog
to create more so we can show an individual post.
Showing The Author
Remember we made our author relationship before. Masonite ORM will take that relationship and make an attribute from it so we can display the author's name as well:
Let's repeat the process but change our workflow a bit.
Next we want to just show a single post. We need to add a route for this method:
Notice here we have a @id
string. We can use this to grab that section of the URL in our controller in the next section below. This is like a route URL capture group.
Let's create a single
method so we show a single post.
We use the param()
method to fetch the id from the URL. Remember this key was set in the route above when we specified the @id
For a real application we might do something like @slug
and then fetch it with request().param('slug')
.
We just need to display 1 post so lets just put together a simple view:
Go ahead and run the server and head over the http://localhost:8000/post/1
route and then http://localhost:8000/post/2
and see how the posts are different.
By now, all of the logic we have gone over so far will take you a long way so let's just finish up quickly with updating and deleting posts. We'll assume you are comfortable with what we have learned so far so we will run through this faster since this is just more of what were doing in the previous parts.
Let's just make an update method on the PostController
:
Since we are more comfortable with controllers we can go ahead and make two at once. We made one that shows a view that shows a form to update a post and then one that actually updates the post with the database.
templates/update.html
Remember we made 2 controller methods so let's attach them to a route here:
That should be it! We can now update our posts.
Let's expand a bit and make a delete method.
Notice we used a GET
route here, It would be much better to use a POST
method but for simplicity sake will assume you can create one by now. We will just add a link to our update method which will delete the post.
We can throw a delete link right inside our update template:
Great! You now have a blog that you can use to create, view, update and delete posts! Go on to create amazing things!
CSRF protection typically entails setting a unique token to the user for that page request that matches the same token on the server. This prevents any person from submitting a form without the correct token. There are many online resources that teach what CSRF does and how it works but Masonite makes it really simple to use.
The CSRF features for Masonite are located in the CsrfProvider
Service Provider and the CsrfMiddleware
. If you do not wish to have CSRF protection then you can safely remove both of these.
The CsrfProvider
simply loads the CSRF features into the container and the CsrfMiddleware
is what actually generates the keys and checks if they are valid.
By default, all POST
requests require a CSRF token. We can simply add a CSRF token in our forms by adding the {{ csrf_field }}
tag to our form like so:
This will add a hidden field that looks like:
If this token is changed or manipulated, Masonite will throw an InvalidCsrfToken
exception from inside the middleware.
If you attempt a POST
request without the {{ csrf_field }}
then you will receive a InvalidCsrfException
exception. This just means you are either missing the Jinja2 tag or you are missing that route from the exempt
class attribute in your middleware.
You can get also get the token that is generated. This is useful for JS frontends where you need to pass a CSRF token to the backend for an AJAX call
For ajax calls, the best way to pass CSRF tokens is by setting the token inside a parent template inside a meta
tag like this:
And then you can fetch the token and put it wherever you need:
You can then pass the token via the X-CSRF-TOKEN
header instead of the __token
input for ease of use.
Not all routes may require CSRF protection such as OAuth authentication or various webhooks. In order to exempt routes from protection we can add it to the exempt
class attribute in the middleware located at app/http/middleware/CsrfMiddleware.py
:
Now any POST routes that are to your-domain.com/oauth/github
are not protected by CSRF and no checks will be made against this route. Use this sparingly as CSRF protection is crucial to application security but you may find that not all routes need it.
You can also use *
wildcards for exempting several routes under the same prefix. For example you may find yourself needing to do this:
This can get a bit repetitive so you may specify a wildcard instead:
Masonite uses Laravel Mix which provides a really simple way to handle asset compiling even greater than simple SASS and LESS. You don't need to be an expert in either Laravel Mix or NPM to compile assets, though.
To get started we can simply run NPM install:
This will install everything you need to start compiling assets.
The configuration settings will be made inside your webpack.mix.js
file located in the root of your project.
You can see there is already an example config setup for you that looks like this:
This will move these 2 files, resources/js/app.js
and resources/css/app.css
and compile them both into the storage/compiled
directory.
Now that we have our compiled assets configured we can now actually compile them.
You can do so by running:
This will compile the assets and put them in the directories you put in the configuration file.
You can also have NPM wait for changes and recompile when changes are detected in frontend files. This is similiar to an auto reloading server. To do this just run:
Laravel Mix can take care of file hashing when releasing assets to production, by adding a hash suffix to your assets to automatically bust cache when loading assets. To enable this you can add the following in your webpack.mix.js
file:
After Laravel Mix compiled your assets you won't be able to know the exact filename (because of the hash) you should use in your views to reference your assets. You can use Masonite mix()
helper to resolve the correct file path to your assets.
Masonite comes with a powerful way to broadcast events in your application. These events can be listened to client-side using Javascript.
These can be things like a new notification which you can show on the frontend without reloading the page.
You should create an account on Pusher Channels and then create a Pusher application on your account and get the related credentials (client, app_id, secret) and the cluster location name and put this into the broadcast pusher options.
Finally make sure you install the pusher
python package
Include the pusher-js script tag on your page
Create a Pusher instance configured with your credentials
Now you're ready to subscribe to channels and listen for channels events.
Broadcast events are simple classes that inherit from CanBroadcast
. You may use any class, including the Masonite Event classes.
A broadcast event will look like this:
Note that the event name emitted to the client will be the name of the class. Here it would be UserAdded
.
You can broadcast the event using the Broadcast
facade or by resolving Broadcast
class from container.
You can broadcast easily without creating a Broadcast event
Or you can broadcast the event class created earlier
You may broadcast on multiple channels as well:
This type of broadcasting will emit all channels as public. For private and presence channels, keep reading.
To listen for events on client-side you must first subscribe to the channel the events are emitted on
Then you can listen for events
Different channel types are included in Masonite.
Inside the event class you can specify a Public channel. These channels allow anyone with a connection to listen to events on this channel:
Private channels require authorization from the user to connect to the channel. You can use this channel to emit only events that a user should listen in on.
Private channels are channels that start with a private-
prefix. When using private channels, the prefix will be prepended for you automatically.
Private channels can only be broadasting on if users are logged in. When the channel is authorized, it will check if the user is currently authenticated before it broadcasts. If the user is not authenticated it will not broadcast anything on this channel.
This will emit events on the private-channel_name
channel.
On the frontend, when you make a connection to a private channel, a POST request is triggered by the broadcast client to authenticate the private channel. Masonite ships with this authentication route for you. All you need to do is add it to your routes:
This will create a route you can authenticate you private channel on the frontend. The authorization route will be /broadcasting/authorize
but you can change this to anything you like:
You will also need to add the /pusher/user-auth
route to the CSRF exemption.
The reason for this is that the broadcast client will not send the CSRF token along with the POST authorization request.
The default behaviour is to authorize everyone to access any private channels.
If you want to customize channels authorization logic you can add your own broadcast authorization route with a custom controller. Let's imagine you want to authenticate channels per users, meaning that user with ID 1
will be able to authenticate to channel private-1
, user with ID 2
to channel private-2
and so on.
First you need to remove Broadcast.routes()
from your routes and add your own route
Then you need to create a custom controller to implement your logic
Presence channels work exactly the same as private channels except you can see who else is inside this channel. This is great for chatroom type applications.
For Presence channels, the user also has to be authenticated.
This will emit events on the presence-channel_name
channel.
To get started more easily with event broadcasting in Masonite, two small examples are available here:
To do this we need to create a NewRelease
Broadcast event and trigger this event from the backend.
On the frontend we need to listen to releases
channel and subscribe to NewRelease
events to display an alert box with the release message.
Let's imagine our User model has two roles basic
and admin
and that we want to send alerts to admin users only. The basic users should not be authorized to subscribe to the alerts.
To achieve this on the backend we need to:
create a custom authentication route to authorize admin users only on channel private-admins
create a AdminUserAlert
Broadcast event
trigger this event from the backend.
Let's first create the authentication route and controller
On the frontend we need to listen to private-admins
channel and subscribe to AdminUserAlert
events to display an alert box with the message.
You're ready to start broadcasting events in your app !
Commands in Masonite are generally designed to make your development life easier. Commands can range from creating controllers and models to installing packages and publishing assets.
Available commands for Masonite can be displayed by running:
This will show a list of commands already available for Masonite.
Every command has a documentation screen which describes the command's available arguments and options. In order to view a command documentation, prefix the name of the command with help
. For example, to see help of serve
command you can run:
Commands can be created with a simple command class inheriting from Masonite Command
class:
Command's name, description and arguments are parsed from the Command docstring.
The docstring should start with the description of the command
and then after a blank line you can define the command name.
After the name of the command in the docstring should come the arguments. Arguments are defined with one indent and enclosed into brackets.
Positional (mandatory) arguments are defined without dashes (-
or --
).
Here is how to define a positional argument called name
with a description:
Inside the command, positional arguments can be retrieved with self.argument(arg_name)
Optional arguments are defined with dashes and can be used in any order in the command call. An optional argument --force
can have a short name --f
.
Here is how to define two optional arguments iterations
and force
with a description:
Notice how we provided the short version for the force
argument but not for the iterations
arguments
Now the command can be used like this:
If the optional argument is requiring a value you should add the =
suffix:
Here when using iterations
, the user should provide a value.
If the argument may or may not have a value, you can use the suffix =?
instead.
Finally if a default value should be used when no value is provided, add the suffix ={default}
:
Inside the command, optional arguments can be retrieved with self.option(arg_name)
You can print messages to console with different formatting:
self.info("Info Message")
: will output a message in green
self.warning("Warning Message")
: will output a message in yellow
self.error("Error Message")
: will output a message in bold red
self.comment("Comment Message")
: will output a message in light blue
Masonite Command
class is inheriting Cleo Command
class so you should be able to use all Cleo features when creating commands.
add()
method takes one or multiple commands:
When you run python craft
you will now see the command you added.
Masonite makes authentication really simply.
Masonite comes with a command to scaffold out a basic authentication system. You may use this as a great starting point for adding authentication to your application. This command will create controllers, views, and mailables for you.
If you would like to implement your own authentication from scratch you can skip to the sections below.
First run the command to add the news files:
Then add the authentication routes to your routes file:
You may then go to the /login
or /register
route to implement your authentication.
The configuration for Masonite's authentication is quite simple:
The default key here is the guard to use for authentication. The web
dictionary is the configuration for the web guard.
You can attempt a login by using the Auth
class and using the attempt
method:
If the attempt succeeds, the user will now be authenticated and the result of the attempt will be the authenticated model.
If the attempt fails then the result will be None
.
If you know the primary key of the model, you can attempt by the id:
You can logout the current user:
You can get the current authenticated user:
If the user is not authenticated, this will return None
.
You can register several routes quickly using the auth class:
This will register the following routes:
Guards are encapsulated logic for logging in, registering and fetching users. The web guard uses a cookie
driver which sets a token
cookie which is used later to fetch the user.
You can switch the guard on the fly to attempt authentication on different guards:
Feel free to change which directories the files get compiled to. For more information on additional configuration values take a look at the
Laravel is using Laravel mix so you can just follow guidelines to setup TailwindCSS for Laravel Mix on .
Please follow the guidelines directly on
More information on this feature in .
Masonite comes with one server-side driver: .
Server side configuration for broadcasting is done in config/broadcast.py
configuration file. For now there is one driver available .
To be able to receive broadcast events in the browser you should install .
This is the quickest way to install Pusher. But for a real app you will often use a build system to and will install the Javascript Pusher SDK with npm install pusher-js
and then import Pusher class with import Pusher from 'pusher-js';
.
It is advised to use environment variables instead of hard-coding credentials client-side. If you're using Laravel Mix to then you should prefix your environment variables with MIX_
.
In this section we will use the client pusher
instance .
Pusher is expecting the authentication route to be /pusher/user-auth
. If you want to change this client-side you can do it when creating Pusher instance
If you want to keep CSRF protection you can read more about it .
Adding the authentication route is the same as for .
Authorizing channels is the same as for .
Sending public app releases notification to every users (using )
Sending private alerts to admin users (using )
Masonite uses the package for shell command feature.
You can find more information and more features for creating commands in .
Once created you can register the command to Masonite's inside a (if you don't have one, you should ):
GET /login
Displays a login form for the user
POST /login
Attempts a login for the user
GET /home
A home page for the user after a login attempt succeeds
GET /register
Displays a registration form for the user
POST /register
Saved the posted information and creates a new user
GET /password_reset
Displays a password reset form
POST /password_reset
Attempts to reset the users password
GET /change_password
Displays a form to request a new password
POST /change_password
Requests a new password
The Masonite hashing feature is a very useful feature to prevent exposing ID's in your application.
Many times you need to expose your database primary keys to the frontend. For example, when updating a record, you might need to pass in a primary key value to a URL like /users/10/settings
.
Typically you want to hide these key values from a hacker trying to change these values easily.
With the Masonite hashing feature you can change a value like 10
to l9avmeG
and prevent exposing those sensitive integer values.
The Masonite hashing feature automatically decodes values before they get to the controller. To do this it you need to specify both a middleware to help decode the values as well as the provider to register the helper in the templates.
For the middleare you can add it easily:
You should put the Hash ID middleware towards the top of the middleware stack so it will decode the request properly before getting to the other middleware in the stack.
The provider should also be added:
This will register a template helper and some other useful features.
You can use the helper directly to encode or decode integers easily:
Inside your templates you can use the hashid
template helper:
When submitted to the backend the will now be the normal integer value of the user id:
When using the template helper, you may also use the hashid feature for request params:
If you have a route like this:
Inside your controller you are able to get the unhashed request parameter:
Masonite includes a rate limiting feature which make it really easy to limit an action during a given time window.
The most frequent use of this feature is to add throttling to some API endpoints but you could also use this to limit how many time a model can be updated, a job can be run in the queue or a mail should be sent.
This feature is based on the application's Cache features. It will store the number of attempts of a given key
and will also store the associated time window.
Rate Limiting feature can be accessed via the container application.make("rate")
or by using the RateLimiter
facade.
To limit an action, we need:
a key
that will uniquely identify the action
a number of authorized attempts
a delay after which number of attempts will be reset
Before we can start using the rate limiting features of Masonite, we need to register the RateProvider
class in our providers list:
You can add throttling to HTTP requests easily by using the ThrottleRequestsMiddleware
.
First register the middleware as a route middleware into your project:
This middleware is taking one argument which can be either a limit string or a limiter name.
By using limit strings such as 100/day
you will be able to add a global limit which does not link this limit to a given user, view or IP address. It's really an absolute limit that you will define on your HTTP requests.
Units available are: minute
, hour
and day
.
We now just have to use it in our routes:
To be able to add throttling per users or to implement more complex logic you should use Limiter
. Limiter
are simple classes with a allow(request)
method that will be called each time a throttled HTTP request is made.
Some handy limiters are bundled into Masonite:
GlobalLimiter
UnlimitedLimiter
GuestsOnlyLimiter
But you can create your own limiter:
Here we are creating a limiter authorizing 2 requests/day for guests users, 10 requests/day for authenticated non-premium users and unlimited requests for premium users. Here we are using by(key)
to define how we should identify users. (TODO: explain more)
Finally you can register your limiter(s) in your application provider:
We now just have to use it in our routes:
Now when making unauthenticated requests to our /api
endpoints we will see some new headers in the response:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 4
After reaching the limit two headers are added in the response, X-Rate-Limit-Reset
which is the timestamp in seconds defining when rate limit will be reset and when api endpoint will be available again and Retry-After
which is the number of seconds in which rate limit will be reset:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 0
X-Rate-Limit-Reset
: 1646998321
Retry-After
: 500
A ThrottleRequestsException
exception is raised and a response with status code 429: Too Many Requests
and content Too many attempts
is returned.
The response can be customized to provide different status code, content and headers. It can be done by adding a get_response()
method to the limiter.
In the previous example it would look like this:
To use Rate Limiting feature we should define limits. A limit is a number of attempts during a given time frame. It could be 100 times per day or 5 times per hour. In Masonite limits are abstracted through the Limit
class.
Here is an overview of the different ways to create limits:
Then to associate a given key to this limit we can do:
This feature allows to simply limit calling a Python callable. Here we will limit the given callable to be called 3 times per hour for the key sam
.
Let's make one attempt:
Alternatively you can manually incrementing attempts:
We can get the number of attempts:
We can get the number of remaining attempts:
We can check if too many attempts have been made:
We can reset the number of attempts:
We can get the seconds in which will be able to attempt the action again:
We can get the UNIX timestamps in seconds when will be able to attempt the action again:
Here is a complete use case, that will determine if an email should be send to the given user:
Although uncommon, You may create your own service provider and add it to your providers list to extend Masonite, or even remove some providers if you don't need their functionality. If you do create your own Service Provider, consider making it available on PyPi so others can install it into their framework.
We can create a Service Provider by simply using a craft command:
This will create a new Service Provider under our app/providers/DashboardProvider.py
. This new Service Provider will have two simple methods, a register
method and a boot
method. We'll explain both in detail.
There are a few architectural examples we will walk through to get you familiar with how Service Providers work under the hood. Let's look at a simple provider and walk through it.
We can see that we have a simple provider that registers the User
model into the container. There are three key features we have to go into detail here.
In our register
method, it's important that we only bind things into the container. When the provider is first registered to the container, the register method is ran and your classes will be registered to the container.
The boot method will have access to everything that is registered in the container. The boot method is ran during requests and is actually resolved by the container. Because of this, we can actually rewrite our provider above as this:
This will be exactly the same as above. Notice that the
boot
method is resolved by the container.
Once you create your own service provider, it will need to be registered in the PROVIDERS
list. This is likely in your config/providers.py
file:
Once registered it will take your register and boot method into account when the framework boots up or registers.
Masonite Tinker is a powerful REPL (Read, Evaluate, Print and Loop) environment for the Masonite framework. It's a supercharged Python interactive shell with access to the container, models and helpers.
Tinker allows you to interact with your entire Masonite project on the command line, including models, jobs, events, and more. To enter the Tinker environment, run the tinker command:
This will open a Python shell with the application container (under the app
variable), the application models and some helpers imported for you.
Syntax highlighting
Tab completion of python variables and keywords, filenames and function keywords
Input history, persistent across sessions
Integrated access to the pdb debugger and the Python profiler
and much more...
You just need to use -i
option and install IPython if not installed yet (pip install IPython
):
By default your app models are loaded from the location configured in your project Kernel. You can override the directory to load models from with the -d
flag. It should be a path relative to your project root. For example you can run the following command if your models are located in a models/
folder located at your project root:
You can use PYTHONSTARTUP
environment variable to add a script that you want to run at the beginning of the shell session.
With IPython you can use this variable or put some Python scripts in ~/.ipython/profile_default/startup/
. IPython will run those scripts for you at the beginning of the shell session.
Session configuration is located at config/session.py
file. In this file, you can configure which driver to use.
Masonite is configured to use the Cookie session driver by default, named cookie
.
Cookie driver will store all session data in the users cookies. It can be used as is.
Redis driver is requiring the redis
python package, that you can install with:
Then you should define Redis as default driver and configure it with your Redis server parameters:
Finally ensure that the Redis server is running and you're ready to start using sessions.
To save session data you can simply "set" data into the session:
Flash data is data specific to the next request. This is useful for data such as error messages or alerts:
To get back the session data you set you can simply using the "get" method:
You can check if a session has a specific key:
You can also delete a key from the session
You can reset all data in a session:
Creating packages is very simple for Masonite. You can create a package and publish it on PyPi in less than 5 minutes. With Masonite packages you will scaffold and centralize some features to reuse it all your Masonite projects with ease. Masonite comes with several helper functions in order to create packages which can add configuration files, routes, controllers, views, commands, migrations and more.
As a developer, you will be responsible for both making packages and consuming packages. In this documentation we'll talk about both. We'll start by talking about how to make a package and then talk about how to use that package or other third party packages.
Masonite, being a Python framework, you can obviously use all Python packages that aren’t designed for a specific framework. For example, you can obviously use a library like requests
but you can’t use specific Django Rest Framework.
Package providers are the connection between your package and Masonite. A service provider is responsible for binding things into Masonite container and specifying from where to load package resources such as views, configuration, routes and assets.
Your Masonite project will discover packages through the PROVIDERS
list defined in your providers.py
configuration file. When a package provider is added to this list, this will allow additional bindings, commands, views, routes, migrations and assets to be registered in your project.
Keep in mind that some simple packages do not need to register resources in your project though.
We provide two ways to quickly scaffold your package layout:
Install cookiecutter
globally (or locally) on your computer:
Then you just have to run (in the directory where you want your package repository to be created):
You can now start developing your package !
There are man ways to create a virtual environment in Python but here is a simple way to do it:
Activating your virtual environment on Mac and Linux is simple:
or if you are on Windows:
The default package layout contains a Makefile that help getting started quickly. You just have to run:
This will install Masonite, development tools such as flake8
and pytest
and it will also install your package locally so that you can start using and testing it in the test project directly.
Now you can run the tests to make sure everything is working properly:
The default package layout come with one basic test. You should see this test passing. You can then start building your package and adding more unit tests.
You can then visit http://localhost:8000
to see the welcome page.
When you make changes to your package as your package is installed locally and registered into your project, your changes will be directly available in the project. You will just need to refresh your pages to see changes.
Registering on PyPi
Creating a publish token
API tokens provide an alternative way (instead of username and password) to authenticate when uploading packages to PyPI. It is is strongly recommended to use an API token where possible.
Go to your PyPi account and find the API tokens
section. Click on Add API token
, give a significant name such as publish-token
and create the token. Write down the token key somewhere for later.
Configuring PyPi on your computer
If you never uploaded a package on PyPi before you will need to configure PyPi on your computer. For this you will need to add a .pypirc
file in your home directory. If you do not have one then you can easily creating one using:
This will move the file to your home directory. If you are using Windows you may need to move this file manually.
Then fill in password
key with the token you created later prefixed by pypi-
. With a token starting with AgEIcHlwaS5vcmcCJGNjYjA4M...
the .pypirc
file would look like:
Publishing the package to PyPI
Now you're ready to upload your package to PyPI. Ensure that all parameters of setup.py
file are up to date. It's the file describing your package. Fill in the correct version number you want to publish. When ready you can run:
You should always check that the package name is available on PyPi and that the version number to publish has not been published before. Else you won't be able to publish your package.
Make the package available on masonite packages list
When developing a package you might need to use a configuration file, to add migrations, routes and controllers or views. All those resources can be located in your package but at one point a user might want to override it and will need to publish those resources locally in its project.
The following section will explain how to register those resources in your package to be used in a Masonite project and how to make those resources publishable.
Masonite makes it really easy to do this by creating a specific package provider that will register your package resources. The default package layout comes with such a provider inheriting from PackageProvider
class:
configure()
method is called in usual register()
method and is used to register all resources used in your package.
root(import_path)
method should be called first and is used to specify the import path of your package. If your package needs to be used like this:
Then super_awesome_package
is the import path of your package. If your package is imported like this:
Then masonite.inertia
is the import path of your package.
name(string)
method should be called in second and is used to specify the name of your package (not the PyPi package name neither the Python module name) but the name that will be used to reference your package in the publish command or in the resources paths (it should be a name without special characters and spaces i.e. a Python valid name). This will also be the name of your package configuration file.
Your package is likely to have a configuration file. You will want to make your package configuration available through the handy config()
Masonite helper. For this you will need to call config(path, publish=False)
inside configure()
method:
This will load the package configuration file located at super_awesome_package/config/super_awesome.py
into Masonite config. The configuration will then be available with config("super_awesome.key")
.
If you want to allow users to publish the configuration file into their own project you should add publish=True
argument.
Configuration values located in packages and in local project will be merged. Values defined locally in the project takes precedance over the default values of the package.
If your package contains migrations you can register the migration files to be published in a project:
If your package contains routes you can register them by providing your route files and the locations to load controllers (used by your routes) from. For this you will need to call controllers(*locations)
and then routes(*routes)
inside configure()
method.
If your routes are defined in super_awesome_package/routes/api.py
and super_awesome_package/routes/web.py
and the controllers files available in super_awesome_package/controllers
you can do:
Now Masonite should be able to resolve new routes from your packages.
If your package contains views you can register them by providing folders containing your views. For this you will need to call views(*folders, publish=False)
inside configure()
method. The views will be namespaced after your package name:
For example if your package contains an admin
folder located at super_awesome_package/admin/
containing a index.html
view you can do:
Views will be available in controllers:
If your project contains assets (such as JS, CSS or images files) you can register them to be published in the project by calling assets(*paths)
inside configure()
method.
For example if your package contains an assets
folder located at super_awesome_package/assets/
containing some asset files and folders you can do:
If your project contains commands you will want to register it when your package is installed in a project so that we can run python craft my_package_command
. For this you will need to call commands(*command_class)
inside configure()
method.
Now when you run python craft
you should see the two registered commands.
When using PackageProvider
class to create your package service provider, you will be able to publish all package resources defined below in a project. You just need to run the command package:publish
with the name of the package (declared inside configure()
method). With our example it would be:
If you want to publish some specific resources only, you can use --resources
flag:
Here this will only publish configuration and views into your project.
Finally if you want to check what resources a package can publish you just need to run:
This will output the list of resources that the package is going to publish into your project.
If the package has been released on PyPi you need to install it as any Python package:
Then you should follow package installation guidelines but often it will consist in:
publishing some files if you need to tweak package resources or configuration:
You should be ready to use the package in your project !
Masonite uses SEMVER versioning schema and your requirements should be fixed on any one of these requirements depending on your dependency manager:
This will allow your application to stay up to date for all minor releases of the framework.
Masonite currently has an 8 month release cycle. Roughly 8 months we will come out with a new major release. Masonite follows a SEMVER versioning schema
Major releases almost always contain some form of breaking changes. You should only upgrade to major versions after careful local upgrades and testing.
Minor versions come with new features and could release every few days or every few months depending on how many features are being added to the codebase and when those features are ready to be released.
Minor version updates will never come with breaking changes. You should always stay up to date with the latest minor versions of the framework.
Patch versions are small fixes or security releases.
Patch version updates will never come with breaking changes. You should always stay up to date with the latest patch versions of the framework.
Service Providers are the key building blocks to Masonite. The only thing they do is register things into the , or run logic on requests. If you look inside the config/providers.py
file, you will find a PROVIDERS
list which contains all the Service Providers involved in building the framework.
Finally you can get an enhanced experience by using the Tinker IPython shell. is an improved Python shell offering some interesting features:
Masonite comes with a simple way to store sessions. Masonite supports the following session drivers: and .
You can browse Masonite packages (official and community) on: (alpha version).
GitHub template
The is just a GitHub template so you only have to click Use this template
to create your own GitHub repository scaffolded with the default package layout and then clone your repository to start developing locally.
The is using cookiecutter
package to scaffold your package with configuration options (name, author, url...). The advantages is that you won't need to edit all the occurences of your package name after generation.
The default package layout comes with a test project located in tests/integrations
. This project is really useful to directly test your package behaviour. It is scaffolded as a default Masonite project with your package already installed (see section). You can run the project with the usual command:
If your package has migrations and you want to migrate your test project you should first , publish them and then run the usual migrate
command:
Once you're satisfied with your package it's time to release it on so that everyone can install it. We made it really easy to do this with the make commands.
If you never uploaded a package on PyPi before you will need to . Verify your email address.
This will install twine
if not installed yet, build the package, upload it to PyPi and delete the build artifacts. You should then see a success message and be able to browse your package on .
To make your package available on (alpha version) you need to add Framework :: Masonite
classifier in setup.py
:
You can find more information on the .
The command will publish the configuration into the defined project configuration folder. With the default project settings it would be in config/super_awesome.py
.
The command will publish the migrations files into the defined project migrations folder. With the default project settings it would be in databases/migrations/
. Migrations file are published with a timestamp, so here it would result in those two files: {timestamp}_create_some_table.py
and {timestamp}_create_other_table.py
.
If you want to allow users to publish the view file into their own project so they can tweak them you should add publish=True
argument. The command will publish the views files into the defined project views folder. With the default project settings it would be in templates/vendor/super_awesome/admin/index.html
.
The command will publish the assets into the defined project resources folder. With the default project settings it would be in resources/vendor/super_awesome/
.
registering the package in your project: