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()
|