Creating routes
The only thing you need to create a route is to wrap a function that you would write in the backend with the createRoute
function. It will return a function with the same signature as the function you passed to it, except that it will do a request under the hood.
import { createRoute } from 'agrume'
const sayHello = createRoute(
async () => {
return 'Hello world!'
},
)
Note
sayHello
will be typed as () => Promise<string>
.
Note
The above code will be transformed to
async function sayHello() {
return fetch('/api/sayHello', { method: 'POST' }).then((response) => response.json())
}
You can then use the sayHello
function to do a request to the route:
sayHello().then(console.log) // Hello world!
You don’t have to necessarily return a value. Your function can have a signature like (parameters: T) => Promise<void>
. In this case, the HTTP response will be 204 No Content
.
import { createRoute } from 'agrume'
const sayHello = createRoute(
async (name: string) => {
console.log(`Hello ${name}!`)
},
)
Warning
At the moment you can only use the createRoute
function in .js
, .jsx
, .ts
and .tsx
files. To use Agrume in other files, you need to export the createRoute
function from one of the valid files and import it into the other files. (See Vue example)
Parameters
You can request parameters from the client just like you would do with a normal function:
import { createRoute } from 'agrume'
const sayHello = createRoute(
async (name: string) => {
return `Hello ${name}!`
},
)
You can then use the sayHello
function to do a request to the route:
sayHello('Arthur').then(console.log) // Hello Arthur!
Note
Agrume is type-safe so if you don’t pass the correct parameters to the function, your IDE will warn you!
Note
Agrume will pass the parameters to the server as body parameters so every request will be a POST
request.
Realtime routes
You can use the createRoute
function to create a realtime route. It can replace WebSockets.
client
→ server
To send data from the client to the server in real time, you can require a generator function as a parameter of your route.
import { createRoute } from 'agrume'
const realtime = createRoute(
async (clicks: AsyncGenerator<[number, number]>) => {
for await (const [x, y] of clicks) {
console.log(x, y)
}
},
)
Then, you can use the realtime
function as follows:
realtime(async function* () {
while (true) {
yield [Math.random(), Math.random()]
await new Promise((resolve) => setTimeout(resolve, 1000))
}
})
The code above will send random coordinates every second. The server will log the coordinates in real time.
server
→ client
To send data from the server to the client in real time, you can pass a generator function to the createRoute
function.
import { createRoute } from 'agrume'
const realtime = createRoute(
async function* () {
while (true) {
yield new Date().toISOString()
await new Promise((resolve) => setTimeout(resolve, 1000))
}
},
)
The code above will send the current date of the server every second.
You can then use your function like a normal generator function:
for await (const date of await realtime()) {
console.log(date)
}
Combining both (client
⇄ server
)
You can receive and send data in real time by combining both methods:
import { createRoute } from 'agrume'
const chat = createRoute(
async function* (userMessages: AsyncGenerator<string>) {
(async () => {
// Receive messages in parallel with sending messages
for await (const message of userMessages) {
sendToAll(message)
}
})()
for await (const message of allMessagesIterator) {
yield message
}
},
)
By passing a generator function (the messages from your user) to the chat
function, you can receive messages from all other users in real time.
Error throwing
You can use the http-errors
package to throw a custom HTTP error. Agrume re-exports http-errors
in a HTTPError
member. You don’t need to install the package yourself.
import { createRoute } from 'agrume'
import { HTTPError } from 'agrume/errors'
const sayHello = createRoute(
async (name: string) => {
throw HTTPError.ImATeapot()
return `Hello ${name}!`
},
)
Options
You can configure each route individually by passing an object to the createRoute
function.
path
You can specify the path of the route by passing a string starting with /
to the path
option:
import { createRoute } from 'agrume'
const getDogImage = createRoute(
async () => {}, {
path: '/dog'
},
)
getClient
By default, Agrume will transform the createRoute
function into a function that can be called to do a request to the route. The default client will use the fetch
API to do the request. However, you can specify your own client by passing a function to the getClient
option.
For example, if you want use a custom server that listen on port 3000
, you can do:
import { createRoute } from 'agrume'
const getDogImage = createRoute(
async () => {},
{
getClient(requestOptions) {
return async (parameters) => {
const response = await fetch(
`http://localhost:3000${requestOptions.url}`,
{
...requestOptions,
body: JSON.stringify(parameters)
}
)
return response.json()
}
}
},
)
Note
The parameters
argument cannot be inferred by TypeScript, so it will be typed as any
. You can type it yourself, but it must be the same type as the parameters of the route.
Important
getClient
will affect the type of the route. For example, if your getClient
function returns the requestOptions
, the type of the route will be () => Promise<RequestOptions>
.
The default getClient
function can be found in the source code (search for the getDefaultClient
function).
Have a look at the Recipes section to see what you can do with the getClient
option.