Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.

Commit 35dda6b

Browse files
committed
Initial commit ✨
0 parents  commit 35dda6b

18 files changed

+4196
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
.swiftpm/

Package.resolved

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// swift-tools-version:5.3
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "questdb-nio",
6+
platforms: [
7+
.macOS(.v10_15)
8+
],
9+
products: [
10+
.library(
11+
name: "QuestDB",
12+
targets: ["QuestDB"]
13+
),
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.2.0"),
17+
],
18+
targets: [
19+
.target(
20+
name: "QuestDB",
21+
dependencies: [
22+
.product(name: "AsyncHTTPClient", package: "async-http-client")
23+
]
24+
),
25+
.testTarget(
26+
name: "QuestDBTests",
27+
dependencies: ["QuestDB"]
28+
),
29+
]
30+
)

README.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# QuestDB + SwiftNIO
2+
3+
A QuestDB REST client designed to work with server-side Swift applications with `Codable` support.
4+
5+
## Install (SPM)
6+
7+
Add the package to your dependencies in `Package.swift`:
8+
9+
```swift
10+
dependencies: [
11+
.package(url: "https://github.com/swift-glide/questdb-nio.git", .from("0.1.0"))
12+
]
13+
```
14+
15+
## Usage
16+
17+
Start by creating a client instance:
18+
19+
```swift
20+
let client = QuestDBClient()
21+
```
22+
23+
When no configuration is passed, the client will look for the `QUESTDB_URL` environment variable and use it if available. Otherwise, it will default to `http://localhost:9000`.
24+
25+
You can pass a configuration object as well as an NIO HTTP client during initialization:
26+
27+
```swift
28+
let client = QuestDBClient(
29+
config: QuestDBConfig(url: "http://localhost:9000"),
30+
httpClient: HTTPClient(
31+
eventLoopGroupProvider: .createNew
32+
)
33+
)
34+
```
35+
36+
Once you have a client, you can call the `execute` instance method to execute your queries:
37+
38+
```swift
39+
client.execute(
40+
options: ExecuteOptions(
41+
query: """
42+
CREATE TABLE readings(
43+
db_ts timestamp,
44+
device_ts timestamp,
45+
device_name symbol,
46+
reading int)
47+
timestamp(db_ts);
48+
"""
49+
)
50+
)
51+
```
52+
53+
If you want the query to execute on a specific event loop, you can pass it as an argument as well:
54+
55+
```swift
56+
client.execute(
57+
on: someEventLoop,
58+
options: ...
59+
)
60+
```
61+
62+
This method returns an event loop future wrapping a `Codable` type.
63+
64+
If the return type can't be inferred from the call site, you can specify it as follows:
65+
66+
```swift
67+
client.execute(
68+
on: someEventLoop,
69+
options: ...,
70+
returning: SomeDecodableType.self
71+
)
72+
```
73+
74+
Many requests return a `QuestOperationResponse` object, so use it accordingly.
75+
76+
You can further customize your request using the `ExecuteOptions` type, including `count`, `limit`, `nm`, and `timings`. Please refer to the [official QuestDb docs](https://questdb.io/docs/reference/api/rest#exec---execute-queries) for more information.
77+
78+
## Supported Endpoints
79+
80+
- [x] `/exec`
81+
- [ ] `/imp`
82+
- [ ] `/exp`
83+
84+
## Vapor + QuestDB
85+
86+
If you plan to use this package with you Vapor 4 app, here are some snippets to get you started. We first create a service type inside `Application`:
87+
88+
```swift
89+
import Vapor
90+
import QuestDB
91+
92+
extension Application {
93+
struct QuestDB {
94+
let app: Application
95+
96+
struct Key: StorageKey {
97+
typealias Value = QuestDBClient
98+
}
99+
100+
var client: QuestDBClient {
101+
get {
102+
guard let client = self.app.storage[Key.self] else {
103+
fatalError("QuestDBClient is not setup. Use application.quest.client to set it up.")
104+
}
105+
106+
return client
107+
}
108+
109+
nonmutating set {
110+
self.app.storage.set(Key.self, to: newValue) {
111+
try $0.syncShutdown()
112+
}
113+
}
114+
}
115+
}
116+
117+
var quest: QuestDB {
118+
.init(app: self)
119+
}
120+
121+
var questClient: QuestDBClient {
122+
quest.client
123+
}
124+
}
125+
```
126+
127+
Then we do the same with `Request`:
128+
129+
```swift
130+
extension Request {
131+
struct QuestDB {
132+
var client: QuestDBClient {
133+
return request.application.quest.client
134+
}
135+
136+
let request: Request
137+
}
138+
139+
var quest: QuestDB { .init(request: self) }
140+
141+
var questClient: QuestDBClient {
142+
quest.client
143+
}
144+
}
145+
```
146+
147+
Then when configuring your `app` instance:
148+
149+
```swift
150+
let client = QuestDBClient(httpClient: app.http.client.shared)
151+
app.quest.client = client
152+
```
153+
154+
For operations that you do frequently, you can extend the client to keep things DRY:
155+
156+
```swift
157+
extension QuestDBClient {
158+
func createTable(
159+
on eventLoop: EventLoop? = nil
160+
) -> Future<QuestOperationResponse> {
161+
execute(
162+
on: eventLoop,
163+
options: .init(
164+
query: """
165+
...
166+
"""
167+
)
168+
)
169+
}
170+
```
171+
172+
## Project Status & Contributions
173+
174+
The package handles all the use cases that it was initially designed for. That being said, PRs are very welcome, especially if they tackle some of the following:
175+
176+
- Adding missing endpoints.
177+
- Adding a test suite.
178+
179+
## License
180+
181+
See LICENSE.

0 commit comments

Comments
 (0)