In this tutorial, we are focused to build a RESTful app using Adonis 4.0, an open source Node.js web framework focused on simplicity and ease of use.
In this tutorial, we are using Adonis 4.0 to build a RESTful application. Adonis is an open source Node.js web framework with primary focus on simplicity and ease of use. It was inspired by Laravel and borrows a lot of features from the PHP framework. The framework comes with features such as Job queues, an authentication system, integration with email systems, data validation, and supports Lucid ORM. The framework is created and maintained by Aman Virk
The following tools are used in this article:
– Adonis 4.0
– Git
– MySQL
– Postman
– Node (A basic knowledge of Node and ES6/7)
You can go ahead and install Node, Git, and Postman. We will walk through installing Adnois and MySQL in the tutorial.
A REST (REpresentational State Transfer) web service is one way of providing communication between applications and services on the Internet. An API (Application Programming Interface) is a set of protocols or tools used in building an application. RESTful APIs allows the system to communicate via HTTP using HTTP verbs like GET, POST, PUT and DELETE. GET is used to retrieve resources, POST is used to store new resources, PUT is used to update existing resources and DELETE is deleting an existing resource. For this tutorial, our resources will be Books
.
Let’s create a new application. Open your terminal or command prompt and type the following command:
1# if you don't have Adonis CLI installed on your machine. 2 $ npm i -g @adonisjs/cli 3 4 # Create a new Adonis app 5 $ adonis new adonisjs-restful-api --api-only
Make sure you have Git installed for the
adonis
command to work
After that is done, navigate into application’s directory and start your server to test if all is working:
1$ cd adonisjs-restful-api 2 3 $ adonis serve --dev
Open Postman and make a request to http://127.0.0.1:3333
. You should see this sample JSON.
Before we start with the migrations, make sure you have your database set-up for this application and add the credentials to the .env
file in the project’s root directory.
1DB_CONNECTION=mysql 2 DB_HOST=127.0.0.1 3 DB_PORT=3306 4 DB_DATABASE=adonis-restful 5 DB_USERNAME=root 6 DB_PASSWORD=adonis
Let’s get started with our first migration and model – Book
. A book should have a Title, an ISBN, an Author, a Publisher and a Creation Date. We will be using the Adonis CLI to create the files and put them in the appropriate folder. To create the Book model, we can run:
1adonis make:model Book --migration
The --``migration
“ argument tells adonis CLI to create a migration file along with the model file.
Go to the database/migrations
directory and modify the migration file as follow:
1// <timestamp>_book_schema.js 2 3 'use strict' 4 5 const Schema = use('Schema') 6 7 class BookSchema extends Schema { 8 up () { 9 this.create('books', (table) => { 10 table.increments() 11 table.string('title').nullable() 12 table.string('isbn').nullable() 13 table.string('publisher_name').nullable() 14 table.string('author_name').nullable() 15 table.timestamps() 16 }) 17 } 18 19 down () { 20 this.drop('books') 21 } 22 } 23 24 module.exports = BookSchema
up()
runs when we migrate while down()
runs when we rollback.
Before you migrate the table, let’s install the MySql module:
1$ npm install --save mysql
Now, we can go ahead and migrate:
1$ adonis migration:run
Next, go to the model file called Book.js
in app/Models
and add references to the table and primary key:
1'use strict' 2 3 const Model = use('Model') 4 5 class Book extends Model { 6 static get table () { 7 return 'books' 8 } 9 10 static get primaryKey () { 11 return 'id' 12 } 13 } 14 15 module.exports = Book
Let’s work on the endpoints of our application: create, fetch the list, fetch a single resource, update and delete. In our start/routes.js
file, we will just do this:
1'use strict' 2 3 const Route = use('Route') 4 const Book = use('App/Models/Book') 5 6 Route.group(() => { 7 Route.post('books', async ({ request, response }) => { 8 const bookInfo = request.only(['title', 'isbn', 'publisher_name', 'author_name']) 9 10 const book = new Book() 11 book.title = bookInfo.title 12 book.isbn = bookInfo.isbn 13 book.publisher_name = bookInfo.publisher_name 14 book.author_name = bookInfo.author_name 15 16 await book.save() 17 18 return response.status(201).json(book) 19 }) 20 21 Route.get('books', async ({ response }) => { 22 let books = await Book.all() 23 24 return response.json(books) 25 }) 26 27 Route.get('books/:id', async ({ params, response }) => { 28 const book = await Book.find(params.id) 29 30 return response.json(book) 31 }) 32 33 Route.put('books/:id', async ({ params, request, response }) => { 34 const bookInfo = request.only(['title', 'isbn', 'publisher_name', 'author_name']) 35 36 const book = await Book.find(params.id) 37 book.title = bookInfo.title 38 book.isbn = bookInfo.isbn 39 book.publisher_name = bookInfo.publisher_name 40 book.author_name = bookInfo.author_name 41 42 await book.save() 43 44 return response.status(200).json(book) 45 }) 46 47 Route.delete('books/:id', async ({ params, response }) => { 48 const book = await Book.find(params.id) 49 if (!book) { 50 return response.status(404).json(null) 51 } 52 await book.delete() 53 54 return response.status(204).json(null) 55 }) 56 }).prefix('api/v1')
The routes created in the start/routes.js
will be prefixed with api/v1
. If you noticed from the code, we grouped all our routes because they share the prefix api/v1
.
When your application grows pretty large, you might want to move this entire logic to a different controller class. You can use the CLI tools to make controllers:
1adonis make:controller BookController
The CLI tool will ask you what the controller is for. Select Http Request
Check your app/Controllers/Http``/
directory and directory where you will discover a controller called BookController``.js
has been created.
Replace BookController.js
with the logic originally in your route file:
1'use strict' 2 3 const Book = use('App/Models/Book') 4 class BookController { 5 async index ({response}) { 6 let books = await Book.all() 7 8 return response.json(books) 9 } 10 11 async show ({params, response}) { 12 const book = await Book.find(params.id) 13 14 return response.json(book) 15 } 16 17 async store ({request, response}) { 18 const bookInfo = request.only(['title', 'isbn', 'publisher_name', 'author_name']) 19 20 const book = new Book() 21 book.title = bookInfo.title 22 book.isbn = bookInfo.isbn 23 book.publisher_name = bookInfo.publisher_name 24 book.author_name = bookInfo.author_name 25 26 await book.save() 27 28 return response.status(201).json(book) 29 } 30 31 async update ({params, request, response}) { 32 const bookInfo = request.only(['title', 'isbn', 'publisher_name', 'author_name']) 33 34 const book = await Book.find(params.id) 35 if (!book) { 36 return response.status(404).json({data: 'Resource not found'}) 37 } 38 book.title = bookInfo.title 39 book.isbn = bookInfo.isbn 40 book.publisher_name = bookInfo.publisher_name 41 book.author_name = bookInfo.author_name 42 43 await book.save() 44 45 return response.status(200).json(book) 46 } 47 48 async delete ({params, response}) { 49 const book = await Book.find(params.id) 50 if (!book) { 51 return response.status(404).json({data: 'Resource not found'}) 52 } 53 await book.delete() 54 55 return response.status(204).json(null) 56 } 57 } 58 59 module.exports = BookController
Then you can leave your route as thin as possible:
1const Route = use('Route') 2 3 Route.group(() => { 4 Route.post('books', 'BookController.store') 5 Route.get('books', 'BookController.index') 6 Route.get('books/:id', 'BookController.show') 7 Route.put('books/:id', 'BookController.update') 8 Route.delete('books/:id', 'BookController.delete') 9 }).prefix('api/v1')
Let’s head back to Postman again and start testing out these new routes and the controller functionalities:
store
method in BookController
is responsible for handling HTTP POST requests to api/v1/books
, and handles creation.index
method in BookController
is responsible for handling HTTP GET requests to api/v1/books
, and handles retrieving all the book resources available.show
method in BookController
is responsible for handling HTTP GET requests to api/v1/books
, and handles retrieving of a particular book resource.update
method in BookController
is responsible for handling HTTP PUT requests to api/v1/books
, and handles updating of a particular book resource. It will only update it if the resource is available, otherwise, it will return an HTTP 404 Not Found error.delete
method in BookController
is responsible for handling HTTP DELETE requests to api/v1/books
and handles deleting of a particular book resource. If the resource is available it will delete it and return an HTTP 204 No Content, otherwise, it will return an HTTP 404 Not Found.Please find the Github repository here.
Adonis is an awesome Node.js framework that brings joy and stability over anything else. I hope building this RESTful API opens your eyes to Adonis.
If you have any questions or observations, feel free to drop them in the comments section below. I would be happy to respond to you.