Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [Choosing a database](choosing-a-database.md)
- [Evolving model with migration](evolving-model-with-migration.md)
- [Integrating authentication](integrating-authentication.md)
- [Server-side rendering](server-side-rendering.md)
- [Set up logging](setup-logging.md)
- [Telemetry](telemetry.md)

Expand Down
23 changes: 3 additions & 20 deletions docs/building-your-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,24 +144,7 @@ const post = await del(id);

Since doing CRUD with hooks is already secure, in many cases, you can implement your business logic right in the frontend code.

In case you need to do server-side coding, either through implementing an API endpoint or by using `getServerSideProps` for SSR, you can directly access the database client generated by Prisma:
ZenStack also supports server-side programming for conducting CRUD without sending HTTP requests, or even direct database access (bypassing access policy checks). Please check the following documentation for details:

```ts
import service from '@zenstackhq/runtime';

export const getServerSideProps: GetServerSideProps = async () => {
const posts = await service.db.post.findMany({
where: { published: true },
include: { author: true },
});
return {
props: { posts },
};
};
```

The Typescript types of data models, filters, sorting, etc., are all shared between the frontend and the backend.

_Note_ Server-side database access is **NOT PROTECTED** by access policies. This is by-design so as to provide a way of bypassing the policies. Please make sure you implement authorization properly.

_TBD_ In the future we'll provide a utility for explicitly validating access policies in backend code, so that you can reuse your policy definitions in the model.
- [Server runtime API](runtime-api.md#zenstackhqruntimeserver)
- [Server-side rendering](server-side-rendering.md)
85 changes: 73 additions & 12 deletions docs/runtime-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,22 @@ A `useXXX` API is generated fo each data model for getting the React hooks. The
const { get, find, create, update, del } = useUser();
```

### RequestOptions
### `RequestOptions`

Options controlling hooks' fetch behavior.

```ts
type RequestOptions = {
type RequestOptions<T> = {
// indicates if fetch should be disabled
disabled: boolean;
disabled?: boolean;

// provides initial data, which is immediately available
// before fresh data is fetched (usually used with SSR)
initialData?: T;
};
```

### HooksError
### `HooksError`

Error thrown for failure of `create`, `update` and `delete` hooks.

Expand All @@ -69,7 +73,7 @@ export type HooksError = {
};
```

#### ServerErrorCode
#### `ServerErrorCode`

| Code | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------- |
Expand All @@ -80,7 +84,7 @@ export type HooksError = {
| REFERENCE_CONSTRAINT_VIOLATION | Violation of database reference constraint (aka. foreign key constraints) |
| READ_BACK_AFTER_WRITE_DENIED | A write operation succeeded but the result cannot be read back due to policy control |

### get
### `get`

```ts
function get(
Expand All @@ -90,7 +94,7 @@ function get(
): SWRResponse<User>;
```

### find
### `find`

```ts
function find(
Expand All @@ -99,34 +103,91 @@ function find(
): SWRResponse<User[]>;
```

### create
### `create`

```ts
function create(args?: UserCreateArgs): Promise<User | undefined>;
```

### update
### `update`

```ts
function update(id: string, args?: UserUpdateArgs): Promise<User | undefined>;
```

### del
### `del`

```ts
function del(id: string): Promise<User | undefined>;
function del(id: string, args?: UserDeleteArgs): Promise<User | undefined>;
```

## `@zenstackhq/runtime/server`

This module contains API for server-side programming. The following declarations are exported:

### `default`
### `service`

The default export of this module is a `service` object which encapsulates most of the server-side APIs.

#### Server-side CRUD

The `service` object contains members for each of the data models, each containing server-side CRUD APIs. These APIs can be used for doing CRUD operations without HTTP request overhead, while still fully protected by access policies.

The server-side CRUD APIs have similar signature with client-side hooks, except that they take an extra `queryContext` parameter for passing in the current login user. They're usually used for implementing SSR or custom API endpoints.

- get

```ts
async get(
context: QueryContext,
id: string,
args?: UserFindFirstArgs
): Promise<User | undefined>;
```

- find

```ts
async find(
context: QueryContext,
args?: UserFindManyArgs
): Promise<User[]>;
```

- create

```ts
async find(
context: QueryContext,
args?: UserCreateArgs
): Promise<User>;
```

- update

```ts
async get(
context: QueryContext,
id: string,
args?: UserUpdateArgs
): Promise<User>;
```

- del
```ts
async get(
context: QueryContext,
id: string,
args?: UserDeleteArgs
): Promise<User>;
```

#### Direct database access

The `service.db` object contains a member field for each data model defined, which you can use to conduct database operations for that model.

_NOTE_ These database operations are **NOT** protected by access policies.

Take `User` model for example:

```ts
Expand Down
26 changes: 26 additions & 0 deletions docs/server-side-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Server-side rendering

You can use the `service` object to conduct CRUD operations on the server side directly without the overhead of HTTP requests. The `service` object contains members for each of the data model defined.

The server-side CRUD methods are similar signature with client-side hooks, except that they take an extra `queryContext` parameter for passing in the current login user. Like client-side hooks, the CRUD operations are fully protected by access policies defined in ZModel.

These methods are handy for implementing SSR (or custom API endpoints). Here's an example (using Next-Auth for authentication):

```ts
import service from '@zenstackhq/runtime/server';
import { unstable_getServerSession } from 'next-auth';
...

export const getServerSideProps = async ({
req,
res,
params,
}) => {
const session = await unstable_getServerSession(req, res, authOptions);
const queryContext = { user: session?.user };
const posts = await service.post.find(queryContext);
return {
props: { posts },
};
};
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-monorepo",
"version": "0.3.19",
"version": "0.3.22",
"description": "",
"scripts": {
"build": "pnpm -r build",
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/runtime",
"displayName": "ZenStack Runtime Library",
"version": "0.3.19",
"version": "0.3.22",
"description": "Runtime of ZenStack for both client-side and server-side environments.",
"repository": {
"type": "git",
Expand Down
3 changes: 0 additions & 3 deletions packages/runtime/src/client.ts

This file was deleted.

Loading