Build a Restful API with AdonisJS

Written by Full-Stack Developer

December 21, 2023
Build a Restful API with AdonisJS

APIs provide smooth communication between web applications, including software systems and services. Many third-party applications provide APIs to enable businesses to utilize their functionality and data. For example, payment gateways like Stripe offer APIs that developers can use to integrate into their web applications for processing payments.

Web applications utilize REST APIs to exchange data using HTTP requests. These requests encompass a range of parameters, including but not limited to GET, POST, PUT, and DELETE. By employing these methods, software and services can seamlessly access, transmit, and modify data stored in databases, facilitating resource sharing and interaction.

There are many cases where you may need web APIs, and there are several frameworks available for developers to create one; they include Spring Boot (JAVA), Express, Laravel (PHP), Django REST, ADONIS (JS), etc. These frameworks come with features that make it easy for developers to create web APIs. Here's an article on APIs that will help you understand them better.

Our focus in this article will be AdonisJS, but before that, we'll discuss RESTful APIs and why we need them, followed by a walkthrough of how to build one.

Let’s get started!

What is Restful API?


Restful API, deduced from REST (Representational State Transfer) - an architectural style or communication standard between computers over the Internet, is an application program that follows REST architecture.

Let's assume we have an application that is designed to showcase a comprehensive list of all the students enrolled in a school. When you search for students on this app, it retrieves the information from a specific source. Additionally, when you input the details of new students who have recently registered at your school, this information is also sent to the same source.

How do we send and retrieve data from this storage location? This process is possible with the use of an API. A Restful API is the middleman between the application and the server, where all the information is stored. It delivers data from the application to the server and from the server to the application.

What is AdonisJS?


AdonisJS is a backend framework inspired by Laravel used to create data-driven web applications. Essentially, AdonisJS is a framework for building RESTful APIs, designed to simplify the development of adaptable web applications.

Aside from creating RESTful APIs, AdonisJS can be used for many other web development tasks, such as database integration and server-side rendering. AdonisJS, which is built on the Node.js web framework, features an MVC (Model View Controller) architecture that allows developers to work on different aspects of an application. An MVC model separates an application into:

  • Model: Represents logic that interacts with the database.
  • View: Handles rendering of data to the user.
  • Controller: Manages the transfer of data between the model and view.

This separation of concerns makes it easier to organize and maintain code.

Reasons for Building a Restful API with AdonisJS


There are numerous advantages to building a RESTful API with AdonisJS due to its robust features and ease of use. Here are a few features of AdonisJS;

  • Model View Controller architecture
  • HTTP layer that supports routing, controllers, middleware, secure cookies, etc.
  • Template engine for creating traditional-style server-rendered apps.
  • First-class support for SQL databases
  • Support for managing file uploads
  • Schema validator to validate forms
  • Built-in support for testing
  • CORS (cross-origin resource sharing) support
  • Authentication layer for user authentication, session management, API tokens, etc

Why would you use AdonisJS to build a Restful API?

Using AdonisJS to build a RESTful API provides a powerful and structured framework that simplifies development. It is an excellent choice for creating scalable and maintainable APIs, among other reasons that include:

  • It’s Developer-friendly with a comprehensive set of features
  • AdonisJS uses modern JavaScript features.
  • AdonisJS is customizable, meaning developers can tailor the framework to project requirements and preferences.
  • AdonisJS has an abundant collection of first-party packages that are useful to speed up the development process.
  • AdonisJS has a growing and active community of developers and extensive documentation.

Let’s Build a RESTful API with AdonisJS


To build a RESTful API using AdonisJS, you need certain tools and components;

  • Text Editor or IDE: A text editor is required for writing AdonisJS code; therefore, you have to choose one. Popular options are VS Code and Sublime Text.

  • NodeJS and NPM (Node Package Manager): AdonisJS is built on NodeJS; therefore, you need to download and install them on your development machine.

  • AdonisJS Framework: Install Adonis global using the following command npm init adonis-ts-app@latest project-name

  • DatatBase: Choose a database from the following databases AdonisJS supports; MySQL, MongoDB, PostgreSQL, and SQLite.

