{"id":20757211,"url":"https://github.com/vapor-community/ferno","last_synced_at":"2025-04-29T08:52:51.060Z","repository":{"id":63921193,"uuid":"131674300","full_name":"vapor-community/ferno","owner":"vapor-community","description":"Vapor Firebase Realtime database provider","archived":false,"fork":false,"pushed_at":"2023-10-25T01:22:04.000Z","size":208,"stargazers_count":71,"open_issues_count":2,"forks_count":12,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-22T10:12:11.136Z","etag":null,"topics":["firebase","swift","vapor","vapor-3","vapor-4"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/vapor-community.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-05-01T03:56:21.000Z","updated_at":"2025-03-04T12:24:36.000Z","dependencies_parsed_at":"2023-10-25T02:25:23.972Z","dependency_job_id":null,"html_url":"https://github.com/vapor-community/ferno","commit_stats":{"total_commits":45,"total_committers":3,"mean_commits":15.0,"dds":0.0444444444444444,"last_synced_commit":"4cb034bec621b5510ac8c33e1b3b1813e8da3bf7"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor-community%2Fferno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor-community%2Fferno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor-community%2Fferno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vapor-community%2Fferno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vapor-community","download_url":"https://codeload.github.com/vapor-community/ferno/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251470280,"owners_count":21594523,"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":["firebase","swift","vapor","vapor-3","vapor-4"],"created_at":"2024-11-17T09:40:48.517Z","updated_at":"2025-04-29T08:52:51.043Z","avatar_url":"https://github.com/vapor-community.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Ferno 🔥](https://github.com/vapor-community/firebase-provider/blob/main/screenshots/FERNO.png)\n\nFerno allows you to easily connect your Vapor project with your Firebase realtime database. It is built with the brand new Vapor 4. It gives you a nice and clean interface to interact with the Firebase Realtime REST API. It will automatically turn the response into your class/struct! \n\n### Prerequisites\nYou will need:\n- Vapor 4.8+\n\n### Installing\n\nIn your Package.swift file, add the line\n\n```swift\n.package(url: \"https://github.com/vapor-community/ferno.git\", from: \"0.6.0\")\n```\n\nAlso make sure you add `Ferno` as a dependency\n\n```swift\ndependencies: [\n  .product(name: \"Ferno\", package: \"ferno\")\n]\n```\n\n## Setup\n\n### Legacy setup\n\n1. Ferno uses an access token to read and write to your database. First we will need to get your service account information.\n\n    * Log into the Firebase console\n    * Click the settings gear next to `Project Overview`\n    * Select `Project settings`\n    * Select the `SERVICE ACCOUNTS` tab\n    * Click the button at the bottom that says `GENERATE NEW PRIVATE KEY`\n    * This will download a `.json` file. You will now have access to the email and private key. You will pass those into the initialize during the next step.\n\n2. Register `Ferno` as a Provider and import `Ferno`. This is usually done in `configure.swift`\n\n### FirebaseSDK setup\n\n  1. Log into the Firebase console\n  2. Click the settings gear next to `Project Overview`\n  3. Select `Project settings`\n  4. Select the `SERVICE ACCOUNTS` tab\n  5. Select `Firebase Admin SDK` option\n  6. Click the button `Generate new private key` to download the json file.\n\n### Ferno setup\n\nYou can use the content of json file information to fill out the `FernoDefaultConfiguration` or use the json file to preper the `FernoServiceJsonConfiguration`.\n\n```swift\nimport Ferno\n\nlet fernoConfiguration = FernoDefaultConfiguration(\n    basePath: \"database-url\", \n    email: \"service-account-email\", \n    privateKey: \"private-key\"\n)\napp.ferno.use(.default(fernoConfiguration))\n```\n\nIf you prefer to use the Firebase `ServiceAccount.json` follow the example below.\n\n```swift\nimport Ferno\n\n// option 1\nlet fernoConfiguration = try FernoServiceJsonConfiguration(json: Data)\n\n// option 2\nlet fernoConfiguration = try FernoServiceJsonConfiguration(path: URL)\n\n// option 3\nlet fernoConfiguration = FernoServiceJsonConfiguration(\n                type: String,\n                projectId: String,\n                privateKeyId: String,\n                privateKey: String,\n                clientEmail: String,\n                clientId: String,\n                authUri: String,\n                tokenUri: String,\n                authProviderX509CertUrl: String,\n                clientX509CertUrl: String,\n                universeDomain: String\n)\n\napp.ferno.use(.serviceAccountKey(fernoConfiguration))\n```\n\n## Parameters\n\nThere are some custom parameters to pass into functions. I want to go over all the parameters you will need to know.\n\n### [FernoQuery]\n\nIn GET requests, you might want to query on your data. This is what `[FernoQuery]` is for.\n\n`FernoQuery` is an enum with:\n\n   1. `case shallow(Bool)`\n   2. `case orderBy(FernoValue)`\n   3. `case limitToFirst(FernoValue)`\n   4. `case limitToLast(FernoValue)`\n   5. `case startAt(FernoValue)`\n   6. `case endAt(FernoValue)`\n   7. `case equalTo(FernoValue)`\n\nThese are all the possible queries that are allowed on Firebase according to the [docs](https://firebase.google.com/docs/reference/rest/database/#section-query-parameters)\n\n#### NOTES on [FernoQuery]\n\n-  `shallow(Bool)` cannot be mixed with any other query parameters.\n- you usually use `orderBy(FernoValue)` in conjunction with enums `3-7`\n- using `orderBy(FernoValue)` alone will just order the data returned\n\n#### FernoValue\n\nYou will notice most cases in `FernoQuery` have a value of `FernoValue`.\n`FernoValue` is just a wrapper for `Bool, String, Int, Double, Float`. So you can just do `.startAt(5)` and everything will work.\n\n#### Examples of [FernoQuery]\n\nJust using shallow:\n\n```swift\n[.shallow(true)]\n```\n\nFilter data to only return data that matches `\"age\": 21`:\n\n```swift\n[.orderBy(\"age\"), .equalTo(21)]\n```\n\nJust orderBy(returns data in ascending order):\n\n```swift\n[.orderBy(\"age\")]\n```\n\n## Usage\n\nThere are 6 functions that allow you to interact with your Firebase realtime database.\n\n### GET\n\nThere are four functions that allow you get your data.\n\n```swift\napp.ferno.retrieve(_ path: [String], queryItems: [FernoQuery] = [])\n```\n\n```swift\napp.ferno.retrieve(_ path: String..., queryItems: [FernoQuery] = [])\n```\n\n```swift\napp.ferno.retrieveMany(_ path: [String], queryItems: [FernoQuery] = [])\n```\n\n```swift\napp.ferno.retrieveMany(_ path: String..., queryItems: [FernoQuery] = [])\n```\n\nThe only difference between `retrieve` and `retrieveMany` is the return type.\n\n- `retrieve` returns -\u003e `F` where `F` is of type `Decodable`\n- `retrieveMany` returns -\u003e `[String: F]` where `F` is of type `Decodable` and `String` is the key\n\n#### Example\n\n1. Define the value you want the data converted.\n\n```swift\nstruct Developer: Content {\n   var name: String\n   var favLanguage: String\n   var age: Int\n}\n```\n\n2. Make the request. Make sure you set the type of the response so Ferno knows what to convert.\n\n```swift\nlet developers: [String: Developer] = try await app.ferno.retrieveMany(\"developers\")\nlet developer: Developer = try await app.ferno.retrieve([\"developers\", \"dev1\"])\n```\n\n### POST\n\nUsed to create a new entry in your database\n\n```swift\napp.ferno.create(_ path: [String], body: T) try await -\u003e FernoChild\n```\n\n```swift\napp.ferno.create(_ path: String..., body: T) try await -\u003e FernoChild\n```\n\n- `body: T` is of type `Content`.\n- `FernoChild` is a struct:\n\n```swift\nstruct FernoChild: Content {\n  var name: String\n}\n```\n\n- `FernoChild` is returned, because the API request returns the key from the newly created child.\n\n#### Example\n\n```swift\nlet newDeveloper = Developer(name: \"Elon\", favLanguage: \"Python\", age: 46) // conforms to Content\nlet newDeveloperKey: FernoChild = try await app.ferno.create(\"developers\", body: newDeveloper)\n```\n\n### DELETE\n\nUsed to delete an entry in your database\n\n```swift\napp.ferno.delete(_ path: [String]) try await -\u003e Bool\n```\n\n```swift\napp.ferno.delete(_ path: String...) try await -\u003e Bool\n```\n\n- the delete method will return a boolean depending on if the delete was successful\n\n#### Example\n\n```swift\nlet successfulDelete: Bool = try await app.ferno.delete([\"developers\", \"dev-1\"])\n```\n\n### PATCH\n\nUpdate values at a specific location, but omitted values won't get removed\n\n```swift\napp.ferno.update(_ path: [String], body: T) try await -\u003e T\n```\n\n```swift\napp.ferno.update(_ path: String..., body: T) try await -\u003e T\n```\n\n- the update method will return the body\n\n### Example\n\n```swift\nstruct UpdateDeveloperName: Content {\n  var name: String\n}\n\nlet newDeveloperName = UpdateDeveloperName(name: \"Kimbal\") //conforms to Content\nlet updatedDeveloperName: UpdateDeveloperName = try await app.ferno.update([\"developers\", newDeveloperKey.name], body: newDeveloper) //newDeveloperKey.name comes from the create method\n```\n\n### PUT\n\nOverwrite the current location with data you are passing in\n\n```swift\nclient.ferno.overwrite(_ path: [String], body: T) try await -\u003e T\n```\n\n```swift\nclient.ferno.overwrite(_ path: String..., body: T) try await -\u003e T\n```\n\n#### Example\n\n```swift\nstruct LeadDeveloper: Content {\n  var name: String\n  var company: String\n  var age: Int\n}\n\nlet leadDeveloper = LeadDeveloper(name: \"Ashley\", company: \"Bio-Fit\", age: 20)\nlet leadDevResponse: LeadDeveloper = try await app.ferno.overwrite([\"developers\", newDeveloperKey.name], body: leadDeveloper)\n```\n\n## Testing\n\nCurrently, tests were written using an actual dummy Firebase realtime database. If you want to run all of the tests, you will need to create a dummy Firebase realtime database.\n\n### Testing Setup\n\nYou need to go to `Application+Testing.swift` and fill in the missing values based on your Firebase service account. Then you will be able to run tests.\n\n## Authors\n\n* **[Austin Astorga](https://github.com/aaastorga)** - *Main developer*\n* **[Maxim Krouk](https://github.com/maximkrouk)** - *Migration to Vapor4*\n* **[Petr Pavlik](https://github.com/petrpavlik)** - *Migrated to async/await*\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details\n\n## Acknowledgments\n\n* Vapor Discord (for helping me with all my issues \u003c3)\n* Stripe Provider as a great template! [stripe-provider](https://github.com/vapor-community/stripe-provider)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvapor-community%2Fferno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvapor-community%2Fferno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvapor-community%2Fferno/lists"}