App
The App class is the heart of Fresh and routes incoming requests to the
correct middlewares. This is where routes,
middlewares, layouts and more are defined.
const app = new App()
.use(staticFiles())
.get("/", () => new Response("hello"));
// Start server
app.listen();TipTo use JSX in your
mainfile (e.g. withctx.render(<h1>Hello</h1>)), rename it tomain.tsxand setserverEntry: "main.tsx"in thefresh()plugin options invite.config.ts.
Configuration
The App constructor accepts an options object:
const app = new App({
// Serve the app from a sub-path instead of root.
// All routes will be prefixed with this path.
basePath: "/my-app",
});With basePath: "/my-app", a route registered at /about will respond to
/my-app/about. This is useful when Fresh runs behind a reverse proxy or is
mounted alongside other apps. The base path is available in handlers via
ctx.config.basePath.
All items are applied from top to bottom. This means that when you defined a
middleware after a .get() handler, it won’t be included.
const app = new App()
.use((ctx) => {
// Will be called for all middlewares
return ctx.next();
})
.get("/", () => new Response("hello"))
.use((ctx) => {
// Will only be called for `/about`
return ctx.next();
})
.get("/about", () => new Response("About me"));.use()
Add one or more middlewares. Middlewares are matched left to right.
// Add a middleware at the root
app.use(async (ctx) => {
console.log("my middleware");
return await ctx.next();
});You can also add multiple middlewares:
app.use(middleware1, middleware2, middleware3);Adding middlewares at a specific path:
app.use("/foo/bar", middleware);Middlewares can also be instantiated lazily:
app.use("/foo/bar", async () => {
const mod = await import("./path/to/my/middleware.ts");
return mod.default;
});.get()
Respond to a GET request with the specified middlewares.
app.get("/about", async (ctx) => {
return new Response(`GET: ${ctx.url.pathname}`);
});Respond with multiple middlewares:
app.get("/about", middleware1, middleware2, async (ctx) => {
return new Response(`GET: ${ctx.url.pathname}`);
});You can also pass lazy middlewares:
app.get("/about", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.post()
Respond to a POST request with the specified middlewares.
app.post("/api/user/:id", async (ctx) => {
await somehowCreateUser(ctx.params.id);
return new Response(`User created`);
});Respond with multiple middlewares:
app.post("/api/user/:id", middleware1, middleware2, async (ctx) => {
await somehowCreateUser(ctx.params.id);
return new Response(`User created`);
});You can also pass lazy middlewares:
app.post("/api/user/:id", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.put()
Respond to a PUT request with the specified middlewares.
app.put("/api/user/:id", async (ctx) => {
await somehowSaveUser(ctx.params.id);
return new Response(`Updated user`);
});Respond with multiple middlewares:
app.put("/api/user/:id", middleware1, middleware2, async (ctx) => {
await somehowSaveUser(ctx.params.id);
return new Response(`Updated user`);
});You can also pass lazy middlewares:
app.put("/api/user/:id", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.delete()
Respond to a DELETE request with the specified middlewares.
app.delete("/api/user/:id", async (ctx) => {
await somehowDeleteUser(ctx.params.id);
return new Response(`User deleted`);
});Respond with multiple middlewares:
app.delete("/api/user/:id", middleware1, middleware2, async (ctx) => {
await somehowDeleteUser(ctx.params.id);
return new Response(`User deleted`);
});You can also pass lazy middlewares:
app.delete("/api/user/:id", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.head()
Respond to a HEAD request with the specified middlewares.
app.head("/api/user/:id", async (ctx) => {
return new Response(null, { status: 200 });
});Respond with multiple middlewares:
app.head("/api/user/:id", middleware1, middleware2, async (ctx) => {
return new Response(null, { status: 200 });
});You can also pass lazy middlewares:
app.head("/api/user/:id", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.all()
Respond to a request for all HTTP verbs with the specified middlewares.
app.all("/api/foo", async (ctx) => {
return new Response("hehe");
});Respond with multiple middlewares:
app.all("/api/foo", middleware1, middleware2, async (ctx) => {
return new Response("hehe");
});You can also pass lazy middlewares:
app.all("/api/foo", async () => {
const mod = await import("./middleware-or-handler.ts");
return mod.default;
});.fsRoute()
Injects all file-based routes, middlewares, layouts and error pages to the app instance.
app.fsRoutes();You can optionally pass a path where they should be mounted.
app.fsRoutes("/foo/bar");InfoIf possible, routes are lazily loaded. Routes that set a route config and set
routeOverridein particular, are never lazily loaded as Fresh would need to load the file to get the route pattern.
.route()
Register a route with a component and optional handlers for data loading.
app.route("/about", {
component: (ctx) => <h1>About {ctx.data.name}</h1>,
handler: {
GET(ctx) {
return page({ name: "Fresh" });
},
},
});.appWrapper()
Set the App Wrapper component. This is where the
outer HTML, typically up until the <body>-tag is rendered.
.layout()
Set a Layout component at the specified path. The app wrapper component and prior layouts are inherited by default unless opted out.
.onError()
Set an error route or middleware that will be rendered when it catches an error.
Setting a middleware:
// top level error handler
app.onError("*", (ctx) => {
return new Response(String(ctx.error), { status: 500 });
});Setting a route with a component:
app.onError("*", {
component: (ctx) => <h1>Oops! {String(ctx.error)}</h1>,
});.notFound()
Call this middleware or route whenever a HTTP 404 error is caught.
app.notFound(() => {
return new Response("Not found", { status: 404 });
});With a component:
app.notFound((ctx) => {
return ctx.render(<h1>Page not found</h1>);
});.mountApp()
Mount an entire other app at the specified path.
const someRoutes = new App()
.get("/sitemap.xml", (ctx) => {/* ... */})
.get("/robots.txt", (ctx) => {/* ... */});
export const app = new App()
.use(staticFiles())
.mountApp("/", someRoutes())
.fsRoutes();.handler()
Create a handler function out of your app. This is a function where you can pass
a Request instance
to and receive a
Response.
const app = new App()
.get("/", () => new Response("hello"));
const handler = app.handler();
const response = await handler(new Request("http://localhost"));
console.log(await response.text()); // Logs: "hello"This functionality is often used during testing or to run Fresh inside other frameworks.
.listen()
Spawns a server and listens for incoming connections. This calls Deno.serve()
internally.
const app = new App()
.get("/", () => new Response("hello"));
app.listen();You can pass an options object to customize which port to listen on and other aspects.
app.listen({ port: 4000 });Important:
.listen()is only used when running your app directly withdeno run -A main.ts. The default project setup usesdeno task dev(Vite dev server) anddeno task start(deno serve), which spawn their own servers - calling.listen()alongside these will create a second server and causeAddrInUseerrors.To customize the port in the default setup:
- Dev: set
server.portinvite.config.ts- Prod: pass
--porttodeno servein your task, e.g."start": "deno serve --port 4000 -A _fresh/server.js"