DEV Community

Danilo Miranda
Danilo Miranda

Posted on

Creating an API using AdonisJS (part 1)

Bulding an API with AdonisJS

Hi everyone! Over this series we will be creating an API using AdonisJS, a framework used to build NodeJS applications.

To be honest I don't how many posts this series will contain because I'll be building the API while writing this tutorial but I'll try to keep each post small and concise, so it will be easy to follow up and search for each topic related to Adonis

The building blocks first

Let's start by making sure that we have Adonis CLI installed:

Installing AdonisJS

In your terminal, run the following command:

npm install -g @adonisjs/cli
Enter fullscreen mode Exit fullscreen mode

You may need administrator privileges to install Adonis CLI.

Now, you're able to run the command adonis and it'll appear in your terminal all the commands available within Adonis CLI

Usage:
  command [arguments] [options]

Global Options:
  --env              Set NODE_ENV before running the commands
  --no-ansi          Disable colored output

Available Commands:
  addon              Create a new AdonisJs addon
  install            Install Adonisjs provider from npm/yarn and run post install instructions
  new                Create a new AdonisJs application
  repl               Start a new repl session
  serve              Start Http server
 key
  key:generate       Generate secret key for the app
 make
  make:command       Make a new ace command
  make:controller    Make a new HTTP or Websocket channel controller
  make:ehandler      Make a new global exception handler
  make:exception     Make a new exception
  make:hook          Make a new lucid model hook
  make:listener      Make a new event or redis listener
  make:middleware    Make a new HTTP or Ws Middleware
  make:migration     Create a new migration file
  make:model         Make a new lucid model
  make:provider      Make a new provider
  make:seed          Create a database seeder
  make:trait         Make a new lucid trait
  make:view          Make a view file
 route
  route:list         List all registered routes
 run
  run:instructions   Run instructions for a given module
Enter fullscreen mode Exit fullscreen mode

Creating a new application

With the CLI installed we'll be able to crete our applications. In this case, we will create a Restfull API. Let's start by running adonis' new command

adonis new -h
Enter fullscreen mode Exit fullscreen mode

With this, we'll be able to see all the options available when creating a new application:

Usage:
  new <name> [options]

Arguments:
  name                Name of the project directory

Options:
  --api-only          Scaffold project for api server
  --api               Scaffold project for api server
  --slim              Scaffold smallest possible Adonisjs application
  --blueprint [value] Path to github project blueprint
  --branch [value]    Specify git branch for project blueprint
  --skip-install      Do not install modules from npm
  --yarn              Use yarn over npm for modules installation
  --cnpm              Use cnpm over npm for installation
  --raw               Disable animations and colored output

About:
  Create a new AdonisJs application
Enter fullscreen mode Exit fullscreen mode

For this tutorial, as we'll be creating an API let's use the option --api-only.

adonis new schedulerapi --api-only
Enter fullscreen mode Exit fullscreen mode

After having the project created let's start our server. So first we change to our project directory, that was created by adonis

cd schedulerapi
Enter fullscreen mode Exit fullscreen mode

And start our server:

adonis serve --dev
Enter fullscreen mode Exit fullscreen mode

By passing the option --dev will enable nodemon, that's a library used to watch changes in your files and refresh the server with the new alterations.

Your command will return an address where your server is running, probably http://127.0.0.1:3333/. Open this in your browser and you'll se a JSON returned, as we are just creating an API and won't need any views

{"greeting":"Hello world in JSON"}
Enter fullscreen mode Exit fullscreen mode

Setting up our database

Let's make a quick configuration of the database we'll be using. For this project we'll use a relational database and in my case I'll use MySQL because it's the one I have installed in my machine, but feel free to use any relational database you want, like Postgres or sqlite3.

So head, to our file responsible for configuring the database in our project, that's located in the config folder. The file is database.js. Now open this file and you'll notice that it's exporting three configurations, for: MySQL, PostgreSQL, and Sqlite. Notice that above each configuration we have a block of comment telling which package we need to install, depending on the database you'll use.

As I'll be using MySQL, I just need to run:

npm i --save mysql
Enter fullscreen mode Exit fullscreen mode

If you're using PostgreSQL, then run:

npm i --save pg
Enter fullscreen mode Exit fullscreen mode

And if you're using sqlite:

npm i --save sqlite3
Enter fullscreen mode Exit fullscreen mode

Perfect! With your package installed, head to our file .env (in the root of our project) to set the variables to correctly connect to our database:

DB_CONNECTION = [mysql, sqlite, pg]
DB_HOST = YOUR_LOCALHOST_ADDRESS
DB_PORT = YOUR_PORT
DB_USER = YOUR_DB_USER
DB_PASSWORD = YOUR_DB_PASSWORD
DB_DATABASE= YOUR_DATABASE_NAME
Enter fullscreen mode Exit fullscreen mode

You'll only need to set these variables. Just make sure you create the database with a name according to the one you're setting the variable DB_DATABASE.

Save this file and now run the migrations using:

