Routes

Routes are the entry point for all requests to your application. They map a specific URL pattern to a specific file or route handler function.

In Hyperspan, routes can live in two main places:

  1. app/routes directory (file-based routes)
  2. app/server.ts file (custom route handlers)

File-Based Routing

To make things easier, Hyperspan uses file-based routing. This means that you can create a file in the app/routes directory and it will be automatically picked up by the framework and added to your routing table.

Here are some examples of how files in the app/routes directory map to URL patterns:

File Path URL Pattern
app/routes/index.ts /
app/routes/about.ts /about
app/routes/posts/index.ts /posts
app/routes/posts/[id].ts /posts/:id
app/routes/auth/[...authPath].ts /auth/*

Route and query params can be accessed from the Hono Request object by name.

// File: app/routes/posts/[id].ts
import { createRoute } from '@hyperspan/framework';
import { fetchPostById } from '@/src/entities/posts'; // whatever your data layer is

export default createRoute(async (c) => {
  const id = c.req.param('id');
  const post = await fetchPostById(id);

  return html`<main>
    <h1>${post.title}</h1>
    <div>${post.content}</div>
  </main>`;
});

The createRoute Function

File-based routes are created by default exporting the results of calling the createRoute function.

The most basic route is a function that returns a string of HTML.

import { createRoute } from '@hyperspan/framework';

export default createRoute(() => html`<div>Hello, world!</div>`);

Using Plain Functions

You can define a route with a plain function, but you won't have the proper types included by default, or other helpful APIs like limiting the route to a specific HTTP method, adding route-specific middleware, etc.

import { Context } from 'hono';
        
export default function (c: Context) {
  return html`<div>Hello, ${c.req.param('name')}!</div>`;
}
Plain functions for routes can be useful for migrating over from another framework, but they are not recommended for new projects due to the lack of type safety and other helpful APIs that come with createRoute.

Route Parameters

The createRoute function has a Hono Context parameter that can be used to access information from the request, like parameters from the route path, query string parameters, headers, cookies, etc.

import { createRoute } from '@hyperspan/framework';

export default createRoute((c) => {
  return html`<div>Hello, ${c.req.param('name')}!</div>`;
});

Custom Route Paths

If you need to use a custom path for a file-based route, you can import any file-based route and make it accessible with any custom path or URL pattern you need.

Just import createRouteFromModule from @hyperspan/framework and pass in the imported module.

import { createServer, createRouteFromModule } from '@hyperspan/framework';
import PostPageRoute from '@/app/routes/posts/[id].ts';

const app = await createServer({
  appDir: './app',
  staticFileRoot: './public',
});

// Post page route is now accessible at /articles/:id in addition to /posts/:id
app.get('/articles/:id', createRouteFromModule(PostPageRoute));

export default app;

This is similar to how rewrites work in Next.js, only this is more direct and flexible instead of limited toa config file.

Custom Route Handlers

If you need more control over routing or need to do something that doesn't fit within file-based routing, you can create a custom route handler function in app/server.ts. The createServer function will return a Hono instance that you can use to add custom Hono Routes or Hono Middleware.

import { createServer } from '@hyperspan/framework';

const app = await createServer({
  appDir: './app',
  staticFileRoot: './public',
});

// Custom Hono Route
app.get('/my-custom-route', (c) => c.html('<div>Hello, world!</div>'));

export default app;

If you need to add routes before the file-based routes are processed, you can do so by using the beforeRoutesAdded hook in createServer.

import { createServer } from '@hyperspan/framework';

const app = await createServer({
  appDir: './app',
  staticFileRoot: './public',
  beforeRoutesAdded: (app) => {
    app.get('/custom-route-before-file-routes', (c) => c.html('<div>Hello, world!</div>'));
  },
});

export default app;

Custom Middleware

You can also add custom middleware to the Hono server in app/server.ts. This is useful for things like authentication, logging, etc.

import { createServer } from '@hyperspan/framework';
import { trimTrailingSlash } from 'hono/trailing-slash';

const app = await createServer({
  appDir: './app',
  staticFileRoot: './public',
});

// Custom Hono Middleware
app.use(trimTrailingSlash());

export default app;

If you are unfamiliar with middleware, you can read more about it in the Hono Middleware documentation.