1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API
when not defined(js):
{.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".}
import std/[asyncjs, jsformdata, jsheaders]
export jsformdata, jsheaders
from std/httpcore import HttpMethod
from std/jsffi import JsObject
type
FetchOptions* = ref object of JsRoot ## Options for Fetch API.
keepalive*: bool
metod* {.importjs: "method".}: cstring
body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring
headers*: Headers
FetchModes* = enum ## Mode options.
fmCors = "cors"
fmNoCors = "no-cors"
fmSameOrigin = "same-origin"
FetchCredentials* = enum ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
fcInclude = "include"
fcSameOrigin = "same-origin"
fcOmit = "omit"
FetchCaches* = enum ## https://developer.mozilla.org/docs/Web/API/Request/cache
fchDefault = "default"
fchNoStore = "no-store"
fchReload = "reload"
fchNoCache = "no-cache"
fchForceCache = "force-cache"
FetchRedirects* = enum ## Redirects options.
frFollow = "follow"
frError = "error"
frManual = "manual"
FetchReferrerPolicies* = enum ## Referrer Policy options.
frpNoReferrer = "no-referrer"
frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade"
frpOrigin = "origin"
frpOriginWhenCrossOrigin = "origin-when-cross-origin"
frpUnsafeUrl = "unsafe-url"
Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response
bodyUsed*, ok*, redirected*: bool
typ* {.importjs: "type".}: cstring
url*, statusText*: cstring
status*: cint
headers*: Headers
body*: cstring
Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request
bodyUsed*, ok*, redirected*: bool
typ* {.importjs: "type".}: cstring
url*, statusText*: cstring
status*: cint
headers*: Headers
body*: cstring
func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".}
## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`.
func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".}
## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`.
func newRequest*(url: cstring; fetchOptions: FetchOptions): Request {.importjs: "(new Request(#, #))".}
## Constructor for `Request` with `fetchOptions`. Same as `fetch(url, fetchOptions)`.
func clone*(self: Response | Request): Response {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone
proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/Response/text
proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/Response/json
proc formData*(self: Response): Future[FormData] {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/Response/formData
proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring;
keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring; headers: Headers = newHeaders()): FetchOptions {.importjs:
"{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #, headers: #}".}
## .. warning:: Unsafe `newfetchOptions`.
func newfetchOptions*(metod = HttpGet; body: cstring = nil;
mode = fmCors; credentials = fcSameOrigin; cache = fchDefault; referrerPolicy = frpNoReferrerWhenDowngrade;
keepalive = false; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring,
headers: Headers = newHeaders()): FetchOptions =
## Constructor for `FetchOptions`.
result = FetchOptions(
body: if metod notin {HttpHead, HttpGet}: body else: nil,
mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy),
keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, headers: headers,
metod: (case metod
of HttpHead: "HEAD".cstring
of HttpGet: "GET".cstring
of HttpPost: "POST".cstring
of HttpPut: "PUT".cstring
of HttpDelete: "DELETE".cstring
of HttpPatch: "PATCH".cstring
else: "GET".cstring
)
)
proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".}
## `fetch()` API, simple `GET` only, returns a `Future[Response]`.
proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".}
## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`.
func toCstring*(self: Request | Response | FetchOptions): cstring {.importjs: "JSON.stringify(#)".}
func `$`*(self: Request | Response | FetchOptions): string = $toCstring(self)
runnableExamples("-r:off"):
import std/[asyncjs, jsconsole, jsformdata, jsheaders]
from std/httpcore import HttpMethod
from std/jsffi import JsObject
from std/sugar import `=>`
block:
let options0: FetchOptions = unsafeNewFetchOptions(
metod = "POST".cstring,
body = """{"key": "value"}""".cstring,
mode = "no-cors".cstring,
credentials = "omit".cstring,
cache = "no-cache".cstring,
referrerPolicy = "no-referrer".cstring,
keepalive = false,
redirect = "follow".cstring,
referrer = "client".cstring,
integrity = "".cstring,
headers = newHeaders()
)
assert options0.keepalive == false
assert options0.metod == "POST".cstring
assert options0.body == """{"key": "value"}""".cstring
assert options0.mode == "no-cors".cstring
assert options0.credentials == "omit".cstring
assert options0.cache == "no-cache".cstring
assert options0.referrerPolicy == "no-referrer".cstring
assert options0.redirect == "follow".cstring
assert options0.referrer == "client".cstring
assert options0.integrity == "".cstring
assert options0.headers.len == 0
block:
let options1: FetchOptions = newFetchOptions(
metod = HttpPost,
body = """{"key": "value"}""".cstring,
mode = fmNoCors,
credentials = fcOmit,
cache = fchNoCache,
referrerPolicy = frpNoReferrer,
keepalive = false,
redirect = frFollow,
referrer = "client".cstring,
integrity = "".cstring,
headers = newHeaders()
)
assert options1.keepalive == false
assert options1.metod == $HttpPost
assert options1.body == """{"key": "value"}""".cstring
assert options1.mode == $fmNoCors
assert options1.credentials == $fcOmit
assert options1.cache == $fchNoCache
assert options1.referrerPolicy == $frpNoReferrer
assert options1.redirect == $frFollow
assert options1.referrer == "client".cstring
assert options1.integrity == "".cstring
assert options1.headers.len == 0
block:
let response: Response = newResponse(body = "-. .. --".cstring)
let request: Request = newRequest(url = "http://nim-lang.org".cstring)
if not defined(nodejs):
block:
proc doFetch(): Future[Response] {.async.} =
fetch "https://httpbin.org/get".cstring
proc example() {.async.} =
let response: Response = await doFetch()
assert response.ok
assert response.status == 200.cint
assert response.headers is Headers
assert response.body is cstring
discard example()
block:
proc example2 {.async.} =
await fetch("https://api.github.com/users/torvalds".cstring)
.then((response: Response) => response.json())
.then((json: JsObject) => console.log(json))
.catch((err: Error) => console.log("Request Failed", err))
discard example2()
|