Code Monkey home page Code Monkey logo

cheatsheet-adonisjs's Introduction

lysla's API with AdonisJS cheatsheet

Simple indexed manual with notes.

installation

npm i -g @adonisjs/cli

To execute in project directory

adonis new . --api-only

Then start it

adonis serve --dev

configuring database connection

MySQL example (on localhost)

npm i --save mysql

In config/database.js

//...
connection: Env.get('DB_CONNECTION', 'mysql')
//...
mysql: {
    client: 'mysql',
    connection: {
      host: Env.get('DB_HOST', 'localhost'),
      port: Env.get('DB_PORT', ''),
      user: Env.get('DB_USER', 'root'),
      password: Env.get('DB_PASSWORD', ''),
      database: Env.get('DB_DATABASE', 'lysla-cms-api')
    },
    debug: Env.get('DB_DEBUG', false)
}

In .env file

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_DATABASE=lysla-cms-api

creating a resource

Each resource it's made up of its model, its controller and its migration

adonis make:model Item --migration --controller

๐ŸŽˆ In the created controller, we can remove 'create' and 'edit' method since we are developing just an API and we don't need any form rendering

Then we want to define its schema via the migration we just created

class ItemSchema extends Schema {
	up () {
		this.create('items', (table) => {
			table.increments()
			table.timestamps()
			// my own resource's fields
			table.string('name')
			table.text('content')
		})
	}

	down () {
		this.drop('items')
	}
}

๐ŸŽˆ Remind we got to create and run proper database structure for all kind of relationships (foreign keys and pivot table for many to many) manually with migrations

migrations

Run migrations to populate, or update, database

adonis migration:run

Or roll em back

adonis migration:rollback

Create a migration for each schema change

adonis make:migration Item
> Select or Create (if a create already exists)

Then update fields and structure in the Schema

class ItemSchema extends Schema {
	up () {
		this.table('items', (table) => {
			// alter table
			table.dropColumn('name')
		})
	}

	down () {
		this.table('items', (table) => {
			// reverse alternations
			table.text('name')
		})
	}
}

๐ŸŽˆ Remind that Adonis migration syntax is developed on the top of knexjs.org whom enable us to define migration for tables with relationship via foreign keys

foreign key

Migration with foreign keys

class CardSchema extends Schema {
	up() {
		this.create('cards', (table) => {
			table.increments()
			table.timestamps()

			table.string('brand')
			table.string('weight')
			// foreign key to relate to items table
			table.integer('item_id').unsigned()
			table.foreign('item_id').references('items.id')
		})
	}

	down() {
		this.drop('cards')
	}
}

We can also add a foreign key to an existing table with a new migration

class CardSchema extends Schema {
	up() {
		this.table('cards', (table) => {
			table.integer('item_id').unsigned()
			table.foreign('item_id').references('items.id')
		})
	}

	down() {
		this.table('cards', (table) => {			
		 	table.dropForeign('item_id')
		})
	}
}

pivot table

Migration for pivot table

adonis make:migration item_tag
> Create table
class ItemTagSchema extends Schema {
	up() {
		this.create('item_tag', (table) => {
			table.integer('item_id').unsigned()
			table.foreign('item_id').references('items.id')
			table.integer('tag_id').unsigned()
			table.foreign('tag_id').references('tags.id')
		})
	}

	down() {
		this.drop('item_tag')
	}
}

Migration for pivot model

adonis make:migration item_box
> Create table
class ItemBoxSchema extends Schema {
	up() {
		this.create('item_box', (table) => {
			table.increments()
			table.timestamps()

			table.integer('item_id').unsigned()
			table.foreign('item_id').references('items.id')
			table.integer('box_id').unsigned()
			table.foreign('box_id').references('boxes.id')

			table.integer('quantity')			
		})
	}

	down() {
		this.drop('item_box')
	}
}

models (๐Ÿ’ฅ wip)

Each model mediates under the hood for every normal table field. Getters and setters will override its default process, as long as they respect the naming constraint.

A field named 'title' will need a getter 'getTitle' and a setter 'setTitle' (camecase)

//example ๐Ÿ’ฅwip

More then getters and setters, models allows computed properties

//example ๐Ÿ’ฅwip

relationships

Where the Adonis models works as ORM we always need to create proper migrations manually for each relationship, following standard RDBMS school.

one to one - 1:1

Where every Item has one and only one Card

With foreign key in Card

class Item extends Model {
	card () {
		return this.hasOne('App/Models/Card')
	}
}

With foreign key in Item

class Card extends Model {
	item () {
		return this.belongsTo('App/Models/Item')
	}
}

one to many - 1:n

Where every Category can contain one or more Item(s)

class Category extends Model {
	items () {
		return this.hasMany('App/Models/Item')
	}
}
class Item extends Model {
	category () {
		return this.belongsTo('App/Models/Category')
	}
}

many to many - n:m

Where every Item(s) can belong to one or more Tag(s)

class Item extends Model {
	tags () {
		return this.belongsToMany('App/Models/Tag')
	}
}
class Tag extends Model {
	items () {
		return this.belongsToMany('App/Models/Item')
	}
}

pivot model

With pivot models we can save and access more data throu the pivot table relation

๐ŸŽˆ Given we already created the proper pivot table migration, we can create its model

adonis make:model ItemBox

And then define the many to many relation in the model

