v 0.1.38

Server API

Ecopages provides a powerful server API that allows you to create endpoints and handle server-side logic with ease.

Basic Usage

Create an app.ts file in your project root:

import { EcopagesApp } from '@ecopages/core/adapters/bun/create-app';
import appConfig from './eco.config';

const app = new EcopagesApp({ appConfig });

app.get('/api/hello', async () => {
  return new Response('Hello World');
});

await app.start();

HTTP Methods

The server supports all standard HTTP methods:

app.get('/api/resource', handler);
app.post('/api/resource', handler);
app.put('/api/resource', handler);
app.patch('/api/resource', handler);
app.delete('/api/resource', handler);
app.options('/api/resource', handler);
app.head('/api/resource', handler);

Route Parameters

You can define dynamic route parameters using the :param syntax:

app.get('/api/users/:id/posts/:postId', async ({ request }) => {
  const { id, postId } = request.params;
  return new Response(JSON.stringify({ userId: id, postId }));
});

Type Safety

Ecopages leverages TypeScript to provide strong type safety for your API handlers, especially for route parameters.

While you can define handlers directly, ensuring the request.params type matches the path string requires careful manual typing. To simplify this and improve code organization, Ecopages provides adapter-specific helper functions.

Using defineBunApiHandler

When using the Bun adapter, you can use the defineBunApiHandler helper function. This function automatically infers the parameter types from the path string literal, ensuring your request.params object is correctly typed without explicit annotations.

// Import the adapter-specific helper
import { defineBunApiHandler } from '@ecopages/core/adapters/bun/define-handler'; // Adjust path if needed

// Define the handler using the helper
const getUserHandler = defineBunApiHandler({
  method: 'GET',
  path: '/api/users/:id', // Type of 'id' is inferred from here
  handler: async ({ request }) => {
    // request.params is automatically typed as { id: string }
    const { id } = request.params; 
    // Fetch user logic...
    const user = { id: id, name: 'Example User' }; 
    
    if (!user) {
      return new Response('User not found', { status: 404 });
    }
    return new Response(JSON.stringify(user));
  },
});

// Register the handler with the app
app.get(getUserHandler.path, getUserHandler.handler);

// You can define multiple handlers this way and keep them organized
const updateUserHandler = defineBunApiHandler({
  method: 'PUT',
  path: '/api/users/:id',
  handler: async ({ request }) => {
    const { id } = request.params;
    // Update user logic...
    return new Response(JSON.stringify({ id, message: 'User updated' }));
  }
});

app.put(updateUserHandler.path, updateUserHandler.handler);

Benefits of Using defineBunApiHandler

Using defineBunApiHandler offers several advantages:

Static Site Generation

During static site generation, the server can still handle API requests, making it possible to generate static content from API responses:

export const getStaticProps: GetStaticProps = async ({ appConfig }) => {
  const response = await fetch(`${appConfig.baseUrl}/api/data`);
  const data = await response.json();
  
  return {
    props: { data },
  };
};

Development Server

The development server includes:

Production Build

When building for production:

  1. Static routes are pre-rendered
  2. API routes are preserved for server-side handling
  3. Assets are optimized and collected
  4. Development-only code is stripped

Error Handling

The server includes built-in error handling:

app.get('/api/error', async () => {
  throw new Error('Something went wrong');
  // Will return a 500 response with error details in development
  // and a safe error message in production
});

Configuration

Server configuration can be customized in your eco.config.ts:

const config = await new ConfigBuilder()
  .setBaseUrl('http://localhost:3000')
  // ... other config
  .build();