wobsoriano / elysia-autoroutes

File system routes for Elysia.js.
MIT License
112 stars 8 forks source link

Routes could be written without this biolerplate #11

Closed azterizm closed 9 months ago

azterizm commented 1 year ago

I find it difficult to type this code every time I have to use a route file. (app: ElysiaApp) => app.get('/') => ... is repeated in many route files. The information on each route specification / is meaningless (same when using params too).

This is current way of using a route file

import { ElysiaApp } from '..'

export default (app: ElysiaApp) =>
  app.get('/', () => {
    const uuid = crypto.randomUUID()
    return (
      <html>
        <head>
          <link rel='stylesheet' href='/public/styles/global.css'/>
          <title>Home</title>
        </head>
        <body class='m-4'>
          <h1 class='text-3xl font-bold'>Home</h1>
          <p>Welcome to the home page!</p>
          <div class="tooltip" data-tip="hello">
          <p>ID: {uuid}</p>
          </div>
        </body>
      </html>
    )
  })
    .post('/', ({ set }) => {
      set.headers['Content-Type'] = 'application/json'
      return ({ hello: 'world' })
    })

I come from RemixJS background and I love their API. They provide us a way of getting into the code without any biolerplate. Also, the blurred line between client and server is highly preferred by me.

Hence, for ElysiaJS, I use this helper function.

import { ElysiaApp } from '..'

type LoaderHandler = Parameters<ElysiaApp['get']>['1']
type ActionHandler = Parameters<ElysiaApp['post']>['1']
type DeleteHandler = Parameters<ElysiaApp['delete']>['1']

export function page(
  loader?: LoaderHandler,
  action?: ActionHandler,
  del?: DeleteHandler,
) {
  return (
    app: ElysiaApp,
  ) =>
    app.get('/', loader || errorResponse).post(
      '/',
      action || errorResponse,
    ).delete('/', del || errorResponse)
}

function errorResponse() {
  return ({ error: 'Page not found.' })
}

And then write each route like this

import { page } from '../utils/api'

export default page(({ path, query, params }) => {
  const uuid = crypto.randomUUID()
  return (
    <html>
      <head>
        <link rel='stylesheet' href='/public/styles/global.css' />
        <title>TEST</title>
      </head>
      <body class='m-4'>
        <h1 class='text-2xl'>TEST PAGE</h1>
        <code>{JSON.stringify({ path, query, params })}</code>
      </body>
    </html>
  )
})

The code has become much cleaner and nice. Everything works as expected as well. I wish this was builtin and we could also remove more boilerplate. Having routes in filesystem is better for organization otherwise we have to think of file name every time we create a new feature. If someone can execute above plan then it is highly appreciated.