Skip to content

Commit

Permalink
docs: document usage in readme
Browse files Browse the repository at this point in the history
  • Loading branch information
kpietraszko committed Jan 16, 2025
1 parent dcfafd7 commit 0c5b419
Showing 1 changed file with 137 additions and 2 deletions.
139 changes: 137 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,142 @@ npm install schemind
```

## Usage
TODO
### Defining a schema
```typescript
import { buildSchema, withIndex as i } from "schemind";

const personSchema = buildSchema({
id: i(0)<number>(),
fullName: i(1)<string>(),
email: i(2)<string>(),
birthDate: i(3)<Date>(),
address: i(4)({
street: i(0)<string>(),
city: i(1)<string>(),
zipcode: i(2)<string>(),
geo: i(3)({
lat: i(0)<number>(),
lng: i(1)<number>()
})
}),
website: i(5)<string>()
});
```
Every field needs to have its index in the message specified using `withIndex`.
Note that this also goes for nested objects, such as `address`.

* If you accidentally pass the same index twice, or you forget to call `withIndex` on any nested object, `buildSchema` will throw an `InvalidSchemaError`.
* If you forget to call `buildSchema` on your object, you'll get a type error when trying to use your schema.


### Reading from a message
Say you have an incoming message (from network/storage/whatever) like this:
```typescript
const incomingMessage = JSON.parse(`
[
1,
"John Doe",
"[email protected]",
"1973-01-22",
[
"123 Main Street",
"Anytown",
"12345-6789",
[
42.1234,
-71.2345
]
],
"www.johndoe.com"
]`);
```

There are 2 ways to read this message:
#### `toPlainObject`
This is the more convenient option.
```typescript
import { toPlainObject } from "schemind";

const messageAsObject = toPlainObject(incomingMessage, personSchema);
// ^ this has the following type:
// {
// id: number,
// fullName: string,
// email: string,
// birthDate: Date,
// address: {
// street: string,
// city: string,
// zipcode: string,
// geo: {
// lat: number,
// lng: number
// }
// },
// website: string
// }
```
#### `get`
This is the more performant option – it doesn't allocate on the heap.
```typescript
const fullName = get(incomingMessage, personSchema.fullName);
// ^ this is of type string

const latitude = get(incomingMessage, personSchema.address.geo.lat);
// ^ this is of type number
```
Alternatively, you can use the method "get". It works in the exact same way.
```typescript
const fullName = personSchema.fullName.get(incomingMessage);
const latitude = personSchema.address.geo.lat.get(incomingMessage);
```

### Writing
There are 2 ways to write a message.

#### `toIndexedKeysMessage`
```typescript
import { toIndexedKeysMessage } from "schemind";

const objectToSerialize = {
id: 1,
fullName: "John Doe",
email: "[email protected]",
birthDate: new Date(),
address: {
street: "123 Main Street",
city: "Anytown",
zipcode: "12345-6789",
geo: {
lat: 42.1234,
lng: -71.2345
}
},
website: "www.johndoe.com"
};

const message = toIndexedKeysMessage(objectToSerialize, personSchema);
// ^ this is an array that's the same as the "incomingMessage" in the previous section

// JSON.stringify(message) or whatever
```

#### `set`

```typescript
import { set } from "schemind";

const newMessage: unknown[] = [];
set(newMessage, personSchema.fullName, "John Doe");
set(newMessage, personSchema.address.geo.lat, 42.1234);
// ^ this is type-checked
// etc
```
Alternatively, you can use the method "set". It works in the exact same way.
```typescript
personSchema.fullName.set(newMessage, "John Doe");
personSchema.address.geo.lat.set(newMessage, 42.1234);
```

## FAQ
### Shouldn't this be an extension of a serializer?
Expand All @@ -72,7 +207,7 @@ Possibly. But if you're already using JSON / MessagePack / CBOR etc. in your app

Additionally, in some languages (backend or frontend) there's a MessagePack or JSON implementation that's faster, or allocates less memory, than protobuf.

### Why is `get` so inconvenient?
### Why would I use `get` if it's inconvenient?
The `get` function prioritizes performance over convenience. The main goal here is to avoid any heap allocations (beyond what your deserializer allocates). I use *schemind* in performance-critical scenarios, where avoiding GC pauses is crucial.
Use the `toPlainObject` function instead, if you don't mind some extra allocations.

Expand Down

0 comments on commit 0c5b419

Please sign in to comment.