jsfetch.nim 7.7 KB

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