SIMPLE CRUD OPERATION


In our example, we’ll be creating a simple CRUD operation. A CRUD operation is simply an acronym for Create, Read, Update, and Delete, for creating and managing persistent data within a database.

STEP 1: Install AdonisJS.

You can create your project using:

  • node package manager(npm)- npm init adonis-ts-app@latest project-name
  • yarn - yarn create adonis-ts-app hello-world
  • pnpm - pnpm create adonis-ts-app hello-world

In our example, we’ll be working with NPM (node package manager) - [Images Provided Serve as Guide]

npm init adonis-ts-app@latest project-name

Once the installation is complete, you need to select the API project structure.

select the API project structure

After you select the API structure, you’ll be given the option to select project name, setup ESLint and Prettier. Once your project setup is done, cd into your project file and run the node ace serve –watch to run your application. [See Image below]

node ace serve –watch

In our example below (see image), we opened our project file using VScode and then used the node ace serve –watch command to run our application. Also, you’ll see that it says starting http server, including the server address. This means our setup is complete an we’ve successfully installed AdonisJs.

AdonisJs

Once the server is up, this is what the API preview page would look like.

API preview page

STEP 2: Create Controller

When dealing with APIs, the pivotal focus lies on defining routes. Within the routes file, we execute various database queries such as GET, POST, DELETE, and more. This file, typically named 'routes.ts,' also contains code responsible for formatting data in the JSON format visible on web browsers, and it's where we define the routes for our API.

In AdonisJS, the logic of route handling is managed by the controller.

route handling

In the image above, we can see that the route receives the HTTP method "GET”. We can define different types of methods. e.g., POST, PATCH, DELETE. There’s also a shorthand method called "Resource,” which can define the methods without us specifying.

Controllers handle the logic of routes handling; they help us handle HTTP requests.

Let’s create a controller for our user model;

Type node ace make: controller User in your terminal and hit the Enter key. This command would create the User controller.

command to create the User controller

To access the UsersController file, you can follow the link directly in your terminal or go to app/Controllers folder.

At the top of UsersContoller file, you'll find a comment with the code snippet: import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' , uncomment this section.

UsersContoller file

HTTPContextContract is used to type-check and access properties and methods related to the HTTP context. We'll explore this further in this article.

Next, let's add a method to the controller.

To add methods, we’ll create two routes in our routes.ts file and register our method in the UsersController.ts file.

Note: Route is a routing system responsible for mapping HTTP requests to specific actions.

To create our routes and register our UserController, type in the following:

Route.get(‘/user’, ‘UsersController.index”)

Route.post(‘/user’, ‘UsersController.store”)

create routes and register UserController

In this example, we’re providing Adonis with the link ‘/users’ and telling it to go to UsersController to access it.

Code Breakdown

  • Get: HTTP method that handles GET requests.
  • Post: HTTP method that handles POST requests.
  • /users: The URL path
  • . index: is the action or method within the UsersController that handles the request when the route is accessed.
  • . store: is the action or method within the UsersController that is used for creating or storing new resources in a database.

Let’s check the summary of the routes we defined using node ace list:routes. In the image below, we can see the method, routes, and handler.

node ace listroutes

Resource Method

When working with CRUD, we would have to define different HTTP methods. We can use a shorthand called route.resources to automate this task. in other words, instead of explicitly defining our HTTP methods by doing the following:

Route.get (“/users”, ‘UsersContoller.index’)

Route.Post(“/users”, ‘UsersController.store’)

We use Route.resource(‘/users’, ‘UsersController’) to define our RESTful resource routes.

RESTful resource routes

Quick Test

In the UsersController below, we’ve added the context object to the different methods we created.

import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
		
export default class UsersController {
  public async index(ctx: HttpContextContract) {
    return 'GET users'
  }


