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.
You can define a route with another request method as well.
import { Router } from'@stricjs/router'; exportdefaultnewRouter()// 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.
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.
This will respond to every request pathname starts with /private/.
Body parsing
Stric has a set of predefined body parser you can use.
import { Router } from'@stricjs/router';exportdefaultnewRouter().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:
import { Router } from'@stricjs/router'; import { query as parse } from'@stricjs/utils';exportdefaultnewRouter().get('/id/:id', req => {// If the query does not exist only respond with the IDif (req.query ===-1) returnnewResponse(req.params.id);// Get the 'name' parameter from query if query existsreturnnewResponse(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.
import { Router } from'@stricjs/router';exportdefaultnewRouter()// Return null to tell the router to use the 404 handler.guard('/', req =>req.path ==='forbidden'?null:true).get('/', () =>newResponse('Hi')).post('/json', req =>req.json().then(Response.json)).get('/forbidden', () =>newResponse('Never response'));
Input validations
The @stricjs/utils component provides a simple and fast guard for checking user input.
import { Router } from'@stricjs/router';import { guard } from'@stricjs/utils';constcheck=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 objectcheck({ name:'Dave', age:23});// Register a new typeguard.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.
import { Router, Group } from'@stricjs/router';// IDsconstidList= {'0':'a','1':'b','2':'c'};// Response stuffconstnotFound= { status:404 };constmsg='Welcome! Go to /search/:id to search for specific IDs';constgroup=newGroup('/search').get('/:id', req => {if (req.params.id in idList)returnnewResponse(idList[req.params.id]);returnnewResponse('Item not found', notFound); });// RouterexportdefaultnewRouter().plug(group).get('/', () =>newResponse(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.
import { Router } from'@stricjs/router';exportdefaultnewRouter().get('/', () =>newResponse('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.
You can store states and methods for using in requests.
import { Router } from'@stricjs/router';exportdefaultnewRouter().store('count',0)// On every request increment count by one and return it as a response.get('/increment', (_, store) =>newResponse(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 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
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.
Optimizations
Here are some optimizations you can do to make your Stric application faster.
Set the base URL
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
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.
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.
Normally req.path is parsed to check the pathname.
If you disable path parsing req.path will not be usable.
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.
import { Router } from'@stricjs/router';import { qs } from'@stricjs/utils';// Only search for a single value of nameconstparse=qs.searchKey('name');exportdefaultnewRouter().get('/', req =>newResponse(// Get the value of the name parameter if presented// If not presented this function will return nullparse(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.