Getting Started with Adonis JS - Creating a REST API using PostgreSQL

Ronaldo B.July 27, 202210 min read

In this article

Adonis JS is a very powerful JavaScript framework, quite similar to Laravel, which is focused on the PHP language. It is also based on the MVC concept.

In this article, we will learn how we can use it to create a courses API, using a PostgreSQL Database as the data source.

Installing Adonis JS

The official Adonis website is adonisjs.com. To start a project with this framework, we first need to install its command line interface (CLI). To do this, we will run the following command in the Terminal:

npm install -g @adonisjs/cli

The npm will use our internet to download Adonis globally on our machine.

Starting the project with Adonis

To start a new project with Adonis, we will run the command adonis new, like this:

adonis new hcode-api-cursos --api-only

With this command, we will define a name for the project and specify that it will only be an API. Thus, Adonis will only bring in the necessary files and resources to create an API. Some folders that are common in projects where Adonis renders HTML, for example, will not be included, making our project lighter and faster.

After executing this command, a success message will be returned in our Terminal:

Project started message in Adonis

The folder structure of our project is as follows:

Folder structure of an Adonis project

Creating the first route

Now, let's create our first route! To do this, we will access the file start/routes.js. Initially, we will see the following structure:

Initial structure of our routes

We will replace it with a route called “/routes”. It will have the following structure:

Route.get('/courses', () => {
	return { message: 'Hcode Trainings Courses!' }
});

To start our Adonis server, we will execute adonis serve --dev in the Terminal. The following message will be returned:

Server started message in our Terminal

Now, if we access http://127.0.0.1:3333/courses in our browser, we will see the following result:

Success message on our first route

Excellent, our first route was a success!

Connecting Adonis to Postgres

Let's start the configuration to connect Adonis to the Database. In this article, we will use the PostgreSQL Database. To manipulate it, we will first install its module in our project through npm:

npm install pg

The next step is to create a new Database in Postgres. We will call it hcode-adonis.

After that, we will add our Database credentials. We can do this in the file config/database.js. However, Adonis already offers us the .env file, which has the environment variables of our project. Thus, we will add the credentials in this file. We will edit only the Database information. It will look like this:

DB_CONNECTION=pg
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=root
DB_DATABASE=hcode-adonis

Creating Database tables

We will now create our Database tables. However, we will do this without accessing Postgres, but through Adonis. You might ask, “How so? How will we create tables without going to the Database?” We will do this using Adonis migrations. They are essentially a class that defines the creation of Database tables through code. This has become a trend in recent years.

To create a migration, we will use the following command:

adonis make:migration courses

With this command, we are stating that our migration will be called courses. Adonis will ask if we want to create a table or select a table with this migration. We will select the first option:

Adonis command to create a migration

After creating the migration, the following message will be returned:

Success message for creating a migration

Now let's define the creation of our courses table by accessing the file courses_schema.js in the folder database/migrations. The class in this file will have two main methods: 1 - up(), which defines what happens when the migration is executed, i.e., when the table is created; and 2 - down(), which defines what happens when we perform a rollback on our migration. Generally, we define the deletion of the table in this method. Note that we can undo the creation of a table if we wish or if an error occurs. Thus, we can say that migrations are like a “version control for our Database.”

Returning to the up() method, we will define the creation of four columns in our table: “name”, “description”, “url” and “price”. To do this, we will use the following code:

up () {
	this.create('courses', (table) => {
        table.increments()
        table.string('name', 64).notNullable()
        table.string('description', 128).notNullable()
        table.string('url', 64).notNullable()
        table.decimal('price')
        table.timestamps()
    )
}

With our migration ready, let's run it. To do this, we will use the command adonis migration:run. We will see the following success message in our Terminal:

Success message for executing the migration

Now our table will already be created in our Postgres. Quite simple, don't you think?

Creating the first course

To register our first course, we will need to create a Controller and a Model. Adonis JS is based on the MVC architecture. Therefore, these classes are necessary for the functioning of our API.

To create our Controller, we will execute the following command:

adonis make:controller Course

Note that our Controller will be created in the singular. When we execute this command, Adonis will ask us if we want the Controller to be used for HTTP requests or as part of a Websocket channel. We will select the first option:

Command to create a new Controller in Adonis

After executing this command, we will see the following success message:

Success message for creating our Controller

Next, we will create our Model, which will be accessed by the Controller. To do this, we will execute the command adonis make:model Course. We will see the following success message in our Terminal:

Success message for creating our Model

Let's edit the file app/Controllers/Http/CourseController.js. The first thing we will do is to request the courses Model. We will do this with the following code:

const Course = use('App/Models/Course');

Now we are ready to connect our Controller to the Model to create a course. The first method we will create will be called store(), and will have the following structure:

async store({ request }) {

	const dataToCreate = request.only(['name', 'description', 'url', 'price']);

	return await Course.create(dataToCreate);

}

Explaining: This method defines which fields Adonis will manipulate to create a course. We do this with the method request.only(). This method prevents improper fields from being provided to our Model.

Next, we perform the return of the call to the create() method, which is already defined in the Course Model class by default. This method returns a promise. Thus, we use the await feature to wait for this promise to be resolved. And, since we used await, our store() method needs to be converted into an asynchronous function, using the reserved word async. This will apply to other methods in this class as well.

The logic for creating our course is ready. What we need now is to define the call to the store() method. We will do this in our routes file, adding a second route, now with the POST method, like this:

Route.post('/courses', 'CourseController.store');

Notice that it's quite simple to relate a route to a Controller. We just need to inform the name of the Controller along with the name of the method.

To test if the route is working, we will use Insomnia. We have already taught how to install this REST Client in the article Using Insomnia to test our API requests.

We will select the POST route test, providing the data we want to register:

Providing the data to be registered

Upon sending this route from our API, we see the following result:

Result of the API call

Very good! Our first course was created successfully, without errors ?

Listing all courses

To list the courses through our API, we will again edit our Controller file. We will create a new method called list(). It will have the following code:

async list() {
	return await Course.all();
}

Just reminding that the name of the Controller method can be chosen by us. We can give it any name we want. However, the name of the method that we call from the Model, in the case of the above code, the all() method, is standard in Adonis and we must follow this standard.

We will relate this new method to the GET URL we created at the beginning of the article to test if Adonis was functioning. This route will look like this now:

Route.get('/courses', 'CourseController.list');

And with that, our listing route will be working :)

Now, when accessing http://127.0.0.1:3333/courses, we will see the following result:

List of all courses in Insomnia

Listing the data of a specific course

For the route to select only one course, we will create a method called show(). It will have the following code:

async show({ params }) {
	return await Course.find(params.id);
}

In this method, we used the parameters variable passed by the request. Based on this information, we can know which user id we need to display on the screen.

We will call this method in our routes file like this:

Route.get('/courses/:id', 'CourseController.show');

Note that in this case we used the expression “:id” in the route. This variable is responsible for selecting the ID provided in our API route and informing its value to our Controller.

When accessing http://127.0.0.1:3333/courses/1, we will see the information of only one specific course.

Editing a Course

To edit a course, we will create a method called update() in our Controller. It will combine what we have seen in listing users and also sending data. Its code will be as follows:

async update({ params, request }) {

	const course = await Course.findOrFail(params.id);

	const dataToUpdate = request.only(['name', 'description', 'url', 'price']);

	course.merge(dataToUpdate);

	await course.save();

	return course;

}

Explaining: We select the user data we want to edit through the id provided in the parameters, but we use the findOrFail() method instead of just find(). Why? We did this because the findOrFail() method will return an error if it cannot find the course, unlike the find() method, which only does not return a body in the request.

We also make use of the merge() method to merge the information sent to the API with the already existing information of the course. Finally, we save the changes and return the new data of the course to whoever called the API.

Let's make the call to this method in our routes file using the PUT method:

Route.put('/courses/:id', 'CourseController.update');

To test, we will change the name and url fields of the course. The form in Insomnia will look like the image below:

Edit form in Insomnia

After sending the data to the API, we will see the following result:

Success message when updating a course

Excellent, our course was successfully updated

Deleting a Course

Our final operation will be to delete a course. To do this, we will create the method delete(), which will have the following content:

async delete({ params }) {

	const course = await Course.findOrFail(params.id);

	await course.delete();

	return {
		message: 'Course deleted!'
	}

}

Note that the return of the method will be just a message indicating the course was successfully deleted.

Our routes file will look like this:

Route.delete('/courses/:id', 'CourseController.delete');

Now, when accessing the URL http://127.0.0.1:3333/courses/1 with the DELETE method, we will see the following result:

Success message when deleting a course

Our course was deleted ?

Conclusion

You can find the code developed in this article in our GitHub.

If you wish, you can also check this same content in video format on our YouTube channel.

In this article, we learned how to create an API using Adonis JS. Notice that we used PostgreSQL as the Database, but we hardly needed to access it. It was only necessary to create the Database, and Adonis took care of the rest.

This framework is indeed an excellent tool for developing fast and robust Web applications, greatly facilitating our work as developers. If you wish to enhance your knowledge of the JavaScript language, be sure to take our Complete JavaScript Course.

Until the next article :)

}

Keep learning with Hcode

Sign up for the course platform and follow the next blog guides.