Routing
Basic Routing
The most basic Formidable routes accept a URI and a Callback, providing a very simple and expressive method of defining routes:
- Imba
- TypeScript
import { Route } from '@formidablejs/framework'
Route.get '/', do 'Hello World'
import { Route } from '@formidablejs/framework'
Route.get('/', () => 'Hello World')
The Default Route Files
All Formidable routes are defined in your route files, which are located in the routes
directory. These files are automatically loaded by the RouterServiceResolver
:
- Imba
- TypeScript
import { UserController } from '../app/Http/Controllers/UserController'
Route.get('/user', [UserController, 'index'])
import { UserController } from '../app/Http/Controllers/UserController'
Route.get('/user', [UserController, 'index'])
Depending on the Language you're using, Formidable defines all routes in the routes/api.imba
and routes/web.imba
or routes/api.ts
and routes/web.ts
files, which are loaded by RouterServiceResolver
. This resolver, loads these routes within a jwt
or session
middleware group. This means, all routes defined in the routes/web.imba
or routes/web.ts
files, will have the session
middleware automatically applied on all of them while all routes defined in the routes/api.imba
or routes/api.ts
will have the jwt
middleware automatically applied on all of them. You may specify a different middleware and other route group options by modifying your RouterServiceResolver
class.
Available Router Methods
The router allows you to register routes that respond to any of the listed HTTP verbs:
Route.get(uri, callback)
Route.post(uri, callback)
Route.put(uri, callback)
Route.head(uri, callback)
Route.delete(uri, callback)
Route.patch(uri, callback)
Route Parameters
Required Parameters
You might find yourself in a situation where you would like to have dynamic routes. Maybe you want to load a specific blog post using the blog post id, you can do this by defining a parameter in your route:
- Imba
- TypeScript
Route.get '/posts/:id', do (request\Request)
const postId\number = request.param('id')
''
Route.get('/posts/:id', (request: Request) => {
const postId: number = request.param('id')
''
})
:id
wil be registered as a route parameter.
You may also define as many route parameters as required by your route:
- Imba
- TypeScript
Route.get '/user/:user/posts/:post', do (request\Request)
const userId\number = request.param('user')
const postId\number = request.param('post')
''
Route.get('/user/:user/posts/:post', (request: Request) => {
const userId: number = request.param('user')
const postId: number = request.param('post')
''
})
Route parameters are always prefixed with :
and should consist of alphabetic characters. Underscores (_) are also acceptable within route parameter names.
Route Query Binding
Formidable makes it easy to automatically load a database record based on a route parameter. You would normally do this by using the @use
decorator next to your controller route action:
- Imba
- TypeScript
import { @use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController < Controller
@use('table:posts') def show post\object
await post
...
import { use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController extends Controller {
@use('table:posts') async show(post: object): Promise {
await post
...
}
}
This will query the database to look for a post under the posts
table with the first param using the id
column.
If you want to load multiple database records, you can do so by passing all the tables you want to load in the @use
decorator:
- Imba
- TypeScript
import { @use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController < Controller
@use('table:users', 'table:posts') def show user, post
user = await user
post = await post
...
import { use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController extends Controller {
@use('table:users', 'table:posts') async show(userRecord: Promise<object>, postRecord: Promise<object>): Promise {
const user = await userRecord
const post = await postRecord
...
}
}
Route Param Binding
You can also promote route params to your controller action using the @use
decorator:
- Imba
- TypeScript
import { @use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController < Controller
@use('param') def show postId\Number
postId
import { use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController extends Controller {
@use('param') show(postId: number) {
return postId
}
}
This will return the first param
in the route.
You can also specify which param should be loaded:
- Imba
- TypeScript
import { @use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController < Controller
@use('param:post') def show postId\Number
postId
import { use } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController extends Controller {
@use('param:post') show(postId: number): number {
return postId
}
}
This will look for the param
named post
and return that instead of the first param
in the route.
You can also load multiple params:
- Imba
- TypeScript
import { @use } from '@formidablejs/framework'
import { DB } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController < Controller
@use('param', 'param') def show userId\Number, postId\Number
DB.table('posts').where('id', postId).where('user_id', userId)
import { use } from '@formidablejs/framework'
import { DB } from '@formidablejs/framework'
import { Controller } from './Controller'
export class PostController extends Controller {
@use('param', 'param') show(userId: number, postId: number): Promise<object[]> {
return DB.table('posts').where('id', postId).where('user_id', userId)
}
}
Named Routes
Named routes
allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the name
method onto the route definition:
- Imba
- TypeScript
Route.get('/posts/:slug', do(request\Request)
# do somthing
).name('posts.show')
Route.get('/posts/:slug', (request: Request) => {
// do somthing
}).name('posts.show')
You may also specify route names for controller actions:
- Imba
- TypeScript
Route.get('/posts/:slug', [PostController, 'show']).name('posts.show')
Route.get('/posts/:slug', [PostController, 'show']).name('posts.show')
Generating URLs To Named Routes
Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via Fomidablejs's Redirect
and URL
classes:
- Imba
- TypeScript
import { URL } from '@formidablejs/framework'
import { Redirect } from '@formidablejs/framework'
# Generating URLs...
const url = URL.route('user')
# Generating Redirects...
return Redirect.route('user')
import { URL } from '@formidablejs/framework'
import { Redirect } from '@formidablejs/framework'
// Generating URLs...
const url = URL.route('user')
// Generating Redirects...
return Redirect.route('user')
If the named route defines parameters, you may pass the parameters as the second argument to the route
function of the Redirect
class. The given parameters will automatically be inserted into the generated URL in their correct positions:
- Imba
- TypeScript
Route.get('/posts/:id', do(request\Request)
# do something
).name('posts.show')
const url = URL.route('posts.show', {
id: 1
})
Route.get('/posts/:id', (request: Request) => {
# do something
}).name('posts.show')
const url = URL.route('posts.show', {
id: 1
})
Inspecting The Current Route
If you would like to determine if the current request was routed to a given named route, you may use the routeIs
method on a FormRequest
instance. For example, you may check the current route name from a route middleware:
- Imba
- TypeScript
def handle request\Request
if request.routeIs 'posts.show'
# do something
handle(request: Request) {
if (request.routeIs('posts.show')) {
// do something
}
}
Route Groups
Route groups allow you to share route attributes, such as middleware or namespaces, across a large number of routes without needing to define those attributes on each individual route. Shared attributes are specified in an array format as the first parameter to the Route.group
method.
Middleware
To assign middleware to all routes within a group, you may add the middleware
keyword inside the group. Middleware are executed in the order they are listed in the object:
- Imba
- TypeScript
Route.group { middleware: ['first', 'second'] }, do
Route.get '/', do
# uses first & second Middleware
Route.get 'user/profile', do
# uses first & second Middleware
Route.group({ middleware: ['first', 'second'] }, () => {
Route.get('/', () => {
// uses first & second Middleware
})
Route.get('user/profile', () => {
// uses first & second Middleware
})
})
Route Prefixes
The prefix
keyword may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin
:
- Imba
- TypeScript
Route.group { prefix: 'admin' }, do
Route.get 'users', do
# matches the "/admin/users" URL
...
Route.group({ prefix: 'admin' }, () => {
Route.get('users', () => {
// matches the "/admin/users" URL
})
...
})