Skip to content

Commit

Permalink
feat: allow dynamic routing based on file paths (#55)
Browse files Browse the repository at this point in the history
* feat: allow dynamic routing based on file paths

* docs: add dynamic routes documentation

* feat: fix collision between static assets and dynamic routes. Update examples and docs

* fix: update documentation to highlight static assets have lower priority than fixed routes

Co-authored-by: Rafael Fernández López <[email protected]>
  • Loading branch information
Angelmmiguel and ereslibre authored Dec 13, 2022
1 parent be99d39 commit f96d544
Show file tree
Hide file tree
Showing 32 changed files with 920 additions and 117 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target
*.wasm
!tests/**/*.wasm
examples/*.toml
.DS_Store
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ glob = "0.3.0"
toml = "0.5.9"
base64 = "0.13.1"
clap = { version = "4.0.10", features = ["derive"] }
regex = "1"

[workspace]
members = [
Expand All @@ -40,5 +41,6 @@ members = [
exclude = [
"examples/pdf-create",
"examples/rust-basic",
"examples/rust-kv"
"examples/rust-kv",
"examples/rust-params"
]
48 changes: 48 additions & 0 deletions docs/docs/features/dynamic-routes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
sidebar_position: 3
---

# Dynamic routes

Defining static routes may not be enough for some applications. You may need a worker to process URLs that includes identifiers. **To create a worker associated with a dynamic route, include the route parameter in brackets when setting the worker filename**.

For example, imagine you want a worker that replies to the following URLs:

- `/show/1`
- `/show/2`

With dynamic routes, you can create a worker with the `show/[id].js` filename. This worker will reply to any `/show/X` route automatically.

After defining the route paremeters, the worker receives a special argument called `params`. It includes the value of all defined parameters. Note that the name of the parameter will be defined by the text between the brackets.

Check these guides to understand how to read parameters in the different supported languages:

* [Dynamic routes in JavaScript](../tutorials/javascript-workers.md#dynamic-routes)
* [Dynamic routes in Rust](../tutorials/rust-workers.md#dynamic-routes)

## Dynamic routes and folders

Folders can follow this pattern too. You can define a folder that has a route parameter:

```
$ tree ./examples/with-params
./examples/with-params
├── [id]
   └── fixed.js
```

In this case, the `./[id]/fixed.js` worker can reply to URLs like `/abc/fixed` and `/test/fixed`.

## Multiple parameters

As both files and folders can be dynamic, workers may receive multiple parameters. The `params` object includes the value of all the different parameters.

```
$ tree .
.
├── [resource]
   └── [id]
└── show.js
```

In this case, the `./[resource]/[id]/show.js` worker replies to URLs like `/articles/2/show`.
2 changes: 1 addition & 1 deletion docs/docs/features/environment-variables.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Environment Variables
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/features/static-assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ The `about.html` file will be served as `/about`.

## Index file in public

An `index.html` can be added to the `public` folder and it will be mounted in `/`. Note that workers have priority, so if there's an `/index.js` and `/public/index.html` files, the `/` route will be served by the former.
An `index.html` can be added to the `public` folder and it will be mounted in `/`. Note that static files have a lower priority, so if there's an `/index.js` and `/public/index.html` files, the `/` route will be served by the former.
19 changes: 19 additions & 0 deletions docs/docs/tutorials/javascript-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,25 @@ To add a KV store to your worker, follow these steps:

1. Finally, open <http://127.0.0.1:8080/counter> in your browser.

## Dynamic routes

You can define [dynamic routes by adding route parameters to your worker files](../features/dynamic-routes.md) (like `[id].js`). To read them in JavaScript, access to the `req.params` object:

```js
/**
* Builds a reply to the given request
*/
const reply = (req) => {
// Build a new response
return new Response(`Hey! I got this parameter: ${req.params.id}`);
}
// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
```

## Read environment variables

Environment variables are configured [via the related TOML configuration file](../features/environment-variables.md). These variables are directly injected as global constants in your worker. To read them, just use the same name you configured in your TOML file:
Expand Down
46 changes: 44 additions & 2 deletions docs/docs/tutorials/rust-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ To add a KV store to your worker, follow these steps:
};
#[worker(cache)]
fn handler(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
fn reply(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
Expand All @@ -169,7 +169,7 @@ To add a KV store to your worker, follow these steps:
};
#[worker(cache)]
fn handler(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
fn reply(_req: Request<String>, cache: &mut Cache) -> Result<Response<Content>> {
// Applied changes here to use the Response method. This requires changes
// on signature and how it returns the data.
let count = cache.get("counter");
Expand Down Expand Up @@ -231,6 +231,48 @@ To add a KV store to your worker, follow these steps:
1. Finally, open <http://127.0.0.1:8080/worker-kv> in your browser.
## Dynamic routes
You can define [dynamic routes by adding route parameters to your worker files](../features/dynamic-routes.md) (like `[id].wasm`). To read them in Rust, follow these steps:
1. Add the `params` configuration parameter to the `worker` macro and update the method signature to receive the values:
```rust title="src/main.rs"
use anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker(params)]
fn reply(req: Request<String>, params: &HashMap<String, String>) -> Result<Response<Content>> {
// ...
}
```
1. Then, you can read the values from the `params` argument:
```rust title="src/main.rs"
use anyhow::Result;
use wasm_workers_rs::{
worker,
http::{self, Request, Response},
Content,
};
#[worker(params)]
fn reply(req: Request<String>, params: &HashMap<String, String>) -> Result<Response<Content>> {
let missing_param = String::from("none");
let id = params.get("id").unwrap_or_else(|| &missing_param);
Ok(http::Response::builder()
.status(200)
.header("x-generated-by", "wasm-workers-server")
.body(format!("Hey! The parameter is: {}", id).into())?)
}
```
## Read environment variables
Environment variables are configured [via the related TOML configuration file](../features/environment-variables.md). These variables are accessible via `std::env` in your worker. To read them, just use the same name you configured in your TOML file:
Expand Down
5 changes: 5 additions & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ rust-kv:
cargo build --target wasm32-wasi --release && \
mv target/wasm32-wasi/release/rust-kv.wasm ./kv.wasm

rust-params:
cd rust-params && \
cargo build --target wasm32-wasi --release && \
mv target/wasm32-wasi/release/rust-params.wasm "./[id].wasm"

all: rust-basic rust-kv
31 changes: 31 additions & 0 deletions examples/js-params/[id].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Builds a reply to the given request
*/
const reply = (req) => {
// Body response
const body = `<!DOCTYPE html>
<head>
<title>Wasm Workers Server</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="/water.min.css">
<link rel="stylesheet" href="/main.css">
</head>
<body>
<main>
<h1>Hello from Wasm Workers Server 👋</h1>
<p>
This is a dynamic route! The <code>[id].js</code> worker is replying this URL.
The <code>id</code> parameter value is: <code>${req.params?.id}</code>
</p>
<p>Read more about dynamic routes <a href="https://workers.wasmlabs.dev/docs/features/dynamic-routes">in the documentation</a></p>
</main>
</body>`;

return new Response(body);
}

// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
31 changes: 31 additions & 0 deletions examples/js-params/[id]/fixed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Builds a reply to the given request
*/
const reply = (req) => {
// Body response
const body = `<!DOCTYPE html>
<head>
<title>Wasm Workers Server</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="/water.min.css">
<link rel="stylesheet" href="/main.css">
</head>
<body>
<main>
<h1>Hello from Wasm Workers Server 👋</h1>
<p>
This is a dynamic route! The <code>[id]/fixed.js</code> worker is replying this URL.
The <code>id</code> parameter value is: <code>${req.params?.id}</code>
</p>
<p>Read more about dynamic routes <a href="https://workers.wasmlabs.dev/docs/features/dynamic-routes">in the documentation</a></p>
</main>
</body>`;

return new Response(body);
}

// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
30 changes: 30 additions & 0 deletions examples/js-params/fixed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Builds a reply to the given request
*/
const reply = () => {
// Body response
const body = `<!DOCTYPE html>
<head>
<title>Wasm Workers Server</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="/water.min.css">
<link rel="stylesheet" href="/main.css">
</head>
<body>
<main>
<h1>Hello from Wasm Workers Server 👋</h1>
<p>
This is a fixed route. There isn't any parameter here.
</p>
<p>Read more about dynamic routes <a href="https://workers.wasmlabs.dev/docs/features/dynamic-routes">in the documentation</a></p>
</main>
</body>`;

return new Response(body);
}

// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
28 changes: 28 additions & 0 deletions examples/js-params/public/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
body {
max-width: 1000px;
}

main {
margin: 5rem 0;
}

h1,
p {
text-align: center;
}

h1 {
margin-bottom: 2rem;
}

pre {
font-size: .9rem;
}

pre>code {
padding: 2rem;
}

p {
margin-top: 2rem;
}
30 changes: 30 additions & 0 deletions examples/js-params/public/water.min.css

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions examples/js-params/sub/[id].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Builds a reply to the given request
*/
const reply = (req) => {
// Body response
const body = `<!DOCTYPE html>
<head>
<title>Wasm Workers Server</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="/water.min.css">
<link rel="stylesheet" href="/main.css">
</head>
<body>
<main>
<h1>Hello from Wasm Workers Server 👋</h1>
<p>
This is a dynamic route! The <code>sub/[id].js</code> worker is replying this URL.
The <code>id</code> parameter value is: <code>${req.params?.id}</code>
</p>
<p>Read more about dynamic routes <a href="https://workers.wasmlabs.dev/docs/features/dynamic-routes">in the documentation</a></p>
</main>
</body>`;

return new Response(body);
}

// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
Loading

0 comments on commit f96d544

Please sign in to comment.