Adonis.js Creating a RESTFUL API Solution

I recently embarked on a new project using the adonisjs framework. I had the option to utilize expressjs, but I was drawn to adonisjs due to its structured nature, especially reminiscent of Laravel.

My current challenge revolves around building a RESTful API and understanding the underlying concepts of routing, middleware, and utilizing an apiController (a custom controller designed to manage all api requests).

Here is a snippet of what I have implemented so far:

routes.js

Route.post('api/v1/login', 'ApiController.login')
Route.post('api/v1/register', 'ApiController.register')

// API Routes
Route.group('api', function() {

  Route.get('users', 'ApiController.getUsers')

}).prefix('/api/v1').middlewares(['auth:api'])

ApiController.js

'use strict'

const User = use('App/Model/User')
const Validator = use('Validator')

const FAIL = 0
const SUCCESS = 1

class ApiController {

  * login (request, response) {

    let jsonResponse = {}

    const email = request.input('email')
    const password = request.input('password')

    // validate form input
    const rules = {
      email: 'required|email',
      password: 'required'
    }

    const messages = {
      'email.required': 'Email field is required.',
      'password.required': 'Password field is required.'
    }

    const validation = yield Validator.validateAll(request.all(), rules, messages)

    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      try {

        yield request.auth.attempt(email, password)

        const user = yield User.findBy('email', email)

        const token = yield request.auth.generate(user)

        jsonResponse.status = SUCCESS
        jsonResponse.response = {}
        jsonResponse.response.message = "Logged In Successfully"
        jsonResponse.response.user = user
        jsonResponse.response.token = token

      } catch (e) {

        jsonResponse.status = FAIL
        jsonResponse.response = {}
        jsonResponse.response.message = e.message

      }

    }

    return response.json(jsonResponse)

  }

}

module.exports = ApiController

config/auth.js

'use strict'

const Config = use('Config')

module.exports = {

  /*
  |--------------------------------------------------------------------------
  | Authenticator
  |--------------------------------------------------------------------------
  |
  | Authenticator is a combination of HTTP Authentication scheme and the
  | serializer to be used for retrieving users. Below is the default
  | authenticator to be used for every request.
  |
  | Available Schemes - basic, session, jwt, api
  | Available Serializers - Lucid, Database
  |
  */
  authenticator: 'session',

  // Additional configurations...

config/shield.js

// Shield configurations...

Encountering an issue when hitting the login web service via Postman where the user is validated, but an exception occurs at

const token = request.auth.generate(user)
with the error message
request.auth.generate is not a function
.

If anyone has encountered this before or can provide insights, your help would be greatly appreciated.

Thank you!

Answer №1

To access the login service, a JWT token needs to be generated when the user calls the login API. This token should be sent back to the requesting app in the "Authorization" header with the value "Bearer [JWT Token String]". When the app sends another request for business categories (including the JWT token), the request will be validated by the API middleware. After validation, the data will be served and returned in JSON format.

Check out an example of what your header looks like:

https://i.sstatic.net/ZH5HP.png

Here's how you can set up your code:

// ROUTES.JS

// API Routes
Route.post('/api/v1/register', 'ApiController.register')
Route.post('/api/v1/login', 'ApiController.login')

Route.group('api', function() {

  Route.get('/business_categories', 'ApiController.business_categories')

}).prefix('/api/v1').middlewares(['api'])

// API.JS (Middleware)

'use strict'

class Api {

  * handle (request, response, next) {

    // Enter your middleware logic here
    const authenticator = request.auth.authenticator('jwt')
    const isLoggedIn = yield authenticator.check()

    if (!isLoggedIn) {
      return response.json({
        status: 0,
        response: {
          message: 'API Authentication Failed.'
        }
      })
    }

    // Pass the request to the next middleware or controller using yield next
    yield next

  }

}

module.exports = Api

// APICONTROLLER.JS

'use strict'

// Dependencies
const Env = use('Env')
const Validator = use('Validator')
const Config = use('Config')
const Database = use('Database')
const Helpers = use('Helpers')
const RandomString = use('randomstring')
const Email = use('emailjs')
const View = use('View')

// Models
const User = use('App/Model/User')
const UserProfile = use('App/Model/UserProfile')
const DesignCenter = use('App/Model/DesignCenter')
const Settings = use('App/Model/Setting')

...
(snipped for brevity)
...

module.exports = ApiController

// CONFIG / AUTH.JS

To ensure valid JWT tokens, we set an expiry period as follows:

jwt: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    secret: Config.get('app.appKey'),
    options: {
        expiresIn: Ms('3m') // Token expires in 3 months
    }
}

// CONFIG / SHIELD.JS

Exclude certain API paths from CSRF token checks in this file:

csrf: {
    enable: true,
    methods: ['POST', 'PUT', 'DELETE'],
    filterUris: [
        '/api/v1/login',
        '/api/v1/register'
    ],
    compareHostAndOrigin: true
}

I hope this information proves helpful!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

It appears that the JavaScript global dynamic variable is undefined as it changes from function to function, suggesting it might be

