Skip to content

Commit

Permalink
SWR support
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Feb 19, 2025
1 parent 5f7eed9 commit e37b3d2
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 8 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,21 @@ let result = await Contract.getPosts->Rest.fetch(
) // ℹ️ It'll do a GET request to http://localhost:3000/posts?skip=0&take=10 with the `{"x-pagination-page": "1"}` headers
```

Fulfil the contract on your sever, with a type-safe Fastify integration:
Or use the [SWR](https://swr.vercel.app/) client-side integration and consume your data in React components:

```rescript
@react.component
let make = () => {
let posts = Contract.getPosts->Swr.use(~input={"skip": 0, "take": 10, "page": Some(1)})
switch posts {
| {error: Some(_)} => "Something went wrong!"->React.string
| {data: None} => "Loading..."->React.string
| {data: Some(posts)} => <Posts posts />
}
}
```

Fulfil the contract on your sever, with a type-safe Fastify or Next.js integrations:

```rescript
let app = Fastify.make()
Expand Down Expand Up @@ -307,6 +321,33 @@ let ping = Rest.route(() => {
})
```

## Client-side Integrations

### [SWR](https://swr.vercel.app/)

React Hooks for Data Fetching - With SWR, components will get a stream of data updates constantly and automatically.
And the UI will be always fast and reactive.

```rescript
@react.component
let make = () => {
let posts = Contract.getPosts->Swr.use(~input={"skip": 0, "take": 10, "page": Some(1)})
switch posts {
| {error: Some(_)} => "Something went wrong!"->React.string
| {data: None} => "Loading..."->React.string
| {data: Some(posts)} => <Posts posts />
}
}
```

It'll automatically refetch the data when the input parameters change. (⚠️ Currently supported only for `query` and `path` fields)

#### Current Limitations

- Supports only `useSwr` hook with GET method routes
- Header field updates don't trigger refetching
- Please create a PR to extend available bindings

## Server-side Integrations

### [Next.js](https://nextjs.org/)
Expand Down Expand Up @@ -349,7 +390,7 @@ let posts = await Contract.getPosts->Rest.fetch(
)
```

#### Known Limitations
#### Current Limitations

- Doesn't support path parameters
- Doesn't support raw body
Expand Down Expand Up @@ -384,7 +425,7 @@ let _ = app->Fastify.listen({port: 3000})

> 🧠 `rescript-rest` ships with minimal bindings for Fastify to improve the integration experience. If you need more advanced configuration, please open an issue or PR.
#### Known Limitations
#### Current Limitations

- Doesn't support array/object-like query params
- Has issues with paths with `:`
Expand Down
58 changes: 54 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rescript-rest",
"version": "2.0.0-rc.1",
"version": "2.0.0-rc.2",
"description": "😴 ReScript RPC-like client, contract, and server implementation for a pure REST API",
"keywords": [
"rest",
Expand Down Expand Up @@ -47,17 +47,22 @@
"ava": "6.2.0",
"c8": "10.1.2",
"fastify": "5.1.0",
"swr": "2.3.2",
"rescript": "11.1.4",
"rescript-schema": "9.0.1"
},
"peerDependencies": {
"@fastify/swagger": "9.x",
"@scalar/fastify-api-reference": "^1.25.60",
"fastify": "5.x",
"swr": "^2.3.2",
"rescript": "11.x",
"rescript-schema": "^9.0.0"
},
"peerDependenciesMeta": {
"swr": {
"optional": true
},
"fastify": {
"optional": true
},
Expand Down
32 changes: 32 additions & 0 deletions src/Swr.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
type return<'data> = {
data: option<'data>,
error: option<exn>,
isLoading: bool,
isValidating: bool,
mutate: unknown,
}

@module("swr")
external useSwrInternal: (Js.Null.t<string>, string => promise<'data>) => return<'data> = "default"

let use = (route, ~input=?, ~baseUrl=?) => {
let {definition} = route->Rest.params
if definition.method !== Get {
Js.Exn.raiseError(`[rescript-rest] Only GET requests are supported by Swr`)
}
useSwrInternal(
switch input {
| Some(input) => Value(route->Rest.url(input, ~baseUrl?))
| None => Null
},
_ => {
route->Rest.fetch(
switch baseUrl {
| Some(url) => url
| None => ""
},
input->Belt.Option.getExn,
)
},
)
}
21 changes: 21 additions & 0 deletions src/Swr.res.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/Swr.resi
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type return<'data> = {
data: option<'data>,
error: option<exn>,
isLoading: bool,
isValidating: bool,
mutate: unknown,
}

let use: (Rest.route<'input, 'response>, ~input: 'input=?, ~baseUrl: string=?) => return<'response>

0 comments on commit e37b3d2

Please sign in to comment.