jsfetch.nim 7.4 KB

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