class Box extends Model {
	items () {
		return this
			.belongsToMany('App/Models/Item')
			.pivotModel('App/Models/ItemBox')
	}
}
class Item extends Model {
	boxes () {
		return this
			.belongsToMany('App/Models/Box')
			.pivotModel('App/Models/ItemBox')
	}
}

routes

Defining routing for each available data api

In start/routes.js
Route.resource('items', 'ItemController').apiOnly()

๐ŸŽˆ this auto generate common routing named as follow

Route.get('items', 'ItemController.index').as('items.index')
Route.post('items', 'ItemController.store').as('items.store')
Route.get('items/:id', 'ItemController.show').as('items.show')
Route.put('items/:id', 'ItemController.update').as('items.update')
Route.patch('items/:id', 'ItemController.update')
Route.delete('items/:id', 'ItemController.destroy').as('items.destroy')

controllers

Controller configuration

Remind the declarations of const model class

'use strict'
const Item = use('App/Models/Item')
//...

index method (GET)

Show all records

async index ({ request, response, view }) {

	// retrieving all data
	const allItems = await Item.all()

	// api response
	response.json({
		message: 'Success',
		data: allItems
	})

}

store method (POST)

Creating a new record

//...
async store ({ request, response }) {

	// retrieving data to store
	const { name, content } = request.all();

	// creating new instance
	const newItem = new Item()
	// assigning data
	newItem.name = name
	newItem.content = content

	// save the data to database
	await newItem.save()

	// api response
	response.json({
		message: 'Success',
		data: newItem
	})
}
// ...

Alternative with selective income values

//...
async store ({ request, response }) {

	// retrieving data to store
	const newData = request.only(['title', 'content'])

	// assigning and saving data
	const newItem = await Item.create(newData)

	// api response
	response.json({
		message: 'Success',
		data: newItem
	})
}
// ...

show method (GET)

Show a single record by a given id

async show ({ params, request, response, view }) {

	// retrieve the data by given id
	const theItem = await Item.find(params.id)

	// api response
	response.json({
		message: 'Success',
		data: theItem
	})

}

update method (PUT or PATCH)

Edit an existing record by a given id and new data

async update ({ params, request, response }) {

	// retrieve the data by given id
	const theItem = await Item.find(params.id)

	// retrieving new data
    const newData = request.only(['name', 'content'])

	// updating data
	theItem.name = newData.name
	theItem.content = newData.content

	// save the data to database
	await theItem.save()

	// api response
	response.json({
		message: 'Success',
		data: theItem
	})

}

destroy method (DELETE)

Remove an existing record by a given id

async destroy ({ params, request, response }) {

	// retrieve the data by given id
	const theItem = await Item.find(params.id)

	// delete the record
	await theItem.delete()
	
	// api response
	response.json({
		message: 'Success',
		data: theItem
	})
}

querying (๐Ÿ’ฅ wip)

Querying complex data throu modeled relationships, retrieving a proper json structure

Querying a specific record via its id, including its relationship(s)

const jsonData = await Item.query().where('id', newRecord.id).with('category').fetch()

middlewares

Creating a middleware

๐ŸŽˆ Adonis apiOnly boilerplate comes with authentication middlewares out of the box so we don't need to create them, the following it's just an example for a custom middleware

adonis make:middleware MyMiddleware
> HTTP Requests

Then in kernel.js we need to register the middleware

// as named middleware (only for some routes)
const namedMiddleware = {
	//...
	myMiddleware: 'App/Middleware/My'
}

Then in the routes.js, in the specific route you want to plate the middleware within

Route.get('my_resources/:id', 'MyResourceController.show').as('my_resources.show').middleware(['myMiddleware'])

Or using resource that autogenerates routes

Route.resource('my_resources', 'MyResourceController')
	.apiOnly()
	.middleware(new Map([
		[['show','update','destroy'], ['myMiddleware']]
	]))

Then in the middleware file

async handle ({ request }, next) {
	
	// do somethign with the request before advancing
	console.log('My Middleware Fired')

	// call next to advance the request
	await next()
}

authentication (๐Ÿ’ฅ wip)

Adonis provide JWT authentication out of the box, with 'auth' and 'guest' middleware

Configure routing for login and guard others

Route
  .post('auth', 'UserController.auth')
  .middleware('guest')

Route.resource('my_resources', 'MyResourceController')
	.apiOnly()
	.middleware(['auth'])

Create and configure User controller (model and starter migration are out of the box)

adonis make:controller User --resource
> For HTTP requests

Then in the user controller

class UserController {

	async auth ({ request, response, auth }) {
		const { email, password } = request.only(['email', 'password'])
		
		const jwt = await auth.withRefreshToken().attempt(email, password)

		// api response
		response.json({
			message: 'Success',
			data: jwt
		})
	}
}

Like so, we receive a JWT token that will need to be passed throu Headers in requests to get authorization for guarded routes.
๐ŸŽˆ We also get a refresh token to use to keep log-in after the base JWT expires.

In Headers request:
Authorization = Bearer <mytoken>

๐ŸŽˆ There is no manual logout with JWT authentication, but we can configure token expiration time

In auth.js

jwt: {
	serializer: 'lucid',
	model: 'App/Models/User',
	scheme: 'jwt',
	uid: 'email',
	password: 'password',
	options: {
		secret: Env.get('APP_KEY'),
		// custom token duration (in seconds)
		expiresIn: 60
	}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.