adonis migration:run
Enter fullscreen mode Exit fullscreen mode

Now if you head to your GUI, that you're using to manage your databases, in my case I use sequel pro you'll see that your database has new tables:

sqlpro-database

Creating our first controller

Now let's explore a bit about controllers by creating a user sign-up controller. By default, when you first create an Adonis application, as we are doing here, it'll come pre-configured with a user model, as most applications requires some kind of user interaction.

Now let's create a controller that will contain the bussiness logic to save a new user to our database. Head over to your terminal and type:

adonis make:controller User
Enter fullscreen mode Exit fullscreen mode

This will show up, asking to select a controller for HTTP requests or websocket channel:

> Select controller type (Use arrow keys)
  For HTTP requests
  For Websocket channel
Enter fullscreen mode Exit fullscreen mode

Let's select HTTP requests

✔ create  app/Controllers/Http/UserController.js
Enter fullscreen mode Exit fullscreen mode

If you succeed in creating the controller you'll see the message above. Now, inside your Controllers folder (located inside app folder) you'll see another folder called Http and finally inside it you'll have your UserController.js

Now, to make our controller work with our model, inside our controller file we need to import the model file, in our case, the user model. So, after 'use stric' let's require our model:

const User = use('App/Models/User')
Enter fullscreen mode Exit fullscreen mode

Now inside our class UserController let's start by adding our first method that will deal with saving the new user to our database. Let's call it store().

Inside our methods, we have access to a variable called ctx, that's a variable that contains the context of the request.

By using destructuring we'll work with the request of this context:

