> For the complete documentation index, see [llms.txt](https://stricjs.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://stricjs.gitbook.io/docs/basic/routing.md).

# Routing

Install the router and utilities:

```bash
bun add @stricjs/router @stricjs/utils
```

The router component provides the routing part for Stric, like Express or Fastify.

The utilities component has helpful utilities to parse and handle requests.

## Define routes

You can define a route using `[method](path, handler)`, for instance:

```typescript
import { Router } from '@stricjs/router';  

export default new Router()
    .get('/', () => new Response('Hi'));
```

In the above example, we create a new router and this router will respond with `Hi` on every `GET` request to pathname `/`. The router can be served with `export default` or `Bun.serve` directly because it is a serve options object.

### All method handlers

To add a handler for all methods, use `app.all` with the same parameter as above.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .all('/', () => new Response('Hi'));
```

## Request methods

You can define a route with another request method as well.

```typescript
import { Router } from '@stricjs/router';  

export default new Router()
    // You should use new Response(JSON.stringify(...), { headers: { ... } })
    // for it to be faster. This example provides a clean way to do this
    .post('/json', () => Response.json({ hi: 'there' }));
```

Compares to the first example, this example add another handler which will run on every `POST` request to pathname `/json`. This handler will return an object as a response.

## Parametric routes

You can parametric routes to retrieve data from URLs, works like Express and Fastify routes.

The type of `req.params` is inferred directly based on the path.

```typescript
import { Router } from '@stricjs/router';  

export default new Router()
    .get('/id/:id', req => new Response(req.params.id));    
```

In this example, the server will respond to every route that matches `/id/:id` with the specified `id` parameter value in the URL. For example, if we send a `GET` request to pathname `/id/90` we will get `90` as a response.

## Wildcard

You can use the wildcard `*` to match the rest of the path.

```typescript
import { Router } from '@stricjs/router';  

export default new Router()
    .get('/private/*', req => 
        new Response(`Cannot access /private/${req.params['*']}`)
    );
```

This will respond to every request pathname starts with `/private/`.

## Body parsing

Stric has a set of predefined body parser you can use.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .post('/text', req => {
        // Use the parsed body here (it has type hint)
        req.data;
    }, { body: 'text' });
    
// More parsers:
{ body: 'json' } // Parse to JSON
{ body: 'blob' } // Parse to Blob
{ body: 'form' } // Parse to FormData
{ body: 'buffer' } // Parse to ArrayBuffer
{ body: 'none' } // Don't parse body. This is the default value
```

## Query parsing

The `req.query` parameter returns the query start index, include the `?` character. You can parse query with `@stricjs/utils`:

```typescript
import { Router } from '@stricjs/router';  
import { query as parse } from '@stricjs/utils';

export default new Router()
    .get('/id/:id', req => {
        // If the query does not exist only respond with the ID
        if (req.query === -1) return new Response(req.params.id);

        // Get the 'name' parameter from query if query exists
        return new Response(
            req.params.id + ' ' + parse(
                // Get the query string without '?'
                req.url.substring(req.query + 1)
            ).name
        );
    });
```

## Guarding

Guarding routes and validating data.

### Routes

Guard routes work like wildcard but these routes are invoked first to check whether a specific sub-route should be handled.&#x20;

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    // Return null to tell the router to use the 404 handler
    .guard('/', req => req.path === 'forbidden' ? null : true)
    .get('/', () => new Response('Hi'))
    .post('/json', req => req.json().then(Response.json))
    .get('/forbidden', () => new Response('Never response'));
```

### Input validations

The `@stricjs/utils` component provides a simple and fast guard for checking user input.

```typescript
import { Router } from '@stricjs/router';
import { guard } from '@stricjs/utils';

const check = guard.create({ 
    name: 'str', // string
    age: 'num', // number
    address: '?str' // Optional string (prefix with ?)
    /*
        More things here, for example:
        data: {
            isRegistered: 'bool'
        }
    */
});

// Return the current object if the type matches, else null
// For this case it matches so it returns the passed object
check({
    name: 'Dave',
    age: 23
});

// Register a new type
guard.register('mycustomtype', (object: any) => {
    // Do validation here, should return true or false
});

/*
    Basic types:
    - 'str': string
    - 'num': number
    - 'bool': boolean
    - 'nil': null
    - 'undef': undefined
    - 'bf': Buffer
*/ 
```

Right now guard function is limited and does not support arrays (Arrays are very hard to compose).

## Router groups

Using groups to split the route handlers.

```typescript
import { Router, Group } from '@stricjs/router';

// IDs
const idList = {
    '0': 'a',
    '1': 'b',
    '2': 'c'
};

// Response stuff
const notFound = { status: 404 };
const msg = 'Welcome! Go to /search/:id to search for specific IDs';
const group = new Group('/search')
    .get('/:id', req => {
        if (req.params.id in idList)
            return new Response(idList[req.params.id]);
        return new Response('Item not found', notFound);
    });

// Router
export default new Router()
    .plug(group)
    .get('/', () => new Response(msg));
```

This is a simple example which create a new group with the root set to `/search`, so the actual path that is registered is `/search/:id`, not `:/id`.

## Special handlers

Stric provides a fast way to handle 404 when a route handler is not found.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .get('/', () => new Response('Hi'))
    .use(404, req => { /* Maybe something here to return */ });
```

Call `use(404)` to use the default `404` handler, `use(404, handler)` to add a custom handler.

For error handling, you can use the default `500` handler or register a custom handler the same way as the `404` handler.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .get('/', () => {
        throw new Error('Error');
    })  
    .use(500, err => new Response(err.message));
```

## Storing states

You can store states and methods for using in requests.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .store('count', 0)
    // On every request increment count by one and return it as a response
    .get('/increment', (_, store) => new Response(
        String(++store.count)
    ));
```

All states are stored in an object which is passed as a reference to the handler as the second argument, which mean you can change the properties of the store but not the store itself.

## WebSocket

You can split routes for WebSocket handlers.

```typescript
import { Router } from '@stricjs/router';

export default new Router()
    .ws('/', {
        message(ws) {
            ws.send('Hi client!');
        }
    });
```

WebSocket routes cannot collide with other route handlers of the router, but you can still register route handlers with the same pathname but not `GET` method.

<details>

<summary>Details</summary>

* WebSocket handlers are stored in a list, which is then attached with the upgrade data when `server.upgrade(req, opts)` is called.
* The WebSocket handler is stored in the static route handlers list as a number which represents the index of the handler in the WebSocket handlers list.
* When the path matches, the WebSocket handler is attached with the current `Request` object as a property `_`. The request object will be sent to the WebSocket event handlers as attached data.
* Each WebSocket event handler will check if the specific handler for that event was registered. If it is registered the event handler will be executed.

</details>

## Optimizations

Here are some optimizations you can do to make your Stric application faster.

### Set the base URL&#x20;

You can specify the exact base URL of your app to make path parsing faster. Note that will not works correctly if your app has subdomains.

<details>

<summary>Details</summary>

* The `createFetch` function calculates the length of the `base` property.
* The calculated length actually is the start index of the path part, so we can inject it directly into the code to check for query start index faster.

</details>

```typescript
import { Router } from '@stricjs/router';

export default new Router({ base: 'http://localhost:3000' })
   .get('/', () => new Response('Hi')); 
```

### No path parsing

Set `parsePath` to `false` will skip slicing the path for matching routes and use the URL directly. This optimization will only work with the base optimization.

```typescript
import { Router } from '@stricjs/router';

export default new Router({ 
   base: 'http://localhost:3000', 
   parsePath: false 
}).get('/', () => new Response('Hi')); 
```

<details>

<summary>Details</summary>

* Normally `req.path` is parsed to check the pathname.
* If you disable path parsing `req.path` will not be usable.

</details>

### Faster query parsing

If you only need the value of a single key in the query, you can create a specific parser with `@stricjs/utils` to search for the value. This function will return the value without decoding.

```typescript
import { Router } from '@stricjs/router';
import { qs } from '@stricjs/utils';

// Only search for a single value of name
const parse = qs.searchKey('name');

export default new Router()
    .get('/', req => new Response(
        // Get the value of the name parameter if presented
        // If not presented this function will return null
        parse(req.url, req.query + 1)
    ));
```

### Using macros

Macros inject your code directly to your `fetch` without caching and calling the handler like usual, which can reduce response time. This feature should **only** be used for small handlers.

```typescript
import { Router, macro } from '@stricjs/router';

export default new Router()
    .get('/', macro(() => new Response('Hi')));
```

For returning a string response, macro has a shorthand.

```typescript
import { Router, macro } from '@stricjs/router';

export default new Router().get('/', macro('Hi'));
```

#### Limitations

Because macro code is injected directly to the `fetch` function, it has some limitations.

* Macros cannot access outside variables. To access variables outside the function scope you need to store it using `app.store()`.
* Macros code cannot use `await`. The fetch function is synchronous. You can return a promise, but can't use `await`.
* Macros need proper parameters name. Code in macros that directly use the request and store need to have the same name as the variables in `fetch`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://stricjs.gitbook.io/docs/basic/routing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
