Skip to content
Ayo Reis edited this page Mar 14, 2023 · 1 revision

This is a simple function that takes in a Request returns a Response

function handle(request: Request) {
    return new Response('Hello world!');
}

Let's serve it with an imaginary function

serve(handle);

And open it in the browser

🌐 Browser
Hello world!

💥 We have a server!

When our website starts growing and we need more routes we could use a switch/case

function handle(request: Request) {
    const url = new URL(request.url).pathname;

    switch (url) {
        case '/':
            return new Response('Home');
        case '/about':
            return new Response('About');
    }
}

Then there are dynamic routes, maybe its better to use if/elses, they are more versatile

const urlPattern = new URLPattern({
	pathname: '/:postId',
});

function handle(request: Request) {
	const url = new URL(request.url).pathname;

	if (urlPattern.test({ pathname: url })) {
		const { postId } = urlPattern.exec({ pathname: url })!.pathname.groups;

		return new Response(`Post #${postId}`);
	}
}

This is starting to get very messy!

Let's organize it by moving each route into their own function

function index() {
	return new Response('Home');
}

function about() {
	return new Response('About');
}

function post(url: string, urlPattern: URLPattern) {
	const { postId } =
		urlPattern.exec({ pathname: url })!.pathname.groups;

	return new Response(`Post #${postId}`);
}

And update handle

function handle(request: Request) {
	const url = new URL(request.url).pathname;

    switch (url) {
        case '/':
            return index();
        case '/about':
            return about();
    }

	if (urlPattern.test({ pathname: url })) {
		return post(url, urlPattern);
	}
}

Better, but what if we defined this with an array instead?

const routes = [
	{
		url: '/',
		handler() {
			return new Response('Home');
		},
	},

	{
		url: '/about',
		handler() {
			return new Response('About');
		},
	},

	{
		url: '/:postId',
		handler(url: string, urlPattern: URLPattern) {
			const { postId } =
				urlPattern.exec({ pathname: url })!.pathname.groups;

			return new Response(`Post #${postId}`);
		},
	},
];

It's ok, but each function is nested more than it needs to, we could use Array.push to dynamicly add our routes and fix that

routes.push({
	url: '/',
	handler() {
		return new Response('Home');
	},
});

🧼 Looking clean! But we can do better!

Let's create a Router class with a custom push method

class Router {
    #routes: string[] = []

    push(url: string, handler: () => Response) {
        this.#routes.push({ url, handler })
    }
}

And use it!

const router = new Router()

router.push('/', () => {
    return new Response('Hello world!')
})

Maybe this should only work for get request, let's add a method for that... now we can do this

const router = new Router()

router.get('/', () => {
    return new Response('Hello world!')
})

Just need to write a new Router.handle and we good to go, awesome!

Relax router does this plus much more!

Clone this wiki locally