class UserController {
  async store ({ request }) { ... }
Enter fullscreen mode Exit fullscreen mode

Now, using request we are able to gather some data coming from this request by using the method only() from the request

class UserController {
  async store ({ request }) {
    // request data coming from the request
    const data = request.only()
    }
Enter fullscreen mode Exit fullscreen mode

By using the request.only() we are able to tell what fields of data we want to get from the request.

First, let's see what data the user is supposed to send. Let's go our user migration data, located in database folder:

|-- factory.js
`-- migrations
    |-- 1503250034279_user.js
    `-- 1503250034280_token.js
Enter fullscreen mode Exit fullscreen mode

The file we are looking for is 1503250034279_user.js. Open the file and you'll see the following:

{ ...some code ... }
class UserSchema extends Schema {
  up () {
    this.create('users', (table) => {
      table.increments()
      table.string('username', 80).notNullable().unique()
      table.string('email', 254).notNullable().unique()
      table.string('password', 60).notNullable()
      table.timestamps()
    })
  }
{ ...more code... }
Enter fullscreen mode Exit fullscreen mode

So the user will send me an username, an email and a password. Now let's get back to our UserController.js file.

As a parameter for request.only() we will pass an array of the values we want to get in the request, in our case, username, email and password

class UserController {
  async store ({ request }) {
    // request data coming from the request
    const data = request.only(['username', 'email', 'password'])
    }
Enter fullscreen mode Exit fullscreen mode

Before creating a new user, let's check in our database if the username used to create the new account has already been used:

// looking for user in database
const userExists = await User.findBy('email', data.email)
Enter fullscreen mode Exit fullscreen mode

If you look in Adonis documentation you'll see you have a method called findByOrFail() but I'll use only findBy() because I want to send a message in case the user already exists.

// if user exists don't save
      if (userExists) {
        return response
          .status(400)
          .send({ message: { error: 'User already registered' } })
      }
Enter fullscreen mode Exit fullscreen mode

And finally, if the user is not registered we save him in our database:

// if user doesn't exist, proceeds with saving him in DB
      const user = await User.create(data)
Enter fullscreen mode Exit fullscreen mode

Your user controller is probably like this now:

class UserController {
  // creating and saving a new user (sign-up)
  async store ({ request, response }) {
    try {
      // getting data passed within the request
      const data = request.only(['username', 'email', 'password'])

      // looking for user in database
      const userExists = await User.findBy('email', data.email)

      // if user exists don't save
      if (userExists) {
        return response
          .status(400)
          .send({ message: { error: 'User already registered' } })
      }

      // if user doesn't exist, proceeds with saving him in DB
      const user = await User.create(data)

      return user
    } catch (err) {
      return response
        .status(err.status)
        .send(err)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

I just forgot to mentio before that I wrapped the main code in a try block and in case any unexpected error occur we capture it inside the catch (err) block and returna response showing the error.

Routing

Now that we created our first controller it's time to test it and for this we'll learn how to work with routes in Adonis

When you created the app you have a file called routes.js that it is located in the start folder in the root of your project

Open routes.js and clean all the code inside.

To work with routing in Adonis we'll need to import the Route from Adonis using the use() functi

'use strict'

const Route = use('Route')
Enter fullscreen mode Exit fullscreen mode

To create a user we must send a POST request. When we access http://127.0.0.1/3333/users we'll send this request:

Route.post('users', 'UserController.store')
Enter fullscreen mode Exit fullscreen mode

The first parameter of post() is the url that will trigger the controller and the second parameter is the controller itself

For the url you can use users or /users. Both options will work

To access a certain method of a controller you just need to type the name of the controller, in our case is UserController and the name method's name: UserController.store

To make the requests in our application I'll use Insomnia REST client. You can download it here: https://insomnia.rest/download/

When you firts open Insomnia you'll see something like this:

insomnia-opened

Let's start by creating a new workspace, so we can create and store our configured requests:

Just click in the down arrow next to "Insomnia".

insomnia-new-workspace

Now select "New Workspace" in the dropdown menu

new-workspace

workspace-name

I'll call my workspace as "schedulerAPI"

Now click on the "plus simbol" that's located just below the "cookies":

new-folder

I'll create a folder called 'User' and create a new post request called 'store'

To change the request's type click in GET and select POST in the dropdown menu

change-request

Now right below POST you'll see a dropdown menu called "Body", click it and select JSON.

As we saw before, we need to send in our request the username, email, and password.

{
    "username": "Danilo",
    "email": "me@danmiranda.io",
    "password": "123456"
}
Enter fullscreen mode Exit fullscreen mode

Now, in Insomnia let's tell to which address we need to send the request.

request-address

To make our life easier, so we don't need to type in every new request we create the full address http://127.0.0.1:3333, let's set a environment variable that we can use inside Insomnia:

manage-environment

Let's call it base_url and put our root address:

{
  "base_url": "http://127.0.0.1:3333"
}
Enter fullscreen mode Exit fullscreen mode

Just click Done to save

Now in our address bar just type base_url and /users, like this

request-address-done

Now click Send to send our request

You'll see in the right side of Insomnia that our request returned the created user just like we told it to do:

request-succes

Let's test our already created user. If I send the request again, with the same email it'll return an error:

request-error

That's it for now, let's continue after

So to review everything we learned:

  • Install AdonisJS
  • Create a new aplication with Adonis
  • Configure database to properly work with our Adonis application
  • Create our first controller
  • Create our first route
  • Send a request to our API with Insomnia

This post was originally posted in https://danmiranda.io. Make sure to check other posts there.

Follow my on twitter: https://twitter.com/Nilomiranda

See you in the second part!

Top comments (7)

Collapse
 
minamotoo profile image
Minamotoo • Edited

hello, i got a problem when i try to run npm install or npm install --save mysql(i use mysql as my db) after i created my project and set things in my .env file, and whatever on the internet cant help me. because i am very new to this so i cant tell how to describe my issue but the error said that


found 6 vulnerabilities (1 low, 1 moderate, 4 high)
run npm audit fix to fix them, or npm audit for details
PS D:\Works\Adonis-Project\test-controller> adonis migration:run
adonis : File D:\Users\4416\AppData\Roaming\npm\adonis.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see
about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1

  • adonis migration:run + ~~~~~~
    • CategoryInfo : SecurityError: (:) [], PSSecurityException
    • FullyQualifiedErrorId : UnauthorizedAccess

actually i 've run "audit fix --force" too but it's nothing, i've added my error picture here too, please check and help me it will be a great appreciation. thank you. link here >> thepracticaldev.s3.amazonaws.com/i...

edited: after i got this issue, i tried to install the older nodejs version (v 8.16.2) because it's the only way i know how to fix but it didnt work out. sorry for my bad english but really hope you can help.

Collapse
 
matheusvsmpimentel profile image
MatheusVSMPimentel

Minamotoo Do you have using a CMD or PowerShell to texting the command Adonis migration:run? Try to use the CMD from windows. This issue happen because security params in power shell. See this post on stackoverflow: superuser.com/questions/106360/how...

Collapse
 
badasscoder profile image
Badasscoder

Set-ExecutionPolicy Unrestricted -Scope Process

run this command before npm

Collapse
 
lexiebkm profile image
Alexander B.K.

In my current project I use PHP+Laravel for backend. However, since Node.js has been mentioned in massive discussion everywhere, I spent my time to read about Node.js and began to do research on its frameworks : Express, Koa, Sails, HAPI, etc. When I came to Adonis.js I was excited, since it was very similar to Laravel, including its documentation. So learning it and trying it was straightforward for me.
Nevertheless, I hold on my desire to use it in a real project, because I am still waiting for its wide acceptance by community/developer. If there are sufficient experienced developers that use it in real projects, I can start to use it. For this reason, I have been suspending my learning of it, until I feel quite sure I wouldn't waste my time to learn it.
Now, seeing that you have used it in a project, it seems that you can you persuade me to use it.
At least now, I can resume my learning of it, because for me, Adonis.js is nice.

Collapse
 
syntaxlexx profile image
/SyntaxLexx

... and just like that I ditched PostMan! This is a very well-written piece!

Collapse
 
mahdipishguy profile image
Mahdi Pishguy

thanks a lot

Collapse
 
badasscoder profile image
Badasscoder

how to use imported routes.
user-routes.ts
admin-routes.ts

routes.ts -- default
import user, admin ................... routes to routes.ts

but how to use them in routes group in routes.ts file