{"id":19680615,"url":"https://github.com/oakserver/acorn","last_synced_at":"2025-04-29T04:31:33.562Z","repository":{"id":57676580,"uuid":"488839800","full_name":"oakserver/acorn","owner":"oakserver","description":"A focused RESTful server framework for Deno 🌰🦕","archived":false,"fork":false,"pushed_at":"2024-11-11T02:10:54.000Z","size":392,"stargazers_count":61,"open_issues_count":3,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T13:05:59.810Z","etag":null,"topics":["bun","cloudflare-workers","deno","javascript","json","nodejs","restful","typescript"],"latest_commit_sha":null,"homepage":"https://oakserver.org/acorn","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oakserver.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-05T05:12:10.000Z","updated_at":"2025-03-18T06:04:24.000Z","dependencies_parsed_at":"2024-11-11T03:19:26.056Z","dependency_job_id":"33760bb4-7021-4eb2-a8ec-49ac82e29589","html_url":"https://github.com/oakserver/acorn","commit_stats":{"total_commits":28,"total_committers":1,"mean_commits":28.0,"dds":0.0,"last_synced_commit":"0fe000354f8c10cefb10ba3f7df86034a9b8aa88"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oakserver%2Facorn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oakserver%2Facorn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oakserver%2Facorn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oakserver%2Facorn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oakserver","download_url":"https://codeload.github.com/oakserver/acorn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251432834,"owners_count":21588664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","cloudflare-workers","deno","javascript","json","nodejs","restful","typescript"],"created_at":"2024-11-11T18:05:33.753Z","updated_at":"2025-04-29T04:31:33.275Z","avatar_url":"https://github.com/oakserver.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# acorn\n\n[![jsr.io/@oak/acorn](https://jsr.io/badges/@oak/acorn)](https://jsr.io/@oak/acorn)\n[![jsr.io/@oak/acorn score](https://jsr.io/badges/@oak/acorn/score)](https://jsr.io/@oak/acorn)\n\nA focused framework for creating RESTful JSON services across various JavaScript\nand TypeScript runtime environments including [Deno runtime](https://deno.com/),\n[Deno Deploy](https://deno.com/deploy), [Node.js](https://nodejs.org/),\n[Bun](https://bun.sh/) and\n[Cloudflare Workers](https://workers.cloudflare.com/).\n\nIt focuses on providing a router which handles inbound requests and makes it\ntrivial to respond to those requests with JSON. It also provides several other\nfeatures which make creating API servers with acorn production ready. acorn is a\nfocused framework for creating RESTful JSON services across\n\n## Basic usage\n\nacorn is designed to work on many different JavaScript and TypeScript runtimes,\nincluding Deno, Node.js, Bun, and Cloudflare Workers. Basic usage requires\ninstalling acorn to your project and then creating a router to handle requests.\n\n### Installing for Deno\n\nTo install acorn for Deno, you can install it via the Deno runtime CLI:\n\n```\ndeno add jsr:@oak/acorn\n```\n\n### Installing for Node.js or Cloudflare Workers\n\nTo install acorn for Node.js or Cloudflare Workers, you can install it via your\npreferred package manager.\n\n#### npm\n\n```\nnpx jsr add @oak/acorn\n```\n\n#### yarn\n\n```\nyarn dlx jsr add @oak/acorn\n```\n\n#### pnpm\n\n```\npnpm dlx jsr add @oak/acorn\n```\n\n### Installing for Bun\n\nTo install acorn for Bun, you can install it via the Bun runtime CLI:\n\n```\nbunx jsr add @oak/acorn\n```\n\n### Usage with Deno, Node.js, and Bun\n\nBasic usage of acorn for Deno, Node.js, and Bun is the same. You import the\n`Router`, create an instance of it, register routes on the router, and then\ncalled the `.listen()` method on the router to start listening for requests:\n\n```ts\nimport { Router } from \"@oak/acorn\";\n\nconst router = new Router();\nrouter.get(\"/\", () =\u003e ({ hello: \"world\" }));\nrouter.listen({ port: 3000 });\n```\n\n### Usage with Cloudflare Workers\n\nBasic usage for Cloudflare Workers requires exporting a fetch handler which is\nintegrated into the router, and therefore you export the router as the default\nexport of the module:\n\n```ts\nimport { Router } from \"@oak/acorn\";\n\nconst router = new Router();\nrouter.get(\"/\", () =\u003e ({ hello: \"world\" }));\nexport default router;\n```\n\n## Router\n\nThe `Router` is the core of acorn and is responsible for handling inbound\nrequests and routing them to the appropriate handler. The router provides\nmethods for registering routes for different HTTP methods and handling requests\nfor those routes.\n\n### Default behaviors\n\nThe router provides several automatic behaviors which are designed to make\ncreating RESTful JSON services easier. These behaviors include handling\n`404 Not Found` responses, `405 Method Not Allowed` responses, and providing a\ndefault response for `OPTIONS` requests.\n\n#### Not Found\n\nWhen a request is received by the router and no route is matched, the router\nwill send a `404 Not Found` response to the client. This is the default behavior\nof the router and can be overridden by providing a `onNotFound` hook to the\nrouter.\n\n#### Method Not Allowed\n\nWhen a request is received by the router and a route is matched but there is no\nhandler for the method of the request, the router will send a\n`405 Method Not Allowed` response to the client which will provide the allowed\nmethods. This is the default behavior of the router and can be overridden by\nproviding a status handler.\n\n#### Options\n\nWhen a request is received by the router and the method of the request is\n`OPTIONS`, the router will send a response to the client with the allowed\nmethods for the route. This is the default behavior of the router and can be\noverridden by providing an `options()` route.\n\n## Context\n\nThe `Context` is the object passed to route handlers and provides information\nabout the request and runtime environment. The context object provides access to\nthe `Request` object as well as other useful properties and methods for handling\nrequests.\n\n### `addr`\n\nThe network address of the originator of the request as presented to the runtime\nenvironment.\n\n### `cookies`\n\nThe cookies object which can be used to get and set cookies for the request. If\nencryptions keys are provided to the router, the cookies will be\ncryptographically verified and signed to ensure their integrity.\n\n### `env`\n\nThe environment variables available to the runtime environment. This assists in\nproviding access to the environment variables for the runtime environment\nwithout having to code specifically for each runtime environment.\n\n### `id`\n\nA unique identifier for the request event. This can be useful for logging and\ntracking requests.\n\n### `params`\n\nThe parameters extracted from the URL path by the router.\n\n### `request`\n\nThe Fetch API standard `Request` object which should be handled.\n\n### `responseHeaders`\n\nThe headers that will be sent with the response. This will be merged with other\nheaders to finalize the reponse.\n\n### `url`\n\nThe URL object representing the URL of the request.\n\n### `userAgent`\n\nA parsed version of the `User-Agent` header from the request. This can be used\nto determine the type of client making the request.\n\n### `body()`\n\nA method which returns a promise that resolves with the body of the request\nassumed to be JSON. If the body is not JSON, an error will be thrown. If a body\nschema is provided to the route, the body will be validated against that schema\nbefore being returned.\n\n### `conflict()`\n\nA method which throws a `409 Conflict` error and takes an optional message and\noptional cause.\n\n### `created()`\n\nA method which returns a `Response` with a `201 Created` status code. The method\ntakes the body of the response and an optional object with options for the\nresponse. If a `location` property is provided in the options, the response will\ninclude a `Location` header with the value of the location.\n\nIf `locationParams` is provided in the options, the location will be\ninterpolated with the parameters provided.\n\n### `notFound()`\n\nA method which throws a `404 Not Found` error and takes an optional message and\noptional cause.\n\n### `queryParams()`\n\nA method which returns a promise that resolves with the query parameters of the\nrequest. If a query parameter schema is provided to the route, the query\nparameters will be validated against that schema before being returned.\n\n### `redirect()`\n\nA method which sends a redirect response to the client. The method takes a\nlocation and an optional init object with options for the response. If the\nlocation is a path with parameters, the `params` object can be provided to\ninterpolate the parameters into the URL.\n\n### `throw()`\n\nA method which can be used to throw an HTTP error which will be caught by the\nrouter and handled appropriately. The method takes a status code and an optional\nmessage which will be sent to the client.\n\n### `created()`\n\nA method which returns a `Response` with a `201 Created` status code. The method\ntakes the body of the response and an optional object with options for the\nresponse.\n\nThis is an appropriate response when a `POST` request is made to a resource\ncollection and the resource is created successfully. The options should be\nincluded with a `location` property set to the URL of the created resource. The\n`params` property can be used to provide parameters to the URL. For example if\n`location` is `/books/:id` and `params` is `{ id: 1 }` the URL will be\n`/books/1`.\n\n### `conflict()`\n\nA method which throws a `409 Conflict` error and takes an optional message and\noptional cause.\n\nThis is an appropriate response when a `PUT` request is made to a resource that\ncannot be updated because it is in a state that conflicts with the request.\n\n### `sendEvents()`\n\nA method which starts sending server-sent events to the client. This method\nreturns a `ServerSentEventTarget` which can be used to dispatch events to the\nclient.\n\n### `upgrade()`\n\nA method which can be used to upgrade the request to a `WebSocket` connection.\nWhen the request is upgraded, the request will be handled as a web socket\nconnection and the method will return a `WebSocket` which can be used to\ncommunicate with the client.\n\n**Note:** This method is only available in the Deno runtime and Deno Deploy\ncurrently. If you call this method in a different runtime, an error will be\nthrown.\n\n## Router Handlers\n\nThe `RouteHandler` is the function which is called when a route is matched by\nthe router. The handler is passed the `Context` object and is expected to return\na response. The response can be a plain object which will be serialized to JSON,\na `Response` object. The handler can also return `undefined` if the handler\nwishes to return a no content response. The handler can also return a promise\nwhich resolves with any of the above.\n\n### Registering Routes\n\nRoutes can be registered on the router using the various methods provided by the\nrouter. The most common methods are `get()`, `post()`, `put()`, `patch()`, and\n`delete()`. In addition `options()` and `head()` are provided.\n\nThe methods take a path pattern and a handler function, and optionally an object\nwith options for the route (`RouteInit`). The path pattern is a string which can\ninclude parameters and pattern matching syntax. The handler function is called\nwhen the route is matched and is passed the context object.\n\nFor example, to register a route which responds to a `GET` request:\n\n```ts\nrouter.get(\"/\", () =\u003e ({ hello: \"world\" }));\n```\n\nThe methods also accept a `RouteDescriptor` object, or a path along with a set\nof options (`RouteInitWithHandler`) which includes the handler function.\n\nFor example, to register a route which responds to a `POST` request:\n\n```ts\nrouter.post(\"/\", {\n  handler: () =\u003e ({ hello: \"world\" }),\n});\n```\n\nAnd for a route which responds to a `PUT` request with the full descriptor:\n\n```ts\nrouter.put({\n  path: \"/\",\n  handler: () =\u003e ({ hello: \"world\" }),\n});\n```\n\n### Hooks\n\nThe router provides hooks which can be used to get information about the routing\nprocess and to potentially modify the response. The hooks are provided when\ncreating the router and are called at various points in the routing process.\n\n#### `onRequest()`\n\nThe `onRequest` hook is called when a request is received by the router. The\n`RequestEvent` object is provided to the hook and can be used to inspect the\nrequest.\n\nThe `onRequest` could invoke the `.respond()` method on the `RequestEvent` but\nthis should be avoided.\n\n#### `onNotFound()`\n\nAs a request is being handled by the router, if no route is matched or the route\nhandler returns a `404 Not Found` response the `onNotFound` hook is called.\nThere is a details object which provides the `RequestEvent`being handled, any\n`Response` that has been provided (but not yet sent to the client) and the\n`Route` that was matched, if any.\n\nThe `onNotFound` hook can return a response to be sent to the client. If the\nhook returns `undefined`, the router will continue processing the request.\n\n#### `onHandled()`\n\nAfter a request has been processed by the router and a response has been sent to\nthe client, the `onHandled` hook is called. The hook is provided with a set of\ndetails which include the `RequestEvent`, the `Response`, the `Route` that was\nmatched, and the time in milliseconds that the request took to process.\n\n#### `onError()`\n\nIf an unhandled error occurs in a handler, the `onError` hook is called. The\nhook is provided with a set of details which include the `RequestEvent`, the\n`Response` that was provided, the error that occurred, and the `Route` that was\nmatched, if any.\n\n## Route Parameters\n\nThe router can extract parameters from the URL path and provide them to the\nroute handler. The parameters are extracted from the URL path based on the\npattern matching syntax provided by the\n[`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) library. The\nparameters are provided to the handler as an object with the parameter names as\nthe keys and the values as the values.\n\nFor example, to register a route which extracts a parameter from the URL path:\n\n```ts\nrouter.get(\"/:name\", (ctx) =\u003e {\n  return { hello: ctx.params.name };\n});\n```\n\n## Status Handlers\n\nacorn provides a mechanism for observing or modifying the response to a request\nbased on the status of the response. This is done using status handlers which\nare registered on the router. The status handlers are called when a response is\nbeing sent to the client and the status of the response matches the status or\nstatus range provided to the handler.\n\nThis is intended to be able to provide consistent and customized responses to\nstatus codes across all routes in the router. For example, you could provide a\nstatus handler to handle all `404 Not Found` responses and provide a consistent\nresponse to the client:\n\n```ts\nimport { Router } from \"@oak/acorn\";\nimport { Status, STATUS_TEXT } from \"@oak/commons/status\";\n\nconst router = new Router();\n\nrouter.on(Status.NotFound, () =\u003e {\n  return Response.json(\n    { error: \"Not Found\" },\n    { status: Status.NotFound, statusText: STATUS_TEXT[Status.NotFound] },\n  );\n});\n```\n\n## Schema Validation\n\nacorn integrates the [Valibot](https://valibot.dev/) library to provide schema\nvalidation for query strings, request bodies, and responses. This allows you to\ndefine the shape of the data you expect to receive and send and have it\nvalidated automatically.\n\nYou can provide a schema to the route when registering it on the router. The\nschema is an object which describes the shape of the data you expect to receive\nor send. The schema is defined using the Valibot schema definition language.\n\nFor example, to define a schema for a request body:\n\n```ts\nimport { Router, v } from \"@oak/acorn\";\n\nconst router = new Router();\n\nrouter.post(\"/\", () =\u003e ({ hello: \"world\" }), {\n  schema: {\n    body: v.object({\n      name: v.string(),\n    }),\n  },\n});\n```\n\nThis ensures that the request body is an object with a `name` property which is\na string. If the request body does not match this schema, an error will be\nthrown and the request will not be processed and a `Bad Request` response will\nbe sent to the client.\n\nYou can provide an optional invalid handler to the schema which will be called\nwhen the schema validation fails. This allows you to provide a custom response\nto the client when the request does not match the schema.\n\n## RESTful JSON Services\n\nacorn is designed to make it easy to create RESTful JSON services. The router\nprovides a simple and expressive way to define routes and has several features\nwhich make it easy to create production ready services.\n\n### HTTP Errors\n\nacorn provides a mechanism for throwing HTTP errors from route handlers. The\n`throw()` method on the context object can be used to throw an HTTP error. HTTP\nerrors are caught by the router and handled appropriately. The router will send\na response to the client with the status code and message provided to the\n`throw()` method with the body of the response respecting the content\nnegotiation headers provided by the client.\n\n### No Content Responses\n\nIf a handler returns `undefined`, the router will send a `204 No Content`\nresponse to the client. This is useful when a request is successful but there is\nno content to return to the client.\n\nNo content responses are appropriate for `PUT` or `PATCH` requests that are\nsuccessful but you do not want to return the updated resource to the client.\n\n### Created Responses\n\nThe `created()` method on the context object can be used to send a `201 Created`\nresponse to the client. This is appropriate when a `POST` request is made to a\nresource collection and the resource is created successfully. The method takes\nthe body of the response and an optional object with options for the response.\n\nThe options should be included with a `location` property set to the URL of the\ncreated resource. The `params` property can be used to provide parameters to the\nURL. For example if `location` is `/books/:id` and `params` is `{ id: 1 }` the\nURL will be `/books/1`.\n\n### Conflict Responses\n\nThe `conflict()` method on the context object can be used to throw a\n`409 Conflict` error. This is appropriate when a `PUT` request is made to a\nresource that cannot be updated because it is in a state that conflicts with the\nrequest.\n\n### Redirect Responses\n\nIf you need to redirect the client to a different URL, you can use the\n`redirect()` method on the context object. This method takes a URL and an\noptional status code and will send a redirect response to the client.\n\nIn addition, if the `location` is a path with parameters, you can provide the\n`params` object to the `redirect()` method which will be used to populate the\nparameters in the URL.\n\n## Logging\n\nacorn integrates the [LogTape](https://jsr.io/@logtape/logtape) library to\nprovide logging capabilities for the router and routes.\n\nTo enable logging, you can provide a `LoggerOptions` object on the property\n`logger` to the router when creating it:\n\n```ts\nconst router = new Router({\n  logger: {\n    console: { level: \"debug\" },\n  },\n});\n```\n\nAlternatively, you can simply set the `logger` property to `true` to log events\nat the `\"WARN\"` level to the console:\n\n```ts\nconst router = new Router({\n  logger: true,\n});\n```\n\n---\n\nCopyright 2018-2024 the oak authors. All rights reserved. MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foakserver%2Facorn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foakserver%2Facorn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foakserver%2Facorn/lists"}