Overview

Start an Elysia server with documentation and error handling.

Features

  • Swagger documentation
  • Graceful shutdown
  • Error handling
  • Health check

Installation

npx hanma add elysia-basic --framework elysia

Dependencies

elysia, @elysiajs/swagger

Code

import { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";

const isProd = process.env.NODE_ENV === "production";


const getErrorMessage = (error: unknown): string => {
  if (error instanceof Error) return error.message;

  if (typeof error === "string") return error;

  if (
    typeof error === "object" &&
    error !== null &&
    "message" in error &&
    typeof (error as { message?: unknown }).message === "string"
  ) {
    return (error as { message: string }).message;
  }

  return "Unknown error";
};

// Store app instance for shutdown
// Using flexible type since Elysia's decorator chain creates complex types
let appInstance: { stop: () => Promise<void> } | null = null;

export const startServer = () => {
  // Create base app with error handling
  let app = new Elysia({ name: "production-api" })

    /* ----------------------------- */
    /* Error definitions             */
    /* ----------------------------- */
    .error({
      BadRequest: class BadRequest extends Error {},
      Unauthorized: class Unauthorized extends Error {},
      Forbidden: class Forbidden extends Error {},
      NotFound: class NotFound extends Error {},
    })

    /* ----------------------------- */
    /* Global error handler          */
    /* ----------------------------- */
    .onError(({ code, error, set }) => {
      const statusMap: Record<string, number> = {
        BadRequest: 400,
        Unauthorized: 401,
        Forbidden: 403,
        NotFound: 404,
        VALIDATION: 400,
        INTERNAL_SERVER_ERROR: 500,
      };

      set.status = statusMap[code] ?? 500;

      const message = getErrorMessage(error);

      if (!isProd || set.status >= 500) {
        console.error(`[${code}]`, error);
      }

      return {
        success: false,
        code,
        message:
          isProd && set.status === 500 ? "Internal Server Error" : message,
        timestamp: new Date().toISOString(),
      };
    });

  // Add swagger in dev mode
  if (!isProd) {
    app = app.use(
      swagger({
        documentation: {
          info: {
            title: process.env.npm_package_name || "API",
            version: process.env.npm_package_version || "1.0.0",
          },
        },
      })
    );
  }

  // Add routes
  app
    .get(
      "/health",
      () => ({
        status: "ok",
        uptime: Math.floor(Bun.nanoseconds() / 1e9),
        timestamp: new Date().toISOString(),
      }),
      {
        response: t.Object({
          status: t.String(),
          uptime: t.Number(),
          timestamp: t.String(),
        }),
      }
    )
    .get("/", () => ({ message: "Elysia API is running" }), {
      response: t.Object({
        message: t.String(),
      }),
    });

  // Store instance and start listening
  appInstance = app;
  
  const PORT = Number(process.env.PORT) || 3000;
  app.listen(PORT, () => {
    console.log(`Elysia running on http://localhost:${PORT}`);
    if (!isProd) {
      console.log(`Swagger docs: http://localhost:${PORT}/swagger`);
    }
  });

  return app;
};


let isShuttingDown = false;

export const shutdown = async (signal: string) => {
  // Prevent multiple shutdown attempts
  if (isShuttingDown || !appInstance) return;
  isShuttingDown = true;
  
  console.log(`${signal} received. Shutting down...`);
  
  try {
    await appInstance.stop();
  } catch (err) {
    // Ignore errors if already stopped
  }
  
  appInstance = null;
  process.exit(0);
};

Usage

// Server starts automatically
bun run src/index.ts

Dependencies

Frequently Asked Questions

How do I install Elysia Basic Server?
Run the following command: npx hanma add elysia-basic --framework elysia
What dependencies are required?
This snippet requires: elysia, @elysiajs/swagger. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to src/index.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.