  public async store({ request }: HttpContextContract) {
    return {
      message: 'POST users',
      body: request.body(),
    }
  }


  public async show({ params }: HttpContextContract) {
    return 'GET users:' + params.id
  }


  public async update({ params }: HttpContextContract) {
    return 'PUT users:' + params.id
  }
  public async destroy({ params }: HttpContextContract) {
    return 'DELETE users:' + params.id
  }
}

For example, in the store method, we added the request object from HttpContextContract which tells us of an incoming HTTP request and also contains information about the request, including the request body. The request body carries the data we want to send to the server. We’ll use the code example to carry out a simple test to see if our request work. Let’s check to see that it works.

In the VS code extension, search for Thunder Client Extension and install it. Thunder client is like Postman in VSCode.

Thunder Client Extension

In thunder client extension, copy your local address or localhost and add the ‘users’ endpoint like so; (http://127.0.0.1:53571/users) , and hit send. If you receive a response like in the image above, then it works.

STEP 3: Create a Database

We have to set up our database to store and manage our data. We can achieve this by installing Lucid (Lucid is an object-relational mapper for managing and interacting with databases).

npm install @adonisjs/lucid.

After the installation is complete, configure using the following command;

node ace configure @adonisjs/lucid

This will display the database driver to select. In this example, we’ll be using SQLite, this action will install all the necessary packages.

node ace configure @adonisjslucid

After we select SQLite and the configuration is complete, we’ll see a file called tmp which will contain our SQLite file.

tmp file

STEP 4: Create Migration

We have created our database connection; now we need to create some tables. This can be achieved with Lucid using migration.

  • Let’s create a migration file for our users model. Type the following command;

node ace make:migration users

  • After running the command, check the database/migrations folder; you’ll see a new file.
database migrations folder

In the file xxxxx_users.ts as shown In the image above, we have two methods, up and down. This is where we structure our database. UP method is where we describe things we want to change or actions to be taken when running the migration, e.g. This method creates a new database table. The down method is the opposite; it describes the actions to be taken when reverting migration.

As can be seen, the table tableName = 'users' contains three columns automatically (id, timestamp created, timestamp updated). This file allows us to create a new column; let's create a username column by typing table.string('username') into the file.

tableName = 'users'
  • Now, we have to run migration to apply this change to our database table using the command node ace migration:run
node ace migration run

STEP 5: Create Model that Represents Table

Our model represents the logic of our CRUD operation. To create a model, type the following command - node ace make:model User

create a model

When you run the command node ace make:model User, you’ll see that a User.ts file has been generated.

User.ts file

At the top of our file we’ll see the following code snippets,

import { DateTime} from 'luxon'

Luxon is a data library that is used to represent date and time operations.

import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'

This imports the BaseModel class and column decorator from Lucid ORM. BaseModel creates model classes, while the column defines the model properties.

In the image above, we can see that the file already has certain properties defined.
We have one that represents the ID to identify records in the database and another for the timestamp.

In the migration (database/migration/users.ts) file we created, we added a new column called username, we have to represent that column in our Model/User.ts file. In other words, our model must correspond to our database table.

  • Let’s assume we want to create a username column - table.string(‘username’) in migrations/xxxxx_users.ts file

  • We have to represent this column in the Model/User.ts file like so;

@column()

public username: string

(see image below)

public username string

Now that we have created our database table and model, let’s add the following code within our UsersController to start implementing CRUD.

  • Import user model in UsersController

import User from 'App/Models/User'

  • Update the following code in your UserControllers class.
export default class UsersController {
  public async index(ctx: HttpContextContract) {
    return User.all()
  }


  public async store({ request, response }: HttpContextContract) {
    response.status(201)


    const body = request.body()
    const user = await User.create(body)


    response.status(201)
    return user
  }


  public async show({ params }: HttpContextContract) {
    User.findOrFail(params.id)
    //query by ID
  }


  public async update({ params, request }: HttpContextContract) {
    //get request body from request object


    const body = request.body()
    const user = await User.findOrFail(params.id)


    user.username = body.username


    user.save()
    //save in database


    return 'PUT users:' + params.id
  }
  public async destroy({ params }: HttpContextContract) {
   const user = await User.findOrFail(params.id)
    response.status(204)
    user.delete()


  }

Code Breakdown:

Index Method

public async index(ctx: HttpContextContract) {
    return User.all()
  }

Handles GET request to retrieve all users from the database using the method User.all()

Store Method

 public async store({ request, response }: HttpContextContract) {
    response.status(201)


    const body = request.body()
    const user = await User.create(body)


    response.status(201)
    return user
  }

Handles POST request to create a new user; it also receives the request body using request.body(), and uses User.create() to create a new user record in the database. Also, we set the HTTP response to 201, which indicates the request has succeeded.

Show Method

public async show({ params }: HttpContextContract) {
    User.findOrFail(params.id)
    //query by ID
  }

This handles requests to retrieve users by their specific ID. User.findOrFail(param.id) is used to query the database for a user with the ID.

Update Method

  public async update({ params, request }: HttpContextContract) {
    //get request body from request object


    const body = request.body()
    const user = await User.findOrFail(params.id)


    user.username = body.username


    user.save()
    //save in database


    return 'PUT users:' + params.id
  }

Handles PUT requests to update a specific user by their ID. It retrieves the request body using request.body() and uses user.findOrFail (param.id) to find the user we want to update. Also, it saves the updated user in our database using user.save()

Destroy Method

 public async destroy({ params }: HttpContextContract) {
   const user = await User.findOrFail(params.id)
    response.status(204)
    user.delete()


  }

Handles DELETE requests to delete specific users by their ID, using user.delete() method. We also set the HTTP response status to 204 which indicates that we’ve successfully deleted the user from our database.

Quick Test

If we do a GET request e.g. http//127.0.0.1:53571/users, then we will get an empty array, which tells us that we are selecting all users from our database. The Array will be empty since we haven't created any users.

Let’s add users to our database using the POST method. Within the body, In the JSON section, create “username”: “add name here” and send a post request. Do this more than once with different names, and when you send the request, you’ll receive a response as shown in the image below.

send a post request
  • Let’s confirm that it worked by doing a GET request. If we change the method to GET we should receive all the usernames we created.
GET request
Command to return the second username

This example shows you how to create a simple CRUD operation using RESTful API

20%

💸EXTRA 20% OFF ALL VERPEX CLOUD WEB HOSTING PLANS

with the discount code

AWESOME

Save Now

Summary


RESTful APIs involve creating interfaces that follow the REST architecture for software to communicate with each other. AdonisJS streamlines the process of crafting APIs for applications, handling both routine and intricate tasks efficiently.

Constructing a RESTful API with AdonisJS entails a series of well-defined steps, as we've outlined. With a clear understanding of the role of RESTful APIs and how to build a RESTful API using the backend framework AdonisJS, developers can take advantage of the features and tools of AdonisJS to create a functional and well-structured API.

Frequently Asked Questions

Can I easily integrate third-party tools and APIs with Verpex Web Development Hosting?

Yes, Verpex Web Development Hosting allows for easy integration of third-party tools and APIs, enabling you to streamline your workflow and enhance your project's functionality.

Are there any specific considerations for integrating third-party APIs with a WooCommerce theme?

Considerations include authentication, data handling, and security measures when integrating third-party APIs.

Can I integrate advanced features like AJAX or REST API in a WordPress theme?

Yes, you can integrate AJAX for dynamic content loading and use the REST API for data retrieval by adding appropriate JavaScript and server-side code.

Is it necessary to implement CSRF protection for APIs, and if so, how does it differ from standard web applications?

Yes, it is necessary. API protection often involves token-based authentication and validation, similar to web applications.

Jivo Live Chat