Skip to main content

EzApp - Basic Routing

Overview

ez-app

The EzApp is the building block of an EzBackend, and you can create routes on them as you wish.

EzApps can have children which inherit options from the parent

  • If a parent has a route-prefix 'user'
  • All child app routes with have route prefix 'user'

Everything in EzBackend is derived from EzApp

  • All Plugins are EzApps
  • EzBackend is an EzApp
  • EzModel is an EzApp
  • EzRepo is an EzApp
info

If all you need are Create, Read, Update, Delete (CRUD) endpoints for your database, refer to the EzModel docs instead

Sample structure

The sample below creates a get, post, patch and delete request at the url /v1/users/details

ez-app-prefixing

const app = new EzBackend()
const v1 = new EzApp()
const users = new EzApp()
const details = new EzApp()

app.addApp(v1,{prefix:'v1'})
v1.addApp(users,{prefix:'users'})
users.addApp(details,{prefix:'details'})

details.get(...)
details.post(...)
details.patch(...)
details.delete(...)

app.start()

Viewing Available Routes

All created routes are automatically documented and displayed in the db-ui

You can access the db-ui at http://localhost:8000/db-ui/ (Assuming port 8000)

db-ui-sample

Creating routes

Creating a route in ezbackend can be done similarly to express or fastify.

There are three ways each route can be created:

  1. Async shorthand
  2. Sync shorthand
  3. Full Declaration

Get

Create an endpoint on the app which returns a json when a GET request is performed

export class Response {
data: string
}

app.get("/",{
reply200: Response
}, async (req,res) => {
return {data: "data"}
});

Post

Create an endpoint on the app which returns a json when a POST request is performed

export class Person {
name: string
}

export class Response {
data: Person
}

app.post("/",{
body: Person
reply200: Response
}, async (req,res) => {
return {data: req.body}
});

Put

Create an endpoint on the app which returns a json when a PUT request is performed

export class Person {
name: string
}

export class Response {
data: Person
}

app.put("/",{
body: Person
reply200: Response
}, async (req,res) => {
return {data: req.body}
});

Patch

Create an endpoint on the app which returns a json when a PATCH request is performed

export class Person {
name: string
}

export class Response {
data: Person
}

app.patch("/",{
body: Person
reply200: Response
}, async (req,res) => {
return {data: req.body}
});

Delete

Create an endpoint on the app which returns a json when a DELETE request is performed

export class Person {
name: string
}

export class Response {
data: Person
}

app.delete("/",{
body: Person
reply200: Response
}, async (req,res) => {
return {data: req.body}
});

Route Validation / Documenting Routes

EzBackend allows you to use TypeScript classes for route validation.

export class Body {
var1: string,
var2?: number,
var3: boolean,
var4: 'enum1' | 'enum2'
}

app.post('/', {
querystring: QueryString,
headers: Headers,
body: Body,
reply200: Reply,
summary: "A summary of the endpoint",
description: "A description of the endpoint"
}, async (req,res) => {
...
})

By specifying the input using classes, you get:

  1. Automatic Documentation
  2. Input/Output Validation
  3. Typescript type safety within the handler
info

If you see the warning no auto documentation nor input validation provided, it means that you have to specify Typescript classes or JSON Schema for automatic documentation to work.

Schema Definition

To use a schema within EzBackend it needs to be exported from the top level file.

For this reason, all schemas used within a single EzBackend project need to have unique names as well.

export class Person {
name: string;
age: number;
gender: 'male' | 'female';
}

Additional JsDoc annotations can also be used to extend the default types

// This adds a format:email field in the generated schema, which is reflected in the auto-generated-docs

export class Person {
/**@format email */
email: string;
}

After creating a class you can use it in your routes for validation and automatic documentation. All the possible endpoints types are listed below. Currently Typescript Class validation only supports 200 and 400 type responses, if you need to define it for other error codes you can use JSON Schema

export class QueryString {data:any}
export class Headers {data:any}
export class Body {data:any}
export class Params {data:any}
export class Reply200 {data:any}
export class Reply400 {data:any}

app.post('/', {
body: Body,
params: Params,
querystring: QueryString,
headers: Headers,
reply200: Reply200,
reply400: Reply400,
summary: 'summary',
description: 'description'
}, async (req,res) => {
return {}
})

EzBackend uses ts-json-schema-generator to convert Typescript classes to the appropriate types.

Summary

You can provide a summary of what your route, which will be reflected in the Db-UI

summary

app.get("/",{
summary: "Server Time"
}, async (req,res) => {
const time = new Date().toLocaleTimeString()
return {time: time}
});

Description

You can provide a detailed description of what your route does, which will be reflected in the Db-UI

description

app.get("/",{
summary: "Server Time",
description: "This method returns the current server time"
}, async (req,res) => {
const time = new Date().toLocaleTimeString()
return {time: time}
});

Schema Caching

When NODE_ENV is not set to production, json-schemas of the exported classes are stored in a auto-generated folder, schemas

The folder schemas should be treated as code and committed in version control.

Running in Production

When NODE_ENV is set to production, instead of generating the json-schema on-the-fly (which is the default for non-production mode), validation schemas are obtained from the schemas folder to reduce startup time.

JSON Schema

If you find any limitations with the automatic schema generation, or you are not using typescript, you can fall back to JSON Schema for route validation.

The schemas provided must be in JSON Schema format.

body - Validates the request body for POST,PUT and PATCH methods

query - Validates the request query string (e.g api.your-app.com?name=bob&age=25)

params - Validates the params (e.g api.your-app.com/user/:userId)

response - Filters the output, according to status code (e.g if the status is 200 OK, it will follow the 200 schema)

Defining these values will also populate the documentation automatically

app.post("/",{
schema:{
body: <YOUR JSON SCHEMA>,
query: <YOUR JSON SCHEMA>,
params: <YOUR JSON SCHEMA>,
response:
200: <YOUR JSON SCHEMA>
}
}, async (req,res) => {
return {data: req.body}
});

Configuration

You can configure the automatic documentation functionality by setting certain environment variables

These values can be set in .env and will automatically be loaded

VariableDescriptionSample
ENTRY_POINT_PATHThe path of the file from which all your types are exported fromENTRY_POINT_PATH=src/index.ts
TS_CONFIG_PATHThe path of your tsconfig.jsonTS_CONFIG_PATH=tsconfig.json
SCHEMA_DIRThe folder name where schemas are storedSCHEMA_DIR=schemas

Route options

Route options can be used to add additional functionality to your routes

prefixTrailingSlash

prefixTrailingSlash: string used to determine how to handle passing / as a route with a prefix.

both (default): Will register both /prefix and /prefix/.

slash: Will register only /prefix/.

no-slash: Will register only /prefix.

app.post("/prefix",{
prefixTrailingSlash:'both' //'both'|'slash'|'no-slash'
}, async (req,res) => {
return {data: req.body}
});

Route Prefixing

When adding apps to apps, you can specify the prefix in the options in order to prefix all the routes in the app and all children apps

app.addApp(childApp, { prefix: "hello-world" });

Additional Functionality

EzApps are expose the functionality of fastify objects, so anything that would work in fastify would also work here.