AdonisJS: Templating, Authentication and Validators
In our previous post about AdonisJS we had covered introduction, installation, modeling, creating and displaying content, creating and deleting task. Let’s move on remaining part of it with this post.
A bit of templating
Our application is going to grow. In order to reuse some parts of the html, we are going to define a main layout, and include the specific code of each page into it.
Create a “layout_main.edge” file into “resources/views”. This file will include the base of our page, and will be used by each page we create.
<!DOCTYPE html> <html> <title>Task list</title> {{style('style')}} </head> <body> @!section('content') </body> </html>
Now you can refactor tasklist.edge
@layout('layout_main') @section('content') @if(flashMessage('taskHandling')) <span>{{ flashMessage('taskHandling') }}</span> @endif <form action="{{ route('TaskController.create') }}" method="POST"> {{ csrfField() }} <input type="text" name="title" placeholder="title" /> <input type="text" name="description" placeholder="description" /> <button type="submit">Create the task</button> </form> <section> <h1>Tasks</h1> @each(task in tasks) <div class="task"> <h2> @if(task.done) <input type="checkbox" disabled checked /> @else <input type="checkbox" disabled /> @endif {{task.title}} </h2> <form action="/task/delete/{{task.id}}" method="DELETE"> <button type="submit">Delete</button> </form> <p>{{task.description}}</p> </div> @endeach </section> @endsection
Authentication
You have probably already seen that there are some files to manage users in your project (“app/Models/User.js”)
First, let’s add a UserController :
#Choose "For HTTP requests" adonis make:controller UserController
Go to the router (“start/routes.js”) and some routes :
- two routes for the login and register process, displaying the templates with the forms
- two routes for receiving login and register data and handling user creation and login
- One route for the logout process
Route.on('/register').render('register') Route.on('/login').render('login') Route.post('/register', 'UserController.create') Route.post('/login', 'UserController.login') Route.get('/logout', 'UserController.logout')
Then, add the templates :
“resources/views/login.edge”
@layout('layout_main') @section('content') <form action="{{ route('UserController.login') }}" method="POST"> {{ csrfField() }} <input type="text" name="email" placeholder="email" /> <input type="password" name="password" placeholder="" /> <button type="submit">Login</button> </form> @endsection
“resources/views/register.edge”
@layout('layout_main') @section('content') <form action="{{ route('UserController.create') }}" method="POST"> {{ csrfField() }} <input type="text" name="email" placeholder="email" /> <input type="password" name="password" placeholder="" /> <button type="submit">Create new user</button> </form> @endsection
“Controllers/http/UserController.js”
'use strict' const User = use('App/Models/User') class UserController { async create({ request, response, auth }) { const userPayload = request.all(); const user = new User; user.email = userPayload.email; //We will also use the mail as username to simplify things here user.username = userPayload.email; user.password = userPayload.password; await user.save(); await auth.login(user); return response.redirect('/'); } async login({ request, response, auth, session }) { const userPayload = request.all(); try { await auth.attempt(userPayload.email, userPayload.password) } catch (e) { console.log(e); session.flash({ loginError: e }); } return response.redirect('/'); } async logout ({ auth, response }) { await auth.logout(); return response.redirect('/'); } } module.exports = UserController
You can also modify the tasklist template to add the register and login links, and a logout link if the user is logged in.
You can also include the create task form into the loggedIn conditionnal, in order to prevent anonymous users to create tasks.
... @section('content') @loggedIn <a href="logout">Log out</a> @else <a href="login">Log in</a> <a href="register">Register</a> @endloggedIn ...
More features
We now have seen what a basic and naive approach to build a web app can look like. You might have thought about a lot of other features to include in the project. Here is a quick list of other things Adonis can provide to you :
Relations between models
You want the tasks to be owned by users, you can create relations between models with the following syntax :
class User { tasks () { return this.hasMany('App/Model/Task'[, primaryKey, foreignKey]) } }
You can also use hasOne, belongsTo, belongsToMany and manyThrough.
Validators
You can use validators to check is the data flowing into your controllers has the right format, and emit messages (via session.flash for instance) when some errors occurs.
Validators are a 3rd party npm module : https://www.npmjs.com/package/adonis-validator
Using websocket instead of HTTP requests
As you might have seen when creating controllers, you can also generate controllers that are designed to use websockets.
Internationalization
A full guide to make you app multilanguage is available on the Adonis docs as well :
Conclusion
Adonis is a great choice for those who need a full-featured web server framework, and who need to keep control over your implementation.
This framework could be of a great use if you want to kickstart a project, if you want to follow usual guidelines and concepts. It will help you to implement data migrations, keep your code clean, handle data validation…
However, integrating exotic libraries can be painful. Adonis extensions need to be specifically built for this framework. This would be perfect for Adonis and it’s users if the framework would be in a monopoly situation, which is not the case.
Hire Node.js Developer from us, as we give you high quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.
To develop the custom web app using Node.js, please visit our technology page.
Content Source:
- blog.sourcerer.io
AdonisJS: A full featured node framework for modern web servers
Node is becoming one of the most elected choices by developers for modern web servers. You can build web servers in various elegant ways, using up-to-date tools such as ExpressJS.
However, a lot of developers had a hard time to stick on a high level framework to build web servers for their applications, sticking with tools that will stay light but will require a lot of configuration and wiring to produce a complete setup for a large project.
we are going to focus on a higher-level framework that comes with batteries included, and that will allow you to implement advanced features easily.
pic courtesy: blog.sourcerer.io
Meet AdonisJS
AdonisJS is a node framework inspired by the well-known Php framework Laravel. It relies on concepts such as dependency injection and service providers to make you design beautiful, reliable and easily testable code.
Get prepared to leave spaghetti code in favor of reusable OO structure.
Installation
First things first, to create a new AdonisJS server, you will need to install Adonis CLI, the command line tool that will help you manage your projects :
npm i -g @adonisjs/cli
This will provide you a “adonis” command to use in your terminal
To create a new adonis application, use the subcommand “new” :
adonis new my-adonis-server
As explained in the log, is uses the default fullstack-app template (adonisjs/adonis-fullstack-app), to create your project in a “my-adonis-server” folder.
You can now go to this folder and start serving your app in development mode :
cd my-adonis-server adonis serve --dev
Your app is now served from your machine, you can now dive into your project !
Keep the server running for the rest of the introduction.
pic courtesy: blog.sourcerer.io
A bit of modeling
Let’s now create one API to manage a resource. In the following chapter, we will create an endpoint to manage tasks.
First, let’s setup the project so it uses sqlite :
adonis install sqlite3
Then create the Task SQL table :
adonis make:migration tasks # select "create table" adonis migration:run
Note that every time you want to edit your database, you will have to create a migration.
Migrations will allow you to save every modification you are doing to your models, and will update data to follow the new formats and rules you are implementing. It is possible to define a "up" method, and a "down" method, depending if you want to switch to go to a newer or older version.
Then, create the model and controller related to this table :
adonis make:model Task adonis make:controller TaskController # select "For HTTP requests"
You now have set up you database, created a table, a model, and a controller. The source code for the model and the controller is present on the app folder.
pic courtesy: blog.sourcerer.io
You might have noticed that as you entered the last commands, the server automatically detected that your project changed, and took care of reloading the new setup.
Now we can add some content to our model. By creating another migration.
adonis make:migration tasks
Now edit the new file under database/migrations
'use strict' const Schema = use('Schema') class TasksSchema extends Schema { up () { this.table('tasks', (table) => { // alter table table.string('title') table.string('description') table.boolean('done') }) } down () { this.table('tasks', (table) => { // reverse alternations }) } } module.exports = TasksSchema
And run this migration
adonis migration:run
Now, you tasks models have a title, description, and done properties.
Creating a page displaying content
Now let’s create a page that displays a list of tasks.
//Route.on('/').render('welcome') Route.get('/', 'TaskController.home')
Then, on your TaskController, add the code handling the “/” route.
'use strict' const Task = use('App/Models/Task') class TaskController { async home({view}) { // Fetch tasks const tasks = await Task.all() return view.render('tasklist', { tasks: tasks.toJSON() }) } } module.exports = TaskController
And add the template of the page in “resources/views/tasklist.edge”
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Task list</title> {{ style('style') }} </head> <body> <section> <h1>Tasks</h1> @each(task in tasks) <div class="task"> <h2> @if(task.done) <input type="checkbox" disabled checked /> @else <input type="checkbox" disabled /> @endif {{task.title}} </h2> <p>{{task.description}}</p> </div> </div> @endeach </section> </body> </html>
In the “public/style.css”, delete all the css rules, to put :
.task { border: 1px solid darkgrey; border-radius: 6px; background: lightgrey; margin: 10px; padding-left: 10px; padding-right: 10px; }
This will display an empty list of tasks on “localhost:3000/” (so basically, nothing at the moment !)
It is currently empty because there is no tasks at the moment on the database. Let’s fix this!
Creating tasks in your database
For the sake of this tutorial, we will create our first tasks on the TaskController method we already defined:
/* ... */ async home({view}) { // Fetch tasks const task1 = new Task task1.title = 'Walk the dog' task1.description = 'Go and walk for half an hour' task1.done = false const task2 = new Task task2.title = 'Feed the cat' task2.description = 'The food is on the fridge' task2.done = false const task3 = new Task task3.title = 'Buy some pizza' task3.description = 'Go at the shop before 7pm' task3.done = true await task1.save() await task2.save() await task3.save() const tasks = await Task.all() return view.render('tasklist', { tasks: tasks.toJSON() }) } /* ... */
Load the tasklist once to insert your tasks in the database, then erase or comment those methods.
You should now see your tasks on the page.
Creating new tasks
Once this working, we would like to add new content via a new task form.
On your task controller, enter the task creation logic :
class TaskController { /* ... */ async create({ request, response, session, auth}) { const taskPayload = request.all() const task = new Task task.title = taskPayload.title task.description = taskPayload.description task.done = false await task.save() session.flash({ message: 'Task created!' }) return response.redirect('back') } }
The task creation form in your tasklist template :
@if(flashMessage('message')) <span>{{ flashMessage('message') }}</span> @endif <form action="{{ route('TaskController.create') }}" method="POST"> <input type="text" name="title" placeholder="title" /> <input type="text" name="description" placeholder="description" /> <button type="submit">Create task</button> </form> ...
And the post route in “start/routes.js”
Route.post('/task/create', 'TaskController.create')
Now you can add tasks from the form displayed in your task list.
Deleting Tasks
To delete tasks, the implementation is pretty much the same as the one we did to create tasks :
- Add a delete method in the controller
- Adapt the template
- Create the DELETE route
TaskController :
/* ... */ class TaskController { /* ... */ async delete({ request, response, session, params}) { const task = await Task.find(params.id) await task.delete() session.flash({ taskHandling: 'Task deleted!'}) return response.redirect('back') } }
“resources/views/tasklist.edge”
... <section> <h1>Tasks</h1> @each(task in tasks) <div class="task"> <h2> @if(task.done) <input type="checkbox" disabled checked /> @else <input type="checkbox" disabled /> @endif {{task.title}} </h2> <a href="/task/delete/{{task.id}}">Delete</a> <p>{{task.description}}</p> </div> </div> @endeach </section> ...
“start/routes.js”
Route.get('/task/delete/:id', 'TaskController.delete')
In this blog we have covered the basics of Adonis JS. If you really enjoyed reading it, please checkout our next blog in series to learn more about ‘Templating’, ‘Authentication’ and many more Adonis JS features.
Hire Node.js Developer from us, as we give you high quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.
To develop the custom web app using Node.js, please visit our technology page.
Content Source:
- blog.sourcerer.io