riseact-node-sdk
Riseact SDK for Node.js to develop Riseact apps.
Installation
npm install @riseact/riseact-node-sdk
yarn add @riseact/riseact-node-sdk
Peer dependencies
Install the following peer dependencies if you don't have them already:
npm install express@^4.18
yarn add express@^4.18
Usage
Refer to riseact-app-template-node repo for a complete example of how to use this SDK in a Node.js app.
const RiseactConfig: RiseactConfig = {
auth: {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
},
storage: {
type: 'file',
},
network: {
appPublicUrl: process.env.RISEACT_APP_URL,
},
...(process.env.NODE_ENV === 'development' && {
dev: {
viteConfig: {
root: path.join(process.cwd(), '../client'),
configFile: path.join(process.cwd(), '../client/vite.config.ts'),
},
},
}),
};
async function createServer() {
const app: Express = express();
const riseact = await initRiseactSDK(RiseactConfig);
const publicEpsRouter = express.Router();
publicEpsRouter.use(
riseact.network.registerWebhook(WebhookEventTopic.SupporterCreated, (data) => {
console.log('Webhook received', data);
}),
);
const privateEpsRouter = express.Router();
privateEpsRouter.get('/api/hello', (req, res) => {
res.send('Hello World!');
});
privateEpsRouter.get('/api/organization-info', OrganizationInfoHandler(riseact));
privateEpsRouter.get('/api/organization-credentials', OrganizationCredentialsHandler());
riseact.utils.startRiseactApp(app, riseact, {
publicRouter: publicEpsRouter,
protectedRouter: privateEpsRouter,
serverPort: process.env.SERVER_PORT ? parseInt(process.env.SERVER_PORT) : 3000,
});
}
createServer();
GQL Client
For information about the Riseact GQL Schema, explore it yourself with ApolloStudio typing https://core.riseact.org/admin/graphql/
const ORGANIZATION_INFO_QUERY = graphql(`
query GetOrganizationInfo {
organization {
name
logo {
square
}
}
}
`);
export const OrganizationInfoHandler = async (req, res) => {
const graphqlClient = await req.riseact.network.createGqlClient(req.organizationDomain);
const { data, error } = await graphqlClient.query<OrganizationInfoResponseQuery>({
query: ORGANIZATION_INFO_QUERY,
});
if (error) {
return res.status(500);
}
res.json({
name: data.organization.name,
logoUrl: data.organization.logo?.square,
});
}
Development
Authentication Architecture
-
Actors
- Client: browser SPA, server inside the Riseact Admin application iframe
- App backend: Node server under your control
- Riseact API: external GraphQL service with OAuth 2 (Authorization-Code + PKCE).
-
Auth flow
- App client hits an app backend route; the backend redirects it to Riseact’s authorize endpoint with code-challenge.
- Riseact calls the backend callback with the OAuth credentials.
- Backend generates a long-lasting client token, stores it together with Riseact credentials, and returns the client token to the browser as a cookie.
-
Requests flow from the client
- App client sends a GraphQL HTTP request to the App backend, attaching its client token cookie.
- Backend looks up the stored Riseact credentials by that token and forwards the original request to Riseact adding
Authorization: Bearer <access_token>
.
- Riseact answers; backend streams the response back to the browser unchanged.
-
Transparent token-renewal loop
- If Riseact returns an auth error (token expired or revoked), the backend:
- Performs a single-flight refresh with the stored
refresh_token
.
- Retries the identical request with the new
access_token
.
- On success, relays the data to the client; on failure (refresh denied) it redirects the browser to restart the OAuth flow.
- This approach covers both normal expiry and out-of-band revocation without relying on local expiry timestamps.
-
Two query surfaces available
Auth flow
Authentication Flow
authMiddleware
secures every request originating from the Riseact Admin iframe, which always appends __organization=<org-domain>
to the URL.
The auth middleware does the following:
Stored credential model
access_token | Bearer token for Riseact GraphQL calls |
refresh_token | Used to renew the access token |
client_token | Long-lived identifier for the browser |
expires_in | TTL of the current access token (seconds) |
expires_at | UTC timestamp when the token expires |
organization_domain | Tenant identifier |
createGqlClient
consumes these credentials to build an authenticated Apollo client for Riseact. It automatically handles token renewal and retries failed requests.
Riseact errors
Riseact backend do not throw errors. If user is not authenticated, it respond with a 200 with this payload:
{
"data": null,
"errors": [
{
"message": "User is not authenticated",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"path"
]
}
]
}
SDK Development
Create a .env
file in the root of your project and add the following lines:
CLIENT_ID=<your-app-client-id>
CLIENT_SECRET=<your-app-client-secret>
Run the development server with:
riseact app dev
Now the changes you make to the SDK will be reflected in the dev app.
License
MIT