AdonisJS is a backend framework. It helps you create data-driven dynamic web applications. Using AdonisJS, you can handle the HTTP requests, query the database, authenticate users, upload files, send emails, and do a lot more.
If you have ever worked with a different backend framework like Rails, Laravel, or Django, then you can consider AdonisJS to be in the same boat.
Create URLs using routes
You define the URLs of your app using the routes module of AdonisJS. Think of routes as a mapping between the URL and the Javascript function you want to execute when someone visits that URL.
import Route from '@ioc:Adonis/Core/Route'
Route.get('/', () => {
return 'Hello! This is the homepage of my new app'
})
The above code defines a static route. However, a data-driven application also needs the power to register dynamic routes. For example: Grab the post id from the URL and then query the database.
import Route from '@ioc:Adonis/Core/Route'
import Database from '@ioc:Adonis/Lucid/Database'
Route.get('posts/:id', ({ params }) => {
return Database
.from('posts')
.select('*')
.where('id', params.id)
.first()
})
Handle requests using controllers
Controllers are the de-facto way of handling HTTP requests in AdonisJS. They help you extract all the inline function calls from the routes file to the dedicated controller files and keep your routes file tidy.
1. Create a controller
node ace make:controller Posts
2. Implement the required methods
import Database from '@ioc:Adonis/Lucid/Database'
export default class PostsController {
public async show({ params }) {
return Database
.from('posts')
.select('*')
.where('id', params.id)
.first()
}
}
3. Register it with the route
Route.get('posts/:id', 'PostsController.show')
Query database using the ORM
You can interact with the SQL databases using the official ORM of AdonisJS - "Lucid". Lucid has a rich API to perform schema migrations, seed databases with dummy data, and construct SQL queries using the Javascript API.
More importantly, Lucid has data models built on top of the Active Record pattern. You can create and use Models as follows:
1. Create a new model
node ace make:model Post
2. Configure columns
import { column, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class Post extends BaseModel {
@column({ isPrimary: true })
public id: number
@column()
public title: string
@column()
public description: string
}
3. Import and use it inside the controller
import Post from 'App/Models/Post'
export default class PostsController {
public async show({ params }) {
const post = await Post.find(1)
return post
}
}
Render HTML using templates
AdonisJS also ships with a homegrown template engine - "Edge". Edge has support for layouts, partials, conditionals, loops, and a lot more. However, the feature that shines the most is the edge components system.
1. Create a view template
node ace make:view posts/index
2. Write the markup
<div class="article">
<h1> {{ post.title }} </h1>
<p> {{ post.description }} </p>
</div>
3. Render it inside the controller
import Post from 'App/Models/Post'
export default class PostsController {
public async show({ params, view }) {
const post = await Post.find(1)
return view.render('posts/index', { post })
}
}
Authenticate users using the auth package
AdonisJS has a diverse authentication system, covering the needs of both the traditional web apps and the API servers. With the help of guards, you can use sessions based login or create an API token for stateless authentication.
1. Install and configure the auth package
npm i @adonisjs/auth
node ace configure @adonisjs/auth
2. Login users using sessions
export default class AuthController {
public async login({ request, auth, response }) {
const email = request.input('email')
const password = request.input('password')
await auth
.use('web') // 👈 using sessions guard
.attempt(email, password)
response.redirect().toRoute('dashboard')
}
}
3. Generate an API token instead
export default class AuthController {
public async login({ request, auth }) {
const email = request.input('email')
const password = request.input('password')
const token = await auth
.use('api') // 👈 using API guard
.attempt(email, password)
return token
}
}
Validate input using the validator
Consider validators as the first line of defense to protect your application from invalid or missing data.
Requests failing to satisfy the validation rules are redirected back to the form with proper error messages. Otherwise, you can access the validated values with correct data types.
1. Create a validator
node ace make:validator CreatePost
2. Define the validation schema
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class CreatePostValidator {
constructor (protected ctx: HttpContextContract) {
}
public schema = schema.create({
title: schema.string(),
description: schema.string({}, [
rules.escape()
]),
})
}
3. Validate the request
import CreatePost from 'App/Validators/CreatePostValidator'
export default class PostsController {
public async store({ request }) {
const post = await request.validate(CreatePost)
console.log(post.title)
console.log(post.description)
}
}
Compile assets using webpack encore
At some point in time, you may reach for a CSS framework and might want to sprinkle some JavaScript to make your web apps interactive.
As per the standards today, the CSS and the frontend JavaScript need to be transpiled and minified before serving it to the browser. This bundling process is not that simple, and hence you must use a bundler for it.
AdonisJS pre-configures Webpack (an industry-standard bundler) with sane defaults so that you don't have to waste time adjusting its knobs.
1. Configure webpack encore
node ace configure encore
2. Start the development server
node ace serve --watch
# Serve command starts both
# ✅ The AdonisJS HTTP server
# ✅ And the Webpack dev server
3. Build for production
node ace build --production
# Build commands compile both
# ✅ The AdonisJS Typescript source to JavaScript
# ✅ And the frontend assets using Webpack