{"id":1015,"url":"https://github.com/orlandos-nl/MongoKitten","last_synced_at":"2025-08-06T13:32:11.261Z","repository":{"id":40504537,"uuid":"50437650","full_name":"orlandos-nl/MongoKitten","owner":"orlandos-nl","description":"Native MongoDB driver for Swift, written in Swift","archived":false,"fork":false,"pushed_at":"2024-12-01T21:00:05.000Z","size":5375,"stargazers_count":722,"open_issues_count":20,"forks_count":104,"subscribers_count":25,"default_branch":"main","last_synced_at":"2024-12-09T09:47:04.186Z","etag":null,"topics":["database","mongodb","mongodb-driver","mongokitten","server-side-swift","swift","swift-package-manager","vapor"],"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/orlandos-nl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["joannis"]}},"created_at":"2016-01-26T15:24:14.000Z","updated_at":"2024-12-04T03:50:45.000Z","dependencies_parsed_at":"2024-03-25T17:23:45.296Z","dependency_job_id":"394e4ada-c99f-4209-be53-3eb7abc57a4a","html_url":"https://github.com/orlandos-nl/MongoKitten","commit_stats":{"total_commits":1468,"total_committers":37,"mean_commits":39.67567567567568,"dds":"0.33446866485013627","last_synced_commit":"ab34b484b4acec8d0991f5a4f29df7a88af8d721"},"previous_names":["openkitten/mongokitten"],"tags_count":257,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlandos-nl%2FMongoKitten","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlandos-nl%2FMongoKitten/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlandos-nl%2FMongoKitten/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlandos-nl%2FMongoKitten/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/orlandos-nl","download_url":"https://codeload.github.com/orlandos-nl/MongoKitten/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228871426,"owners_count":17984448,"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":["database","mongodb","mongodb-driver","mongokitten","server-side-swift","swift","swift-package-manager","vapor"],"created_at":"2024-01-05T20:15:37.085Z","updated_at":"2025-08-06T13:32:11.227Z","avatar_url":"https://github.com/orlandos-nl.png","language":"Swift","funding_links":["https://github.com/sponsors/joannis"],"categories":["Database","Libs","Libraries","Swift","Data and Storage"],"sub_categories":["Getting Started","Data Management","Swift"],"readme":"\u003ca href=\"https://unbeatable.software\"\u003e\u003cimg src=\"./assets/Header.png\" /\u003e\u003c/a\u003e\n\n\u003cimg src=\"./assets/Descriptions.gif\" /\u003e\n\nA fast, pure swift [MongoDB](https://mongodb.com) driver based on [Swift NIO](https://github.com/apple/swift-nio) built for Server Side Swift. It features a great API and a battle-tested core. Supporting both MongoDB in server and embedded environments.\n\nMongoKitten is a fully asynchronous driver, which means that it doesn't block any threads. This also means that it can be used in any asynchronous environment, such as [Vapor](https://github.com/vapor/vapor) or [Hummingbird](https://github.com/hummingbird-project/hummingbird).\n\n## Table of Contents\n\n- [Docs \\\u0026 Community](#docs--community)\n    - [Projects](#projects)\n- [Installation](#installation)\n  - [Set up MongoDB server](#set-up-mongodb-server)\n  - [Add MongoKitten to your Swift project 🚀](#add-mongokitten-to-your-swift-project-)\n    - [Add Meow (Optional)](#add-meow-optional)\n- [Basic usage](#basic-usage)\n  - [Connect vs. LazyConnect](#connect-vs-lazyconnect)\n  - [CRUD](#crud)\n    - [Create](#create)\n    - [Read](#read)\n      - [Cursors](#cursors)\n        - [Fetching results](#fetching-results)\n        - [Cursors are generic](#cursors-are-generic)\n    - [Update \\\u0026 Delete](#update--delete)\n  - [Indexes](#indexes)\n  - [Aggregation](#aggregation)\n  - [Transactions](#transactions)\n  - [GridFS](#gridfs)\n- [About BSON](#about-bson)\n  - [Literals](#literals)\n  - [Just Another Collection](#just-another-collection)\n    - [Think twice before converting between `Document` and `Dictionary`](#think-twice-before-converting-between-document-and-dictionary)\n  - [Codable](#codable)\n- [Advanced Features](#advanced-features)\n  - [Change Streams](#change-streams)\n  - [Logging and Monitoring](#logging-and-monitoring)\n  - [Best Practices](#best-practices)\n    - [Connection Management](#connection-management)\n    - [Performance Optimizations](#performance-optimizations)\n- [Troubleshooting](#troubleshooting)\n- [Meow ORM](#meow-orm)\n  - [Setting up with Vapor](#setting-up-with-vapor)\n  - [Setting up with Hummingbird](#setting-up-with-hummingbird)\n  - [Models](#models)\n    - [Queries](#queries)\n    - [References](#references)\n  - [Backers](#backers)\n\n## Quick Start\n\nGet up and running in under 5 minutes:\n\n```swift\n// Add to Package.swift\n.package(url: \"https://github.com/orlandos-nl/MongoKitten.git\", from: \"7.9.0\")\n\n// In your code\nimport MongoKitten\n\n// Connect to database\nlet db = try await MongoDatabase.connect(to: \"mongodb://localhost/my_database\")\n\n// Insert a document\ntry await db[\"users\"].insert([\"name\": \"Alice\", \"age\": 30])\n\n// Query documents\nlet users = try await db[\"users\"].find(\"age\" \u003e= 18).drain()\n\n// Use with Codable\nstruct User: Codable {\n    let name: String\n    let age: Int\n}\n\nlet typedUsers = try await db[\"users\"]\n    .find()\n    .decode(User.self)\n    .drain()\n```\n\n### Key Requirements\n\n- **Swift**: 5.5 or later (for async/await support)\n- **MongoDB**: 3.6 or later\n- **Platforms**: macOS, iOS, Linux\n\n### Optional Features Requirements\n\n- **Change Streams**: MongoDB 3.6+ (replica set or sharded cluster)\n- **Transactions**: MongoDB 4.0+ (replica set or sharded cluster)\n\n# Docs \u0026 Community\n\n[Join our Discord](https://discord.gg/H6799jh) for any questions and friendly banter.\n\nIf you need hands-on support on your projects, our team is available at [joannis@unbeatable.software](mailto:joannis@unbeatable.software).\n\n[Look into Sample Code](https://github.com/orlandos-nl/MongoKitten-Examples) using MongoKitten \u0026 Vapor\n\n### Projects\n\nA couple of MongoKitten based projects have arisen, check them out!\n\n- [MongoQueue](https://github.com/orlandos-nl/MongoQueue)\n- [Vapor's Fluent + MongoDB](https://github.com/vapor/fluent-mongo-driver)\n- [MongoDB + Vapor Queues](https://github.com/vapor-community/queues-mongo-driver)\n\n# Installation\n\n## Set up MongoDB server\n\nIf you haven't already, you should set up a MongoDB server to get started with MongoKitten. MongoKitten supports MongoDB 3.6 and above.\n\nFor development, this can be on your local machine.\n\nInstall MongoDB for [Ubuntu](https://www.mongodb.com/docs/v6.0/tutorial/install-mongodb-on-ubuntu/), [macOS](https://www.mongodb.com/docs/v6.0/tutorial/install-mongodb-on-os-x/) or any other supported Linux Distro.\n\nAlternatively, make use of a DAAS (Database-as-a-service) like [MongoDB Atlas](https://cloud.mongodb.com).\n\n## Add MongoKitten to your Swift project 🚀\n\nMongoKitten uses the [Swift Package Manager](https://swift.org/getting-started/#using-the-package-manager). Add MongoKitten to your dependencies in your **Package.swift** file:\n\n`.package(url: \"https://github.com/orlandos-nl/MongoKitten.git\", from: \"7.9.0\")`\n\nAlso, don't forget to add the product `\"MongoKitten\"` as a dependency for your target.\n\n```swift\n.product(name: \"MongoKitten\", package: \"MongoKitten\"),\n```\n\n### Add Meow (Optional)\n\nMeow is an ORM that resides in this same package.\n\n```swift\n.product(name: \"Meow\", package: \"MongoKitten\"),\n```\n\n# Basic usage\n\nFirst, connect to a database:\n\n```swift\nimport MongoKitten\n\nlet db = try await MongoDatabase.connect(to: \"mongodb://localhost/my_database\")\n```\n\nVapor users only should register the database as service:\n```swift\nextension Request {\n    public var mongo: MongoDatabase {\n        return application.mongo.adoptingLogMetadata([\n            \"request-id\": .string(id)\n        ])\n    }\n}\n\nprivate struct MongoDBStorageKey: StorageKey {\n    typealias Value = MongoDatabase\n}\n\nextension Application {\n    public var mongo: MongoDatabase {\n        get {\n            storage[MongoDBStorageKey.self]!\n        }\n        set {\n            storage[MongoDBStorageKey.self] = newValue\n        }\n    }\n    \n    public func initializeMongoDB(connectionString: String) throws {\n        self.mongo = try MongoDatabase.lazyConnect(to: connectionString)\n    }\n}\n```\nMake sure to instantiate the database driver before starting your application.\n```\ntry app.initializeMongoDB(connectionString: \"mongodb://localhost/my-app\")\n```\n\n\n## Connect vs. LazyConnect\n\nIn MongoKitten, you'll find two main variations of connecting to MongoDB.\n\n- `connect` calls are `async throws`, and will _immediately_ attempt to establish a connection. These functions throw an error if unsuccessful.\n- `lazyConnect` calls are `throws`, and will defer establishing a connection until it's necessary. Errors are only thrown if the provided credentials are unusable.\n\nConnect's advantage is that a booted server is known to have a connection. Any issues with MongoBD will arise _immediately_, and the error is easily inspectable.\n\nLazyConnect is helpful during development, because connecting to MongoDB can be a time-consuming process in certain setups. LazyConnect allows you to start working with your system almost immediately, without waiting for MongoKitten. Another advantage is that cluster outages or offly timed topology changes do not influence app boot. Therefore, MongoKitten can simply attempt to recover in the background. However, should something go wrong it can be hard to debug this.\n\n## CRUD\n\nBefore doing operations, you need access to a collection where you store your models. This is MongoDB's equivalent to a table.\n\n```swift\n// The collection \"users\" in your database\nlet users = db[\"users\"]\n```\n\n### Create\n\n```swift\n// Create a document to insert\nlet myUser: Document = [\"username\": \"kitty\", \"password\": \"meow\"]\n\n// Insert the user into the collection\n// The _id is automatically generated if it's not present\ntry await users.insert(myUser)\n```\n\n### Read\n\nTo perform the following query in MongoDB:\n\n```json\n{\n  \"username\": \"kitty\"\n}\n```\n\nUse the following MongoKitten code:\n\n```swift\nif let kitty = try await users.findOne(\"username\" == \"kitty\") {\n  // We've found kitty!\n}\n```\n\nTo perform the following query in MongoDB:\n\n```json\n{\n  \"$or\": [\n    { \"age\": { \"$lte\": 16 } },\n    { \"age\": { \"$exists\": false } }\n  ]\n}\n```\n\nUse the following MongoKitten code:\n\n```swift\nfor try await user in users.find(\"age\" \u003c= 16 || \"age\" == nil) {\n  // Asynchronously iterates over each user in the cursor\n}\n```\n\nYou can also type out the queries yourself, without using the query builder, like this:\n\n```swift\n// This is the same as the previous example\nusers.findOne([\"username\": \"kitty\"])\n```\n\n#### Cursors\n\nFind operations return a `Cursor`. A cursor is a pointer to the result set of a query. You can obtain the results from a cursor by iterating over the results, or by fetching one or all of the results.\n\nCursors will close automatically if the enclosing `Task` is cancelled.\n\n##### Fetching results\n\nYou can fetch all results as an array:\n\n```swift\n// Fetch all results and collect them in an array\nlet users = try await users.find().drain()\n```\n\nNote that this is potentially dangerous with very large result sets. Only use `drain()` when you are sure that the entire result set of your query fits comfortably in memory.\n\n##### Cursors are generic\n\nFind operations return a `FindQueryBuilder`. You can lazily transform this (and other) cursors into a different result type by using `map`, which works similar to `map` on arrays or documents. A simple commonly used helper based on map is `.decode(..)` which decodes each result Document into a `Decodable` entity of your choosing.\n\n```swift\nlet users: [User] = try await users.find().decode(User.self).drain()\n```\n\n### Update \u0026 Delete\n\nYou can do update and delete entities the same way you'd see in the MongoDB docs.\n\n```swift\ntry await users.updateMany(where: \"username\" == \"kitty\", setting: [\"age\": 3], unsetting: nil)\n```\n\nThe result is implicitly discarded, but you can still get and use it.\n\n```swift\ntry await users.deleteOne(where: \"username\" == \"kitty\")\n\nlet reply = try await users.deleteAll(where: \"furType\" == \"fluffy\")\nprint(\"Deleted \\(reply.deletes) kitties 😿\")\n```\n\n## Indexes\n\nYou can create indexes on a collection using the `buildIndexes` method.\n\n```swift\ntry await users.buildIndexes {\n  // Unique indexes ensure that no two documents have the same value for a field\n  // See https://docs.mongodb.com/manual/core/index-unique/s\n  UniqueIndex(\n    named: \"unique-username\", \n    field: \"username\"  \n  )\n\n  // Text indexes allow you to search for documents using text\n  // See https://docs.mongodb.com/manual/text-search/\n  TextScoreIndex(\n    named: \"search-description\", \n    field: \"description\"\n  )\n\n  // TTL Indexes expire documents after a certain amount of time\n  // See https://docs.mongodb.com/manual/core/index-ttl/\n  TTLIndex(\n    named: \"expire-createdAt\", \n    field: \"createdAt\", \n    expireAfterSeconds: 60 * 60 * 24 * 7 // 1 week\n  )\n}\n```\n\n## Aggregation\n\nMongoDB supports aggregation pipelines. You can use them like this:\n\n```swift\nlet pipeline = try await users.buildAggregate {\n  // Match all users that are 18 or older\n  Match(where: \"age\" \u003e= 18)\n\n  // Sort by age, ascending\n  Sort(by: \"age\", direction: .ascending)\n\n  // Limit the results to 3\n  Limit(3)\n}\n\n// Pipeline is a cursor, so you can iterate over it\n// This will iterate over the first 3 users that are 18 or older in ascending age order\nfor try await user in pipeline {\n  // Do something with the user\n}\n```\n\n## Transactions\n\n\nExecute multiple operations atomically:\n\n```swift\ntry await db.transaction { session in\n    let users = db[\"users\"]\n    let accounts = db[\"accounts\"]\n    \n    try await users.insert(newUser)\n    try await accounts.insert(newAccount)\n    \n    // Changes are only committed if no errors occur\n}\n```\n\n## GridFS\n\nMongoKitten supports GridFS. You can use it like this:\n\n```swift\nlet database: MongoDatabase = ...\nlet gridFS = GridFSBucket(in: database)\n```\n\nYou can then use the GridFSBucket to upload and download files.\n\n```swift\nlet blob: ByteBuffer = ...\nlet file = try await gridFS.upload(\n  blob,\n  filename: \"invoice.pdf\",\n  metadata: [\n    \"invoiceNumber\": 1234,\n    \"invoiceDate\": Date(),\n    \"invoiceAmount\": 123.45\n  ]\n)\n```\n\nOptionally, you can define a custom chunk size. The default is 255kb.\n\nFor chunked file uploads, you can use the `GridFSFileWriter`:\n\n```swift\nlet writer = GridFSFileWriter(toBucket: gridFS)\n\ndo {\n  // Stream the file from HTTP\n  for try await chunk in request.body {\n    // Assuming `chunk is ByteBuffer`\n    // Write each HTTP chunk to GridFS\n    try await writer.write(data: chunk)\n  }\n\n  // Finalize the file, making it available for reading\n  let file = try await writer.finalize(filename: \"invoice.pdf\", metadata: [\"invoiceNumber\": 1234])\n} catch {\n  // Clean up written chunks, as the file upload failed\n  try await writer.cancel()\n\n  // rethrow original error\n  throw error\n}\n```\n\nYou can read the file back using the `GridFSReader` or by iterating over the `GridFSFile` as an `AsyncSequence`:\n\n```swift\n// Find your file in GridFS\nguard let file = try await gridFS.findFile(\"metadata.invoiceNumber\" == 1234) else {\n  // File does not exist\n  throw Abort(.notFound)\n}\n\n// Get all bytes in one contiguous buffer\nlet bytes = try await file.reader.readByteBuffer()\n\n// Stream the file\nfor try await chunk in file {\n  // `chunk is ByteBuffer`, now do something with the chunk!\n}\n```\n\n# About BSON\n\nMongoDB is a document database that uses BSON under the hood to store JSON-like data. MongoKitten implements the [BSON specification](http://bsonspec.org) in its companion project, [OpenKitten/BSON](https://github.com/OpenKitten/BSON). You can find out more about our BSON implementation in the separate BSON repository, but here are the basics:\n\n## Literals\n\nYou normally create BSON Documents like this:\n\n```swift\nlet documentA: Document = [\"_id\": ObjectId(), \"username\": \"kitty\", \"password\": \"meow\"]\nlet documentB: Document = [\"kitty\", 4]\n```\n\nFrom the example above, we can learn a few things:\n\n- A BSON document can represent an array *or* a dictionary\n- You can initialize a document like you initialize normal dictionaries and arrays, using literals\n- The values in a Document (either the array elements or the values of a dictionary pair) can be of any BSON primitive type\n- BSON primitives include core Swift types such as `Int`, `String`, `Double` and `Bool`, as well as `Date` from Foundation\n- BSON also features some unique types, such as `ObjectId`\n\n## Just Another Collection\n\nLike normal arrays and dictionaries, `Document` conforms to the `Collection` protocol. Because of this, you can often directly work with your `Document`, using the APIs you already know from `Array` and `Dictionary`. For example, you can iterate over a document using a for loop:\n\n```swift\nfor (key, value) in documentA {\n\t// ...\n}\n\nfor value in documentB.values {\n\t// ...\n}\n```\n\nDocument also provides subscripts to access individual elements. The subscripts return values of the type `Primitive?`, so you probably need to cast them using `as?` before using them.\n\n```swift\nlet username = documentA[\"username\"] as? String\n```\n\n### Think twice before converting between `Document` and `Dictionary`\n\nOur `Document` type is implemented in an optimized, efficient way and provides many useful features to read and manipulate data, including features not present on the Swift `Dictionary` type. On top of that, `Document` also implements most APIs present on `Dictionary`, so there is very little learning curve.\n\n## Codable\n\nMongoKitten supports the `Encodable` and `Decodable` (`Codable`) protocols by providing the `BSONEncoder` and `BSONDecoder` types. Working with our encoders and decoders is very similar to working with the Foundation `JSONEncoder` and `JSONDecoder` classes, with the difference being that `BSONEncoder` produces instances of `Document` and `BSONDecoder` accepts instances of `Document`, instead of `Data`.\n\nFor example, say we want to code the following struct:\n\n```swift\nstruct User: Codable {\n\tvar profile: Profile?\n\tvar username: String\n\tvar password: String\n\tvar age: Int?\n\t\n\tstruct Profile: Codable {\n\t\tvar profilePicture: Data?\n\t\tvar firstName: String\n\t\tvar lastName: String\n\t}\n}\n```\n\nWe can encode and decode instances like this:\n\n```swift\nlet user: User = ...\n\nlet encoder = BSONEncoder()\nlet encoded: Document = try encoder.encode(user)\n\nlet decoder = BSONDecoder()\nlet decoded: User = try decoder.decode(User.self, from: encoded)\n```\n\nA few notes:\n\n- `BSONEncoder` and `BSONDecoder` work very similar to other encoders and decoders\n- Nested types can also be encoded and are encouraged\n\t- Nested structs and classes are most often encoded as embedded documents\n- You can customize the representations using encoding/decoding strategies\n\n# Advanced Features\n\n## Change Streams\n\nMongoKitten provides powerful support for MongoDB Change Streams, allowing you to monitor real-time changes to your collections:\n\n```swift\n// Basic change stream usage\nlet stream = try await users.watch()\n\nfor try await change in stream {\n    switch change.operationType {\n    case .insert:\n        print(\"New document: \\(change.fullDocument)\")\n    case .update:\n        print(\"Updated fields: \\(change.updateDescription?.updatedFields)\")\n    case .delete:\n        print(\"Deleted document: \\(change.documentKey)\")\n    default:\n        break\n    }\n}\n\n// Type-safe change streams\nstruct User: Codable {\n    let id: ObjectId\n    let name: String\n    let email: String\n}\n\nlet typedStream = try await users.watch(type: User.self)\nfor try await change in typedStream {\n    if let user = change.fullDocument {\n        print(\"User modified: \\(user.name)\")\n    }\n}\n```\n\n## Logging and Monitoring\n\nMongoKitten provides built-in support for logging and monitoring:\n\n```swift\n// Add logging metadata\nlet loggedDb = db.adoptingLogMetadata([\n    \"service\": \"user-api\",\n    \"environment\": \"production\"\n])\n\n// Use the logged database\nlet users = loggedDb[\"users\"]\n```\n\n## Best Practices\n\n### Connection Management\n\n- Use `lazyConnect` for development and non-critical services\n- Use `connect` for production services where immediate feedback is important\n- Always specify `authSource` in your connection string\n- Use connection pooling for better performance\n\n### Performance Optimizations\n\n- Use appropriate indexes for your queries\n- Limit result sets when possible\n- Use projection to fetch only needed fields\n- Consider using change streams instead of polling\n- Use batch operations for bulk updates/inserts\n\n# Troubleshooting\n\n\u003cdetails\u003e\n  \u003csummary\u003eI can't connect to MongoDB, authentication fails!\u003c/summary\u003e\n  \n  1. Make sure you've specified `authSource=admin`, unless you know what your authSource is. MongoDB's default value is really confusing.\n  2. If you've specified an `authMechanism`, try removing it. MongoKitten can detect the correct one automatically.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eChange streams not working\u003c/summary\u003e\n  \n  1. Ensure you're connected to a replica set or sharded cluster\n  2. Check that your user has the required privileges\n  3. Verify that you're not trying to watch system collections\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003ePerformance issues\u003c/summary\u003e\n  \n  1. Check your indexes using `explain()`\n  2. Monitor connection pool usage\n  3. Ensure you're not fetching unnecessary fields\n  4. Use appropriate batch sizes for bulk operations\n\u003c/details\u003e\n\n# Meow ORM\n\nMeow works as a lightweight but powerful ORM layer around MongoKitten.\n\n## Setting up Meow\nAdd the product `\"Meow\"` as a dependency for your target.\n```swift\n.product(name: \"Meow\", package: \"MongoKitten\"),\n```\nImport `\"Meow\"` before using it.\n```swift\nimport Meow\n```\n## Setting up with Vapor\n```swift\nextension Application {\n    public var meow: MeowDatabase {\n        MeowDatabase(mongo)\n    }\n}\n\nextension Request {\n    public var meow: MeowDatabase {\n        MeowDatabase(mongo)\n    }\n}\n```\n\n## Setting up with Hummingbird\n\nHummingbird doesn't require special setup for external dependencies, and uses [explicit dependency injection](https://docs.hummingbird.codes/2.0/documentation/hummingbird/migratingtov2#Application). Consult [the Hummingbird + MongoDB tutorial](https://docs.hummingbird.codes/2.0/tutorials/hummingbird/mongokitten-1-setup/) on setting things up.\n\n## Models\n\nThere are two main types of models in Meow, these docs will focus on the most common one.\n\nWhen creating a model, your type must implement the `Model` protocol.\n\n```swift\nimport Meow\n\nstruct User: Model {\n  ..\n}\n```\n\nEach Model has an `_id` field, as required by MongoDB. The type must be Codable and Hashable, the rest is up to you. You can therefore also make `_id` a compound key such as a `struct`. It must still be unique and hashable, but the resulting Document is acceptable for MongoDB.\n\nEach field must be marked with the `@Field` property wrapper:\n\n```swift\nimport Meow\n\nstruct User: Model {\n  @Field var _id: ObjectId\n  @Field var email: String\n}\n```\n\nYou can also mark use nested types, as you'd expect of MongoDB. Each field in these nested types must also be marked with the `@Field` property wrapper to make it queryable.\n\n```swift\nimport Meow\n\nstruct UserProfile: Model {\n  @Field var firstName: String?\n  @Field var lastName: String?\n  @Field var age: Int\n}\n\nstruct User: Model {\n  @Field var _id: ObjectId\n  @Field var email: String\n  @Field var profile: UserProfile\n}\n```\n\n### Queries\n\nUsing the above model, we can query it from a MeowCollection. Get your instance from the MeowDatabase using a typed subscript!\n\n```swift\nlet users = meow[User.self]\n```\n\nNext, run a `find` or `count` query, but using a type-checked syntax instead! Each portion of the path needs to be prefixed with `$` to access the `Field` property wrapper.\n\n```swift\nlet adultCount = try await users.count(matching: { user in\n  user.$profile.$age \u003e= 18\n})\n```\n\nAs meow just recycles common MongoKitten types, you can use a find query cursor as you'd do in MongoKitten.\n\n```swift\nlet kids = try await users.find(matching: { user in\n  user.$profile.$age \u003c 18\n})\n\nfor try await kid in kids {\n  // TODO: Send verification email to parents\n}\n```\n\n### References\n\nMeow has a helper type called `Reference`, you can use this in your model instead of copying the identifier over. This will give you some extra helpers when trying to resolve a models.\n\nReference is also `Codable` and inherit's the identifier's `LosslessStringConvertible`. So it can be used in Vapor's JWT Tokens as a subject, or in a Vapor's Route Parameters.\n\n```swift\n// GET /users/:id using Vapor\napp.get(\"users\", \":id\") { req async throws -\u003e User in\n  let id: Reference\u003cUser\u003e = req.parameters.require(\"id\")\n  return try await id.resolve(in: req.meow)\n}\n```\n\n## Backers\n\n\u003ca href=\"https://github.com/ultim8p\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/4804985?s=460\u0026v=4\" width=\"128\" height=\"128\" /\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlandos-nl%2FMongoKitten","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forlandos-nl%2FMongoKitten","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlandos-nl%2FMongoKitten/lists"}