Skip to content

imjuni/fast-maker

Repository files navigation

fast-maker

ts Download Status Github Star Github Issues NPM version License ci codecov code style: prettier

fast-maker generate fastify.js route configuration using by directory structure.

Why fast-maker?

  1. Zeor cost for routing configuration at runtime
  2. Static analysis: The fast-maker generates the TypeScript source code. Because it does not generate the route configuration through dynamic analysis at runtime, it can find errors during the TypeScript compilation process
  3. flexible routing: supports various routing such as variable joining, regular expressions, etc. for example, /student/:grade-:id and /hero/:id/power/:id?, /avengers/heroes/:id/:hour(^d{2})h:minute(^d{2})m
  4. single way: there is only one way to generate one routing configuration, so there is no risk of generating routing configurations in different ways even when collaborating
  5. less code conflicts: by excluding auto-generated code from the VCS(eg. git), there is less chance of code conflicts when people collaborate
  6. beautiful CLI interface
  7. generate a route-map.ts file that summarizes routing information. It can be used for a variety of purposes, including logging. The route-map.ts file is useful because it is generated before run-time

Table of Contents

Getting started

npx fast-maker init
npx fast-maker route

You can create configuration file using init command. And you can run route command, fast-maker generate route.ts file on your output directory in configuration file.

You can see this mechanics!

fast-maker-showcase.gif

How it works?

fast-maker using TypeScript Compiler API. So fast-maker exactly know handler function and route option in each file.

graph LR

A[route file] --> fast-maker
subgraph fast-maker
direction TB
C[TypeScript Compiler API]-->|extract <br/>handler function,<br /> option variable|B[fast-maker]
end
fast-maker-->|extract <br />route path|D[route.ts]

The image below briefly shows how the directory is converted to route configurations.

AS-IS (directory structure) TO-BE (route function)
directory_structure.png route_config_ts.png

Installation

npm i fast-maker --save-dev

Usage

You can see help from --help option.

# display help for each commands
npx fast-maker --help

# display help for route commands
npx fast-maker route --help

# display help for init commands
npx fast-maker init --help

Also you can see detail option here.

Url building

route-component-architecture.png

The figure above shows at a quick look how fast-maker generates a route configuration. Routing with a file-system is intuitive and easy to understand, and it also allows many people to collaborate . This concept borrows from Next.js routing system. However, I've improved it to determine the http method as the filename.

Parametric

file-system

/handlers/example/[userId]/get.ts

generated TypeScript code like that,

// parametric
fastify.route({
  method: ['get'],
  url: '/example/:userId',
  handler: function (request, reply) {
    // curl ${app-url}/example/12345
    // userId === '12345'
    const { userId } = request.params;
    // your code here
  }
});

Combined parameter

file-system

/handlers/[lat]-[lng]/radius/[[r]]/patch.ts

generated TypeScript code like that,

fastify.route({
  method: ['patch'],
  url: '/example/near/:lat-:lng/radius/:r?',
  handler: function (request, reply) {
    // curl ${app-url}/example/near/15°N-30°E/radius/20
    // lat === "15°N"
    // lng === "30°E"
    // r ==="20"
    const { lat, lng, r } = request.params;
    // your code here
  }
});

Regular Expression, Wildcard

file-system

/handlers/example/at/[$time]/get.ts

replace map in get.ts

// replace map
const map = new Map<string, string>(['$time', ':hour(^\\d{2})h:minute(^\\d{2})m'])

generated TypeScript code like that,

fastify.route({
  method: ['get'],
  url: '/example/at/:hour(^\\d{2})h:minute(^\\d{2})m',
  handler: function (request, reply) {
    // curl ${app-url}/example/at/08h24m
    // hour === "08"
    // minute === "24"
    const { hour, minute } = request.params;
    // your code here
  }
});

Route options

You can pass RouteShorthandOptions option like that,

// When not using a `fastify` instance, you can declare it as a variable like this
export const option: RouteShorthandOptions = {
  schema: {
    querystring: schema.properties?.Querystring,
    body: schema.properties?.Body,
  },
};

When using the fastify instance, you can declare it as a function like this,

// completly same, 
// export function option(fastify: FastifyInstance): RouteShorthandOptions { ... }
export const option = (fastify: FastifyInstance): RouteShorthandOptions => {
  return {
    schema: {
      querystring: schema.properties?.Querystring,
      body: schema.properties?.Body,
    },
     preHandler: fastify.auth([
      fastify.allowAnonymous,
      fastify.verifyBearerAuth
    ]),
  };
};

You have to named export and variable name must be a option.

Route handler

You can pass route handler function like that,

import { FastifyRequest } from 'fastify';
import type { IReqSearchPokemonQuerystring, IReqSearchPokemonParams } from '../../interface/IReqSearchPokemon';

export async function handler(
  req: FastifyRequest<{ Querystring: IReqSearchPokemonQuerystring; Params: IReqSearchPokemonParams }>,
) {
  console.debug(req.query);
  console.debug(req.body);

  return 'hello';
}

You have to named export and variable name must be a handler. Also you can use arrow function, as well as type arguments perfectly applied on route configuration.

Example using fastify.js

A complete example of using fast-maker can be found at Ma-eum and examples

Examples

Relate To

  • ts-morph
    • TypeScript Compiler API wrapper

Roadmaps

  • display each route path in cli-table
  • add new option silent
  • documentation site
  • add more test

License

This software is licensed under the MIT.