{"id":1983,"url":"https://github.com/kylef-archive/Frank","last_synced_at":"2025-07-31T12:33:30.554Z","repository":{"id":66697062,"uuid":"50345563","full_name":"kylef-archive/Frank","owner":"kylef-archive","description":"Frank is a DSL for quickly writing web applications in Swift","archived":false,"fork":false,"pushed_at":"2019-01-14T04:43:47.000Z","size":72,"stargazers_count":373,"open_issues_count":2,"forks_count":8,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-09-06T01:48:24.457Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kylef-archive.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}},"created_at":"2016-01-25T11:12:24.000Z","updated_at":"2023-09-13T19:50:59.000Z","dependencies_parsed_at":"2023-06-04T03:45:13.915Z","dependency_job_id":null,"html_url":"https://github.com/kylef-archive/Frank","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylef-archive%2FFrank","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylef-archive%2FFrank/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylef-archive%2FFrank/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylef-archive%2FFrank/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kylef-archive","download_url":"https://codeload.github.com/kylef-archive/Frank/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228248431,"owners_count":17891447,"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":[],"created_at":"2024-01-05T20:16:00.490Z","updated_at":"2024-12-05T06:31:21.937Z","avatar_url":"https://github.com/kylef-archive.png","language":"Swift","funding_links":[],"categories":["Server","Swift"],"sub_categories":["Keychain"],"readme":"# Frank\n\nFrank is a DSL for quickly writing web applications in Swift with type-safe\npath routing.\n\n##### `Sources/main.swift`\n\n```swift\nimport Frank\n\n// Handle GET requests to path /\nget { request in\n  return \"Hello World\"\n}\n\n// Handle GET requests to path /users/{username}\nget(\"users\", *) { (request, username: String) in\n  return \"Hello \\(username)\"\n}\n```\n\n##### `Package.swift`\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n  name: \"Hello\",\n  dependencies: [\n    .Package(url: \"https://github.com/kylef/Frank.git\", majorVersion: 0, minor: 4)\n  ]\n)\n```\n\nThen build, and run it via:\n\n```shell\n$ swift build --configuration release\n$ .build/release/Hello\n[2016-01-25 07:13:21 +0000] [25678] [INFO] Listening at http://0.0.0.0:8000 (25678)\n[2016-01-25 07:13:21 +0000] [25679] [INFO] Booting worker process with pid: 25679\n```\n\nCheck out the [full example](https://github.com/nestproject/Frank-example)\nwhich can be deployed to Heroku.\n\n### Usage\n\n#### Routes\n\nRoutes are constructed with passing your path split by slashes `/` as\nseparate arguments to the HTTP method (e.g. `get`, `post` etc) functions.\n\nFor example, to match a path of `/users/kyle/followers` you can use the following:\n\n```swift\nget(\"users\", \"kyle\", \"followers\") { request in\n\n}\n```\n\nYou may pass path components along with wildcard (`*`) to match variables in\npaths. The wildcard is a placemarker to annotate where the variable path\ncomponents are in your path. Frank allows you to use any number of wildcards\nin any place of the path, allowing you to match all paths.\n\nThe wildcards will map directly to parameters in the path and the variables\npassed into your callback. Wildcard parameters are translated to the type\nspecified in your closure.\n\n```swift\n// /users/{username}\nget(\"users\", *) { (request, username: String) in\n  return \"Hi \\(username)\"\n}\n\n// /users/{username}/followers\nget(\"users\", *, \"followers\") { (request, username: String) in\n  return \"\\(username) has 5 followers\"\n}\n```\n\nYou may place any type that conforms to `ParameterConvertible`\nin your callback, this allows the types to be correctly\nconverted to your type or user will face a 404 since the\nURL will be invalid.\n\n```swift\n// /users/{userid}\nget(\"users\", *) { (request, userid: Int) in\n  return \"Hi user with ID: \\(userid)\"\n}\n```\n\n##### Custom Parameter Types\n\nWildcard parameters may be of any type that conforms to `ParameterConvertible`,\nthis allows you to match against custom types providing you conform to\n`ParameterConvertible`.\n\nFor example, we can create a Status enum which can be Open or Closed which\nconforms to `ParameterConvertible`:\n\n```swift\nenum Status : ParameterConvertible {\n  case open\n  case closed\n\n  init?(parser: ParameterParser) {\n    switch parser.shift() ?? \"\" {\n      case \"open\":\n        self = .open\n      case \"closed\":\n        self = .closed\n      default:\n        return nil\n    }\n  }\n}\n\nget(\"issues\", *) { (request, status: Status) in\n  return \"Issues using status: \\(status)\"\n}\n```\n\n##### Adding routes\n\nRoutes are matched in the order they are defined. The first route that matches\nthe request is invoked.\n\n```swift\nget {\n  ...\n}\n\nput {\n  ...\n}\n\npatch {\n  ...\n}\n\ndelete {\n  ...\n}\n\nhead {\n  ...\n}\n\noptions {\n  ...\n}\n```\n\n#### Return Values\n\nThe return value of route blocks takes a type that conforms to the\n`ResponseConvertible` protocol, which means you can make any type Response\nConvertible. For example, you can return a simple string:\n\n```swift\nget {\n  return \"Hello World\"\n}\n```\n\nReturn a full response:\n\n```swift\nget {\n  return Response(.ok, headers: [\"Custom-Header\": \"value\"])\n}\n\npost {\n  return Response(.created, content: \"User created\")\n}\n```\n\n#### Templates\n\n##### Stencil\n\nYou can easily use the [Stencil](https://github.com/kylef/Stencil) template\nlanguage with Frank. For example, you can create a convenience function to\nrender templates (called `stencil`):\n\n```swift\nimport Stencil\nimport Inquiline\nimport PathKit\n\n\nfunc stencil(path: String, _ context: [String: Any]? = nil) -\u003e ResponseConvertible {\n  do {\n    let template = try Template(path: Path(path))\n    let body = try template.render(Context(dictionary: context))\n    return Response(.ok, headers: [(\"Content-Type\", \"text/html\")], content: body)\n  } catch {\n    return Response(.internalServerError)\n  }\n}\n```\n\nWhich can easily be called from your route to render a template:\n\n```swift\nget {\n  return stencil(\"hello.html\", [\"user\": \"world\"])\n}\n```\n\n###### `hello.swift`\n\n```html+django\n\u003chtml\u003e\n  \u003cbody\u003e\n    Hello {{ user }}!\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Nest\n\nFrank is design around the [Nest](https://github.com/nestproject/Nest) Swift\nWeb Server Gateway Interface, which allows you to use any Nest-compatible web\nservers. The exposed `call` function is a Nest compatible application which can\nbe passed to a server of your choice.\n\n```swift\nimport Frank\n\nget {\n  return \"Custom Server\"\n}\n\n// Pass \"call\" to your HTTP server\nserve(call)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylef-archive%2FFrank","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkylef-archive%2FFrank","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylef-archive%2FFrank/lists"}