I'm encountering an issue with the way these two functions interact when used in onclick calls of elements. Due to external circumstances beyond my control, I need to keep track of when and how elements are hidden. Everything works perfectly as inten ...

An issue has arisen with NextJS Link where it is failing to populate an anchor tag

In my React + NextJS project, I am struggling to display a list of products similar to what you would find on an ecommerce category page. Each product is wrapped in a p tag and should link to its corresponding detail page using an anchor a tag. Although t ...

Display hyperlink depending on spinner value

I stumbled upon this JavaScript spinner that displays a countdown feature, which I find quite interesting. The spinner counts down from 15 seconds. I'm curious if it's feasible to customize it so that if you land on a specific category like geogr ...

React Native - Implementing asynchronous array filtering using async/await

In my code, there is a filtering method implemented as follows: _filterItems(items) { return items.filter(async item => { let isTrue = await AsyncStorage.getItem('key'); return isTrue; }) } However, when calling the method this._ ...

What is the best way to integrate a notification system using jQuery?

I am looking to create a notification area that will refresh whenever a new message is sent using jQuery or Ajax (my database is stored in a soap server). I want to make a soap call every few seconds to achieve this, but I'm not sure how to go about i ...

Adjust the FlipClock time based on the attribute value

When setting up multiple FlipClock objects on a page, I need to retrieve an attribute from the HTML element. The specific HTML tag for the clock is: <span class="expire-clock" data-expire="2015-09-22"> Is there a way to access '2015-09-22&apos ...

Setting the default date format in dayjs: A quick guide

Is there a way for me to set the default date and time format to "YYYY-MM-DD HH:mm:ss" in order to avoid unnecessary repetitive code like the following: this.dayjs().startOf("year").format("YYYY-MM-DD HH:mm:ss") this.dayjs().format("YYYY-MM-DD HH:mm:ss") t ...

Error Encountered: Module Missing Following NPM Installation

I just started using Node and ran into an issue after setting up a new node project with NPM init. I tried installing lodash by running the command: npm install lodash --save However, the command resulted in the following error: npm ERR! code MODULE_NOT ...

Preserving quotation marks when utilizing JSON parsing

Whenever I try to search for an answer to this question, I am unable to find any relevant results. So please excuse me if this has been asked before in a different way. I want to preserve all quotation marks in my JSON when converting from a string. In m ...

Add a component to another component in real-time

Check out my StackBlitz demo: DEMO I'm attempting to insert the 'table' component into the #test section of the app component when the visualization type is 'table'. To achieve this, I am using the createTable() function which gen ...

The Javascript Date constructor struggles to interpret date strings in certain timezones that are not enclosed in brackets

Take a look at the examples below: new Date("Wed, 28 May 2014 09:50:06 EEST"); // Invalid Date new Date("Thu, 26 Jun 2014 09:09:27 EDT"); // OK, is parsed new Date("Wed, 28 May 2014 09:50:06 (EEST)"); // OK, is parsed new Date("Thu, 26 Jun 2014 09:09:27 ( ...

Access the JavaScript variable in a webview and store it in an Android variable

I have been attempting to retrieve a variable from a webview, but I am only able to make modifications like this: browser.loadUrl("javascript:var x = document.getElementById('login').value = 'something';"); However, I need to be able ...

Issue NG1006: Class has two conflicting decorators applied

I encountered an issue while compiling my project: PS C:\Users\hasna\Downloads\A La Marocaine git - Copie\ALaMarocaineFinal\frontend\src\app> ng serve Compiling @angular/forms : es2015 as esm2015 An unhandled exc ...

I'm currently trying to scrape websites that have JavaScript enabled, but I'm having trouble extracting any content from them

from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import ...

Is it secure to attach the password field to a data object property in Vue.js?

This particular inquiry has been bothering me lately. Imagine I aim to create a login element for my application (using Vue), containing 2 specific input fields - one for the userID and the other for the password. The focus here is on the security of the ...

Creating a canvas element within an iframe: A step-by-step guide

I have a unique situation where I have an iframe containing a DIV element named "pageContainer1". My goal is to insert a canvas element into that specific DIV and then be able to access it in order to draw something on it. Despite my attempts so far, this ...

The Joi Validation feature is ineffective when used with the schema provided below

{ "email": "{{$randomExampleEmail}}", "dateofbirth": "2000-01-09", "personal_document":{ "document_front":"url", "document_back":"url", ...

Unable to submit form with Jquery

Having some trouble with my form submission using Jquery. The submit part of my code seems to be malfunctioning, and I can't pinpoint the issue. <?php if(!isset($_SESSION["useridentity"])){ die(header("Location:index.php")); } ...

Angular 4: Triggering a function by clicking a link with specific parameters

I am relatively new to working with Angular 4. I have an anchor tag that, when clicked, should redirect me to a link where I also need to pass parameters. I'm unsure if my current approach is correct or not. Above all, I really need guidance on how to ...

What is the best way to detect when an option is selected in a Material UI autocomplete component?

Utilizing the autocomplete feature with filterOptions to propose adding a new value: <Autocomplete multiple name="participant-tags" options={people} getOptionLabel={(option) => option.name} renderInput={(param ...