Routing

As you probably noticed from visiting the API page in the admin UI (http://localhost:8788/admin/api), there are many automatically generated endpoints that support CRUD (Create - Read - Update - Delete) operations on you database tables.

In order to create custom routes with custom business rules, we use SonicJs's routing system.

If you are already familiar with Node/Express, you'll be right at home as the concepts and syntax are very similar. Let's checkout a simple example:

router.get("/ping", (ctx) => {
  return ctx.json({'date': Date()});
});

SonicJs uses HonoJs for its requests routing, a lightweight, ultra-fast web framework that supports various edge platforms including (you guessed it!) Cloudflare Workers.

So technically, SonicJs will run on anything that HonoJs runs on (Cloudflare Workers, Fastly Compute@Edge, Deno, Bun, Vercel, Lagon, AWS Lambda, Lambda@Edge, and Node.js). However, since we're obsessed with 🔥 speed 🔥, we're putting all focus on deploying to Cloudflare exclusively while the other providers play catch-up.

Hello World

Write your first custom set of routes in /src/custom/hello.ts.

import { Hono } from 'hono'

const hello = new Hono()

hello.get('/', (c) => {
  return c.text('Hello SonicJs!')
});

export { hello };

Next all we need to do it import our new hello router into our main server, so let's update src/server.ts and add the 2 lines marked with // <--- add this line:

import { Hono } from "hono";

import { api } from "./cms/api/api";
import { Bindings } from "./cms/types/bindings";
import { admin } from "./cms/admin/admin";
import { content } from "./cms/api/content";
import { hello } from "./cms/api/hello"; // <--- add this line


const app = new Hono<{ Bindings: Bindings }>()

app.get("/", async (ctx) => {
  return ctx.redirect('/admin');
});

app.get("/public/*", async (ctx) => {
  return await ctx.env.ASSETS.fetch(ctx.req.raw);
});

app.route('/v1', api)
app.route('/v1/content', content)
app.route('/admin', admin)
app.route('/hello', hello)  // <--- add this line


export default app;

Startup the app with npm run dev and visit:

http://localhost:8788/v1/hello

Easy Peasy right! To continue, make sure you've ⭐⭐⭐⭐⭐ starred the repo on Github 🙏. Here at SonicJs we work for stars my friend 🤩.

Return JSON

Returning JSON is also easy. The following is an example of handling a GET Request to /v1/hello and returning an application/json Response. Don't forget to write return.

app.get('/api/hello', (c) => {
  return c.json({
    ok: true,
    message: 'Of course I talk to myself. Sometimes I need expert advice.',
  })
})

Request and Response

Getting a path parameter, URL query value, and appending Response header:

app.get('/posts/:id', (c) => {
  const page = c.req.query('page')
  const id = c.req.param('id')
  c.header('X-Message', 'Hi!')
  return c.text(`You want see ${page} of ${id}`)
})

We can of course also handle POST, PUT, and DELETE:

app.post('/posts', (c) => c.text('Created!', 201))
app.delete('/posts/:id', (c) => c.text(`${c.req.param('id')} is deleted!`))

Return HTML

SonicJs is also suitable for returning HTML. This is how the admin section available at http://localhost:8788/admin is generated. You can return any html you want, but in the case of the admin section, we use React SSR. More info on that in the core developers guide.

const View = () => {
  return (
    <html>
      <body>
        <h1>Excuse my naivety. I was born at a very early age.</h1>
      </body>
    </html>
  )
}

app.get('/page', (c) => {
  return c.html(<View />)
})

Return raw Response

You can also return the raw Response.

app.get('/', (c) => {
  return new Response("I’d agree with you, but then we’d both be wrong.")
})

Using Middleware

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle.

For example, add the Basic Authentication:

import { basicAuth } from 'hono/basic-auth'

// ...

app.use(
  '/admin/*',
  basicAuth({
    username: 'admin',
    password: 'secret',
  })
)

app.get('/admin', (c) => {
  return c.text('Your are authorized!')
})

There are useful built-in middleware including Bearer and authentication using JWT, CORS and ETag. Also, we have third-party middleware using external libraries such as GraphQL Server and Firebase Auth. Additional, you can create your own middleware functions.

Adapter

There are Adapters for platform-dependent functions, e.g., handling static files. For example, to handle static files in Cloudflare Workers, import hono/cloudflare-workers.

import { serveStatic } from 'hono/cloudflare-workers'

app.get('/static/*', serveStatic({ root: './' }))

Need More?

SonicJs uses Hono for routing. Hono is an ultra fast routing system for edge frameworks. Please see their docs for any routing needs not covered in the above.