Odi — JSX, OpenAPI and more

Dan Tsk
5 min readJan 13, 2019

--

Introduction

Hello 👋!

I have news about Odi to share. If you missed something, you can check the first article about Odi — Link. After first the article and posts of Reddit, I got a lot of feedback and ideas about the framework. Thank you all guys! When you see that people are interested in your work, it inspires for further development and improvements!

Today I want to tell you about new features in Odi 0.3.0 and reveal plans for the future.

Let’s go!

JSX

A lot of applications use SSR (server-side rendering), so we decided to add a convenient way for templating and support easy logic implementing. We choose JSX for this goal, as you get all benefits (Typings, IntelliSense and etc.) from React ecosystem and tools.

Overview

//greeting.view.tsx
interface GreetingProps {
message: string;
}
export const Greeting = ({ message }: GreetingProps) => (
<h4>{message}</h4>
);
//render.controller.tsx
@Controller()
export class RenderController extends IController {
@Autowired()
renserService: RenderService;
@Get index() {
const message = this.renserService.getGreeting();
return <Greeting message={message} />
}
}

You don’t need any additional building step if you use JSX for templating goals only, any logic included in Components will be ignored.
Install few dependencies before server start.

npm i react react-dom

And tell TypeScript how to transpile JSX

//tsconfig.json
"jsx": "react"
....

Now simply run

npm start

And that’s all!
Odi automatically detects React Components in route handler result and render it to the browser as plain HTML.

SSR

Full React application can be used with Odi including client-side logic but with pre-initialized data on the backend. Such usage improves rendering performance and optimizes SEO. Also, such architecture looks pretty convenient for some kind of applications.

Currently, there are a lot of things must be configured for such way of rendering, as you should compile not only backend part but frontend part too.

We trying to keep everything minimalistic and clear in Odi, so give us more time to implement it. Even now you can use webpack for bundling and everything will work, but it’s not that we need. You will have tons of config files and boilerplate code. Such way does not satisfy Odi goals.

P.S. Hopefully, full SSR will be released on next weekend 😉

Sandbox

Query and Handler Improvements

Odi 0.3.0 support request query validation. All decorators for body validation is available. So you can simply define your query DTO and pass it to route handler method as an argument.

//foo.qto.ts
@Query()
export class FooQueryDTO {
@IsRequired()
enabled: boolean;
@IsOptional()
role: string;
}
//foo.controller.ts
@Controller()
export class FooController extends IController {
@Get index(query: FooQueryDTO) {
...some code
}
}

Another thing that was added — support of parameters destruction. Odi relies on argument names of route handler. In previous versions, destructuring syntax leads to errors but now you can use it.

Example:

@RouteGet({id})
index(id: string, { enabled, role }: FooQueryDTO) {
...some code
}

OpenAPI

Documentation is one of the most important things in server-side development, so we decided to implement OpenAPI docs generation for Odi projects.

Other popular frameworks also support this feature. All of them requires schema description in the source code (even metadata), so it exists in runtime. It’s really inconvenient to describe everything manually. So Odi trying to add some magic 🧙‍♂️ to this process.

Overview

To start generating docs, you need only run CLI command on the existing project. You don’t need to add any additional metadata, schema descriptors and etc.

We have such a simple controller:

import { IController, Controller, RouteGet, Ok, BadRequest } from 'odi';@Controller()
export class HomeController extends IController {
@RouteGet('{id}')
async index(id: string) {
if(!id)
return BadRequest();
const date = new Date(); return { id, date };
}
}

Now we run

odi docs -s ./src -l http//localhost:8080 -t "Sample Application"

Docs will be generated into swagger-{version}.json file. Let's visualize this file using Swagger UI.

As you see we don’t have any additional descriptions, decorators or something like that. CLI automatically collects docs even if it’s async (Wrapper in Promise) method, helper function call or even dynamic type without strict class/interface definition.

More complex example:

//body.dto.ts
@Data()
export class FooBodyDTO {
@Format('uuid')
id: string;
@IsRequired()
name: string;
@IsEmail()
email: string;
@ArrayOf(String)
tags: string[];
}
//query.dto.ts
@Query()
export class FooQueryDTO {
@IsRequired()
enabled: boolean;
@IsOptional()
role: string;
}
//foo.controller.ts
@Controller('/foo')
export class FooController extends IController {
/**
* @title Bar
* @description Route to show all features
* @tags [Foo]
*/
@RoutePost('{id}/bar/{code}')
async bar(id: string, code: number, body: FooBodyDTO, query: FooQueryDTO) {
if(!id)
return BadRequest();
const date = new Date(); return ({ code, date, ...body, ...query});
}
}

And that’s it!

JSDoc can also participate in OpenAPI generation. For now, it’s only 3 things available:

  • title
  • description
  • tags
  • send

Custom return types and status codes can be set even without helper functions, just in JSDoc:

@Controller()
export class BarController extends IController {
/**
* @title Bar
* @description Route to show all features
* @tags [Foo]
*
* @send [400=>text/plain]
* @send [200=>application/json]
*/
@RouteGet()
async bar(id: string) {
if(!id) {
this.setStatus(400);
return "No id provided"
}
return ({ id, date: new Date() });
}
}

We tried to implement it as automatically as possible, so the developer doesn’t need to do anything additional.

Usage

In our team, we generate this file into CI and publish it to our corporate server with Swagger UI, so every developer can have access to the latest documentation.

Plans

There will be a lot more magic. Our roadmap for nearest time:

  • Full SSR support
  • Code generation for client-side (With full typings and various settings)
  • Interceptors (AOP)
  • WebSockets and Transactions refactor
  • …more

Also in plans to create the landing page for Odi.
Be sure, there will be a lot of interesting things 🧙‍♂️ in the next releases, 😉!

Links

View on GitHub

P.S.

Thanks for your support! If you have any questions, feel free to ask! If you like Odi idea, support us with a simple start on GitHub, join our small and friendly family!

--

--