https://github.com/westandskif/synclane
synclane is a framework-agnostic RPC API with a smart auto-generated TypeScript client.
https://github.com/westandskif/synclane
python rpc typescript validation
Last synced: 4 months ago
JSON representation
synclane is a framework-agnostic RPC API with a smart auto-generated TypeScript client.
- Host: GitHub
- URL: https://github.com/westandskif/synclane
- Owner: westandskif
- License: mit
- Created: 2024-02-13T21:10:44.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-05-11T20:33:18.000Z (11 months ago)
- Last Synced: 2025-11-28T02:11:24.252Z (4 months ago)
- Topics: python, rpc, typescript, validation
- Language: Python
- Homepage: http://synclane.readthedocs.io
- Size: 146 KB
- Stars: 8
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: docs/README.md
- License: LICENSE.txt
- Security: .github/SECURITY.md
Awesome Lists containing this project
README
# Welcome to synclane
`synclane` is a framework-agnostic RPC API with a smart auto-generated
TypeScript client.
[](https://github.com/westandskif/synclane/blob/master/LICENSE.txt)
[](https://codecov.io/gh/westandskif/synclane)
[](https://github.com/westandskif/synclane/actions/workflows/pytest.yml)
[](https://synclane.readthedocs.io/en/latest/?badge=latest)
[](https://pypi.org/project/synclane/)
[](https://pepy.tech/project/synclane)
[](https://pypi.org/project/synclane/)
## Idea
The below must be enough to define an API:
```python
class UserParams(pydantic.BaseModel):
uid: str
class GetUsers(AbstractProcedure):
def call(self, in_: UserParams, context) -> List[UserDetails]:
...
```
and use an automatically generated frontend TypeScript client:
```typescript
import { callGetUsers } from "./src/out";
expect(callGetUsers(userParams).$promise).resolves.toEqual(listOfUserDetails);
```
## Benefits
#### Automated typescript client generation
Of course, it's possible to annotate your API, export an OpenAPI schema and
generate a typescript client from it. However it will lack the below nice bits.
#### Browser Dates done right
Javascript doesn't have a separate `date` type, so it uses `Date` for both
python's `date` and `datetime`.
Hence when you pass `2000-01-01` to a browser in New York, the browser will
read it as UTC datetime and then convert it to the local timezone, so it will
give you Dec 31, 1999 7:00PM, which is fine if you wanted to work with a
particular moment in time, but what if you wanted to display someone's date of
birth? That's why lacking date type is a problem.
`synclane` will see that you wanted to pass python's `date` to the browser and
will automatically prepare it in the browser, so that Jan 1st is preserved in
the case above.
#### Browser friendly types only
`synclane` raises an exception if you use types, which browser won't be able to
understand.
#### No need to define URLs
Once you name a procedure, e.g. `AddUser`, you just get `callAddUser` function
in the typescript client. You don't need to define any other identifier like
API endpoint url.
#### Enums
If your procedure in/out types include enums, they will become available in the
typescript client.
## Installation
```bash
pip install synclane
```
[pydantic](https://github.com/pydantic/pydantic) is the only dependency.
## Usage
1. define procedures
1. define RPC instance, its error handling method, register procedures and dump
TypeScript client code
1. connect RPC to an API
1. on TypeScript side: import `rpcConfig` and initialize:
- `rpcConfig.url`: url where RPC is listening
- `rpcConfig.initFetch` (optional): function, which accepts and can mutate
[fetch options](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
as needed
## Example
#### Step 1: Define procedures
```python
--8<-- "tests/int_tst/main.py:def_procedures"
```
#### Step 2: Define RPC, dump TS
```python
--8<-- "tests/int_tst/main.py:def_rpc"
```
#### Step 3.a: Connect to Django
/// tab | async rpc
```python
--8<-- "tests/int_tst/main.py:django_async"
```
///
/// tab | sync rpc
```python
--8<-- "tests/int_tst/main.py:django_sync"
```
///
#### Step 3.b: Connect to FastAPI
/// tab | async rpc
```python
--8<-- "tests/int_tst/main.py:fastapi_async"
```
///
/// tab | async rpc
```python
--8<-- "tests/int_tst/main.py:fastapi_sync"
```
///
#### Step 4: Use autogenerated TS client
```typescript
--8<-- "tests/int_tst/tests/client.test.ts:imports"
--8<-- "tests/int_tst/tests/client.test.ts:rpc_config"
--8<-- "tests/int_tst/tests/client.test.ts:get_user"
```