Skip to content

Commit

Permalink
Merge pull request #121 from roqtech/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
junwatu authored Nov 20, 2023
2 parents 0f27426 + 0016a0f commit c0ad884
Show file tree
Hide file tree
Showing 48 changed files with 722 additions and 493 deletions.
56 changes: 18 additions & 38 deletions pages/development/rest-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,41 +89,33 @@ ROQ_API_URL=https://book-story-v1-ca2b-baas.vercel.app
## How to get the user token

Whenever a user successfully logs in, a user token is generated. This token is used to identify the user for subsequent
requests made to the ROQ BaaS.
requests made to the ROQ BaaS. To access the ROQ BaaS REST API, you need an authorization token known as `roq-session-token`. In a Next.js application, you can programmatically retrieve the user token for authorization. There are a few ways to get the user token. You can get the token programmatically, or you can get it manually for fast testing and development.

To access the ROQ BaaS REST API, you need an authorization token known as `roq-session-token`. In a Next.js application,
you can programmatically retrieve the user token for authorization. This token, by default, is stored in a browser
cookie with the `HttpOnly` flag, which means it cannot be directly accessed using JavaScript on the client side.

There are a few ways to get the user token. You can get the token programmatically, or you can get it manually for fast
testing and development.
<Callout type="info">
Using ROQ SDK will automatically get the user token for you. Its rarely needed to get the token manually except for testing and development purposes.
</Callout>

### Programmatically

In the Next.js application, we can use the `getServerSideProps()` method to get the token. The `getServerSideProps()`
function allows you to fetch data server-side per request and use it as props in components.

```tsx
export async function getServerSideProps(context) {
const {req} = context;
const cookies = req.headers.cookie?.split(';');
const tokenCookie = cookies?.find(cookie => cookie.trim().startsWith('roq-session-token='));
const token = tokenCookie?.split('=')[1];

return {
props: {token}
};
}
```
The user token, by default, is stored in a browser cookie with the `HttpOnly` flag to `false`, which means you can directly accessed using JavaScript on the client side. In the latest Next.js version, we can use the `document.cookie` method directly to get the token. However, this method only available on the client side with `use client` directive.

This is the full example code on how to get the `roq-session-token` and use the token to query book count data.

```tsx filename="book.tsx" {21, 53-63}
import {useRoqClient} from 'lib/roq'
import {requireNextAuth, useSession} from '@roq/nextjs'
"use client"

import {roqBrowserClient} from "@/lib/roq/roq-client";
import {useSession} from '@/lib/roq'
import {useEffect, useState} from 'react'

export default function Book({token}) {
function getCookie(name) {
const cookies = document.cookie.split('; ');
const cookieValue = cookies.find(cookie => cookie.startsWith(name + '='));
return cookieValue ? cookieValue.split('=')[1] : null;
}

export default function Book() {
const token = getCookie('roq-session-token')
return <AuthenticatedSection token={token}/>
}

Expand Down Expand Up @@ -170,17 +162,6 @@ const AuthenticatedSection = ({token}) => {
</div>
)
}

export async function getServerSideProps(context) {
const {req} = context;
const cookies = req.headers.cookie?.split(';');
const tokenCookie = cookies?.find(cookie => cookie.trim().startsWith('roq-session-token='));
const token = tokenCookie?.split('=')[1];

return {
props: {token}
};
}
```

### Manually
Expand Down Expand Up @@ -224,8 +205,7 @@ necessary request authorization using **Bearer Token**. Remember to add token

## Disabling the `HttpOnly` flag

To disable the `HttpOnly` flag, you can modify the environment variable. This action will allow you to access the cookie
directly using JavaScript.
To enable or disable the `HttpOnly` flag, you can modify the environment variable. This action will allow or dissallow you to access the cookie directly using JavaScript.

Step-by-step guide:

Expand Down
50 changes: 17 additions & 33 deletions pages/development/sdk/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,52 @@ import { Callout } from 'nextra/components'

## Introduction

ROQ provides a complete and secure authentication solution for SaaS application development.
The generated SDK provides a set of APIs to authenticate users. All authentication APIs on the client side are live by default in the `/lib/roq` folder, and all authentication APIs are accessible through the ROQ client `roqBrowserClient` API.

Key Features:
To use it in your application, you need to import it from the SDK:

- Customizable user login and registration (customization on ROQ Console)
- Full multi-tenancy support (e.g., creation of new organizations during sign-up)
- Secure session handling for your application
- Social sign-in with Google, Github, or Facebook (setup on ROQ Console)
- Multi-factor Authentication or MFA (setup on ROQ Console)
- User invites
- 360-degree view of all user data

## How It Works

ROQ's authentication and session management are based on the [OAuth protocol](https://oauth.net/2/). Here is how it works:

1. The user clicks the Login button or is redirected to a login page provided by ROQ.
2. ROQ prompts the user to log in using username and password or social sign-ins (e.g., Google) and then sends a special token back indicating that the user has granted permission to access your web application.
3. Using the authentication wrapper such as `requireNextAuth` for Next.js, you can easily define which pages are only accessible to authenticated users.
```tsx
import { roqBrowserClient } from '@/lib/roq/roq-client';
```

## API
The API will redirecting users to the sign-in and sign-up pages and clean the session upon logout.

All authentication APIs on the client side are live by default in the `/lib/roq` folder, and all authentication APIs are accessible through the ROQ client `useRoqClient()` method.
<Callout type="info">
For more information about user registration or authentication, check out this [documentation](/user-registration) section.
</Callout>

The front-end SDK provides authentication helper functions for redirecting users to the sign-in and sign-up pages and a function to clean the session upon logout.
## Code examples

Here is the summary of the most important API for authentication:
Here is code examples for the most important API for authentication: **signIn**, **signUp**, and **logout**.

### `signIn`

Redirect users to the sign-in page.

```tsx
import {useRoqClient} from "/lib/roq"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

roqClient.signIn()
roqBrowserClient.signIn()
```

### `signUp`

Redirect users to the sign-up page.

```tsx
import {useRoqClient} from "/lib/roq"
import { roqBrowserClient } from '@/lib/roq/roq-client';

const roqClient = useRoqClient()

roqClient.signUp()
roqBrowserClient.signUp()
```

### `logout`

Clean the session.

```tsx
import {useRoqClient} from "/lib/roq"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

roqClient.logout()
roqBrowserClient.logout()
```

## Framework integration
Expand Down
6 changes: 6 additions & 0 deletions pages/development/sdk/emails.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {Callout} from 'nextra/components'

# Emails

## Introduction
Expand All @@ -24,6 +26,10 @@ const sendMyMail = async() => {
}
```

<Callout type="info">
In the framework such as Next.js. We only can use the `sendMail()` on the server side. Any method from `RoqServerClient` is always executed on the server side.
</Callout>

To be able to send an email to a user, we also need to create a mail template in the [ROQ Console](https://console.roq.tech). The mail template is a simple HTML template with some variables. The variables are replaced with the data that we send in the `sendMail()` method. Please check the [mail template](/mails/email-templates-and-customization) section for more information about the mail template.

## Frameworks integration
Expand Down
10 changes: 3 additions & 7 deletions pages/development/sdk/file-handling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ The front-end SDK provides the necessary API for file handling on the client sid
To use these APIs, you need to import it first.

```tsx
import { useRoqClient } from "lib/roq"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';
```

And because file handling APIs are ROQ platform features, they live not in specific entities but in the ROQ platform.
Expand All @@ -36,12 +34,10 @@ Let's take an example of how to list all uploaded files:
<Tabs items={["TypeScript", "Response"]}>
<Tab>
```tsx
import { useRoqClient } from "lib/roq"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

const getFiles = async () => {
return await roqClient.roqPlatform.files({})
return await roqBrowserClient.roqPlatform.files({})
}
```
</Tab>
Expand Down
41 changes: 16 additions & 25 deletions pages/development/sdk/query-syntax.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ The query syntax used in BaaS is quite similar to the syntax used in the Prisma
**ROQ**

```tsx filename="ROQ Query"
roqClient.book.create(data, options);
roqClient.book.createMany(data, options);
roqBrowserClient.book.create(data, options);
roqBrowserClient.book.createMany(data, options);
```
**Prisma**

Expand All @@ -23,7 +23,7 @@ prisma.book.createMany({ data });
From the comparison, we know that both offer single and bulk creation methods and have the same pattern query syntax:

```shell
roqClient.[entity].[operation](condition)
roqBrowserClient.[entity].[operation](condition)
```

- **entity**: Project entity representing a specific data model or table. For example: `book`, `review`, `chapter`, etc.
Expand All @@ -37,7 +37,7 @@ The front-end SDK also provides API for accessing ROQ Platform features such as
The query syntax for API:

```tsx
roqClient.roqPlatform.[operation](args)
roqBrowserClient.roqPlatform.[operation](args)
```
The operation could be: `userInvite`, `userInvites`, `role`, `roles`, `file`, `files`, `tenant`, and many others.

Expand All @@ -51,14 +51,13 @@ For this example and to provide context, we will use Book Creators. An imaginary
Suppose we want to create a chapter for a specific book title, and for the return data, we also want to include all the book data. We can create a new chapter using the `create()` API.

```tsx
import {useRoqClient} from '/lib/roq'
import { roqBrowserClient } from '@/lib/roq/roq-client';
import { v4 as uuidv4 } from 'uuid';

const roqClient = useRoqClient()
const uuid = uuidv4()

const createChapter = async() => {
const chapter = await roqClient.chapter.create({
const chapter = await roqBrowserClient.chapter.create({
data: {
id: `${uuid}`,
title: "Take All Your Pride",
Expand All @@ -81,13 +80,10 @@ By using the keyword `include` for the entity `book`, the data response will als
We can also update the existing data. For example, suppose we want to update a chapter's content. We can use the `update()` API.

```tsx

import {useRoqClient} from '/lib/roq'

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

const updateChapter = async() => {
const updateChap = await roqClient.chapter.update({
const updateChap = await roqBrowserClient.chapter.update({
data: {
content: "Another update draft content is here",
},
Expand All @@ -111,7 +107,7 @@ The `delete()` API can be used to delete specific data on any entity. For exampl

```tsx
const delReview = async() => {
const status = await roqClient.review.delete({
const status = await roqBrowserClient.review.delete({
where:{
id: "78e99df7-30d1-4205-b88f-6e804d1ec0fc"
}
Expand All @@ -127,7 +123,7 @@ To get how many users are already registered or currently active on the project,

```tsx
const userCount = async() => {
const count = await roqClient.user.count({
const count = await roqBrowserClient.user.count({
orderBy: {
created_at: 'desc'
}
Expand All @@ -144,7 +140,7 @@ Suppose we want to find a book with the genre containing the "Science" word. We

```tsx
const findBook = async() =>{
const bookData = await roqClient.book.findMany({
const bookData = await roqBrowserClient.book.findMany({
where: {
genre: {
contains: "Science"
Expand All @@ -166,12 +162,10 @@ The `findManyWithCount()` will fetch multiple records and their count data.
Let's say we want to retrieve books along with their review and user data. We can use the `include` and `user` keys to also include the user data.

```tsx
import { useRoqClient } from 'lib/roq'

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

const dataBook = async() => {
const allBook = await roqClient.book.findManyWithCount({
const allBook = await roqBrowserClient.book.findManyWithCount({
orderBy: { created_at: 'desc' },
include: {
review: {
Expand All @@ -198,7 +192,7 @@ To retrieve all user profiles, we can use the `userProfiles()` API that specific

```tsx
const users = async () => {
const profiles = await roqClient.roqPlatform.userProfiles();
const profiles = await roqBrowserClient.roqPlatform.userProfiles();
return profiles;
}
```
Expand All @@ -214,7 +208,7 @@ To get all files from the users, we can use the `files()` API.

```tsx
const userFiles = async() => {
const allFiles = await roqClient.roqPlatform.files()
const allFiles = await roqBrowserClient.roqPlatform.files()
return allFiles
}
```
Expand All @@ -225,7 +219,7 @@ Suppose we have a user ID `8b249b07-bdd9-43f7-a10e-ace10f27ff66` and want to kno

```tsx
const userRoles = async() => {
const roles = await roqClient.roqPlatform.roles({
const roles = await roqBrowserClient.roqPlatform.roles({
filter: {
userId: {
equalTo: "8b249b07-bdd9-43f7-a10e-ace10f27ff66"
Expand All @@ -239,6 +233,3 @@ const userRoles = async() => {
The example response from the code above is:

![user roles filter response](/screenshots/user-roles-filter.png)


{/* roqPlatform examples*/}
14 changes: 5 additions & 9 deletions pages/development/sdk/user-invites.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@ ROQ SDK for BaaS provides a comprehensive user invite solution to be used alongs

The user invitation API is part of the ROQ Platform and it also generated by the `@roq/cli` tool on the client side for easy integration with whichever framework you use. On the client side the generated SDK are live by default in the `/lib/roq` folder.

The user invitation API can be accessed through the `roqPlatform` property of the ROQ client, that's the `useRoqClient()` method. So to use it, we can import it like this:
The user invitation API can be accessed through the `roqPlatform` property of the ROQ client, that's the `roqBrowserClient` method. So to use it, we can import it like this:

```tsx
import { useRoqClient } from "lib/roq"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';
```

To use any user invitation API we can call it like this:

```tsx
await roqClient.roqPlatform.someUserInvitationMethod()
await roqBrowserClient.roqPlatform.someUserInvitationMethod()
```

## Code examples
Expand All @@ -29,15 +27,13 @@ If we want to invite a user, then we should use the `sendUserInvites()`. This me
This example shows how to invite a user using the `sendUserInvites()` method:

```tsx
import { useRoqClient } from "@roq/nextjs"

const roqClient = useRoqClient()
import { roqBrowserClient } from '@/lib/roq/roq-client';

const tenantId = "tenant_id_here"
const roqUserId = "roq_user_id"

const sendInvitation = async () => {
const status = await roqClient.roqPlatform.sendUserInvites({
const status = await roqBrowserClient.roqPlatform.sendUserInvites({
userInvites: {
tenantId: tenantId,
userInvites: [{
Expand Down
Loading

1 comment on commit c0ad884

@vercel
Copy link

@vercel vercel bot commented on c0ad884 Nov 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.